[med-svn] [libssj-java] 07/09: New upstream version 2.6
Andreas Tille
tille at debian.org
Tue Dec 26 12:44:45 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository libssj-java.
commit 11ac960e3cf131876528a356a3ed0e552e9dfbc8
Author: Andreas Tille <tille at debian.org>
Date: Tue Dec 26 13:42:14 2017 +0100
New upstream version 2.6
---
COMPILE.txt | 186 ++
INSTALL.txt | 162 +
NEWS | 533 ++++
README.txt | 15 +
Ssj.bat | 9 +
Ssj.sh | 29 +
Ssjrc | 56 +
bin/Ssj.bat | 4 +
bin/Ssj.sh | 13 +
bin/Ssjrc | 17 +
build.xml | 641 ++++
build/interpreter.properties | 23 +
.../umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat | 20 +
.../umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat | 45 +
.../umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat | 35 +
.../hups/dataSer/Nieder/NiedSequenceBase2.ser | Bin 0 -> 38187 bytes
.../dataSer/Nieder/NiedXingSequenceBase2Trans.ser | Bin 0 -> 62667 bytes
build/umontreal/iro/lecuyer/rng/GenF2w32.dat | Bin 0 -> 192284 bytes
build/umontreal/iro/lecuyer/rng/WELL1024.dat | Bin 0 -> 303388 bytes
build/umontreal/iro/lecuyer/rng/WELL512.dat | Bin 0 -> 86300 bytes
build/umontreal/iro/lecuyer/rng/WELL607.dat | Bin 0 -> 117020 bytes
cdssj.bat | 2 +
cdssjsrc.bat | 2 +
debian/README.source | 8 -
debian/bin/pdflatex | 7 -
debian/changelog | 24 -
debian/compat | 1 -
debian/control | 52 -
debian/copyright | 17 -
debian/libssj-java-doc.docs | 1 -
debian/patches/fix_build_script | 22 -
debian/patches/fix_pdflatex | 24 -
debian/patches/series | 3 -
debian/patches/set_build_properties | 21 -
debian/rules | 32 -
debian/source/format | 1 -
debian/watch | 2 -
lib/Blas.jar | Bin 0 -> 4406 bytes
lib/colt.jar | Bin 0 -> 581945 bytes
lib/event-1.6.5.jar | Bin 0 -> 38779 bytes
lib/interpreter-1.6.8.jar | Bin 0 -> 253076 bytes
lib/interpreter.properties | 23 +
lib/jcommon-1.0.13.jar | Bin 0 -> 307860 bytes
lib/jfreechart-1.0.10.jar | Bin 0 -> 1311979 bytes
lib/language-1.6.7.jar | Bin 0 -> 59318 bytes
lib/librandvar.so | Bin 0 -> 583728 bytes
lib/libssjutil.so | Bin 0 -> 4216 bytes
lib/logger-1.6.4.jar | Bin 0 -> 32221 bytes
lib/optimization.jar | Bin 0 -> 46524 bytes
lib/randvar.dll | Bin 0 -> 466944 bytes
lib/ssj.jar | Bin 0 -> 1200499 bytes
lib/ssjutil.dll | Bin 0 -> 6656 bytes
lib/tcode.jar | Bin 0 -> 6644 bytes
lib64/libssjutil.so | Bin 0 -> 4544 bytes
setl2hinit.pl | 71 +
source/interpreter.properties | 23 +
source/overview.bbl | 46 +
source/overview.tex | 388 +++
source/packdep.tex | 170 ++
source/ssj.perl | 558 ++++
source/ssj.sty | 171 ++
source/umontreal/iro/lecuyer/charts/Axis.java | 500 ++++
source/umontreal/iro/lecuyer/charts/Axis.tex | 517 ++++
source/umontreal/iro/lecuyer/charts/BoxChart.java | 324 ++
source/umontreal/iro/lecuyer/charts/BoxChart.tex | 332 +++
.../iro/lecuyer/charts/BoxSeriesCollection.java | 326 ++
.../iro/lecuyer/charts/BoxSeriesCollection.tex | 341 +++
.../iro/lecuyer/charts/CategoryChart.java | 230 ++
.../umontreal/iro/lecuyer/charts/CategoryChart.tex | 242 ++
.../iro/lecuyer/charts/ContinuousDistChart.java | 199 ++
.../iro/lecuyer/charts/ContinuousDistChart.tex | 204 ++
.../iro/lecuyer/charts/CustomHistogramDataset.java | 711 +++++
.../iro/lecuyer/charts/DiscreteDistIntChart.java | 306 ++
.../iro/lecuyer/charts/DiscreteDistIntChart.tex | 308 ++
.../iro/lecuyer/charts/EmpiricalChart.java | 368 +++
.../iro/lecuyer/charts/EmpiricalChart.tex | 372 +++
.../iro/lecuyer/charts/EmpiricalRenderer.java | 217 ++
.../lecuyer/charts/EmpiricalSeriesCollection.java | 443 +++
.../lecuyer/charts/EmpiricalSeriesCollection.tex | 458 +++
.../iro/lecuyer/charts/HistogramChart.java | 472 +++
.../iro/lecuyer/charts/HistogramChart.tex | 470 +++
.../lecuyer/charts/HistogramSeriesCollection.java | 686 +++++
.../lecuyer/charts/HistogramSeriesCollection.tex | 701 +++++
.../iro/lecuyer/charts/MultipleDatasetChart.java | 518 ++++
.../iro/lecuyer/charts/MultipleDatasetChart.tex | 536 ++++
source/umontreal/iro/lecuyer/charts/PPPlot.java | 167 ++
source/umontreal/iro/lecuyer/charts/PPPlot.tex | 155 +
.../umontreal/iro/lecuyer/charts/PlotFormat.java | 489 +++
source/umontreal/iro/lecuyer/charts/PlotFormat.tex | 491 +++
source/umontreal/iro/lecuyer/charts/QQPlot.java | 171 ++
source/umontreal/iro/lecuyer/charts/QQPlot.tex | 161 +
.../charts/SSJCategorySeriesCollection.java | 294 ++
.../lecuyer/charts/SSJCategorySeriesCollection.tex | 306 ++
.../iro/lecuyer/charts/SSJXYSeriesCollection.java | 353 +++
.../iro/lecuyer/charts/SSJXYSeriesCollection.tex | 361 +++
.../umontreal/iro/lecuyer/charts/ScatterChart.java | 486 +++
.../umontreal/iro/lecuyer/charts/ScatterChart.tex | 485 +++
source/umontreal/iro/lecuyer/charts/XYChart.java | 592 ++++
source/umontreal/iro/lecuyer/charts/XYChart.tex | 599 ++++
.../umontreal/iro/lecuyer/charts/XYLineChart.java | 555 ++++
.../umontreal/iro/lecuyer/charts/XYLineChart.tex | 545 ++++
.../iro/lecuyer/charts/XYListSeriesCollection.java | 891 ++++++
.../iro/lecuyer/charts/XYListSeriesCollection.tex | 898 ++++++
.../umontreal/iro/lecuyer/charts/YListChart.java | 201 ++
source/umontreal/iro/lecuyer/charts/YListChart.tex | 199 ++
.../iro/lecuyer/charts/YListSeriesCollection.java | 237 ++
.../iro/lecuyer/charts/YListSeriesCollection.tex | 235 ++
.../umontreal/iro/lecuyer/charts/exam/BoxTest.java | 25 +
.../umontreal/iro/lecuyer/charts/exam/BoxTest.png | Bin 0 -> 8162 bytes
.../iro/lecuyer/charts/exam/ChartTest1.java | 45 +
.../iro/lecuyer/charts/exam/ChartTest1.tex | 262 ++
.../iro/lecuyer/charts/exam/ChartTest2.java | 67 +
.../iro/lecuyer/charts/exam/ChartTest2.tex | 101 +
.../iro/lecuyer/charts/exam/ContDistPlot.java | 11 +
.../iro/lecuyer/charts/exam/DistIntTest.java | 17 +
.../iro/lecuyer/charts/exam/DistIntTest.tex | 69 +
.../lecuyer/charts/exam/EmpiricalChartTest.java | 44 +
.../iro/lecuyer/charts/exam/EmpiricalChartTest.tex | 62 +
.../lecuyer/charts/exam/HistogramChartTest.java | 45 +
.../iro/lecuyer/charts/exam/HistogramChartTest.tex | 79 +
.../iro/lecuyer/charts/exam/HistogramTest1.java | 32 +
.../iro/lecuyer/charts/exam/HistogramTest1.tex | 100 +
.../iro/lecuyer/charts/exam/NormalChart.java | 26 +
.../iro/lecuyer/charts/exam/NormalChart.tex | 433 +++
.../umontreal/iro/lecuyer/charts/guidecharts.bbl | 3 +
.../umontreal/iro/lecuyer/charts/guidecharts.tex | 71 +
source/umontreal/iro/lecuyer/charts/overview.tex | 371 +++
source/umontreal/iro/lecuyer/charts/title.tex | 42 +
source/umontreal/iro/lecuyer/examples/Asian.java | 74 +
source/umontreal/iro/lecuyer/examples/Asian.res | 7 +
.../umontreal/iro/lecuyer/examples/AsianQMC.java | 66 +
source/umontreal/iro/lecuyer/examples/AsianQMC.res | 21 +
.../umontreal/iro/lecuyer/examples/AsianQMCk.java | 67 +
source/umontreal/iro/lecuyer/examples/Bank.res | 8 +
source/umontreal/iro/lecuyer/examples/Bank.tex | 167 ++
source/umontreal/iro/lecuyer/examples/BankEv.java | 114 +
.../umontreal/iro/lecuyer/examples/BankProc.java | 88 +
.../umontreal/iro/lecuyer/examples/Cafeteria.java | 160 +
.../umontreal/iro/lecuyer/examples/CallCenter.dat | 46 +
.../umontreal/iro/lecuyer/examples/CallCenter.java | 197 ++
.../umontreal/iro/lecuyer/examples/CallCenter.res | 14 +
source/umontreal/iro/lecuyer/examples/CallEv.dat | 22 +
source/umontreal/iro/lecuyer/examples/CallEv.java | 193 ++
source/umontreal/iro/lecuyer/examples/CallEv.res | 23 +
.../umontreal/iro/lecuyer/examples/Collision.java | 43 +
.../umontreal/iro/lecuyer/examples/Collision.res | 6 +
.../umontreal/iro/lecuyer/examples/Inventory.java | 63 +
.../umontreal/iro/lecuyer/examples/Inventory.res | 6 +
.../iro/lecuyer/examples/InventoryCRN.java | 54 +
.../iro/lecuyer/examples/InventoryCRN.res | 14 +
source/umontreal/iro/lecuyer/examples/Jobshop.dat | 14 +
source/umontreal/iro/lecuyer/examples/Jobshop.java | 129 +
source/umontreal/iro/lecuyer/examples/Jobshop.tex | 79 +
.../umontreal/iro/lecuyer/examples/Nonuniform.java | 46 +
.../umontreal/iro/lecuyer/examples/Nonuniform.res | 8 +
.../umontreal/iro/lecuyer/examples/PreyPred.java | 54 +
source/umontreal/iro/lecuyer/examples/PreyPred.tex | 38 +
source/umontreal/iro/lecuyer/examples/Queue.tex | 464 +++
source/umontreal/iro/lecuyer/examples/QueueEv.java | 73 +
source/umontreal/iro/lecuyer/examples/QueueEv.res | 8 +
.../iro/lecuyer/examples/QueueLindley.java | 37 +
.../umontreal/iro/lecuyer/examples/QueueObs.java | 76 +
.../umontreal/iro/lecuyer/examples/QueueObs2.java | 83 +
.../umontreal/iro/lecuyer/examples/QueueProc.java | 43 +
.../umontreal/iro/lecuyer/examples/QueueProc.res | 10 +
.../umontreal/iro/lecuyer/examples/TimeShared.java | 87 +
.../umontreal/iro/lecuyer/examples/TimeShared.res | 5 +
.../umontreal/iro/lecuyer/examples/TimeShared.tex | 173 ++
source/umontreal/iro/lecuyer/examples/Visits.java | 95 +
source/umontreal/iro/lecuyer/examples/Visits.res | 5 +
source/umontreal/iro/lecuyer/examples/Visits.tex | 108 +
source/umontreal/iro/lecuyer/examples/event.tex | 543 ++++
source/umontreal/iro/lecuyer/examples/examples.bbl | 97 +
source/umontreal/iro/lecuyer/examples/examples.tex | 152 +
source/umontreal/iro/lecuyer/examples/intro.tex | 71 +
source/umontreal/iro/lecuyer/examples/overview.tex | 235 ++
source/umontreal/iro/lecuyer/examples/packdep.tex | 170 ++
source/umontreal/iro/lecuyer/examples/process.tex | 627 ++++
source/umontreal/iro/lecuyer/examples/simple.tex | 659 ++++
.../umontreal/iro/lecuyer/functionfit/BSpline.java | 611 ++++
.../umontreal/iro/lecuyer/functionfit/BSpline.tex | 607 ++++
.../iro/lecuyer/functionfit/LeastSquares.java | 316 ++
.../iro/lecuyer/functionfit/LeastSquares.tex | 288 ++
.../iro/lecuyer/functionfit/PolInterp.java | 177 ++
.../iro/lecuyer/functionfit/PolInterp.tex | 178 ++
.../lecuyer/functionfit/SmoothingCubicSpline.java | 482 +++
.../lecuyer/functionfit/SmoothingCubicSpline.tex | 468 +++
.../iro/lecuyer/functionfit/guidefunctionfit.bbl | 15 +
.../iro/lecuyer/functionfit/guidefunctionfit.tex | 31 +
.../iro/lecuyer/functions/AverageMathFunction.java | 107 +
.../iro/lecuyer/functions/AverageMathFunction.tex | 104 +
.../lecuyer/functions/IdentityMathFunction.java | 55 +
.../iro/lecuyer/functions/IdentityMathFunction.tex | 58 +
.../iro/lecuyer/functions/MathFunction.java | 50 +
.../iro/lecuyer/functions/MathFunction.tex | 54 +
.../iro/lecuyer/functions/MathFunctionUtil.java | 535 ++++
.../iro/lecuyer/functions/MathFunctionUtil.tex | 483 +++
.../functions/MathFunctionWithDerivative.java | 55 +
.../functions/MathFunctionWithDerivative.tex | 56 +
.../functions/MathFunctionWithFirstDerivative.java | 49 +
.../functions/MathFunctionWithFirstDerivative.tex | 52 +
.../functions/MathFunctionWithIntegral.java | 51 +
.../lecuyer/functions/MathFunctionWithIntegral.tex | 53 +
.../iro/lecuyer/functions/MultiFunction.java | 48 +
.../iro/lecuyer/functions/MultiFunction.tex | 52 +
.../functions/PiecewiseConstantFunction.java | 90 +
.../functions/PiecewiseConstantFunction.tex | 92 +
.../iro/lecuyer/functions/Polynomial.java | 260 ++
.../umontreal/iro/lecuyer/functions/Polynomial.tex | 249 ++
.../iro/lecuyer/functions/PowerMathFunction.java | 138 +
.../iro/lecuyer/functions/PowerMathFunction.tex | 134 +
.../iro/lecuyer/functions/ShiftedMathFunction.java | 101 +
.../iro/lecuyer/functions/ShiftedMathFunction.tex | 101 +
.../iro/lecuyer/functions/SqrtMathFunction.java | 71 +
.../iro/lecuyer/functions/SqrtMathFunction.tex | 73 +
.../iro/lecuyer/functions/SquareMathFunction.java | 122 +
.../iro/lecuyer/functions/SquareMathFunction.tex | 120 +
.../iro/lecuyer/functions/guidefunctions.bbl | 0
.../iro/lecuyer/functions/guidefunctions.tex | 48 +
source/umontreal/iro/lecuyer/gof/FBar.java | 262 ++
source/umontreal/iro/lecuyer/gof/FBar.tex | 307 ++
source/umontreal/iro/lecuyer/gof/FDist.java | 162 +
source/umontreal/iro/lecuyer/gof/FDist.tex | 212 ++
source/umontreal/iro/lecuyer/gof/GofFormat.java | 1170 ++++++++
source/umontreal/iro/lecuyer/gof/GofFormat.tex | 1195 ++++++++
source/umontreal/iro/lecuyer/gof/GofStat.java | 1429 +++++++++
source/umontreal/iro/lecuyer/gof/GofStat.tex | 1284 ++++++++
.../umontreal/iro/lecuyer/gof/KernelDensity.java | 101 +
source/umontreal/iro/lecuyer/gof/KernelDensity.tex | 112 +
source/umontreal/iro/lecuyer/gof/guidegof.bbl | 104 +
source/umontreal/iro/lecuyer/gof/guidegof.tex | 44 +
source/umontreal/iro/lecuyer/gof/overview.tex | 122 +
.../iro/lecuyer/hups/AntitheticPointSet.java | 101 +
.../iro/lecuyer/hups/AntitheticPointSet.tex | 112 +
.../iro/lecuyer/hups/BakerTransformedPointSet.java | 117 +
.../iro/lecuyer/hups/BakerTransformedPointSet.tex | 127 +
.../umontreal/iro/lecuyer/hups/CachedPointSet.java | 143 +
.../umontreal/iro/lecuyer/hups/CachedPointSet.tex | 173 ++
.../iro/lecuyer/hups/ContainerPointSet.java | 205 ++
.../iro/lecuyer/hups/ContainerPointSet.tex | 216 ++
.../iro/lecuyer/hups/CycleBasedPointSet.java | 316 ++
.../iro/lecuyer/hups/CycleBasedPointSet.tex | 329 ++
.../iro/lecuyer/hups/CycleBasedPointSetBase2.java | 229 ++
.../iro/lecuyer/hups/CycleBasedPointSetBase2.tex | 238 ++
source/umontreal/iro/lecuyer/hups/DigitalNet.java | 1291 ++++++++
source/umontreal/iro/lecuyer/hups/DigitalNet.tex | 1250 ++++++++
.../iro/lecuyer/hups/DigitalNetBase2.java | 674 +++++
.../umontreal/iro/lecuyer/hups/DigitalNetBase2.tex | 683 +++++
.../iro/lecuyer/hups/DigitalNetBase2FromFile.java | 248 ++
.../iro/lecuyer/hups/DigitalNetBase2FromFile.tex | 284 ++
.../iro/lecuyer/hups/DigitalNetFromFile.java | 596 ++++
.../iro/lecuyer/hups/DigitalNetFromFile.tex | 660 +++++
.../iro/lecuyer/hups/DigitalSequence.java | 386 +++
.../umontreal/iro/lecuyer/hups/DigitalSequence.tex | 383 +++
.../iro/lecuyer/hups/DigitalSequenceBase2.java | 305 ++
.../iro/lecuyer/hups/DigitalSequenceBase2.tex | 303 ++
.../iro/lecuyer/hups/EmptyRandomization.java | 84 +
.../iro/lecuyer/hups/EmptyRandomization.tex | 93 +
.../iro/lecuyer/hups/F2wCycleBasedLFSR.java | 178 ++
.../iro/lecuyer/hups/F2wCycleBasedLFSR.tex | 175 ++
.../iro/lecuyer/hups/F2wCycleBasedPolyLCG.java | 179 ++
.../iro/lecuyer/hups/F2wCycleBasedPolyLCG.tex | 180 ++
source/umontreal/iro/lecuyer/hups/F2wNetLFSR.java | 128 +
source/umontreal/iro/lecuyer/hups/F2wNetLFSR.tex | 135 +
.../umontreal/iro/lecuyer/hups/F2wNetPolyLCG.java | 114 +
.../umontreal/iro/lecuyer/hups/F2wNetPolyLCG.tex | 120 +
.../umontreal/iro/lecuyer/hups/F2wStructure.java | 784 +++++
source/umontreal/iro/lecuyer/hups/F2wStructure.tex | 570 ++++
.../umontreal/iro/lecuyer/hups/FaureSequence.java | 367 +++
.../umontreal/iro/lecuyer/hups/FaureSequence.tex | 337 +++
.../umontreal/iro/lecuyer/hups/HaltonSequence.java | 207 ++
.../umontreal/iro/lecuyer/hups/HaltonSequence.tex | 213 ++
.../iro/lecuyer/hups/HammersleyPointSet.java | 159 +
.../iro/lecuyer/hups/HammersleyPointSet.tex | 155 +
.../umontreal/iro/lecuyer/hups/KorobovLattice.java | 144 +
.../umontreal/iro/lecuyer/hups/KorobovLattice.tex | 147 +
.../iro/lecuyer/hups/KorobovLatticeSequence.java | 125 +
.../iro/lecuyer/hups/KorobovLatticeSequence.tex | 130 +
source/umontreal/iro/lecuyer/hups/LCGPointSet.java | 133 +
source/umontreal/iro/lecuyer/hups/LCGPointSet.tex | 135 +
.../iro/lecuyer/hups/LMScrambleShift.java | 92 +
.../umontreal/iro/lecuyer/hups/LMScrambleShift.tex | 104 +
.../iro/lecuyer/hups/NiedSequenceBase2.java | 166 ++
.../iro/lecuyer/hups/NiedSequenceBase2.tex | 182 ++
.../iro/lecuyer/hups/NiedXingSequenceBase2.java | 208 ++
.../iro/lecuyer/hups/NiedXingSequenceBase2.tex | 221 ++
.../umontreal/iro/lecuyer/hups/PaddedPointSet.java | 332 +++
.../umontreal/iro/lecuyer/hups/PaddedPointSet.tex | 345 +++
source/umontreal/iro/lecuyer/hups/PointSet.java | 724 +++++
source/umontreal/iro/lecuyer/hups/PointSet.tex | 751 +++++
.../iro/lecuyer/hups/PointSetIterator.java | 238 ++
.../iro/lecuyer/hups/PointSetIterator.tex | 244 ++
.../iro/lecuyer/hups/PointSetRandomization.java | 92 +
.../iro/lecuyer/hups/PointSetRandomization.tex | 99 +
.../umontreal/iro/lecuyer/hups/RQMCPointSet.java | 116 +
source/umontreal/iro/lecuyer/hups/RQMCPointSet.tex | 126 +
.../umontreal/iro/lecuyer/hups/RadicalInverse.java | 691 +++++
.../umontreal/iro/lecuyer/hups/RadicalInverse.tex | 643 ++++
.../iro/lecuyer/hups/RandShiftedPointSet.java | 192 ++
.../iro/lecuyer/hups/RandShiftedPointSet.tex | 220 ++
source/umontreal/iro/lecuyer/hups/RandomShift.java | 110 +
source/umontreal/iro/lecuyer/hups/RandomShift.tex | 122 +
source/umontreal/iro/lecuyer/hups/RandomStart.java | 121 +
source/umontreal/iro/lecuyer/hups/RandomStart.tex | 134 +
.../umontreal/iro/lecuyer/hups/Rank1Lattice.java | 249 ++
source/umontreal/iro/lecuyer/hups/Rank1Lattice.tex | 250 ++
.../iro/lecuyer/hups/SMScrambleShift.java | 92 +
.../umontreal/iro/lecuyer/hups/SMScrambleShift.tex | 105 +
.../umontreal/iro/lecuyer/hups/SobolSequence.java | 924 ++++++
.../umontreal/iro/lecuyer/hups/SobolSequence.tex | 869 ++++++
.../iro/lecuyer/hups/SubsetOfPointSet.java | 390 +++
.../iro/lecuyer/hups/SubsetOfPointSet.tex | 412 +++
.../umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat | 20 +
.../umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat | 45 +
.../umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat | 35 +
.../hups/dataSer/Nieder/NiedSequenceBase2.dat | 318 ++
.../hups/dataSer/Nieder/NiedSequenceBase2.ser | Bin 0 -> 38187 bytes
.../dataSer/Nieder/NiedXingSequenceBase2Trans.dat | 3132 ++++++++++++++++++++
.../dataSer/Nieder/NiedXingSequenceBase2Trans.ser | Bin 0 -> 62667 bytes
source/umontreal/iro/lecuyer/hups/guidehups.bbl | 259 ++
source/umontreal/iro/lecuyer/hups/guidehups.tex | 104 +
source/umontreal/iro/lecuyer/hups/overview.tex | 638 ++++
.../iro/lecuyer/probdist/AndersonDarlingDist.java | 359 +++
.../iro/lecuyer/probdist/AndersonDarlingDist.tex | 380 +++
.../lecuyer/probdist/AndersonDarlingDistQuick.java | 447 +++
.../lecuyer/probdist/AndersonDarlingDistQuick.tex | 464 +++
.../iro/lecuyer/probdist/BernoulliDist.java | 316 ++
.../iro/lecuyer/probdist/BernoulliDist.tex | 323 ++
.../umontreal/iro/lecuyer/probdist/BetaDist.java | 1013 +++++++
source/umontreal/iro/lecuyer/probdist/BetaDist.tex | 1047 +++++++
.../iro/lecuyer/probdist/BetaSymmetricalDist.java | 971 ++++++
.../iro/lecuyer/probdist/BetaSymmetricalDist.tex | 984 ++++++
.../iro/lecuyer/probdist/BinomialDist.java | 847 ++++++
.../iro/lecuyer/probdist/BinomialDist.tex | 906 ++++++
.../umontreal/iro/lecuyer/probdist/CauchyDist.java | 359 +++
.../umontreal/iro/lecuyer/probdist/CauchyDist.tex | 376 +++
source/umontreal/iro/lecuyer/probdist/ChiDist.java | 316 ++
source/umontreal/iro/lecuyer/probdist/ChiDist.tex | 337 +++
.../iro/lecuyer/probdist/ChiSquareDist.java | 448 +++
.../iro/lecuyer/probdist/ChiSquareDist.tex | 519 ++++
.../iro/lecuyer/probdist/ChiSquareDistQuick.java | 158 +
.../iro/lecuyer/probdist/ChiSquareDistQuick.tex | 167 ++
.../lecuyer/probdist/ChiSquareNoncentralDist.java | 713 +++++
.../lecuyer/probdist/ChiSquareNoncentralDist.tex | 750 +++++
.../iro/lecuyer/probdist/ConstantDist.java | 105 +
.../iro/lecuyer/probdist/ConstantDist.tex | 130 +
.../iro/lecuyer/probdist/ConstantIntDist.java | 59 +
.../iro/lecuyer/probdist/ConstantIntDist.tex | 87 +
.../lecuyer/probdist/ContinuousDistribution.java | 472 +++
.../lecuyer/probdist/ContinuousDistribution.tex | 487 +++
.../iro/lecuyer/probdist/CramerVonMisesDist.java | 353 +++
.../iro/lecuyer/probdist/CramerVonMisesDist.tex | 367 +++
.../iro/lecuyer/probdist/DiscreteDistribution.java | 395 +++
.../iro/lecuyer/probdist/DiscreteDistribution.tex | 418 +++
.../lecuyer/probdist/DiscreteDistributionInt.java | 279 ++
.../lecuyer/probdist/DiscreteDistributionInt.tex | 271 ++
.../iro/lecuyer/probdist/Distribution.java | 107 +
.../iro/lecuyer/probdist/Distribution.tex | 125 +
.../iro/lecuyer/probdist/DistributionFactory.java | 472 +++
.../iro/lecuyer/probdist/DistributionFactory.tex | 490 +++
.../iro/lecuyer/probdist/EmpiricalDist.java | 326 ++
.../iro/lecuyer/probdist/EmpiricalDist.tex | 325 ++
.../umontreal/iro/lecuyer/probdist/ErlangDist.java | 229 ++
.../umontreal/iro/lecuyer/probdist/ErlangDist.tex | 231 ++
.../iro/lecuyer/probdist/ExponentialDist.java | 303 ++
.../iro/lecuyer/probdist/ExponentialDist.tex | 308 ++
.../lecuyer/probdist/ExponentialDistFromMean.java | 60 +
.../lecuyer/probdist/ExponentialDistFromMean.tex | 72 +
.../iro/lecuyer/probdist/ExtremeValueDist.java | 391 +++
.../iro/lecuyer/probdist/ExtremeValueDist.tex | 396 +++
.../iro/lecuyer/probdist/FatigueLifeDist.java | 397 +++
.../iro/lecuyer/probdist/FatigueLifeDist.tex | 421 +++
.../iro/lecuyer/probdist/FisherFDist.java | 309 ++
.../umontreal/iro/lecuyer/probdist/FisherFDist.tex | 348 +++
.../iro/lecuyer/probdist/FoldedNormalDist.java | 363 +++
.../iro/lecuyer/probdist/FoldedNormalDist.tex | 353 +++
.../iro/lecuyer/probdist/FrechetDist.java | 455 +++
.../umontreal/iro/lecuyer/probdist/FrechetDist.tex | 551 ++++
.../umontreal/iro/lecuyer/probdist/GammaDist.java | 600 ++++
.../umontreal/iro/lecuyer/probdist/GammaDist.tex | 619 ++++
.../iro/lecuyer/probdist/GammaDistFromMoments.java | 76 +
.../iro/lecuyer/probdist/GammaDistFromMoments.tex | 74 +
.../iro/lecuyer/probdist/GeometricDist.java | 322 ++
.../iro/lecuyer/probdist/GeometricDist.tex | 334 +++
.../umontreal/iro/lecuyer/probdist/GumbelDist.java | 487 +++
.../umontreal/iro/lecuyer/probdist/GumbelDist.tex | 508 ++++
.../iro/lecuyer/probdist/HalfNormalDist.java | 368 +++
.../iro/lecuyer/probdist/HalfNormalDist.tex | 380 +++
.../iro/lecuyer/probdist/HyperbolicSecantDist.java | 373 +++
.../iro/lecuyer/probdist/HyperbolicSecantDist.tex | 388 +++
.../iro/lecuyer/probdist/HypergeometricDist.java | 461 +++
.../iro/lecuyer/probdist/HypergeometricDist.tex | 490 +++
.../iro/lecuyer/probdist/HypoExponentialDist.java | 432 +++
.../iro/lecuyer/probdist/HypoExponentialDist.tex | 442 +++
.../lecuyer/probdist/HypoExponentialDistEqual.java | 228 ++
.../lecuyer/probdist/HypoExponentialDistEqual.tex | 234 ++
.../lecuyer/probdist/HypoExponentialDistQuick.java | 325 ++
.../lecuyer/probdist/HypoExponentialDistQuick.tex | 335 +++
.../lecuyer/probdist/InverseDistFromDensity.java | 966 ++++++
.../lecuyer/probdist/InverseDistFromDensity.tex | 966 ++++++
.../iro/lecuyer/probdist/InverseGammaDist.java | 310 ++
.../iro/lecuyer/probdist/InverseGammaDist.tex | 338 +++
.../iro/lecuyer/probdist/InverseGaussianDist.java | 362 +++
.../iro/lecuyer/probdist/InverseGaussianDist.tex | 388 +++
.../iro/lecuyer/probdist/JohnsonSBDist.java | 485 +++
.../iro/lecuyer/probdist/JohnsonSBDist.tex | 484 +++
.../iro/lecuyer/probdist/JohnsonSLDist.java | 473 +++
.../iro/lecuyer/probdist/JohnsonSLDist.tex | 480 +++
.../iro/lecuyer/probdist/JohnsonSUDist.java | 304 ++
.../iro/lecuyer/probdist/JohnsonSUDist.tex | 314 ++
.../iro/lecuyer/probdist/JohnsonSystem.java | 161 +
.../iro/lecuyer/probdist/JohnsonSystem.tex | 157 +
.../lecuyer/probdist/KolmogorovSmirnovDist.java | 462 +++
.../iro/lecuyer/probdist/KolmogorovSmirnovDist.tex | 519 ++++
.../probdist/KolmogorovSmirnovDistQuick.java | 470 +++
.../probdist/KolmogorovSmirnovDistQuick.tex | 481 +++
.../probdist/KolmogorovSmirnovPlusDist.java | 392 +++
.../lecuyer/probdist/KolmogorovSmirnovPlusDist.tex | 424 +++
.../iro/lecuyer/probdist/LaplaceDist.java | 324 ++
.../umontreal/iro/lecuyer/probdist/LaplaceDist.tex | 341 +++
.../iro/lecuyer/probdist/LogarithmicDist.java | 326 ++
.../iro/lecuyer/probdist/LogarithmicDist.tex | 358 +++
.../iro/lecuyer/probdist/LogisticDist.java | 427 +++
.../iro/lecuyer/probdist/LogisticDist.tex | 445 +++
.../iro/lecuyer/probdist/LoglogisticDist.java | 425 +++
.../iro/lecuyer/probdist/LoglogisticDist.tex | 464 +++
.../iro/lecuyer/probdist/LognormalDist.java | 355 +++
.../iro/lecuyer/probdist/LognormalDist.tex | 358 +++
.../lecuyer/probdist/LognormalDistFromMoments.java | 65 +
.../lecuyer/probdist/LognormalDistFromMoments.tex | 73 +
.../iro/lecuyer/probdist/NakagamiDist.java | 369 +++
.../iro/lecuyer/probdist/NakagamiDist.tex | 354 +++
.../iro/lecuyer/probdist/NegativeBinomialDist.java | 933 ++++++
.../iro/lecuyer/probdist/NegativeBinomialDist.tex | 971 ++++++
.../umontreal/iro/lecuyer/probdist/NormalDist.java | 633 ++++
.../umontreal/iro/lecuyer/probdist/NormalDist.tex | 650 ++++
.../iro/lecuyer/probdist/NormalDistQuick.java | 966 ++++++
.../iro/lecuyer/probdist/NormalDistQuick.tex | 1004 +++++++
.../probdist/NormalInverseGaussianDist.java | 368 +++
.../lecuyer/probdist/NormalInverseGaussianDist.tex | 396 +++
.../umontreal/iro/lecuyer/probdist/ParetoDist.java | 335 +++
.../umontreal/iro/lecuyer/probdist/ParetoDist.tex | 355 +++
.../umontreal/iro/lecuyer/probdist/PascalDist.java | 204 ++
.../umontreal/iro/lecuyer/probdist/PascalDist.tex | 234 ++
.../iro/lecuyer/probdist/Pearson5Dist.java | 316 ++
.../iro/lecuyer/probdist/Pearson5Dist.tex | 344 +++
.../iro/lecuyer/probdist/Pearson6Dist.java | 425 +++
.../iro/lecuyer/probdist/Pearson6Dist.tex | 455 +++
.../probdist/PiecewiseLinearEmpiricalDist.java | 308 ++
.../probdist/PiecewiseLinearEmpiricalDist.tex | 306 ++
.../iro/lecuyer/probdist/PoissonDist.java | 632 ++++
.../umontreal/iro/lecuyer/probdist/PoissonDist.tex | 695 +++++
.../umontreal/iro/lecuyer/probdist/PowerDist.java | 394 +++
.../umontreal/iro/lecuyer/probdist/PowerDist.tex | 399 +++
.../iro/lecuyer/probdist/RayleighDist.java | 427 +++
.../iro/lecuyer/probdist/RayleighDist.tex | 417 +++
.../iro/lecuyer/probdist/StudentDist.java | 438 +++
.../umontreal/iro/lecuyer/probdist/StudentDist.tex | 448 +++
.../iro/lecuyer/probdist/StudentDistQuick.java | 217 ++
.../iro/lecuyer/probdist/StudentDistQuick.tex | 228 ++
.../iro/lecuyer/probdist/TriangularDist.java | 432 +++
.../iro/lecuyer/probdist/TriangularDist.tex | 430 +++
.../iro/lecuyer/probdist/TruncatedDist.java | 292 ++
.../iro/lecuyer/probdist/TruncatedDist.tex | 307 ++
.../iro/lecuyer/probdist/UniformDist.java | 316 ++
.../umontreal/iro/lecuyer/probdist/UniformDist.tex | 326 ++
.../iro/lecuyer/probdist/UniformIntDist.java | 312 ++
.../iro/lecuyer/probdist/UniformIntDist.tex | 346 +++
.../iro/lecuyer/probdist/WatsonGDist.java | 714 +++++
.../umontreal/iro/lecuyer/probdist/WatsonGDist.tex | 711 +++++
.../iro/lecuyer/probdist/WatsonUDist.java | 392 +++
.../umontreal/iro/lecuyer/probdist/WatsonUDist.tex | 427 +++
.../iro/lecuyer/probdist/WeibullDist.java | 490 +++
.../umontreal/iro/lecuyer/probdist/WeibullDist.tex | 495 ++++
.../umontreal/iro/lecuyer/probdist/continuous.tex | 1 +
source/umontreal/iro/lecuyer/probdist/discrete.tex | 1 +
.../iro/lecuyer/probdist/discretereal.tex | 1 +
source/umontreal/iro/lecuyer/probdist/edf.tex | 1 +
source/umontreal/iro/lecuyer/probdist/general.tex | 1 +
.../iro/lecuyer/probdist/guideprobdist.bbl | 348 +++
.../iro/lecuyer/probdist/guideprobdist.tex | 133 +
source/umontreal/iro/lecuyer/probdist/overview.tex | 135 +
.../umontreal/iro/lecuyer/probdist/rvclasses.tex | 7 +
.../umontreal/iro/lecuyer/probdist/scclasses.tex | 7 +
.../iro/lecuyer/probdist/staticclasses.tex | 7 +
.../iro/lecuyer/probdistmulti/BiNormalDist.java | 510 ++++
.../iro/lecuyer/probdistmulti/BiNormalDist.tex | 525 ++++
.../probdistmulti/BiNormalDonnellyDist.java | 342 +++
.../lecuyer/probdistmulti/BiNormalDonnellyDist.tex | 356 +++
.../lecuyer/probdistmulti/BiNormalGenzDist.java | 306 ++
.../iro/lecuyer/probdistmulti/BiNormalGenzDist.tex | 317 ++
.../iro/lecuyer/probdistmulti/BiStudentDist.java | 327 ++
.../iro/lecuyer/probdistmulti/BiStudentDist.tex | 357 +++
.../probdistmulti/ContinuousDistribution2Dim.java | 175 ++
.../probdistmulti/ContinuousDistribution2Dim.tex | 155 +
.../probdistmulti/ContinuousDistributionMulti.java | 98 +
.../probdistmulti/ContinuousDistributionMulti.tex | 93 +
.../iro/lecuyer/probdistmulti/DirichletDist.java | 352 +++
.../iro/lecuyer/probdistmulti/DirichletDist.tex | 374 +++
.../DiscreteDistributionIntMulti.java | 130 +
.../probdistmulti/DiscreteDistributionIntMulti.tex | 129 +
.../iro/lecuyer/probdistmulti/MultiNormalDist.java | 338 +++
.../iro/lecuyer/probdistmulti/MultiNormalDist.tex | 342 +++
.../iro/lecuyer/probdistmulti/MultinomialDist.java | 342 +++
.../iro/lecuyer/probdistmulti/MultinomialDist.tex | 358 +++
.../probdistmulti/NegativeMultinomialDist.java | 509 ++++
.../probdistmulti/NegativeMultinomialDist.tex | 542 ++++
.../iro/lecuyer/probdistmulti/continuous.tex | 1 +
.../iro/lecuyer/probdistmulti/continuous2dim.tex | 1 +
.../iro/lecuyer/probdistmulti/discrete.tex | 1 +
.../iro/lecuyer/probdistmulti/general.tex | 1 +
.../lecuyer/probdistmulti/guideprobdistmulti.bbl | 37 +
.../lecuyer/probdistmulti/guideprobdistmulti.tex | 74 +
.../iro/lecuyer/probdistmulti/norta/NI1.java | 192 ++
.../iro/lecuyer/probdistmulti/norta/NI1.tex | 202 ++
.../iro/lecuyer/probdistmulti/norta/NI2a.java | 206 ++
.../iro/lecuyer/probdistmulti/norta/NI2a.tex | 216 ++
.../iro/lecuyer/probdistmulti/norta/NI2b.java | 194 ++
.../iro/lecuyer/probdistmulti/norta/NI2b.tex | 204 ++
.../iro/lecuyer/probdistmulti/norta/NI3.java | 135 +
.../iro/lecuyer/probdistmulti/norta/NI3.tex | 146 +
.../lecuyer/probdistmulti/norta/NortaInitDisc.java | 203 ++
.../lecuyer/probdistmulti/norta/NortaInitDisc.tex | 214 ++
.../iro/lecuyer/probdistmulti/norta/overview.tex | 7 +
.../iro/lecuyer/probdistmulti/overview.tex | 45 +
.../iro/lecuyer/probdistmulti/rvclasses.tex | 7 +
.../iro/lecuyer/probdistmulti/scclasses.tex | 7 +
.../iro/lecuyer/probdistmulti/staticclasses.tex | 7 +
.../iro/lecuyer/randvar/BernoulliGen.java | 96 +
.../umontreal/iro/lecuyer/randvar/BernoulliGen.tex | 105 +
source/umontreal/iro/lecuyer/randvar/BetaGen.java | 161 +
source/umontreal/iro/lecuyer/randvar/BetaGen.tex | 183 ++
.../randvar/BetaRejectionLoglogisticGen.java | 307 ++
.../randvar/BetaRejectionLoglogisticGen.tex | 431 +++
.../randvar/BetaStratifiedRejectionGen.java | 526 ++++
.../lecuyer/randvar/BetaStratifiedRejectionGen.tex | 726 +++++
.../lecuyer/randvar/BetaSymmetricalBestGen.java | 180 ++
.../iro/lecuyer/randvar/BetaSymmetricalBestGen.tex | 183 ++
.../iro/lecuyer/randvar/BetaSymmetricalGen.java | 67 +
.../iro/lecuyer/randvar/BetaSymmetricalGen.tex | 79 +
.../lecuyer/randvar/BetaSymmetricalPolarGen.java | 172 ++
.../lecuyer/randvar/BetaSymmetricalPolarGen.tex | 172 ++
.../lecuyer/randvar/BinomialConvolutionGen.java | 100 +
.../iro/lecuyer/randvar/BinomialConvolutionGen.tex | 110 +
.../umontreal/iro/lecuyer/randvar/BinomialGen.java | 124 +
.../umontreal/iro/lecuyer/randvar/BinomialGen.tex | 139 +
.../umontreal/iro/lecuyer/randvar/CauchyGen.java | 122 +
source/umontreal/iro/lecuyer/randvar/CauchyGen.tex | 134 +
source/umontreal/iro/lecuyer/randvar/ChiGen.java | 106 +
source/umontreal/iro/lecuyer/randvar/ChiGen.tex | 121 +
.../iro/lecuyer/randvar/ChiRatioOfUniformsGen.java | 137 +
.../iro/lecuyer/randvar/ChiRatioOfUniformsGen.tex | 149 +
.../iro/lecuyer/randvar/ChiSquareGen.java | 106 +
.../umontreal/iro/lecuyer/randvar/ChiSquareGen.tex | 119 +
.../lecuyer/randvar/ChiSquareNoncentralGamGen.java | 103 +
.../lecuyer/randvar/ChiSquareNoncentralGamGen.tex | 112 +
.../lecuyer/randvar/ChiSquareNoncentralGen.java | 121 +
.../iro/lecuyer/randvar/ChiSquareNoncentralGen.tex | 124 +
.../randvar/ChiSquareNoncentralPoisGen.java | 104 +
.../lecuyer/randvar/ChiSquareNoncentralPoisGen.tex | 108 +
.../umontreal/iro/lecuyer/randvar/ConstantGen.java | 67 +
.../umontreal/iro/lecuyer/randvar/ConstantGen.tex | 88 +
.../iro/lecuyer/randvar/ErlangConvolutionGen.java | 91 +
.../iro/lecuyer/randvar/ErlangConvolutionGen.tex | 98 +
.../umontreal/iro/lecuyer/randvar/ErlangGen.java | 118 +
source/umontreal/iro/lecuyer/randvar/ErlangGen.tex | 121 +
.../iro/lecuyer/randvar/ExponentialGen.java | 107 +
.../iro/lecuyer/randvar/ExponentialGen.tex | 107 +
.../randvar/ExponentialInverseFromDensityGen.java | 130 +
.../randvar/ExponentialInverseFromDensityGen.tex | 140 +
.../iro/lecuyer/randvar/ExtremeValueGen.java | 135 +
.../iro/lecuyer/randvar/ExtremeValueGen.tex | 135 +
.../iro/lecuyer/randvar/FNoncentralGen.java | 96 +
.../iro/lecuyer/randvar/FNoncentralGen.tex | 109 +
.../iro/lecuyer/randvar/FatigueLifeGen.java | 134 +
.../iro/lecuyer/randvar/FatigueLifeGen.tex | 147 +
.../umontreal/iro/lecuyer/randvar/FisherFGen.java | 118 +
.../umontreal/iro/lecuyer/randvar/FisherFGen.tex | 131 +
.../iro/lecuyer/randvar/FoldedNormalGen.java | 126 +
.../iro/lecuyer/randvar/FoldedNormalGen.tex | 139 +
.../umontreal/iro/lecuyer/randvar/FrechetGen.java | 142 +
.../umontreal/iro/lecuyer/randvar/FrechetGen.tex | 150 +
.../randvar/GammaAcceptanceRejectionGen.java | 364 +++
.../randvar/GammaAcceptanceRejectionGen.tex | 368 +++
source/umontreal/iro/lecuyer/randvar/GammaGen.java | 139 +
source/umontreal/iro/lecuyer/randvar/GammaGen.tex | 139 +
.../randvar/GammaRejectionLoglogisticGen.java | 190 ++
.../randvar/GammaRejectionLoglogisticGen.tex | 194 ++
.../iro/lecuyer/randvar/GeometricGen.java | 107 +
.../umontreal/iro/lecuyer/randvar/GeometricGen.tex | 110 +
.../umontreal/iro/lecuyer/randvar/GumbelGen.java | 125 +
source/umontreal/iro/lecuyer/randvar/GumbelGen.tex | 134 +
.../iro/lecuyer/randvar/HalfNormalGen.java | 122 +
.../iro/lecuyer/randvar/HalfNormalGen.tex | 135 +
.../iro/lecuyer/randvar/HyperbolicSecantGen.java | 126 +
.../iro/lecuyer/randvar/HyperbolicSecantGen.tex | 137 +
.../iro/lecuyer/randvar/HypergeometricGen.java | 135 +
.../iro/lecuyer/randvar/HypergeometricGen.tex | 155 +
.../iro/lecuyer/randvar/HypoExponentialGen.java | 103 +
.../iro/lecuyer/randvar/HypoExponentialGen.tex | 102 +
.../iro/lecuyer/randvar/InverseFromDensityGen.java | 308 ++
.../iro/lecuyer/randvar/InverseFromDensityGen.tex | 232 ++
.../iro/lecuyer/randvar/InverseGammaGen.java | 126 +
.../iro/lecuyer/randvar/InverseGammaGen.tex | 140 +
.../iro/lecuyer/randvar/InverseGaussianGen.java | 120 +
.../iro/lecuyer/randvar/InverseGaussianGen.tex | 131 +
.../iro/lecuyer/randvar/InverseGaussianMSHGen.java | 109 +
.../iro/lecuyer/randvar/InverseGaussianMSHGen.tex | 118 +
.../iro/lecuyer/randvar/JohnsonSBGen.java | 77 +
.../umontreal/iro/lecuyer/randvar/JohnsonSBGen.tex | 83 +
.../iro/lecuyer/randvar/JohnsonSLGen.java | 76 +
.../umontreal/iro/lecuyer/randvar/JohnsonSLGen.tex | 83 +
.../iro/lecuyer/randvar/JohnsonSUGen.java | 76 +
.../umontreal/iro/lecuyer/randvar/JohnsonSUGen.tex | 83 +
.../iro/lecuyer/randvar/JohnsonSystemG.java | 133 +
.../iro/lecuyer/randvar/JohnsonSystemG.tex | 143 +
.../iro/lecuyer/randvar/KernelDensityGen.java | 241 ++
.../iro/lecuyer/randvar/KernelDensityGen.tex | 209 ++
.../randvar/KernelDensityVarCorrectGen.java | 111 +
.../lecuyer/randvar/KernelDensityVarCorrectGen.tex | 116 +
.../umontreal/iro/lecuyer/randvar/LaplaceGen.java | 125 +
.../umontreal/iro/lecuyer/randvar/LaplaceGen.tex | 136 +
.../iro/lecuyer/randvar/LogarithmicGen.java | 201 ++
.../iro/lecuyer/randvar/LogarithmicGen.tex | 209 ++
.../umontreal/iro/lecuyer/randvar/LogisticGen.java | 133 +
.../umontreal/iro/lecuyer/randvar/LogisticGen.tex | 143 +
.../iro/lecuyer/randvar/LoglogisticGen.java | 113 +
.../iro/lecuyer/randvar/LoglogisticGen.tex | 124 +
.../iro/lecuyer/randvar/LognormalGen.java | 136 +
.../umontreal/iro/lecuyer/randvar/LognormalGen.tex | 144 +
.../iro/lecuyer/randvar/LognormalSpecialGen.java | 59 +
.../iro/lecuyer/randvar/LognormalSpecialGen.tex | 69 +
.../umontreal/iro/lecuyer/randvar/NakagamiGen.java | 134 +
.../umontreal/iro/lecuyer/randvar/NakagamiGen.tex | 139 +
.../iro/lecuyer/randvar/NegativeBinomialGen.java | 121 +
.../iro/lecuyer/randvar/NegativeBinomialGen.tex | 134 +
.../iro/lecuyer/randvar/NormalACRGen.java | 169 ++
.../umontreal/iro/lecuyer/randvar/NormalACRGen.tex | 176 ++
.../iro/lecuyer/randvar/NormalBoxMullerGen.java | 118 +
.../iro/lecuyer/randvar/NormalBoxMullerGen.tex | 127 +
.../umontreal/iro/lecuyer/randvar/NormalGen.java | 172 ++
source/umontreal/iro/lecuyer/randvar/NormalGen.tex | 162 +
.../randvar/NormalInverseFromDensityGen.java | 126 +
.../randvar/NormalInverseFromDensityGen.tex | 136 +
.../lecuyer/randvar/NormalInverseGaussianGen.java | 143 +
.../lecuyer/randvar/NormalInverseGaussianGen.tex | 155 +
.../randvar/NormalInverseGaussianIGGen.java | 121 +
.../lecuyer/randvar/NormalInverseGaussianIGGen.tex | 123 +
.../lecuyer/randvar/NormalKindermannRamageGen.java | 157 +
.../lecuyer/randvar/NormalKindermannRamageGen.tex | 165 ++
.../iro/lecuyer/randvar/NormalPolarGen.java | 121 +
.../iro/lecuyer/randvar/NormalPolarGen.tex | 130 +
.../umontreal/iro/lecuyer/randvar/ParetoGen.java | 121 +
source/umontreal/iro/lecuyer/randvar/ParetoGen.tex | 135 +
.../iro/lecuyer/randvar/PascalConvolutionGen.java | 91 +
.../iro/lecuyer/randvar/PascalConvolutionGen.tex | 103 +
.../umontreal/iro/lecuyer/randvar/PascalGen.java | 111 +
source/umontreal/iro/lecuyer/randvar/PascalGen.tex | 119 +
.../umontreal/iro/lecuyer/randvar/Pearson5Gen.java | 131 +
.../umontreal/iro/lecuyer/randvar/Pearson5Gen.tex | 143 +
.../umontreal/iro/lecuyer/randvar/Pearson6Gen.java | 152 +
.../umontreal/iro/lecuyer/randvar/Pearson6Gen.tex | 164 +
.../umontreal/iro/lecuyer/randvar/PoissonGen.java | 108 +
.../umontreal/iro/lecuyer/randvar/PoissonGen.tex | 119 +
.../iro/lecuyer/randvar/PoissonTIACGen.java | 289 ++
.../iro/lecuyer/randvar/PoissonTIACGen.tex | 307 ++
source/umontreal/iro/lecuyer/randvar/PowerGen.java | 137 +
source/umontreal/iro/lecuyer/randvar/PowerGen.tex | 146 +
source/umontreal/iro/lecuyer/randvar/RandUnuran.c | 713 +++++
.../umontreal/iro/lecuyer/randvar/RandUnuran.java | 80 +
.../umontreal/iro/lecuyer/randvar/RandUnuran.tex | 82 +
.../iro/lecuyer/randvar/RandomVariateGen.java | 164 +
.../iro/lecuyer/randvar/RandomVariateGen.tex | 184 ++
.../iro/lecuyer/randvar/RandomVariateGenInt.java | 108 +
.../iro/lecuyer/randvar/RandomVariateGenInt.tex | 117 +
.../lecuyer/randvar/RandomVariateGenWithCache.java | 336 +++
.../lecuyer/randvar/RandomVariateGenWithCache.tex | 332 +++
.../umontreal/iro/lecuyer/randvar/RayleighGen.java | 136 +
.../umontreal/iro/lecuyer/randvar/RayleighGen.tex | 142 +
.../umontreal/iro/lecuyer/randvar/StudentGen.java | 130 +
.../umontreal/iro/lecuyer/randvar/StudentGen.tex | 140 +
.../iro/lecuyer/randvar/StudentNoncentralGen.java | 95 +
.../iro/lecuyer/randvar/StudentNoncentralGen.tex | 109 +
.../iro/lecuyer/randvar/StudentPolarGen.java | 143 +
.../iro/lecuyer/randvar/StudentPolarGen.tex | 150 +
.../iro/lecuyer/randvar/TriangularGen.java | 158 +
.../iro/lecuyer/randvar/TriangularGen.tex | 161 +
.../umontreal/iro/lecuyer/randvar/UniformGen.java | 126 +
.../umontreal/iro/lecuyer/randvar/UniformGen.tex | 130 +
.../iro/lecuyer/randvar/UniformIntGen.java | 109 +
.../iro/lecuyer/randvar/UniformIntGen.tex | 125 +
.../iro/lecuyer/randvar/UnuranContinuous.java | 115 +
.../iro/lecuyer/randvar/UnuranContinuous.tex | 123 +
.../iro/lecuyer/randvar/UnuranDiscreteInt.java | 115 +
.../iro/lecuyer/randvar/UnuranDiscreteInt.tex | 124 +
.../iro/lecuyer/randvar/UnuranEmpirical.java | 180 ++
.../iro/lecuyer/randvar/UnuranEmpirical.tex | 188 ++
.../iro/lecuyer/randvar/UnuranException.java | 56 +
.../iro/lecuyer/randvar/UnuranException.tex | 60 +
.../umontreal/iro/lecuyer/randvar/WeibullGen.java | 149 +
.../umontreal/iro/lecuyer/randvar/WeibullGen.tex | 146 +
.../umontreal/iro/lecuyer/randvar/continuous.tex | 1 +
source/umontreal/iro/lecuyer/randvar/discrete.tex | 1 +
.../iro/lecuyer/randvar/exam/normaltest.java | 31 +
source/umontreal/iro/lecuyer/randvar/general.tex | 1 +
.../umontreal/iro/lecuyer/randvar/guiderandvar.bbl | 217 ++
.../umontreal/iro/lecuyer/randvar/guiderandvar.tex | 174 ++
source/umontreal/iro/lecuyer/randvar/overview.tex | 177 ++
.../umontreal_iro_lecuyer_randvar_RandUnuran.h | 109 +
source/umontreal/iro/lecuyer/randvar/unuran.tex | 1 +
.../iro/lecuyer/randvarmulti/DirichletGen.java | 153 +
.../iro/lecuyer/randvarmulti/DirichletGen.tex | 157 +
.../lecuyer/randvarmulti/IIDMultivariateGen.java | 109 +
.../lecuyer/randvarmulti/IIDMultivariateGen.tex | 124 +
.../randvarmulti/MultinormalCholeskyGen.java | 267 ++
.../randvarmulti/MultinormalCholeskyGen.tex | 249 ++
.../iro/lecuyer/randvarmulti/MultinormalGen.java | 298 ++
.../iro/lecuyer/randvarmulti/MultinormalGen.tex | 282 ++
.../lecuyer/randvarmulti/MultinormalPCAGen.java | 319 ++
.../iro/lecuyer/randvarmulti/MultinormalPCAGen.tex | 299 ++
.../randvarmulti/RandomMultivariateGen.java | 130 +
.../lecuyer/randvarmulti/RandomMultivariateGen.tex | 148 +
.../iro/lecuyer/randvarmulti/guiderandvarmulti.bbl | 9 +
.../iro/lecuyer/randvarmulti/guiderandvarmulti.tex | 67 +
.../iro/lecuyer/randvarmulti/overview.tex | 7 +
.../iro/lecuyer/rng/AntitheticStream.java | 146 +
.../umontreal/iro/lecuyer/rng/AntitheticStream.tex | 147 +
.../iro/lecuyer/rng/BakerTransformedStream.java | 155 +
.../iro/lecuyer/rng/BakerTransformedStream.tex | 153 +
.../iro/lecuyer/rng/BasicRandomStreamFactory.java | 147 +
.../iro/lecuyer/rng/BasicRandomStreamFactory.tex | 145 +
.../iro/lecuyer/rng/CloneableRandomStream.java | 52 +
.../iro/lecuyer/rng/CloneableRandomStream.tex | 60 +
source/umontreal/iro/lecuyer/rng/F2NL607.java | 577 ++++
source/umontreal/iro/lecuyer/rng/F2NL607.tex | 580 ++++
source/umontreal/iro/lecuyer/rng/F2wPoly.java | 425 +++
source/umontreal/iro/lecuyer/rng/GenF2w32.java | 584 ++++
source/umontreal/iro/lecuyer/rng/GenF2w32.tex | 591 ++++
source/umontreal/iro/lecuyer/rng/LFSR113.java | 388 +++
source/umontreal/iro/lecuyer/rng/LFSR113.tex | 400 +++
source/umontreal/iro/lecuyer/rng/LFSR258.java | 516 ++++
source/umontreal/iro/lecuyer/rng/LFSR258.tex | 532 ++++
source/umontreal/iro/lecuyer/rng/MRG31k3p.java | 349 +++
source/umontreal/iro/lecuyer/rng/MRG31k3p.tex | 356 +++
source/umontreal/iro/lecuyer/rng/MRG32k3a.java | 401 +++
source/umontreal/iro/lecuyer/rng/MRG32k3a.tex | 407 +++
source/umontreal/iro/lecuyer/rng/MRG32k3aL.java | 337 +++
source/umontreal/iro/lecuyer/rng/MRG32k3aL.tex | 413 +++
source/umontreal/iro/lecuyer/rng/MT19937.java | 199 ++
source/umontreal/iro/lecuyer/rng/MT19937.tex | 212 ++
source/umontreal/iro/lecuyer/rng/RandMrg.java | 603 ++++
source/umontreal/iro/lecuyer/rng/RandMrg.tex | 622 ++++
source/umontreal/iro/lecuyer/rng/RandRijndael.java | 299 ++
source/umontreal/iro/lecuyer/rng/RandRijndael.tex | 307 ++
.../iro/lecuyer/rng/RandomPermutation.java | 673 +++++
.../iro/lecuyer/rng/RandomPermutation.tex | 604 ++++
source/umontreal/iro/lecuyer/rng/RandomStream.java | 192 ++
source/umontreal/iro/lecuyer/rng/RandomStream.tex | 261 ++
.../iro/lecuyer/rng/RandomStreamBase.java | 266 ++
.../umontreal/iro/lecuyer/rng/RandomStreamBase.tex | 267 ++
.../iro/lecuyer/rng/RandomStreamFactory.java | 68 +
.../iro/lecuyer/rng/RandomStreamFactory.tex | 70 +
.../rng/RandomStreamInstantiationException.java | 147 +
.../rng/RandomStreamInstantiationException.tex | 140 +
.../iro/lecuyer/rng/RandomStreamManager.java | 157 +
.../iro/lecuyer/rng/RandomStreamManager.tex | 153 +
.../iro/lecuyer/rng/RandomStreamWithCache.java | 348 +++
.../iro/lecuyer/rng/RandomStreamWithCache.tex | 344 +++
.../iro/lecuyer/rng/Rijndael_Algorithm.java | 860 ++++++
.../iro/lecuyer/rng/Rijndael_Properties.java | 201 ++
.../iro/lecuyer/rng/TruncatedRandomStream.java | 96 +
.../iro/lecuyer/rng/TruncatedRandomStream.tex | 101 +
source/umontreal/iro/lecuyer/rng/WELL1024.java | 325 ++
source/umontreal/iro/lecuyer/rng/WELL1024.tex | 338 +++
source/umontreal/iro/lecuyer/rng/WELL512.java | 304 ++
source/umontreal/iro/lecuyer/rng/WELL512.tex | 318 ++
source/umontreal/iro/lecuyer/rng/WELL607.java | 229 ++
source/umontreal/iro/lecuyer/rng/WELL607.tex | 239 ++
source/umontreal/iro/lecuyer/rng/WELL607base.java | 167 ++
source/umontreal/iro/lecuyer/rng/guiderng.bbl | 105 +
source/umontreal/iro/lecuyer/rng/guiderng.tex | 60 +
source/umontreal/iro/lecuyer/rng/overview.tex | 98 +
.../iro/lecuyer/simevents/Accumulate.java | 318 ++
.../umontreal/iro/lecuyer/simevents/Accumulate.tex | 330 +++
.../iro/lecuyer/simevents/Continuous.java | 268 ++
.../umontreal/iro/lecuyer/simevents/Continuous.tex | 303 ++
.../iro/lecuyer/simevents/ContinuousState.java | 271 ++
.../iro/lecuyer/simevents/ContinuousState.tex | 278 ++
source/umontreal/iro/lecuyer/simevents/Event.java | 369 +++
source/umontreal/iro/lecuyer/simevents/Event.tex | 423 +++
.../iro/lecuyer/simevents/LinkedListStat.java | 190 ++
.../iro/lecuyer/simevents/LinkedListStat.tex | 206 ++
.../iro/lecuyer/simevents/ListWithStat.java | 627 ++++
.../iro/lecuyer/simevents/ListWithStat.tex | 627 ++++
source/umontreal/iro/lecuyer/simevents/Sim.java | 156 +
source/umontreal/iro/lecuyer/simevents/Sim.tex | 175 ++
.../umontreal/iro/lecuyer/simevents/Simulator.java | 285 ++
.../umontreal/iro/lecuyer/simevents/Simulator.tex | 291 ++
.../lecuyer/simevents/eventlist/BinaryTree.java | 772 +++++
.../iro/lecuyer/simevents/eventlist/BinaryTree.tex | 777 +++++
.../lecuyer/simevents/eventlist/DoublyLinked.java | 491 +++
.../lecuyer/simevents/eventlist/DoublyLinked.tex | 496 ++++
.../iro/lecuyer/simevents/eventlist/EventList.java | 203 ++
.../iro/lecuyer/simevents/eventlist/EventList.tex | 203 ++
.../iro/lecuyer/simevents/eventlist/Henriksen.java | 526 ++++
.../iro/lecuyer/simevents/eventlist/Henriksen.tex | 529 ++++
.../lecuyer/simevents/eventlist/RedblackTree.java | 419 +++
.../lecuyer/simevents/eventlist/RedblackTree.tex | 422 +++
.../iro/lecuyer/simevents/eventlist/SplayTree.java | 878 ++++++
.../iro/lecuyer/simevents/eventlist/SplayTree.tex | 882 ++++++
.../iro/lecuyer/simevents/eventlist/overview.tex | 10 +
.../iro/lecuyer/simevents/guidesimevents.bbl | 25 +
.../iro/lecuyer/simevents/guidesimevents.tex | 52 +
.../umontreal/iro/lecuyer/simevents/overview.tex | 36 +
source/umontreal/iro/lecuyer/simevents/title.tex | 44 +
source/umontreal/iro/lecuyer/simprocs/Bin.java | 420 +++
source/umontreal/iro/lecuyer/simprocs/Bin.tex | 418 +++
.../umontreal/iro/lecuyer/simprocs/Condition.java | 248 ++
.../umontreal/iro/lecuyer/simprocs/Condition.tex | 247 ++
.../iro/lecuyer/simprocs/DSOLProcessSimulator.java | 210 ++
.../iro/lecuyer/simprocs/DSOLProcessSimulator.tex | 217 ++
.../iro/lecuyer/simprocs/ProcessSimulator.java | 205 ++
.../iro/lecuyer/simprocs/ProcessSimulator.tex | 210 ++
.../umontreal/iro/lecuyer/simprocs/Resource.java | 636 ++++
source/umontreal/iro/lecuyer/simprocs/Resource.tex | 646 ++++
.../lecuyer/simprocs/SSJInterpretationOracle.java | 98 +
.../lecuyer/simprocs/SSJInterpretationOracle.tex | 101 +
.../umontreal/iro/lecuyer/simprocs/SimProcess.java | 409 +++
.../umontreal/iro/lecuyer/simprocs/SimProcess.tex | 462 +++
.../lecuyer/simprocs/ThreadProcessSimulator.java | 314 ++
.../lecuyer/simprocs/ThreadProcessSimulator.tex | 321 ++
.../umontreal/iro/lecuyer/simprocs/UserRecord.java | 100 +
.../umontreal/iro/lecuyer/simprocs/UserRecord.tex | 105 +
.../iro/lecuyer/simprocs/guidesimprocs.bbl | 53 +
.../iro/lecuyer/simprocs/guidesimprocs.tex | 48 +
source/umontreal/iro/lecuyer/simprocs/overview.tex | 91 +
source/umontreal/iro/lecuyer/simprocs/title.tex | 24 +
.../iro/lecuyer/stat/ObservationListener.java | 48 +
.../iro/lecuyer/stat/ObservationListener.tex | 49 +
source/umontreal/iro/lecuyer/stat/StatProbe.java | 432 +++
source/umontreal/iro/lecuyer/stat/StatProbe.tex | 426 +++
source/umontreal/iro/lecuyer/stat/Tally.java | 686 +++++
source/umontreal/iro/lecuyer/stat/Tally.tex | 661 +++++
.../umontreal/iro/lecuyer/stat/TallyHistogram.java | 255 ++
.../umontreal/iro/lecuyer/stat/TallyHistogram.tex | 254 ++
source/umontreal/iro/lecuyer/stat/TallyStore.java | 249 ++
source/umontreal/iro/lecuyer/stat/TallyStore.tex | 263 ++
source/umontreal/iro/lecuyer/stat/guidestat.bbl | 8 +
source/umontreal/iro/lecuyer/stat/guidestat.tex | 45 +
.../stat/list/ArrayOfObservationListener.java | 51 +
.../stat/list/ArrayOfObservationListener.tex | 52 +
.../iro/lecuyer/stat/list/ListOfStatProbes.java | 469 +++
.../iro/lecuyer/stat/list/ListOfStatProbes.tex | 454 +++
.../iro/lecuyer/stat/list/ListOfTallies.java | 427 +++
.../iro/lecuyer/stat/list/ListOfTallies.tex | 398 +++
.../stat/list/ListOfTalliesWithCovariance.java | 323 ++
.../stat/list/ListOfTalliesWithCovariance.tex | 305 ++
.../umontreal/iro/lecuyer/stat/list/overview.tex | 51 +
source/umontreal/iro/lecuyer/stat/overview.tex | 100 +
.../iro/lecuyer/stochprocess/BrownianMotion.java | 263 ++
.../iro/lecuyer/stochprocess/BrownianMotion.tex | 255 ++
.../lecuyer/stochprocess/BrownianMotionBridge.java | 330 +++
.../lecuyer/stochprocess/BrownianMotionBridge.tex | 326 ++
.../lecuyer/stochprocess/BrownianMotionPCA.java | 171 ++
.../iro/lecuyer/stochprocess/BrownianMotionPCA.tex | 174 ++
.../stochprocess/BrownianMotionPCAEqualSteps.java | 162 +
.../stochprocess/BrownianMotionPCAEqualSteps.tex | 172 ++
.../iro/lecuyer/stochprocess/CIRProcess.java | 303 ++
.../iro/lecuyer/stochprocess/CIRProcess.tex | 313 ++
.../iro/lecuyer/stochprocess/CIRProcessEuler.java | 251 ++
.../iro/lecuyer/stochprocess/CIRProcessEuler.tex | 258 ++
.../iro/lecuyer/stochprocess/GammaProcess.java | 304 ++
.../iro/lecuyer/stochprocess/GammaProcess.tex | 296 ++
.../lecuyer/stochprocess/GammaProcessBridge.java | 299 ++
.../lecuyer/stochprocess/GammaProcessBridge.tex | 300 ++
.../iro/lecuyer/stochprocess/GammaProcessPCA.java | 216 ++
.../iro/lecuyer/stochprocess/GammaProcessPCA.tex | 204 ++
.../stochprocess/GammaProcessPCABridge.java | 205 ++
.../lecuyer/stochprocess/GammaProcessPCABridge.tex | 213 ++
.../GammaProcessPCASymmetricalBridge.java | 144 +
.../GammaProcessPCASymmetricalBridge.tex | 152 +
.../GammaProcessSymmetricalBridge.java | 242 ++
.../stochprocess/GammaProcessSymmetricalBridge.tex | 241 ++
.../stochprocess/GeometricBrownianMotion.java | 218 ++
.../stochprocess/GeometricBrownianMotion.tex | 212 ++
.../lecuyer/stochprocess/GeometricLevyProcess.java | 227 ++
.../lecuyer/stochprocess/GeometricLevyProcess.tex | 220 ++
.../GeometricNormalInverseGaussianProcess.java | 153 +
.../GeometricNormalInverseGaussianProcess.tex | 153 +
.../GeometricVarianceGammaProcess.java | 284 ++
.../stochprocess/GeometricVarianceGammaProcess.tex | 290 ++
.../stochprocess/InverseGaussianProcess.java | 248 ++
.../stochprocess/InverseGaussianProcess.tex | 244 ++
.../stochprocess/InverseGaussianProcessBridge.java | 285 ++
.../stochprocess/InverseGaussianProcessBridge.tex | 293 ++
.../stochprocess/InverseGaussianProcessMSH.java | 291 ++
.../stochprocess/InverseGaussianProcessMSH.tex | 297 ++
.../stochprocess/InverseGaussianProcessPCA.java | 174 ++
.../stochprocess/InverseGaussianProcessPCA.tex | 180 ++
.../stochprocess/NormalInverseGaussianProcess.java | 417 +++
.../stochprocess/NormalInverseGaussianProcess.tex | 398 +++
.../stochprocess/OrnsteinUhlenbeckProcess.java | 260 ++
.../stochprocess/OrnsteinUhlenbeckProcess.tex | 266 ++
.../OrnsteinUhlenbeckProcessEuler.java | 160 +
.../stochprocess/OrnsteinUhlenbeckProcessEuler.tex | 160 +
.../lecuyer/stochprocess/StochasticProcess.java | 327 ++
.../iro/lecuyer/stochprocess/StochasticProcess.tex | 316 ++
.../lecuyer/stochprocess/VarianceGammaProcess.java | 314 ++
.../lecuyer/stochprocess/VarianceGammaProcess.tex | 307 ++
.../stochprocess/VarianceGammaProcessDiff.java | 323 ++
.../stochprocess/VarianceGammaProcessDiff.tex | 294 ++
.../stochprocess/VarianceGammaProcessDiffPCA.java | 180 ++
.../stochprocess/VarianceGammaProcessDiffPCA.tex | 184 ++
.../VarianceGammaProcessDiffPCABridge.java | 69 +
.../VarianceGammaProcessDiffPCABridge.tex | 72 +
...rianceGammaProcessDiffPCASymmetricalBridge.java | 73 +
...arianceGammaProcessDiffPCASymmetricalBridge.tex | 79 +
.../iro/lecuyer/stochprocess/guidestochprocess.bbl | 99 +
.../iro/lecuyer/stochprocess/guidestochprocess.tex | 89 +
.../iro/lecuyer/stochprocess/overview.tex | 14 +
.../umontreal/iro/lecuyer/util/AbstractChrono.java | 203 ++
.../umontreal/iro/lecuyer/util/AbstractChrono.tex | 226 ++
.../umontreal/iro/lecuyer/util/ArithmeticMod.java | 595 ++++
.../umontreal/iro/lecuyer/util/ArithmeticMod.tex | 554 ++++
source/umontreal/iro/lecuyer/util/BitMatrix.java | 595 ++++
source/umontreal/iro/lecuyer/util/BitMatrix.tex | 577 ++++
source/umontreal/iro/lecuyer/util/BitVector.java | 657 ++++
source/umontreal/iro/lecuyer/util/BitVector.tex | 644 ++++
source/umontreal/iro/lecuyer/util/Chrono.c | 81 +
source/umontreal/iro/lecuyer/util/Chrono.java | 80 +
source/umontreal/iro/lecuyer/util/Chrono.tex | 93 +
.../iro/lecuyer/util/ChronoSingleThread.java | 67 +
.../iro/lecuyer/util/ChronoSingleThread.tex | 76 +
source/umontreal/iro/lecuyer/util/ClassFinder.java | 369 +++
source/umontreal/iro/lecuyer/util/ClassFinder.tex | 357 +++
source/umontreal/iro/lecuyer/util/DMatrix.java | 1048 +++++++
source/umontreal/iro/lecuyer/util/DMatrix.tex | 1039 +++++++
.../iro/lecuyer/util/DoubleArrayComparator.java | 75 +
.../iro/lecuyer/util/DoubleArrayComparator.tex | 86 +
.../iro/lecuyer/util/GlobalCPUTimeChrono.java | 72 +
.../iro/lecuyer/util/GlobalCPUTimeChrono.tex | 80 +
.../umontreal/iro/lecuyer/util/Introspection.java | 394 +++
.../umontreal/iro/lecuyer/util/Introspection.tex | 376 +++
source/umontreal/iro/lecuyer/util/JDBCManager.java | 859 ++++++
source/umontreal/iro/lecuyer/util/JDBCManager.tex | 842 ++++++
source/umontreal/iro/lecuyer/util/Misc.java | 330 +++
source/umontreal/iro/lecuyer/util/Misc.tex | 302 ++
.../iro/lecuyer/util/MultivariateFunction.java | 125 +
.../iro/lecuyer/util/MultivariateFunction.tex | 107 +
.../iro/lecuyer/util/NameConflictException.java | 102 +
.../iro/lecuyer/util/NameConflictException.tex | 107 +
source/umontreal/iro/lecuyer/util/Num.java | 1385 +++++++++
source/umontreal/iro/lecuyer/util/Num.tex | 1332 +++++++++
.../umontreal/iro/lecuyer/util/PrintfFormat.java | 1345 +++++++++
source/umontreal/iro/lecuyer/util/PrintfFormat.tex | 1277 ++++++++
.../umontreal/iro/lecuyer/util/RatioFunction.java | 125 +
.../umontreal/iro/lecuyer/util/RatioFunction.tex | 127 +
source/umontreal/iro/lecuyer/util/RootFinder.java | 241 ++
source/umontreal/iro/lecuyer/util/RootFinder.tex | 241 ++
.../iro/lecuyer/util/SystemTimeChrono.java | 61 +
.../iro/lecuyer/util/SystemTimeChrono.tex | 69 +
source/umontreal/iro/lecuyer/util/Systeme.java | 92 +
source/umontreal/iro/lecuyer/util/Systeme.tex | 105 +
source/umontreal/iro/lecuyer/util/TableFormat.java | 342 +++
source/umontreal/iro/lecuyer/util/TableFormat.tex | 321 ++
.../umontreal/iro/lecuyer/util/TextDataReader.java | 1000 +++++++
.../umontreal/iro/lecuyer/util/TextDataReader.tex | 946 ++++++
.../iro/lecuyer/util/ThreadCPUTimeChrono.java | 110 +
.../iro/lecuyer/util/ThreadCPUTimeChrono.tex | 113 +
source/umontreal/iro/lecuyer/util/TimeUnit.java | 200 ++
source/umontreal/iro/lecuyer/util/TimeUnit.tex | 198 ++
.../iro/lecuyer/util/TransformingList.java | 208 ++
.../iro/lecuyer/util/TransformingList.tex | 212 ++
source/umontreal/iro/lecuyer/util/guideutil.bbl | 25 +
source/umontreal/iro/lecuyer/util/guideutil.tex | 87 +
.../iro/lecuyer/util/io/AbstractDataReader.java | 199 ++
.../iro/lecuyer/util/io/AbstractDataReader.tex | 218 ++
.../iro/lecuyer/util/io/AbstractDataWriter.java | 79 +
.../iro/lecuyer/util/io/AbstractDataWriter.tex | 89 +
.../iro/lecuyer/util/io/BinaryDataReader.java | 438 +++
.../iro/lecuyer/util/io/BinaryDataReader.tex | 459 +++
.../iro/lecuyer/util/io/BinaryDataWriter.java | 438 +++
.../iro/lecuyer/util/io/BinaryDataWriter.tex | 447 +++
.../iro/lecuyer/util/io/CachedDataWriter.java | 193 ++
.../iro/lecuyer/util/io/CachedDataWriter.tex | 217 ++
.../umontreal/iro/lecuyer/util/io/DataField.java | 313 ++
source/umontreal/iro/lecuyer/util/io/DataField.tex | 345 +++
.../umontreal/iro/lecuyer/util/io/DataReader.java | 179 ++
.../umontreal/iro/lecuyer/util/io/DataReader.tex | 206 ++
.../umontreal/iro/lecuyer/util/io/DataWriter.java | 175 ++
.../umontreal/iro/lecuyer/util/io/DataWriter.tex | 213 ++
.../iro/lecuyer/util/io/TextDataWriter.java | 320 ++
.../iro/lecuyer/util/io/TextDataWriter.tex | 340 +++
source/umontreal/iro/lecuyer/util/io/overview.tex | 127 +
source/umontreal/iro/lecuyer/util/overview.tex | 7 +
...montreal_iro_lecuyer_util_GlobalCPUTimeChrono.h | 22 +
ssj.properties | 15 +
ssjant.bat | 3 +
usessj | 39 +
usessj.sh | 31 +
1000 files changed, 254183 insertions(+), 215 deletions(-)
diff --git a/COMPILE.txt b/COMPILE.txt
new file mode 100644
index 0000000..464d9a2
--- /dev/null
+++ b/COMPILE.txt
@@ -0,0 +1,186 @@
+Compiling SSJ
+===========
+To rebuild SSJ, the SSJ source distribution is required. The TCode
+tool is needed, together with a Perl interpreter, a Java 2 compiler
+and Apache Ant 1.5.3 or later. To rebuild the Javadoc documentation,
+LaTeX2HTML is also needed.
+To build SSJ under Windows, the MSYS and MinGW packages are required.
+Although Ant can be called from the DOS prompt directly, the MSYS
+shell will be needed to compile UNURAN under Windows.
+
+Unpacking the SSJ source distribution ZIP file will create a ssj
+subdirectory containing all the required files. This source directory
+will be called here SSJSRCHOME, but it does not have to be set as an
+environment variable. However, for development convenience, SSJHOME
+should be set to SSJSRCHOME and SSJ should be initialized using one of
+the Ssjrc, Ssj.sh or Ssj.bat scripts in the SSJSRCHOME directory. The
+directory contains the following files and directories
+
+ bin - Files included in the binary distribution's root directory
+ build - This directory is created during the build process and
+ contains all the class files.
+ doc - This directory is created during the build process and
+ contains the Javadoc documentation.
+ lib - This directory is created during the build process and
+ contains the JAR file and shared libraries.
+ source - source tree containing all the SSJ LaTeX files. When
+ texjava.pl is used, this directory also contains Java
+ files.
+ build.xml - Ant build file
+ README.txt - Readme file
+ COMPILE.txt - This file
+ INSTALL.txt - Installation manual
+ Ssjrc - C-shell initialization script
+ Ssj.sh - Bourne shell initialization script
+ Ssj.bat - Windows initialization script
+ ssj.properties - User-customizable properties file
+
+
+The ssj.properties file must be edited to customize the build process.
+This file is a standard ASCII files containing name=value or name
+lines. Each name corresponds to a property that will be used by the
+Ant build system. If a pound sign precedes a line, this line will be
+ignored. This can be used to unset unmamed properties. In that file,
+the texjava.texjava variable must point to the absolute path of
+texjava.pl, including texjava.pl itself. The path can also be relative
+to SSJHOME. If LaTeX2HTML is not available, texjava.html must be set
+to no and ssj.htmldoc should be unset. The former will prevent
+texjava.pl from trying to call LaTeX2HTML whereas the latter will
+prevent building the Javadoc documentation. If pdfLaTeX is not
+available, the ssj.pdfdoc line must be commented to avoid bulding the
+PDF documentation. To avoid building the JNI (Chrono or UNURAN
+support) libraries, the ssj.buildjnichrono or ssj.buildjniunuran must
+be unset. The ssj.debug can be changed as needed; it affects every call
+to the java compiler.
+
+SSJ is provided with an Ant build.xml file that should not have to be
+modified to build SSJ successfully. It defines targets allowing to
+perform the build tasks. Targets are specified as an argument to the
+ant command. There is one target for each SSJ subpackage along with
+maintenance targets allowing to perform tasks related to the entire
+SSJ package. For example, to recompile the Probdist package, the
+following command can be used
+
+ ant probdist
+
+The current directory must be SSJSRCHOME or Ant will not find the
+build.xml file. To address this inconvenience, the setup scripts of SSJ
+define an alias called ssjant which acts like ant but can be executed
+everywhere. For each subpackage target, there is a j-suffixed target
+that only converts the .tex files into .java file. This is useful to
+build the documentation without recompiling the sources. For example,
+randvarj will convert the .tex files into .java files for the
+umontreal.iro.lecuyer.randvar package. These targets have no
+description, so they will not be displayed when typing ssjant
+-projecthelp.
+
+The build.xml also provides targets to build the SSJ JAR file and
+documentation and to distribute the library.
+
+lib:
+ The lib target builds ssj.jar into SSJSRCHOME/lib/ssj.jar. The
+ compiled class files are stored into the SSJSRCHOME's build
+ subdirectory. They are not needed as soon as the JAR file is
+ generated.
+
+jni:
+ The jni target can be used to compile to native C libraries for the
+ Chrono and UNURAN support. These libraries will be compiled only if
+ the ssj.buildjnichrono or ssj.buildjniunuran properties are set in
+ ssj.properties.
+
+doc, pdfdoc and htmldoc:
+ The pdfdoc and htmldoc respectively create the PDF and HTML
+ documentation. The doc target can be used as a shortcut to create
+ both documentation formats. The HTML documentation is stored into
+ SSJSRCHOME/doc whereas the PDF files are stored in subpackage's
+ directories. For example, the guideutil.pdf is stored in
+ SSJSRCHOME/source/umontreal/iro/lecuyer/util/guideutil.pdf.
+
+dist:
+ The dist target can be used to create a binary-only SSJ
+ distribution. The distribution comprises the ssj.jar archive,
+ the shared libraries for native code, the PDF and HTML
+ documentation, and the example programs.
+ The binary distribution offers simpler setup scripts that are
+ copied from the bin directory of the source distribution. The
+ binary distribution should not be unpacked into the source
+ distribution, otherwise the source distribution setup scripts would
+ be overwritten.
+
+srcdist:
+ The srcdist target can be used to package the source distribution.
+ It cleans all generated files and directories and zips the SSJHOME
+ directory.
+
+clean targets:
+ The clean targets can be used to delete generated
+ files. cleanbuild, cleanlib and cleandoc erases the build, doc and
+ lib directories, respectively. The clean target removes all backup
+ and LaTeX auxiliary files from the source tree. The cleanmore
+ target executes clean and removes all classes and HTML files from
+ the source tree, but source/overview.html remains untouched. The
+ cleanall target calls cleanmore and removes the .bbl files from the
+ source tree. After cleanall is applied, the SSJSRCHOME directory is
+ ready to be packaged and distributed.
+
+JNI libraries
+==============
+SSJ contains two optional native-mode libraries: Chrono and
+UNURAN. The Chrono can be used to determine the CPU time taken by
+parts of a Java application whereas UNURAN is a rich set of
+non-uniform random number generators. The native code is compiled
+using the jni project target which create files in the SSJSRCHOME/lib
+subdirectory. To be able to compile DLLs using Ant, the PATH
+environment variable must point to the location of GCC (usually
+c:\mingw\bin), and the C_INCLUDE_PATH and LIBRARY_PATH must be set to
+include and library paths, respectively (usually c:\msys\1.0\include
+and c:\msys\1.0\lib, respectively).
+
+Chrono can be compiled easily under Linux and Windows because it does
+not require any additional library. Its compilation will generate
+libssjutil.so under Linux and ssjutil.dll under Windows.
+
+To use the interface to UNURAN, the UNURAN library must be compiled
+and installed. Inside MSYS shell, the installation is the same as in Linux.
+See the UNURAN user's manual for more information about
+the installation procedure. To get the library compatible with SSJ,
+UNURAN must be configured to use the UNUR_URNG_GENERIC generator type.
+It is also a good idea to disable logging in order to avoid cluttering
+directories with log files. Disabling shared library compilation, by
+passing --disable-shared to UNURAN's configure script, is
+recommended. It will make a self-contained shared library and the
+builded SSJ will not need UNURAN to be installed on the target system.
+
+Development environment
+=======================
+The SSJ setup scripts define some useful aliases that can save time
+when working with the SSJ source tree.
+
+ssjant:
+ The ssjant alias mentioned earlier allows to call Ant with the
+ SSJ's build.xml from any directory. This allows to compile and
+ test parts of the library without having to go to the root SSJHOME
+ directory all the times.
+
+cdssj:
+ The cdssj alias can be used to change to current directory to any
+ SSJHOME's subdirectory. For example,
+
+ cdssj doc
+
+ will switch to SSJHOME/doc.
+
+cdssjsrc:
+ The cdssjsrc alias is similar but it is rooted to the
+ SSJHOME/source/umontreal/iro/lecuyer directory. It can be useful to
+ cd to a subpackage source directory. For example,
+
+ cdssjsrc randvar
+
+ will switch to the randvar source directory.
+
+Envirnment variables:
+ The SSJSRC environment variable contains
+ SSJHOME/source/umontreal/iro/lecuyer. It can be used in text
+ editors unaware of shell aliases.
diff --git a/INSTALL.txt b/INSTALL.txt
new file mode 100644
index 0000000..d4a1268
--- /dev/null
+++ b/INSTALL.txt
@@ -0,0 +1,162 @@
+SSJ Installation Manual
+=======================
+
+1. Requirements
+==================================
+
+SSJ requires a Java 2 Virtual Machine.
+
+-----------------------
+The Colt library is used by a few SSJ classes. The library, its source
+code and documentation, can be downloaded for free from its home page.
+The colt.jar archive is already included in the SSJ distribution and it
+must be in the CLASSPATH environment variable.
+
+
+-----------------------
+The Blas library is based on public domain LINPACK routines.
+It was translated from FORTRAN to Java by Steve Verrill at
+ USDA Forest Products Laboratory
+ 1 Gifford Pinchot Drive
+ Madison, Wisconsin, USA.
+This software is also in the public domain and is included in the
+SSJ distribution as the Blas.jar archive. It is used only in the
+probdist package to compute maximum likelihood estimators.
+
+
+-----------------------
+The optimization package of Steve Verrill includes Java translations of the
+MINPACK nonlinear least squares routines as well as UNCMIN routines for
+unconstrained optimization. They were translated from FORTRAN to Java by
+Steve Verrill and are in the public domain. They are included in the SSJ
+distribution as the optimization.jar archive. It is used only in the
+probdist package to compute maximum likelihood estimators.
+
+
+-----------------------
+The (url="http://www.jfree.org/jfreechart/") JFreeChart library is
+used by the SSJ package charts to draw curves, histograms
+and different kinds of plots. JFreeChart is copyrighted under the
+(url="http://www.gnu.org/licenses/lgpl.html") GNU LGPL License.
+It is included in the SSJ distribution as the jfreechart-1.0.10.jar
+and the jcommon-1.0.13.jar.
+
+
+-----------------------
+The UNURAN library is used by the classes UnuranContinuous, UnuranDiscrete and
+UnuranEmpirical in the package called randvar. It can be downloaded for free at
+http://statistik.wu-wien.ac.at/unuran/.
+Downloading, compiling and installing UNURAN is optional. It is required only
+if SSJ must be rebuilt. However, the UNURAN documentation is required to use
+the SSJ UNURAN interface efficiently.
+
+
+
+2. Unpacking
+-------------
+Unpack the ZIP file to any location, this will create a ssj subdirectory
+containing all files of the distribution.
+
+For Linux/Unix, use the command
+ unzip ssj-<DATE>.zip
+
+Under Windows, WinZip can be used to unpack the distribution.
+
+After the ssj zip file is extracted, the ssj subdirectory will contain
+the following files and directories.
+
+ lib - SSJ JAR file and shared libraries
+ ssj.jar - SSJ JAR archive containing all classes
+ colt.jar - Colt JAR archive containing all Colt classes
+ libssjutil.so - Linux shared library for the Chrono
+ ssjutil.dll - Windows shared library for the Chrono
+ librandvar.so - Linux shared library for UNURAN
+ randvar.dll - Windows shared library for UNURAN
+ event-1.6.5.jar
+ interpreter-1.6.8.jar
+ language-1.6.7.jar
+ logger-1.6.4.jar
+ Blas.jar - linear_algebra JAR archive containing some LINPACK routines
+ optimization.jar - optimization JAR archive containing some MINPACK
+ and UNCMIN routines
+ jfreechart-1.0.10.jar - JFreeChart library for charts
+ jcommon-1.0.13.jar - useful to JFreeChart
+
+ doc - Documentation
+ html - HTML format
+ pdf - PDF format
+ examples - Some simulation examples in Java
+ README.txt - readme file
+ INSTALL.txt - Installation manual
+ Ssjrc - C-Shell initialization script
+ Ssj.sh - Bourne shell initialization script
+ Ssj.bat - Windows initialization script
+
+
+3. Setup the environment
+------------------------
+To use SSJ, the ssj.jar and the colt.jar must be part of the CLASSPATH
+environment variable. The ssj, Colt, and other JAR files JAR files are
+located in the lib subdirectory of the package. The class path in the
+MANIFEST includes all other jar files in the lib subdirectory.
+
+For the native methods (Chrono and UNURAN support) to work properly, the
+library path must include the lib subdirectory of the SSJ package.
+GlobalCPUTimeChrono is the only class of SSJ that is programmed directly
+in C. It measures the CPU time used by a program. Under UNIX/Linux, this
+can be done by modifying the LD_LIBRARY_PATH variable whereas under
+Windows, it is done by modifying the PATH environment variable. The
+procedure used to modify environment variables depends on the operating
+system. For convenience, scripts are available to setup the environment
+for the user. The SSJHOME environment must be set prior to calling the
+setup script.
+
+Commands (for C-shell) :
+ setenv SSJHOME <path to SSJ>
+ source $SSJHOME/Ssjrc
+
+Commands (for Bourne shell) :
+ export SSJHOME=<path to SSJ>
+ . $SSJHOME/Ssj.sh
+
+Commands (for DOS/Windows) :
+ set SSJHOME=<path to SSJ>
+ call %SSJHOME%\Ssj.bat
+
+Here, <path to SSJ> must be replaced by the absolute path to the
+unpacked ssj subdirectory. The command lines should be added to an
+appropriate startup script for convenience.
+
+Under Windows 2000/XP, there is no startup file. If the user uses the
+Ssj.bat convenience script, he must call it manually every time he
+opens a new DOS prompt. Alternatively, the user can set the
+environment variables manually. The user must right-click on the
+My Computer icon on the desktop, select Properties, access the
+Advanced tab and click on the Environment Variables button. Under
+Windows, the paths are separated using semicolons in the CLASSPATH and
+PATH variables. The CLASSPATH must contain the full path and filename
+to ssj.jar and colt.jar whereas the PATH variable
+contains only the location of the lib subdirectory of SSJ.
+
+Note: Some Java Integrated Development Environments (IDEs) such as
+JCreator do not use the standard CLASSPATH environment variable.
+These environments must be notified about the location of
+colt.jar and ssj.jar manually. The procedure used to set these
+parameters can be found
+in the documentation of the specific Java IDE. If the ssj.jar cannot
+be located, the SSJ programs will not compile or not execute properly.
+
+
+Documentation
+============
+SSJ is bundled with documentation in PDF and HTML formats. It can be
+found in the SSJHOME/doc directory. The html subdirectory contains
+the HTML version processed using Javadoc. It allows to find classes
+and methods documentation easily, but it is difficult to print and
+some information is missing. The PDF documentation, stored in the pdf
+subdirectory, can be easily printed, it is the most complete
+documentation, but method descriptions are not accessible through
+hyperlinks.
+
+The examples subdirectory contains examples of simulation programs
+using SSJ and can be a useful tool to learn SSJ.
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..72d3012
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,533 @@
+==========================================================================
+What is new in version SSJ-2.6
+--------------------------------
+
+- package util
+ * DMatrix: new method solveLU
+
+- package rng
+ * New seeds for the first stream of generators LFSR113 and LFSR258.
+ * Corrections of the streams and substreams in LFSR258: streams and substreams
+ were 2^100 and 2^200 steps apart, instead of 2^200 and 2^100 steps apart.
+
+- package gof
+ * GofStat: new methods:
+ andersonDarling (double[], ContinuousDistribution),
+ kolmogorovSmirnov (double[], ContinuousDistribution, double[], double[])
+
+- package hups
+ * new classes RQMCPointSet
+ * more general KorobovLattice, Rank1Lattice
+
+- package probdist
+ * new classses: JohnsonSLDist, JohnsonSystem, HypoExponentialDistEqual
+ * JohnsonSBDist: new methods getMean, getVariance, getStandardDeviation, getMLE
+ * AndersonDarlingDistQuick: better approximations in the tails for cdf and barF
+ * HypoExponentialDist, HypoExponentialDistQuick:
+ better approximations for cdf and barF
+ * BetaDist: better approximations for cdf and barF
+
+- package randvar:
+ * new classses: JohnsonSystemG, JohnsonSLGen
+
+- package probdistmulti:
+ * new package probdistmulti.norta
+
+- package functions:
+ * new interface MultiFunction
+
+- package functionfit
+ * LeastSquares: new methods calcCoefficients
+
+
+
+==========================================================================
+What is new in version SSJ-2.5
+--------------------------------
+
+- package randvar
+ * New classes: ExponentialInverseFromDensityGen, NormalInverseFromDensityGen,
+ FNoncentralGen, HypoExponentialGen, StudentNoncentralGen,
+ ConstantGen
+
+- package hups
+ * New class: RandomStart
+ * HaltonSequence: new methods: addFaureLemieuxPermutations, setStart
+ * RadicalInverse: new methods: radicalInverseInteger, radicalInverseLong,
+ getFaureLemieuxPermutation
+
+- package charts
+ * New classes: PPPlot, QQPlot
+ * New constructors in classes YListChart and YListSeriesCollection
+
+- package probdist
+ * New classes: HypoExponentialDist, HypoExponentialDistQuick,
+ ConstantIntDist, ConstantDist
+ * Class KolmogorovSmirnovDistQuick: better approximations for cdf, barF
+
+- package stat
+ * New class: TallyHistogram
+
+- package util
+ * New class: Systeme
+ * New package: util.io
+ * DMatrix: new methods exp for matrix exponential
+ * Num: new methods sumKahan, erfcInv.
+
+- package gof
+ * GofStat: new methods: chi2(OutcomeCategoriesChi2, int[]),
+ andersonDarling(double[]), kolmogorovSmirnov(double[]),
+
+
+
+==========================================================================
+What is new in version SSJ-2.4
+--------------------------------
+
+- package probdist
+ * New class: BernoulliDist
+ * NegativeBinomialDist: new methods getMLE1, getInstanceFromMLE1.
+ * TriangularDist: new methods getMLE, getInstanceFromMLE.
+ * GammaDist: more precise inverseF for small u.
+
+- package randvar
+ * The class Pearson5Gen has been renamed InverseGammaGen.
+ * New class: BernoulliGen
+
+- package rng
+ * MT19937: correction in the initialization.
+
+- package util
+ * Misc: new method evalPoly.
+ * RootFinder: new method bisection.
+
+
+
+==========================================================================
+What is new in version SSJ-2.3
+--------------------------------
+
+- package probdist
+ * TruncatedDist: bug corrections.
+ * BetaSymmetricalDist: more precise barF in the tail.
+ * New classes: StudentDist, StudentDistQuick, InverseGammaDist.
+ The old class StudentDist has been renamed StudentDistQuick, and
+ a new StudentDist has been added.
+ * The class Pearson5Dist has been renamed InverseGammaDist.
+ * KolmogorovSmirnovDistQuick: better versions of cdf and barF.
+
+- package randvar
+ * New classes InverseGammaGen
+
+- package util
+ * Num: news methods lnBeta, expBesselK1, erfInv.
+
+
+
+==========================================================================
+What is new in version SSJ-2.2
+--------------------------------
+
+- new package functionfit
+ * Provides basic tools for curve fitting, polynomial interpolation,
+ least square fit and spline interpolation.
+
+- package probdist
+ * New classes InverseDistFromDensity, GumbelDist, FrechetDist.
+ * KolmogorovSmirnovDistQuick: more precise versions of cdf and barF.
+ * CauchyDist: more precise versions of cdf and barF in the tails.
+ * StudentDist: more precise versions of cdf and barF in the tails.
+
+- package randvar
+ * New classes InverseFromDensityGen, GumbelGen, FrechetGen.
+
+- package randvarmulti
+ * New class IIDMultivariateGen.tex
+
+- package gof
+ * New class KernelDensity.
+
+- package hups
+ * SobolSequence: new constructor reading its parameters from a file.
+ * PointSet: new methods formatPoints, formatPointsBase.
+
+- package functions
+ * MathFunctionUtil: new methods gaussLobatto.
+
+- package util
+ * Corrections in BatchSort
+ * Num: new methods gcd.
+ * PrintfFormat: new method formatBase.
+
+
+
+==========================================================================
+What is new in version SSJ-2.1.3
+--------------------------------
+
+ The HTML doc was incomplete for some class methods in SSJ-2.1.2 due to
+ the automatic Latex to HTML translator eating some of the text.
+
+
+
+==========================================================================
+What is new in version SSJ-2.1.2
+--------------------------------
+
+- package util
+ * New interfaces MultiDimComparable, MultiDimSort
+
+ * New classes SplitSort, OneDimSort, BatchSort, MultiDimComparator
+ DoubleArrayComparator.
+
+ * New methods in class Num: harmonic, harmonic2, bernoulliPoly.
+
+- package simevents
+ * Events can now be scheduled with different priorities in the event lists.
+
+- package hups
+ * New class EmptyRandomization
+
+ * Removed subdirectory dataF2w and all its parameters files. The files have
+ been removed also from the ssj.jar archive. (See class F2wStructure).
+ The files are now stored at address
+ http://www.iro.umontreal.ca/~simardr/ssj-1/dataF2w/.
+
+- package stochprocess
+ * New class OrnsteinUhlenbeckProcess. The old class by the same name
+ has been renamed OrnsteinUhlenbeckProcessEuler.
+
+ * New class CIRProcess. The old class by the same name has been renamed
+ CIRProcessEuler.
+
+- package probdist
+ * ChiSquareDist: corrections in the far tails of methods cdf and barF.
+
+
+
+==========================================================================
+What is new in version SSJ-2.1.1
+--------------------------------
+
+- package charts
+ * New constructors in each *Chart classes and in the related *SeriesCollection
+ classes, that take an additionnal argument (int numPoints).
+
+- package gof
+ * Bug correction in GofStat.chi2(IntArrayList data, ....)
+ The number of categories was not always returned correctly in m[0].
+
+- package probdistmulti
+ * Bug correction in NegativeMultinomialDist.getMaximumLikelihoodEstimate:
+ sometimes did not converge to the right values.
+
+- package util
+ * New class DMatrix
+
+
+
+==========================================================================
+What is new in version SSJ-2.1
+--------------------------------
+
+- new package charts
+ * Allows the creation of charts and graphics, either on screen or in
+ Latex format.
+
+- new package stochprocess
+ * Provides classes to define stochastic processes and to simulate their
+ sample paths.
+
+- package rng
+ * Correction of the setSeed and setPackageSeed methods in LFSR113 and
+ LFSR258, which did not accept all valid seeds.
+
+ * RandomPermutation: added methods to shuffle parts of arrays.
+
+- package probdist
+ * New classes AndersonDarlingDist, AndersonDarlingDistQuick,
+ CramerVonMisesDist, KolmogorovSmirnovPlusDist, KolmogorovSmirnovDist,
+ KolmogorovSmirnovDistQuick, WatsonGDist, WatsonUDist,
+ NormalInverseGaussianDist, RayleighDist, PowerDist, NakagamiDist,
+ FoldedNormalDist, HalfNormalDist.
+
+ * New classes ExponentialDistFromMean, GammaDistFromMoments,
+ LognormalDistFromMoments, ChiSquareNoncentralDist.
+
+ * Bug correction in HypergeometricDist. Combinations sometimes
+ overflowed for large values of parameters.
+
+ * Bug correction in BinomialDist: overflow for some large n and small p.
+
+ * BetaDist: computation of mean and variance for arbitrary [a, b].
+
+ * InverseGaussianDist: new method inverseF
+
+- package randvar
+ * Added constructors, in each class, that do not need a distribution as
+ argument but take instead the parameters determining the distribution.
+
+ * New classes ChiSquareNoncentralGen, ChiSquareNoncentralGamGen,
+ ChiSquareNoncentralPoisGen, JohnsonSBGen, JohnsonSUGen,
+ InverseGaussianGen, InverseGaussianMSHGen, NormalInverseGaussianGen,
+ NormalInverseGaussianIGGen, RayleighGen, PowerGen, NakagamiGen,
+ FoldedNormalGen, HalfNormalGen.
+
+- package randvarmulti
+ * Removed class RandomMultiVariateGen: it has been replaced by
+ RandomMultivariateGen.
+
+ * Deprecated class MultiNormalGen: it has been replaced by
+ MultinormalCholeskyGen, which has been thinned.
+
+ * New class MultinormalGen, mother of all Multinormal*Gen.
+ * New class MultinormalPCAGen.
+
+- package hups
+ * New classes LMScrambleShift, RandomShift, SMScrambleShift,
+ PointSetRandomization.
+
+ * PointSet: new methods getStream, setStream.
+
+- package gof
+ * In class GofFormat, new methods drawDensity, drawCdf.
+ * In class FBar, improved methods to compute the complementary distributions.
+ * In class FDist, improved methods to compute the distribution functions.
+
+- package util
+ * Num: new methods (complementary) error functions erf, erfc. Ratio of
+ 2 gamma functions gammaRatioHalf.
+
+
+
+==========================================================================
+What is new in version SSJ-2.0
+--------------------------------
+
+SSJ-2.0 works ONLY WITH JDK-1.5 (and later versions of Java), and will not work
+with JDK-1.4 or earlier versions of Java.
+
+- new package functions
+ * Used to create function objects and to pass an arbitrary function of one
+ variable as argument to another function.
+
+- package util
+ * New classes ClassFinder, Introspection, TimeUnit, NameConflictException,
+ TransformingList, SystemTimeChrono, ThreadCPUTimeChrono, AbstractChrono,
+ GlobalCPUTimeChrono, MultivariateFunction, RatioFunction.
+
+ * Removed class MathFunction; it is replaced by the new package functions.
+
+ * New methods in JDBCManager, TextDataReader.
+
+- package rng
+ * New interface CloneableRandomStream. All RNG's are now cloneable.
+
+ * All RNG's are now serializable.
+
+ * New class RandomPermutation.
+
+ * New class MRG32k3aL. Same generator as MRG32k3a;
+ implementation with type long.
+
+ * Bug correction in initialization of F2NL607.
+
+- package stat
+ * New class ObservationListener.
+
+ * new package stat.list
+ Provides support for lists of statistical probes.
+
+ * Tally: new methods confidenceIntervalVarianceChi2, formatCIVarianceChi2.
+
+ * TallyStore: the method getArray has been renamed getDoubleArrayList. The
+ new method getArray returns a double[].
+ * TallyStore: new method quickSort.
+
+
+- package simevents
+ * It is now possible to carry many simulations simultaneously with simulator
+ objects and the new Simulator class.
+
+ * New classes ListWithStat, ContinuousState, Simulator.
+
+- package simprocs
+ * New classes ProcessSimulator, SSJInterpretationOracle, DSOLProcessSimulator,
+ ThreadProcessSimulator
+
+- package gof
+ * New method GofStat.chi2Equal.
+
+- package probdist
+ * Bug corrections and improvements in some *Dist.getMaximumLikelihoodEstimate
+
+ * Improvements of some distributions far in the tails.
+
+ * Added getParams and toString methods in all probability classes.
+
+ * All methods getMaximumLikelihoodEstimate have been renamed getMLE.
+ The old methods are still there but deprecated.
+
+- package randvar
+ * The generator NegativeBinomialGen now takes a real as its parameter n.
+ The old NegativeBinomialGen with integer n has been renamed as the new
+ class PascalGen.
+
+- package randvarmulti
+ * Class MultiNormalGen has been renamed MultinormalCholeskyGen.
+ * Class RandomMultiVariateGen has been renamed RandomMultivariateGen.
+
+
+
+==========================================================================
+
+Update to JDK-1.5 for future versions of SSJ
+
+==========================================================================
+What is new in version SSJ-1.2
+--------------------------------
+
+- new package probdistmulti
+ For multivariate probability distributions.
+
+- new package randvarmulti
+ For multivariate non-uniform random number generators.
+
+- package probdist
+ * Removed the two-dimensionnal distributions BiNormalDist, BiNormalGenzDist,
+ ContinuousDistribution2Dim, BiNormalDonnellyDist, BivariateTDist and
+ moved them to package probdistmulti. The BivariateTDist distribution has
+ been renamed BiStudentDist.
+
+ * New methods getMean, getVariance and getStandardDeviation to get the mean,
+ the variance and the standard deviation for probability distributions.
+
+ * New methods getMaximumLikelihoodEstimate and getInstanceFromMLE that imple-
+ ment the maximum likelihood estimator for most probability distributions.
+
+ * New probability distributions: UniformIntDist, FatigueLifeDist, PascalDist,
+ FisherFDist, HyperbolicSecantDist, InverseGaussianDist, LoglogisticDist,
+ Pearson5Dist, Pearson6Dist.
+
+ * The distribution NegativeBinomialDist now takes a double instead of an int
+ as its first parameter; the distribution with an integer as first
+ parameter is now called PascalDist (it is the former NegativeBinomialDist).
+
+ * New methods in DistributionFactory.
+
+- package randvar
+ * New non-uniform random number generators: UniformIntGen, FatigueLifeGen,
+ FisherFGen, HyperbolicSecantGen, InverseGaussianGen, LoglogisticGen,
+ Pearson5Gen, Pearson6Gen.
+
+ * New class RandomVariateGenWithCache: caches random values for more
+ efficiency when using common random numbers.
+
+ * The generator NegativeBinomialConvolutionGen has been renamed
+ PascalConvolutionGen.
+
+- package rng
+ * New classes RandomStreamWithCache, which caches random values for more
+ efficiency, and TruncatedRandomStream, which generates uniform random
+ numbers in a subinterval of [0, 1].
+
+- package util
+ * New classes TextDataReader (reads data from text files), JDBCManager (
+ interface for accessing a database), MathFunction (to pass an arbitrary
+ function as argument to another) and Misc (miscellaneous functions).
+
+ * New RootFinder class to solve one-dimensional non-linear equations.
+
+ * New methods Num.digamma, Num.trigamma, Num.tetragamma: the logarithmic
+ derivatives of the Gamma function. Added Num.evalChebyStar for the
+ evaluation of shifted Chebychev polynomials.
+
+ * New methods PrintfFormat.formatWithError to print real intervals.
+
+
+
+==========================================================================
+What is new in version SSJ-1.1.9
+
+- package util
+ Added Num.log1p(x) for better accuracy of log(1 + x) at small x.
+
+- package randvar
+ Added the classes BetaSymmetricalPolarGen and BetaSymmetricalBestGen
+ that generate symmetrical Beta variates.
+
+- package rng
+ Provided a faster method to jump the the next substreams in all the WELL*
+ generators and in the F2NL607 generator.
+
+- package probdist
+ * Replaced all functions Math.log(1+x) by Num.log1p(x) for better accuracy.
+
+ * Corrected LogarithmicDist:
+ the probabilities were not always computed correctly.
+
+ * Corrected a bug in BinomialDist:
+ the method inverseF(u) had a bug for n = 1.
+
+ * Added the bivariate normal distribution:
+ classes BiNormalDist, BiNormalDonnellyDist and BiNormalGenzDist
+
+ * Added the bivariate t distribution:
+ class BivariateTDist.
+
+ * Added the Brent-Dekker method to compute the inverse distribution function
+ for an arbitrary continuous probability function.
+
+- package hups
+ * Changed the constructors in class NiedXingSequenceBase2. The old 3
+ arguments constructor has been eliminated and the 4 arguments constructor
+ of the previous version has become the 3 arguments constructor
+ of the new version. This gives the correct generating matrices for the
+ Niederreiter-Xing sequence; the old 3 arguments constructor used
+ the transposed generating matrices.
+
+ * Correction in NiedXingSequenceBase2 and in NiedSequenceBase2 and in
+ DigitalNetBase2FromFile: did not work correctly for number of bits = w < 31.
+
+
+
+==========================================================================
+What is new in version SSJ-1.1.8
+--------------------------------
+
+- package rng
+ LFSR113, LFSR258:
+ programmed a faster method nextInt
+
+- package gof
+ FBar, FDist:
+ added a better approximation for the Anderson-Darling distribution.
+
+- package probdist
+ BetaSymmetricalDist:
+ added a much faster inverse distribution for parameter alpha > 1.
+
+ BetaDist:
+ corrected a bug that gave the wrong values for the CDF for some
+ range of parameters.
+
+ BinomialDist, PoissonDist,
+ ExponentialDist, JohnsonSUDist, ParetoDist, WeibullDist:
+ added a better approximation in the tails of the distributions.
+
+ GammaDist: added a better approximation for large alpha.
+
+ Corrected a bug in DiscreteDistribution: the inverseF function was
+ not correct for cdf(x) > 0.5.
+
+ Made a correction in EmpiricalDist: the barF function.
+
+- package randvar
+ BetaStratifiedRejectionGen object always returned 0.
+ Bug correction
+
+ GammaRejectionLoglogisticGen object went into infinite loop.
+ Bug correction
+
+- package util
+ class ChronoSingleThread has been added.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..8143dc2
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,15 @@
+SSJ, Stochastic Simulation in Java
+==========================
+
+SSJ is a Java library for stochastic simulation, developed in the
+Département d'Informatique et de Recherche
+Opérationnelle (DIRO), at the Université de Montréal.
+It provides facilities for generating uniform and nonuniform random
+variates, computing different measures related to probability
+distributions, performing goodness-of-fit tests, applying
+quasi-Monte Carlo methods, collecting statistics (elementary),
+and programming discrete-event simulations with both events and processes.
+Additional Java packages are also developed on top of SSJ for simulation
+applications in finance, call centers management, communication networks, etc
+
+See the file INSTALL.txt for more details.
diff --git a/Ssj.bat b/Ssj.bat
new file mode 100644
index 0000000..6ade35b
--- /dev/null
+++ b/Ssj.bat
@@ -0,0 +1,9 @@
+ at echo off
+
+call %SSJHOME%\bin\Ssj.bat
+
+set CLASSPATH=.;%SSJHOME%\build;%SSJHOME%\source;%CLASSPATH%
+set SSJSRC=%SSJHOME%\source\umontreal\iro\lecuyer
+set TEXINPUTS=.:%SSJHOME%\source:%TEXINPUTS%
+
+perl %SSJHOME%\setl2hinit.pl %SSJHOME%\source
diff --git a/Ssj.sh b/Ssj.sh
new file mode 100644
index 0000000..8861cd5
--- /dev/null
+++ b/Ssj.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+export SSJHOME=<SSJ home path>/ssj-2
+
+if [ ! $SSJHOME ]; then
+ echo "You must set SSJHOME before calling this script."
+ exit
+fi
+
+
+case "`uname -m`" in
+ 'i686' )
+ export LD_LIBRARY_PATH=$SSJHOME/lib:$LD_LIBRARY_PATH ;;
+ 'x86_64' )
+ export LD_LIBRARY_PATH=$SSJHOME/lib64:$LD_LIBRARY_PATH ;;
+esac
+
+export CLASSPATH=.:$SSJHOME/lib/ssj.jar:$SSJHOME/lib/colt.jar:$SSJHOME/lib/tcode.jar:$CLASSPATH
+
+export SSJSRC=$SSJHOME/source/umontreal/iro/lecuyer
+export TEXINPUTS=.:$SSJHOME/source:$PGFLIBRARY//:$TEXINPUTS
+
+function cdssj() {
+ cd $SSJHOME/$1
+}
+
+function cdssjsrc() {
+ cd $SSJHOME/source/umontreal/iro/lecuyer/$1
+}
diff --git a/Ssjrc b/Ssjrc
new file mode 100644
index 0000000..25ec650
--- /dev/null
+++ b/Ssjrc
@@ -0,0 +1,56 @@
+#!/bin/csh
+
+setenv TCODEHOME <TCode home path>/tcode
+setenv SSJHOME <SSJ home path>/ssj-2
+
+# For the LaTex documentation of package charts, packages tikz and pgf
+setenv PGFLIBRARY <path to pgf>/tex/pgf-2.00
+
+if ( ! $?TCODEHOME ) then
+ echo "You must set TCODEHOME before calling this script."
+ exit
+endif
+
+# source $TCODEHOME/Tcoderc
+
+if ( ! $?SSJHOME ) then
+ echo "You must set SSJHOME before calling this script."
+ exit
+endif
+
+if ( ! $?LIBRARY_PATH ) then
+ setenv LIBRARY_PATH
+endif
+if ( ! $?CLASSPATH ) then
+ setenv CLASSPATH
+endif
+if ( ! $?LD_LIBRARY_PATH ) then
+ setenv LD_LIBRARY_PATH
+endif
+if ( ! $?TEXINPUTS ) then
+ setenv TEXINPUTS
+endif
+
+setenv CLASSPATH .:$SSJHOME/lib/ssj.jar:$SSJHOME/lib/colt.jar:$SSJHOME/lib/tcode.jar:$CLASSPATH
+
+setenv SSJSRC $SSJHOME/source/umontreal/iro/lecuyer
+
+setenv TEXINPUTS .:$SSJHOME/source:$PGFLIBRARY//:$TEXINPUTS
+
+switch ( "`uname -m`" )
+ case 'i686':
+ setenv LD_LIBRARY_PATH $SSJHOME/lib:$LD_LIBRARY_PATH
+ breaksw
+
+ case 'x86_64':
+ setenv LD_LIBRARY_PATH $SSJHOME/lib64:$LD_LIBRARY_PATH
+ breaksw
+endsw
+
+alias ssjant 'ant -Dbasedir=$SSJHOME -f $SSJHOME/build.xml'
+alias cdssj 'cd $SSJHOME/\!*'
+alias cdssjsrc 'cd $SSJHOME/source/umontreal/iro/lecuyer/\!*'
+alias cdj 'cd $SSJHOME'
+alias cdjj 'cd $SSJHOME/source/umontreal/iro/lecuyer'
+
+# perl $SSJHOME/setl2hinit.pl $SSJHOME/source
diff --git a/bin/Ssj.bat b/bin/Ssj.bat
new file mode 100644
index 0000000..23bb9c2
--- /dev/null
+++ b/bin/Ssj.bat
@@ -0,0 +1,4 @@
+ at echo off
+
+set CLASSPATH=%SSJHOME%\lib\ssj.jar;%SSJHOME%\lib\colt.jar;%SSJHOME%\lib\tcode.jar;%CLASSPATH%
+set PATH=%SSJHOME%;%SSJHOME%\lib;%PATH%
diff --git a/bin/Ssj.sh b/bin/Ssj.sh
new file mode 100644
index 0000000..793b594
--- /dev/null
+++ b/bin/Ssj.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+if [ ! $SSJHOME ]; then
+ echo "You must set SSJHOME before calling this script."
+ exit
+fi
+
+export LD_LIBRARY_PATH=$SSJHOME/lib:$LD_LIBRARY_PATH
+export CLASSPATH=.:$SSJHOME/lib/ssj.jar:$SSJHOME/lib/colt.jar:$SSJHOME/lib/tcode.jar:$CLASSPATH
+
+function cdssj() {
+ cd $SSJHOME/$1
+}
diff --git a/bin/Ssjrc b/bin/Ssjrc
new file mode 100644
index 0000000..1703061
--- /dev/null
+++ b/bin/Ssjrc
@@ -0,0 +1,17 @@
+#!/bin/tcsh
+
+if ( ! $?SSJHOME ) then
+ echo "You must set SSJHOME before calling this script."
+ exit
+endif
+
+if ( ! $?CLASSPATH ) then
+ setenv CLASSPATH
+endif
+if ( ! $?LD_LIBRARY_PATH ) then
+ setenv LD_LIBRARY_PATH
+endif
+
+setenv LD_LIBRARY_PATH $SSJHOME/lib:$LD_LIBRARY_PATH
+setenv CLASSPATH .:$SSJHOME/lib/ssj.jar:$SSJHOME/lib/colt.jar:$SSJHOME/lib/tcode.jar:$CLASSPATH
+alias cdssj 'cd $SSJHOME/\!*'
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..cf85d65
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,641 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="ssj" default="lib">
+<description>
+ Stochastic simulation in Java
+</description>
+
+<property environment="env"/>
+<property file="ssj.properties"/>
+
+<property name="ssj.version" value="2.6"/>
+<property name="putil" value="umontreal/iro/lecuyer/util"/>
+<property name="pprobdist" value="umontreal/iro/lecuyer/probdist"/>
+<property name="pprobdistmulti" value="umontreal/iro/lecuyer/probdistmulti"/>
+<property name="pgof" value="umontreal/iro/lecuyer/gof"/>
+<property name="prng" value="umontreal/iro/lecuyer/rng"/>
+<property name="phups" value="umontreal/iro/lecuyer/hups"/>
+<property name="prandvar" value="umontreal/iro/lecuyer/randvar"/>
+<property name="prandvarmulti" value="umontreal/iro/lecuyer/randvarmulti"/>
+<property name="pstat" value="umontreal/iro/lecuyer/stat"/>
+<property name="psimevents" value="umontreal/iro/lecuyer/simevents"/>
+<property name="psimprocs" value="umontreal/iro/lecuyer/simprocs"/>
+<property name="pfunctions" value="umontreal/iro/lecuyer/functions"/>
+<property name="pfunctionfit" value="umontreal/iro/lecuyer/functionfit"/>
+<property name="pstochprocess" value="umontreal/iro/lecuyer/stochprocess"/>
+<property name="pcharts" value="umontreal/iro/lecuyer/charts"/>
+<property name="pexamples" value="umontreal/iro/lecuyer/examples"/>
+
+<property name="texjava.htmloutdir" value="doc"/>
+
+<taskdef name="texjava" classname="umontreal.iro.lecuyer.tcode.Texjava"/>
+<taskdef name="pdflatex" classname="umontreal.iro.lecuyer.tcode.PdfLatex"/>
+
+<target name="init">
+ <mkdir dir="build"/>
+ <tstamp/>
+ <condition property="ssj.colt">
+ <available classname="cern.colt.list.DoubleArrayList"/>
+ </condition>
+</target>
+
+<!-- ********************************
+ * JNI INTERFACES *
+ ******************************** -->
+
+<target name="jni" description="Builds SSJ native methods, only if ssj.buildjnichrono or ssj.buildjniunuran are set">
+ <echo message="Building JNI"/>
+ <condition property="jnilinux">
+ <os family="unix"/>
+ </condition>
+ <condition property="jniwindows">
+ <os family="windows"/>
+ </condition>
+
+ <antcall target="jnilin"/>
+ <antcall target="jniwin"/>
+</target>
+
+<target name="jniwin" if="jniwindows">
+ <antcall target="jnichronowin"/>
+ <antcall target="jniunuranwin"/>
+</target>
+
+<target name="jnichronowin" if="ssj.buildjnichrono">
+ <echo message="Compiling Chrono for Windows"/>
+ <javah class="umontreal.iro.lecuyer.util.GlobalCPUTimeChrono" destdir="source/${putil}"/>
+<!-- Non-cygwin DLL -->
+ <exec executable="gcc" dir="source/${putil}" failonerror="yes">
+ <arg value="-Wall"/>
+<!-- <arg value="-mno-cygwin"/> -->
+ <arg value="-c"/>
+ <arg value="-D_WIN32"/>
+ <arg value="-D_REENTRANT"/>
+ <arg value="-DBUILDING_DLL=1"/>
+ <arg value="-D_DLL=1"/>
+<!-- <arg value="-D__int64='long long'"/> -->
+ <arg value="-O2"/>
+ <arg value="-I${java.home}\..\include"/>
+ <arg value="-I${java.home}\..\include\win32"/>
+ <arg value="-o"/>
+ <arg file="source/${putil}/Chrono.o"/>
+ <arg file="source/${putil}/Chrono.c"/>
+ </exec>
+ <exec executable="gcc" dir="source/${putil}" failonerror="yes">
+ <arg value="-Wl,--add-stdcall-alias"/>
+ <arg value="-mno-cygwin"/>
+ <arg value="-shared"/>
+ <arg value="-static"/>
+ <arg value="-s"/>
+ <arg value="-o"/>
+ <arg file="lib/ssjutil.dll"/>
+ <arg file="source/${putil}/Chrono.o"/>
+ </exec>
+</target>
+
+<target name="jniunuranwin" if="ssj.buildjniunuran">
+ <echo message="Compiling UNURAN for Windows"/>
+ <javah class="umontreal.iro.lecuyer.randvar.RandUnuran" destdir="source/${prandvar}"/>
+<!-- We must build a Cygwin DLL because UNURAN can only be built
+ inside Cygwin due to the automake/autoconf build process.
+ It cannot be built natively under Win32. -->
+ <exec executable="gcc" dir="source/${prandvar}" failonerror="yes">
+ <arg value="-Wall"/>
+ <arg value="-c"/>
+ <arg value="-D_WIN32"/>
+ <arg value="-D_REENTRANT"/>
+ <arg value="-DBUILDING_DLL=1"/>
+ <arg value="-D_DLL=1"/>
+<!-- <arg value="-D__int64='long long'"/> -->
+ <arg value="-O2"/>
+ <arg value="-I${java.home}\..\include"/>
+ <arg value="-I${java.home}\..\include\win32"/>
+ <arg value="-o"/>
+ <arg file="source/${prandvar}/RandUnuran.o"/>
+ <arg file="source/${prandvar}/RandUnuran.c"/>
+ </exec>
+ <exec executable="gcc" dir="source/${prandvar}" failonerror="yes">
+<!-- <arg value="-Wl,- -add-stdcall-alias,-e,__cygwin_noncygwin_dll_entry at 12" /> -->
+ <arg value="-Wl,--add-stdcall-alias"/>
+ <arg value="-shared"/>
+ <arg value="-static"/>
+ <arg value="-s"/>
+ <arg value="-o"/>
+ <arg file="lib/randvar.dll"/>
+ <arg file="source/${prandvar}/RandUnuran.o"/>
+ <arg value="-L/usr/local/lib"/>
+ <arg value="-lunuran"/>
+ </exec>
+</target>
+
+<target name="jnilin" if="jnilinux">
+ <antcall target="jnichronolin"/>
+ <antcall target="jniunuranlin"/>
+</target>
+
+<target name="jnichronolin" if="ssj.buildjnichrono" depends="util">
+ <echo message="Compiling Chrono for Linux"/>
+ <javah class="umontreal.iro.lecuyer.util.GlobalCPUTimeChrono" destdir="source/${putil}"/>
+ <exec executable="gcc" dir="source/${putil}" failonerror="yes">
+ <arg value="-Wall"/>
+ <arg value="-shared"/>
+ <arg value="-fPIC"/>
+ <arg value="-D_linux"/>
+ <arg value="-D_REENTRANT"/>
+ <arg value="-O2"/>
+ <arg value="-s"/>
+<!-- <arg value="-static"/> -->
+ <arg value="-I${java.home}/include"/>
+ <arg value="-I${java.home}/include/linux"/>
+ <arg value="-o"/>
+ <arg file="lib/libssjutil.so"/>
+ <arg file="source/${putil}/Chrono.c"/>
+ </exec>
+</target>
+
+<target name="jniunuranlin" if="ssj.buildjniunuran" depends="randvar">
+ <echo message="Compiling UNURAN interface for Linux"/>
+ <javah class="umontreal.iro.lecuyer.randvar.RandUnuran" destdir="source/${prandvar}"/>
+ <exec executable="gcc" dir="source/${prandvar}" failonerror="yes">
+ <arg value="-Wall"/>
+ <arg value="-shared"/>
+ <arg value="-s"/>
+<!-- <arg value="-static"/> -->
+ <arg value="-fPIC"/>
+ <arg value="-D_linux"/>
+ <arg value="-D_REENTRANT"/>
+ <arg value="-O2"/>
+ <arg value="-I${java.home}/include"/>
+ <arg value="-I${java.home}/include/linux"/>
+ <arg value="-o"/>
+ <arg file="lib/librandvar.so"/>
+ <arg file="source/${prandvar}/RandUnuran.c"/>
+ <arg value="-lunuran"/>
+ <arg value="-lrngstreams"/>
+ </exec>
+</target>
+
+<!-- ********************************
+ * Compilation of subpackages *
+ ******************************** -->
+
+<target name="utilj">
+ <texjava master="source/${putil}/guideutil.tex">
+ <texfilelist dir="source/${putil}" files="Num.tex,PrintfFormat.tex,TableFormat.tex,RatioFunction.tex,RootFinder.tex,ChronoSingleThread.tex,BitVector.tex,BitMatrix.tex,ArithmeticMod.tex,Misc.tex,JDBCManager.tex,TextDataReader.tex,ClassFinder.tex,Introspection.tex,TimeUnit.tex,NameConflictException.tex,TransformingList.tex,DMatrix.tex,DoubleArrayComparator.tex,Systeme.tex"/>
+ <texfileset dir="source/${putil}" includes="Multi*.tex,*Chrono.tex"/>
+ <texfilelist dir="source/${putil}/io" files="DataField.tex"/>
+ <texfileset dir="source/${putil}/io" includes="*Writer.tex,*Reader.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${putil}/guideutil.tex">
+ <texfilelist dir="source/${putil}" files="overview.tex,io/overview.tex"/>
+ </texjava>
+</target>
+
+<target name="util" depends="init,utilj" description="Compiles the basic utilities">
+ <javac srcdir="source" destdir="build" includes="${putil}/*.java,${putil}/io/*.java" source="1.5" target="1.5" debug="${ssj.debug}">
+ </javac>
+</target>
+
+
+<target name="probdistj">
+ <texjava master="source/${pprobdist}/guideprobdist.tex">
+ <texfilelist dir="source/${pprobdist}" files="DiscreteDistribution.tex,DiscreteDistributionInt.tex,ContinuousDistribution.tex,Distribution.tex,DistributionFactory.tex,JohnsonSystem.tex,InverseDistFromDensity.tex,HypoExponentialDistEqual.tex"/>
+ <texfileset dir="source/${pprobdist}" includes="*Dist.tex,*DistQuick.tex,*FromMean.tex,*FromMoments.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pprobdist}/guideprobdist.tex">
+ <texfilelist dir="source/${pprobdist}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="probdist" depends="util,functions,probdistj" description="Compiles the probability distributions">
+ <javac srcdir="source" destdir="build" includes="${pprobdist}/*.java" source="1.5" target="1.5" debug="${ssj.debug}">
+ <classpath>
+ <pathelement location="lib/Blas.jar"/>
+ <pathelement location="lib/optimization.jar"/>
+ </classpath>
+ </javac>
+</target>
+
+<target name="probdistmultij">
+ <texjava master="source/${pprobdistmulti}/guideprobdistmulti.tex">
+ <texfilelist dir="source/${pprobdistmulti}" files="ContinuousDistribution2Dim.tex,ContinuousDistributionMulti.tex,DiscreteDistributionIntMulti.tex"/>
+ <texfileset dir="source/${pprobdistmulti}" includes="*Dist.tex"/>
+ <texfileset dir="source/${pprobdistmulti}/norta" includes="NI*.tex"/>
+ <texfilelist dir="source/${pprobdistmulti}/norta" files="NortaInitDisc.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pprobdistmulti}/guideprobdistmulti.tex">
+ <texfilelist dir="source/${pprobdistmulti}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="probdistmulti" depends="util,probdist,probdistmultij" description="Compiles the multivariate probability distributions">
+ <javac srcdir="source" destdir="build" includes="${pprobdistmulti}/*.java,${pprobdistmulti}/norta/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+<target name="gofcolt" if="ssj.colt">
+ <javac srcdir="source" destdir="build" includes="${pgof}/GofStat.java,${pgof}/GofFormat.java" source="1.5" target="1.5"/>
+</target>
+
+<target name="gofj">
+ <texjava master="source/${pgof}/guidegof.tex">
+ <texfilelist dir="source/${pgof}" files="KernelDensity.tex,FDist.tex,FBar.tex,GofStat.tex,GofFormat.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pgof}/guidegof.tex">
+ <texfilelist dir="source/${pgof}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="gof" depends="probdist,gofj" description="Compiles the goodness of fit tests">
+ <javac srcdir="source" destdir="build" includes="${pgof}/*.java" excludes="${pgof}/GofStat.java,${pgof}/GofFormat.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+ <antcall target="gofcolt"/>
+</target>
+
+<target name="rngj">
+ <texjava master="source/${prng}/guiderng.tex">
+ <texfilelist dir="source/${prng}" files="BasicRandomStreamFactory.tex,LFSR113.tex,LFSR258.tex,F2NL607.tex,GenF2w32.tex,MT19937.tex"/>
+ <texfileset dir="source/${prng}" includes="Rand*.tex,MRG*.tex,*Stream.tex,WELL*.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${prng}/guiderng.tex">
+ <texfilelist dir="source/${prng}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="rng" depends="util,rngj" description="Compiles the uniform random streams">
+ <javac srcdir="source" destdir="build" includes="${prng}/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+
+ <condition property="GenF2w32dat.uptodate">
+ <uptodate srcfile="source/${prng}/GenF2w32.java" targetfile="build/${prng}/GenF2w32.dat"/>
+ </condition>
+
+ <antcall target="GenF2w32dat"/>
+</target>
+
+<target name="GenF2w32dat" depends="rngj" unless="GenF2w32dat.uptodate">
+ <java classname="umontreal.iro.lecuyer.rng.GenF2w32">
+ <arg file="build/${prng}/GenF2w32.dat"/>
+ </java>
+</target>
+
+<target name="hupsj">
+ <texjava master="source/${phups}/guidehups.tex">
+ <texfilelist dir="source/${phups}" files="PointSetIterator.tex,RadicalInverse.tex"/>
+ <texfileset dir="source/${phups}" includes="Digital*.tex,*Randomization.tex,*PointSet.tex,*Base2.tex"/>
+ <texfileset dir="source/${phups}" includes="F2w*.tex,*Sequence.tex,*Lattice.tex,*LFSR.tex,*Shift.tex"/>
+ <texfileset dir="source/${phups}" includes="Random*.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${phups}/guidehups.tex">
+ <texfilelist dir="source/${phups}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="hupscolt" if="ssj.colt">
+ <javac srcdir="source" destdir="build" includes="${phups}/CycleBasedPointSet.java,${phups}/CycleBasedPointSetBase2.java,${phups}/LCGPointSet.java,${phups}/RandShiftedPointSet.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+<target name="hups" depends="rng,hupsj,hupsdata" description="Compiles the highly uniform point sets">
+ <javac srcdir="source" destdir="build" includes="${phups}/*.java" excludes="${phups}/CycleBasedPointSet.java,${phups}/CycleBasedPointSetBase2.java,${phups}/LCGPointSet.java,${phups}/RandShiftedPointSet.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+ <antcall target="hupscolt"/>
+</target>
+
+<target name="hupsdata">
+ <copy todir="build/${phups}/dataSer">
+ <fileset dir="source/${phups}/dataSer">
+ <include name="**/*.ser"/>
+ </fileset>
+ </copy>
+ <copy todir="build/${phups}/dataLFSR">
+ <fileset dir="source/${phups}/dataLFSR">
+ <include name="**/*.dat"/>
+ </fileset>
+ </copy>
+</target>
+
+
+
+<target name="randvarj">
+ <texjava master="source/${prandvar}/guiderandvar.tex">
+ <texfilelist dir="source/${prandvar}" files="JohnsonSystemG.tex"/>
+ <texfileset dir="source/${prandvar}" includes="*Gen.tex,RandomVaria*.tex"/>
+ <texfileset dir="source/${prandvar}" includes="RandUnur*tex,Unuran*.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${prandvar}/guiderandvar.tex">
+ <texfilelist dir="source/${prandvar}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="randvar" depends="probdist,rng,randvarj" description="Compiles the random variate generators">
+ <javac srcdir="source" destdir="build" includes="${prandvar}/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+
+<target name="randvarmultij">
+ <texjava master="source/${prandvarmulti}/guiderandvarmulti.tex">
+ <texfileset dir="source/${prandvarmulti}" includes="*Gen.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${prandvarmulti}/guiderandvarmulti.tex">
+ <texfilelist dir="source/${prandvarmulti}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="randvarmulti" depends="rng,probdist,randvar,probdistmulti,randvarmultij" description="Compiles the random multi-variate generators">
+ <javac srcdir="source" destdir="build" includes="${prandvarmulti}/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+<target name="statcolt" if="ssj.colt">
+ <javac srcdir="source" destdir="build" includes="${pstat}/TallyStore.java" source="1.5" target="1.5"/>
+</target>
+
+<target name="statj">
+ <texjava master="source/${pstat}/guidestat.tex">
+ <texfileset dir="source/${pstat}" includes="Tally*.tex"/>
+ <texfilelist dir="source/${pstat}" files="ObservationListener.tex,StatProbe.tex"/>
+ <texfilelist dir="source/${pstat}/list" files="ArrayOfObservationListener.tex,ListOfStatProbes.tex,ListOfTallies.tex,ListOfTalliesWithCovariance.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pstat}/guidestat.tex">
+ <texfilelist dir="source/${pstat}" files="overview.tex,list/overview.tex"/>
+ </texjava>
+</target>
+
+<target name="stat" depends="util,statj" description="Compiles the statistics tools">
+ <javac srcdir="source" destdir="build" includes="${pstat}/*.java,${pstat}/list/*.java" excludes="${pstat}/TallyStore.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+ <antcall target="statcolt"/>
+</target>
+
+<target name="simeventsj">
+ <texjava master="source/${psimevents}/guidesimevents.tex">
+ <texfilelist dir="source/${psimevents}" files="Accumulate.tex,ListWithStat.tex,LinkedListStat.tex,Event.tex,ContinuousState.tex,Continuous.tex,Sim.tex,Simulator.tex"/>
+ <texfilelist dir="source/${psimevents}/eventlist" files="EventList.tex,BinaryTree.tex,DoublyLinked.tex,Henriksen.tex,RedblackTree.tex,SplayTree.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${psimevents}/guidesimevents.tex">
+ <texfilelist dir="source/${psimevents}" files="overview.tex,eventlist/overview.tex"/>
+ </texjava>
+</target>
+
+<target name="simevents" depends="stat,simeventsj" description="Compiles the event-driven simulation package">
+ <javac srcdir="source" destdir="build" includes="${psimevents}/*.java,${psimevents}/eventlist/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+<target name="simprocsj">
+ <texjava master="source/${psimprocs}/guidesimprocs.tex">
+ <texfilelist dir="source/${psimprocs}" files="ProcessSimulator.tex,ThreadProcessSimulator.tex,DSOLProcessSimulator.tex,SSJInterpretationOracle.tex,SimProcess.tex,Resource.tex,Bin.tex,Condition.tex,UserRecord.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${psimprocs}/guidesimprocs.tex">
+ <texfilelist dir="source/${psimprocs}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="simprocs" depends="simprocsj,simevents" description="Compiles the process-driven simulation package">
+ <javac srcdir="source" destdir="build" includes="${psimprocs}/*.java" excludes="${psimprocs}/keep/*.java" source="1.5" target="1.5" debug="${ssj.debug}">
+ <classpath>
+ <pathelement location="lib/interpreter-1.6.8.jar"/>
+ <pathelement location="lib/event-1.6.5.jar"/>
+ </classpath>
+ </javac>
+ <copy file="source/interpreter.properties" todir="build"/>
+</target>
+
+<target name="functionsj">
+ <texjava master="source/${pfunctions}/guidefunctions.tex">
+ <texfilelist dir="source/${pfunctions}" files="Polynomial.tex"/>
+ <texfileset dir="source/${pfunctions}" includes="*Function.tex,Math*.tex,Multi*.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pfunctions}/guidefunctions.tex">
+ <texfilelist dir="source/${pfunctions}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="functions" depends="functionsj" description="Provides tools to use mathematical functions">
+ <javac srcdir="source" destdir="build" includes="${pfunctions}/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+<target name="functionfitj">
+ <texjava master="source/${pfunctionfit}/guidefunctionfit.tex">
+ <texfilelist dir="source/${pfunctionfit}" files="LeastSquares.tex,BSpline.tex,PolInterp.tex,SmoothingCubicSpline.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pfunctionfit}/guidefunctionfit.tex">
+ <texfilelist dir="source/${pfunctionfit}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="functionfit" depends="functionfitj" description="Compiles tools to compute function fittings">
+ <javac srcdir="source" destdir="build" includes="${pfunctionfit}/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+
+<target name="stochprocessj">
+ <texjava master="source/${pstochprocess}/guidestochprocess.tex">
+ <texfileset dir="source/${pstochprocess}" includes="Geometric*.tex,*Motion*.tex,*Process*.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pstochprocess}/guidestochprocess.tex">
+ <texfilelist dir="source/${pstochprocess}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="stochprocess" depends="stochprocessj" description="Simulates stochastic processes">
+ <javac srcdir="source" destdir="build" includes="${pstochprocess}/*.java" source="1.5" target="1.5" debug="${ssj.debug}"/>
+</target>
+
+
+<target name="chartsj">
+ <texjava master="source/${pcharts}/guidecharts.tex">
+ <texfilelist dir="source/${pcharts}" files="Axis.tex,PlotFormat.tex"/>
+ <texfileset dir="source/${pcharts}" includes="*Chart.tex,*Collection.tex,*Plot.tex"/>
+ </texjava>
+ <texjava overviewtopackage="yes" htmlonly="yes" master="source/${pcharts}/guidecharts.tex">
+ <texfilelist dir="source/${pcharts}" files="overview.tex"/>
+ </texjava>
+</target>
+
+<target name="charts" depends="chartsj" description="Provides tools to draw charts and plots">
+ <javac srcdir="source" destdir="build" includes="${pcharts}/*.java" source="1.5" target="1.5" debug="${ssj.debug}">
+ <classpath>
+ <pathelement location="lib/jcommon-1.0.13.jar"/>
+ <pathelement location="lib/jfreechart-1.0.10.jar"/>
+ </classpath>
+ </javac>
+</target>
+
+
+
+<!-- ********************************
+ * JAR FILE AND DOCUMENTATION *
+ ******************************** -->
+
+<target name="lib" depends="util,probdist,probdistmulti,gof,rng,hups,randvar,randvarmulti,stat,simevents,simprocs,charts,functions,functionfit,stochprocess" description="Creates the JAR file">
+ <mkdir dir="lib"/>
+ <jar jarfile="lib/ssj.jar" basedir="build" excludes="">
+ <manifest>
+ <attribute name="Extension-Name" value="SSJ"/>
+ <attribute name="Specification-Title" value="Stochastic Simulation in Java"/>
+ <attribute name="Specification-Version" value="${ssj.version}"/>
+ <attribute name="Specification-Vendor" value="DIRO of the Université de Montréal"/>
+ <attribute name="Implementation-Title" value="SSJ"/>
+ <attribute name="Implementation-Version" value="${ssj.version} ${TODAY}"/>
+ <attribute name="Implementation-Vendor" value="DIRO of the Université de Montréal"/>
+ <attribute name="Class-Path" value="colt.jar optimization.jar Blas.jar tcode.jar jfreechart-1.0.10.jar jcommon-1.0.13.jar interpreter-1.6.8.jar event-1.6.5.jar logger-1.6.4.jar language-1.6.7.jar"/>
+ </manifest>
+ </jar>
+</target>
+
+<target name="doc" depends="pdfdoc,htmldoc" description="Creates the PDF and HTML documentation">
+</target>
+
+<target name="htmldoc" depends="utilj,probdistj,probdistmultij,gofj,rngj,hupsj,randvarj,randvarmultij,statj,simeventsj,simprocsj,functionsj,functionfitj,stochprocessj,chartsj" description="Creates the HTML documentation" if="ssj.htmldoc">
+ <texjava htmlonly="yes">
+ <texfilelist dir="source" files="overview.tex"/>
+ </texjava>
+ <mkdir dir="doc"/>
+ <javadoc destdir="doc/html" public="yes" overview="source/overview.html" windowtitle="Java Libraries for Stochastic Simulation">
+ <packageset dir="source" defaultexcludes="yes">
+ <include name="${putil}"/>
+ <include name="${putil}/io"/>
+ <include name="${pprobdist}"/>
+ <include name="${pprobdistmulti}"/>
+ <include name="${pprobdistmulti}/norta"/>
+ <include name="${pgof}"/>
+ <include name="${prng}"/>
+ <include name="${phups}"/>
+ <include name="${prandvar}"/>
+ <include name="${prandvarmulti}"/>
+ <include name="${pstat}/**"/>
+ <include name="${psimevents}/**"/>
+ <include name="${psimprocs}"/>
+ <include name="${pfunctions}"/>
+ <include name="${pfunctionfit}"/>
+ <include name="${pstochprocess}"/>
+ <include name="${pcharts}"/>
+ </packageset>
+ <classpath>
+ <pathelement location="lib/ssj.jar"/>
+ <pathelement location="lib/colt.jar"/>
+ <pathelement location="lib/tcode.jar"/>
+ <pathelement location="lib/optimization.jar"/>
+ <pathelement location="lib/Blas.jar"/>
+ <pathelement location="lib/jfreechart-1.0.10.jar"/>
+ <pathelement location="lib/jcommon-1.0.13.jar"/>
+ <pathelement location="lib/interpreter-1.6.8.jar"/>
+ <pathelement location="lib/event-1.6.5.jar"/>
+ <pathelement location="lib/logger-1.6.4.jar"/>
+ <pathelement location="lib/language-1.6.7.jar"/>
+ </classpath>
+ <doctitle><![CDATA[<b>SSJ : A Java library for a stochastic
+ simulation <br> API specification </b>]]></doctitle>
+ <header><![CDATA[<b>SSJ </b><br>V. ${ssj.version}.]]></header>
+ <footer><![CDATA[<b>SSJ </b><br>V. ${ssj.version}.]]></footer>
+ <bottom><![CDATA[To submit a bug or ask questions, send an e-mail to
+ <a href="mailto:Pierre L'Ecuyer <lecuyer at IRO.UMontreal.CA>">Pierre L'Ecuyer</a>.]]>
+ </bottom>
+ <link href="http://download.oracle.com/javase/6/docs/api/"/>
+ <link href="http://dsd.lbl.gov/~hoschek/colt/api/"/>
+ <link href="http://www.jfree.org/jfreechart/api/javadoc/"/>
+ </javadoc>
+</target>
+
+<target name="pdfdoc" description="Creates the PDF documentation" if="ssj.pdfdoc">
+ <pdflatex latexfile="source/${putil}/guideutil.tex"/>
+ <pdflatex latexfile="source/${pprobdist}/guideprobdist.tex"/>
+ <pdflatex latexfile="source/${pprobdistmulti}/guideprobdistmulti.tex"/>
+ <pdflatex latexfile="source/${pgof}/guidegof.tex"/>
+ <pdflatex latexfile="source/${prng}/guiderng.tex"/>
+ <pdflatex latexfile="source/${phups}/guidehups.tex"/>
+ <pdflatex latexfile="source/${prandvar}/guiderandvar.tex"/>
+ <pdflatex latexfile="source/${prandvarmulti}/guiderandvarmulti.tex"/>
+ <pdflatex latexfile="source/${pstat}/guidestat.tex"/>
+ <pdflatex latexfile="source/${psimevents}/guidesimevents.tex"/>
+ <pdflatex latexfile="source/${psimprocs}/guidesimprocs.tex"/>
+ <pdflatex latexfile="source/${pfunctions}/guidefunctions.tex"/>
+ <pdflatex latexfile="source/${pfunctionfit}/guidefunctionfit.tex"/>
+ <pdflatex latexfile="source/${pstochprocess}/guidestochprocess.tex"/>
+ <pdflatex latexfile="source/${pcharts}/guidecharts.tex"/>
+ <pdflatex latexfile="source/${pexamples}/examples.tex"/>
+ <pdflatex latexfile="source/overview.tex"/>
+</target>
+
+
+
+<!-- ********************************
+ * DISTRIBUTION *
+ ******************************** -->
+
+<target name="dist" depends="lib,jni,doc" description="Creates the SSJ binary distribution">
+ <mkdir dir="doc/pdf"/>
+ <copy todir="doc/pdf">
+ <fileset dir="source" includes="**/*.pdf" excludes="${pexamples}/examples.pdf"/>
+ <mapper type="flatten"/>
+ </copy>
+
+ <filter token="DSTAMP" value="${DSTAMP}"/>
+ <filter token="TODAY" value="${TODAY}"/>
+<!-- <copy file="index.html.in" tofile="dist/index.html" filtering="true"/>
+-->
+ <zip destfile="dist/ssj-${DSTAMP}.zip">
+ <zipfileset dir="lib" prefix="ssj/lib" excludes="Console.jar,finance.jar,jspline.jar"/>
+ <zipfileset dir="bin" prefix="ssj" includes="Ssjrc,Ssj.sh" filemode="755"/>
+ <zipfileset dir="bin" prefix="ssj" includes="Ssj.bat"/>
+ <zipfileset dir="." prefix="ssj" includes="cdssj.bat,README.txt,INSTALL.txt,NEWS"/>
+ <zipfileset dir="doc/html" prefix="ssj/doc/html"/>
+ <zipfileset dir="doc/pdf" prefix="ssj/doc/pdf"/>
+ <zipfileset dir="source/${pexamples}" prefix="ssj/examples" includes="*.java,*.dat,*.pdf"/>
+ </zip>
+</target>
+
+<target name="srcdist" depends="cleanmore" description="Cleans the tree and zip it as a source distribution">
+ <tstamp/>
+ <fail message="Non terminé; voir le zip source dans mon HTML"/>
+ <zip destfile="ssj-src-${DSTAMP}.zip">
+ <zipfileset dir="." prefix="ssj" excludes="dist/cpdist,Ssjrc,Ssj.sh,usessj,usessj.sh"/>
+ <zipfileset dir="." prefix="ssj" includes="Ssjrc,Ssj.sh" filemode="755"/>
+ </zip>
+</target>
+
+
+<!-- ********************************
+ * CLEANUP *
+ ******************************** -->
+
+<target name="cleanbuild" description="Cleans the build tree">
+<!-- Problème ici avec les *.dat dans rng. Il faudrait les recopier dans le
+ build/umontreal/iro/lecuyer/rng quand on reconstruit ab initio. -->
+ <delete dir="build" includeEmptyDirs="true"/>
+</target>
+
+<target name="cleandoc" description="Cleans the generated documentation">
+ <delete dir="doc" includeEmptyDirs="true"/>
+ <delete>
+ <fileset dir="source" includes="**/*.pdf"/>
+ </delete>
+</target>
+
+<target name="cleanlib" description="Cleans the generated library files">
+ <delete dir="lib" includeEmptyDirs="true"/>
+</target>
+
+<target name="cleanjava">
+ <delete>
+ <fileset dir="source" includes="${putil}/*.java,${putil}/io/*.java,${pprobdist}/*.java,${pstochprocess}/*.java,${pfunctions}/*.java"/>
+ <fileset dir="source" includes="${pprobdistmulti}/*.java,{pprobdistmulti}/norta/*.java,${pgof}/*.java,${pfunctionfit}/*.java"/>
+ <fileset dir="source" includes="${pcharts}/*.java" excludes="${pcharts}/CustomHistogramDataset.java,${pcharts}/EmpiricalRenderer.java"/>
+ <fileset dir="source" includes="${prng}/*.java" excludes="${prng}/WELL607base.java,${prng}/Rijndael_Algorithm.java,${prng}/Rijndael_Properties.java,${prng}/F2wPoly.java"/>
+ <fileset dir="source" includes="${prandvar}/*.java,${prandvarmulti}/*.java,${pstat}/*.java,${pstat}/list/*.java"/>
+ <fileset dir="source" includes="${psimevents}/*.java,${psimevents}/eventlist/*.java,${psimprocs}/*.java,${phups}/*.java"/>
+ </delete>
+</target>
+
+<target name="clean" description="Cleans auxiliary and backup files">
+ <delete>
+ <fileset dir="source" includes="**/*~ **/*.bck **/*% **/*.bak **/*.aux **/*.log **/*.blg **/*.toc **/*.dvi"/>
+ </delete>
+</target>
+
+<target name="cleanmore" depends="clean,cleanbuild,cleandoc" description="Cleans backup and class files">
+ <delete>
+ <fileset dir="source" includes="**/*.class **/*.html"/>
+ </delete>
+</target>
+
+<target name="cleanall" depends="cleanmore,cleanlib" description="Cleans all generated files">
+ <delete>
+ <fileset dir="source" includes="**/*.bbl"/>
+ </delete>
+</target>
+
+</project>
diff --git a/build/interpreter.properties b/build/interpreter.properties
new file mode 100644
index 0000000..fc59d3f
--- /dev/null
+++ b/build/interpreter.properties
@@ -0,0 +1,23 @@
+# -------------------------------------------------------------------
+# Copyright 2002-2005 Delft University of Technology.
+#
+# Licensed under the Lesser General Purpose, (the "License");
+# The properties in this file control the interpreter
+# -------------------------------------------------------------------
+
+# -------------------------------------------------------------------
+# Copyright 2002-2005 Delft University of Technology.
+#
+# Licensed under the Lesser General Purpose, (the "License");
+# The properties in this file control the interpreter
+# -------------------------------------------------------------------
+
+# Interpreter Factory------------------------------------------------
+#interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.operations.InterpreterFactory
+#interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.operations.reflection.ReflectionFactory
+interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.process.ProcessFactory
+interpreter.operation.oracle=umontreal.iro.lecuyer.simprocs.SSJInterpretationOracle
+
+# Interpreter Log Level----------------------------------------------
+interpreter.logLevel=WARNING
+
diff --git a/build/umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat b/build/umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat
new file mode 100644
index 0000000..66b8046
--- /dev/null
+++ b/build/umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat
@@ -0,0 +1,20 @@
+#-------------------------------------------------------#
+# Equidistributed LFSR generator with one composent
+# Criteria : DELTA(k,k,k/2) <= 1
+#-------------------------------------------------------#
+# Format :
+#
+# Number Of Component (only at the beginning of the file)
+#
+# Comments (value of the criteria)
+# s
+# Non-zeros coefficients of the polynomial
+#-------------------------------------------------------#
+
+
+1
+
+# DELTA = 1
+5
+11 5 3 1 0
+
diff --git a/build/umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat b/build/umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat
new file mode 100644
index 0000000..0332d79
--- /dev/null
+++ b/build/umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat
@@ -0,0 +1,45 @@
+#-------------------------------------------------------#
+# Equidistributed LFSR Generator with 2 composents
+# Criteria : DELTA(k,k,k/2) <= 1
+#-------------------------------------------------------#
+# Format :
+#
+# Number Of Component (only at the beginning of the file
+#
+# Comments (value of the criteria)
+# s1 s2
+# Non-zeros coefficients of the first polynomial
+# Non-zeros coefficients of the second polynomial
+#-------------------------------------------------------#
+
+2
+
+#DELTA = 1
+4 2
+7 1 0
+10 4 3 1 0
+
+#DELTA = 1
+1 4
+7 1 0
+10 4 3 1 0
+
+#DELTA = 1
+5 7
+6 1 0
+11 4 2 1 0
+
+#DELTA = 1
+2 3
+6 1 0
+11 5 3 2 0
+
+#DELTA = 1
+2 8
+6 1 0
+11 2 0
+
+#DELTA = 1
+1 4
+6 1 0
+11 4 2 1 0
diff --git a/build/umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat b/build/umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat
new file mode 100644
index 0000000..209d9d1
--- /dev/null
+++ b/build/umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat
@@ -0,0 +1,35 @@
+#-------------------------------------------------------#
+# Equidistributed LFSR Generator with 2 composents
+# Criteria : DELTA(k,k,k/2) <= 1
+#-------------------------------------------------------#
+# Format :
+#
+# Number Of Component (only at the beginning of the file
+#
+# Comments (value of the criteria)
+# s1 s2
+# Non-zeros coefficients of the first polynomial
+# Non-zeros coefficients of the second polynomial
+#-------------------------------------------------------#
+
+2
+
+#DELTA = 1
+5 4
+9 4 0
+10 4 3 1 0
+
+#DELTA = 1
+2 4
+9 4 0
+10 3 0
+
+#DELTA = 1
+1 5
+6 1 0
+13 6 4 1 0
+
+#DELTA = 1
+2 4
+6 1 0
+13 6 4 1 0
diff --git a/build/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser b/build/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser
new file mode 100644
index 0000000..fe13d07
Binary files /dev/null and b/build/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser differ
diff --git a/build/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser b/build/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser
new file mode 100644
index 0000000..9f760ac
Binary files /dev/null and b/build/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser differ
diff --git a/build/umontreal/iro/lecuyer/rng/GenF2w32.dat b/build/umontreal/iro/lecuyer/rng/GenF2w32.dat
new file mode 100644
index 0000000..6b2641e
Binary files /dev/null and b/build/umontreal/iro/lecuyer/rng/GenF2w32.dat differ
diff --git a/build/umontreal/iro/lecuyer/rng/WELL1024.dat b/build/umontreal/iro/lecuyer/rng/WELL1024.dat
new file mode 100644
index 0000000..918cd26
Binary files /dev/null and b/build/umontreal/iro/lecuyer/rng/WELL1024.dat differ
diff --git a/build/umontreal/iro/lecuyer/rng/WELL512.dat b/build/umontreal/iro/lecuyer/rng/WELL512.dat
new file mode 100644
index 0000000..3315ca8
Binary files /dev/null and b/build/umontreal/iro/lecuyer/rng/WELL512.dat differ
diff --git a/build/umontreal/iro/lecuyer/rng/WELL607.dat b/build/umontreal/iro/lecuyer/rng/WELL607.dat
new file mode 100644
index 0000000..cba21c7
Binary files /dev/null and b/build/umontreal/iro/lecuyer/rng/WELL607.dat differ
diff --git a/cdssj.bat b/cdssj.bat
new file mode 100644
index 0000000..8e5a0c7
--- /dev/null
+++ b/cdssj.bat
@@ -0,0 +1,2 @@
+ at echo off
+cd %SSJHOME%\%1
diff --git a/cdssjsrc.bat b/cdssjsrc.bat
new file mode 100644
index 0000000..3991170
--- /dev/null
+++ b/cdssjsrc.bat
@@ -0,0 +1,2 @@
+ at echo off
+cd %SSJHOME%\source\umontreal\iro/lecuyer\%1
diff --git a/debian/README.source b/debian/README.source
deleted file mode 100644
index b799a06..0000000
--- a/debian/README.source
+++ /dev/null
@@ -1,8 +0,0 @@
-Note from Tim Booth, Apr 2014:
-
-I started building this as a Maven package with a dependency on maven-debian-helper (>= 1.5.1)
-and libmaven-javadoc-plugin-java but on returning to the package I can't work out why, or
-remember where I got the POM from. A mystery. Anyway, ANT is simpler surely?
-
-Note this package needs TCode to build from the original source which is embedded in the .tex
-files.
diff --git a/debian/bin/pdflatex b/debian/bin/pdflatex
deleted file mode 100755
index 5dbc486..0000000
--- a/debian/bin/pdflatex
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-# pdfLaTeX is far too fussy. How about a version that always returns
-# exit status 0? Much better :-)
-
-/usr/bin/pdflatex "$@"
-true
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index a54c094..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,24 +0,0 @@
-libssj-java (2.6-1) UNRELEASED; urgency=medium
-
- * New upstream version
- * Use libcolt-free-java instead of libcolt-java
- * Initial upload to Debian (Closes: #???)
-
- -- Andreas Tille <tille at debian.org> Mon, 30 Mar 2015 08:14:49 +0200
-
-libssj-java (2.5-0ubuntu3) precise; urgency=low
-
- * Add missing ant dep
- * Ensure javahelper runs to generate deps
-
- -- Tim Booth <tbooth at ceh.ac.uk> Thu, 10 Apr 2014 15:41:08 +0100
-
-libssj-java (2.5-0ubuntu1) precise; urgency=low
-
- * Initial release
- * For some reason I started trying to build this with Maven, but the
- recommended build system is Ant. Not sure what I was doing there.
- * Don't build the Unuran JNI stuff as it just seems to drag in many
- dependencies. If I do build it, I'll probably make a separate binary pkg.
-
- -- Tim Booth <tbooth at ceh.ac.uk> Fri, 04 Apr 2014 09:54:37 +0100
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index 7f8f011..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-7
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 302e4d7..0000000
--- a/debian/control
+++ /dev/null
@@ -1,52 +0,0 @@
-Source: libssj-java
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Tim Booth <tbooth at ceh.ac.uk>,
- Andreas Tille <tille at debian.org>
-Section: java
-Priority: optional
-Build-Depends: debhelper (>= 9),
- default-jdk,
- javahelper,
- ant,
- libcolt-free-java (>= 1.2.0),
- liboptimization-java,
- libjfreechart-java,
- libdsol1-java,
- tcode,
- libjcommon-java
-Build-Depends-Indep: default-jdk-doc
-Standards-Version: 3.9.6
-Vcs-Browser: http://anonscm.debian.org/viewvc/debian-med/trunk/packages/libssj-java
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/libssj-java
-Homepage: http://www.iro.umontreal.ca/~simardr/ssj/indexe.html
-
-Package: libssj-java
-Architecture: any
-Depends: ${misc:Depends},
- ${java:Depends},
- ${shlibs:Depends}
-Suggests: libssj-java-doc
-Description: Simulation Stochastique en Java
- SSJ is a Java library for stochastic simulation, developed under the direction
- of Pierre L'Ecuyer, in the Département d'Informatique et de Recherche
- Opérationnelle (DIRO), at the Université de Montréal. It provides facilities
- for generating uniform and nonuniform random variates, computing different
- measures related to probability distributions, performing goodness-of-fit
- tests, applying quasi-Monte Carlo methods, collecting (elementary) statistics,
- and programming discrete-event simulations with both events and processes.
-
-Package: libssj-java-doc
-Architecture: all
-Section: doc
-Depends: ${misc:Depends}
-Suggests: libssj-java
-Description: Documentation for SSJ
- SSJ is a Java library for stochastic simulation, developed under the direction
- of Pierre L'Ecuyer, in the Département d'Informatique et de Recherche
- Opérationnelle (DIRO), at the Université de Montréal. It provides facilities
- for generating uniform and nonuniform random variates, computing different
- measures related to probability distributions, performing goodness-of-fit
- tests, applying quasi-Monte Carlo methods, collecting (elementary) statistics,
- and programming discrete-event simulations with both events and processes.
- .
- This package contains the API documentation of libssj-java.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 6c85de2..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,17 +0,0 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: ssj
-Source: http://www.iro.umontreal.ca/~simardr/ssj/indexe.html
-
-Files: *
-Copyright: 2013, Richard Simard <simardr at iro.umontreal.ca>
-License: GPL-3
-
-Files: debian/*
-Copyright: 2013, Tim Booth <tbooth at ceh.ac.uk>
-License: GPL-3
-
-License: GPL-3
- On Debian GNU/Linux system you can find the complete text of the
- GPL-3 license in '/usr/share/common-licenses/GPL-3'
-
-
diff --git a/debian/libssj-java-doc.docs b/debian/libssj-java-doc.docs
deleted file mode 100644
index 30d29de..0000000
--- a/debian/libssj-java-doc.docs
+++ /dev/null
@@ -1 +0,0 @@
-doc/*
diff --git a/debian/patches/fix_build_script b/debian/patches/fix_build_script
deleted file mode 100644
index 041ce62..0000000
--- a/debian/patches/fix_build_script
+++ /dev/null
@@ -1,22 +0,0 @@
---- a/build.xml
-+++ b/build.xml
-@@ -144,8 +144,8 @@
- <arg value="-O2"/>
- <arg value="-s"/>
- <!-- <arg value="-static"/> -->
-- <arg value="-I${java.home}/include"/>
-- <arg value="-I${java.home}/include/linux"/>
-+ <arg value="-I${env.JAVA_HOME}/include"/>
-+ <arg value="-I${env.JAVA_HOME}/include/linux"/>
- <arg value="-o"/>
- <arg file="lib/libssjutil.so"/>
- <arg file="source/${putil}/Chrono.c"/>
-@@ -502,7 +502,7 @@
- <attribute name="Implementation-Title" value="SSJ"/>
- <attribute name="Implementation-Version" value="${ssj.version} ${TODAY}"/>
- <attribute name="Implementation-Vendor" value="DIRO of the Université de Montréal"/>
-- <attribute name="Class-Path" value="colt.jar optimization.jar Blas.jar tcode.jar jfreechart-1.0.10.jar jcommon-1.0.13.jar interpreter-1.6.8.jar event-1.6.5.jar logger-1.6.4.jar language-1.6.7.jar"/>
-+ <attribute name="Class-Path" value="colt.jar optimization.jar Blas.jar tcode.jar jfreechart.jar jcommon.jar dsol-interpreter.jar dsol-event.jar dsol-logger.jar dsol-language.jar"/>
- </manifest>
- </jar>
- </target>
diff --git a/debian/patches/fix_pdflatex b/debian/patches/fix_pdflatex
deleted file mode 100644
index c7a979f..0000000
--- a/debian/patches/fix_pdflatex
+++ /dev/null
@@ -1,24 +0,0 @@
---- a/source/umontreal/iro/lecuyer/util/guideutil.tex
-+++ b/source/umontreal/iro/lecuyer/util/guideutil.tex
-@@ -5,9 +5,7 @@
- \dateheadtrue
- \usepackage{tikz}
- \usepackage{color}
--\usepackage{crayola}
- \usepackage[procnames]{listings}
--\usepackage{lstpatch}
-
- \lstloadlanguages{Java}
- \lstset{language=Java,
---- a/source/umontreal/iro/lecuyer/randvar/guiderandvar.tex
-+++ b/source/umontreal/iro/lecuyer/randvar/guiderandvar.tex
-@@ -8,9 +8,7 @@
- %end{latexonly}
- \usepackage{ssj}
- \usepackage{color}
--\usepackage{crayola}
- \usepackage[procnames]{listings}
--\usepackage{lstpatch}
-
- \lstloadlanguages{Java}
- \lstset{language=Java,
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index c13415a..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1,3 +0,0 @@
-set_build_properties
-fix_build_script
-fix_pdflatex
diff --git a/debian/patches/set_build_properties b/debian/patches/set_build_properties
deleted file mode 100644
index a0928de..0000000
--- a/debian/patches/set_build_properties
+++ /dev/null
@@ -1,21 +0,0 @@
---- a/ssj.properties
-+++ b/ssj.properties
-@@ -1,9 +1,9 @@
- # Properties specific to SSJ
- # ssj.debug configures how javac will be called
--ssj.debug = on
-+ssj.debug = off
-
- # These properties, when set, activate the compilation of JNI C files.
--# ssj.buildjnichrono
-+ssj.buildjnichrono
- # ssj.buildjniunuran
-
- # Documentation building switches
-@@ -11,5 +11,5 @@
- ssj.htmldoc
-
- # Properties specific to TCode
--texjava.texjava = ${env.TCODEHOME}/texjava.pl
-+texjava.texjava = /usr/bin/texjava
- texjava.html = yes
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 96ac7ae..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/make -f
-
-export JAVA_HOME=/usr/lib/jvm/default-java
-export CLASSPATH=/usr/share/java/colt.jar:/usr/share/java/optimization.jar:/usr/share/tcode/tcode.jar:/usr/share/java/jfreechart.jar:/usr/share/java/jcommon.jar:/usr/share/java/dsol-language.jar:/usr/share/java/dsol-interpreter.jar:/usr/share/java/dsol-event.jar:/usr/share/java/dsol-logger.jar:./build
-export TEXINPUTS=.:$(shell pwd)/source:/usr/share/tcode:/usr/share/latex2html/styles:
-
-%:
- dh $@ --with javahelper
-
-override_dh_auto_build:
- #Building lib then dist gets around the fact that the dependencies
- #in the build.xml aren't quite right.
- env PATH="$(shell pwd)/debian/bin:$$PATH" ant lib
- env PATH="$(shell pwd)/debian/bin:$$PATH" ant dist
-
-override_dh_auto_clean:
- ant cleanall
- rm -rf lib/* lib64/* home doc/html
- find -name '*.pdf' -delete
- find -name '*.zip' -delete
- find -name '*.out' -delete
- find -name '*.log' -delete
- find -name '*.aux' -delete
- find -name '*.h' -delete
-
-override_dh_install:
- dh_install
- jh_installlibs -plibssj-java -v lib/*.jar
- dh_install -plibssj-java -v lib/*.so usr/lib
-
-get-orig-source:
- uscan --force-download --verbose --repack --compress xz
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index 4fc626f..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,2 +0,0 @@
-version=3
-http://www.iro.umontreal.ca/~simardr/ssj/ssj-source.html (?:.*/|)ssj-([0-9.]+)-source.zip
diff --git a/lib/Blas.jar b/lib/Blas.jar
new file mode 100644
index 0000000..65382be
Binary files /dev/null and b/lib/Blas.jar differ
diff --git a/lib/colt.jar b/lib/colt.jar
new file mode 100644
index 0000000..a7192f6
Binary files /dev/null and b/lib/colt.jar differ
diff --git a/lib/event-1.6.5.jar b/lib/event-1.6.5.jar
new file mode 100644
index 0000000..3e1bd75
Binary files /dev/null and b/lib/event-1.6.5.jar differ
diff --git a/lib/interpreter-1.6.8.jar b/lib/interpreter-1.6.8.jar
new file mode 100644
index 0000000..8e05fe5
Binary files /dev/null and b/lib/interpreter-1.6.8.jar differ
diff --git a/lib/interpreter.properties b/lib/interpreter.properties
new file mode 100644
index 0000000..fc59d3f
--- /dev/null
+++ b/lib/interpreter.properties
@@ -0,0 +1,23 @@
+# -------------------------------------------------------------------
+# Copyright 2002-2005 Delft University of Technology.
+#
+# Licensed under the Lesser General Purpose, (the "License");
+# The properties in this file control the interpreter
+# -------------------------------------------------------------------
+
+# -------------------------------------------------------------------
+# Copyright 2002-2005 Delft University of Technology.
+#
+# Licensed under the Lesser General Purpose, (the "License");
+# The properties in this file control the interpreter
+# -------------------------------------------------------------------
+
+# Interpreter Factory------------------------------------------------
+#interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.operations.InterpreterFactory
+#interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.operations.reflection.ReflectionFactory
+interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.process.ProcessFactory
+interpreter.operation.oracle=umontreal.iro.lecuyer.simprocs.SSJInterpretationOracle
+
+# Interpreter Log Level----------------------------------------------
+interpreter.logLevel=WARNING
+
diff --git a/lib/jcommon-1.0.13.jar b/lib/jcommon-1.0.13.jar
new file mode 100644
index 0000000..634447d
Binary files /dev/null and b/lib/jcommon-1.0.13.jar differ
diff --git a/lib/jfreechart-1.0.10.jar b/lib/jfreechart-1.0.10.jar
new file mode 100644
index 0000000..f6f7bbf
Binary files /dev/null and b/lib/jfreechart-1.0.10.jar differ
diff --git a/lib/language-1.6.7.jar b/lib/language-1.6.7.jar
new file mode 100644
index 0000000..1b5098c
Binary files /dev/null and b/lib/language-1.6.7.jar differ
diff --git a/lib/librandvar.so b/lib/librandvar.so
new file mode 100755
index 0000000..c113f92
Binary files /dev/null and b/lib/librandvar.so differ
diff --git a/lib/libssjutil.so b/lib/libssjutil.so
new file mode 100755
index 0000000..63d7959
Binary files /dev/null and b/lib/libssjutil.so differ
diff --git a/lib/logger-1.6.4.jar b/lib/logger-1.6.4.jar
new file mode 100644
index 0000000..2f79b9d
Binary files /dev/null and b/lib/logger-1.6.4.jar differ
diff --git a/lib/optimization.jar b/lib/optimization.jar
new file mode 100644
index 0000000..45a3282
Binary files /dev/null and b/lib/optimization.jar differ
diff --git a/lib/randvar.dll b/lib/randvar.dll
new file mode 100755
index 0000000..d0d7bec
Binary files /dev/null and b/lib/randvar.dll differ
diff --git a/lib/ssj.jar b/lib/ssj.jar
new file mode 100644
index 0000000..7b9b766
Binary files /dev/null and b/lib/ssj.jar differ
diff --git a/lib/ssjutil.dll b/lib/ssjutil.dll
new file mode 100755
index 0000000..1de85e5
Binary files /dev/null and b/lib/ssjutil.dll differ
diff --git a/lib/tcode.jar b/lib/tcode.jar
new file mode 100644
index 0000000..b65d241
Binary files /dev/null and b/lib/tcode.jar differ
diff --git a/lib64/libssjutil.so b/lib64/libssjutil.so
new file mode 100755
index 0000000..d041cf3
Binary files /dev/null and b/lib64/libssjutil.so differ
diff --git a/setl2hinit.pl b/setl2hinit.pl
new file mode 100644
index 0000000..30eb7e5
--- /dev/null
+++ b/setl2hinit.pl
@@ -0,0 +1,71 @@
+#!/bin/perl
+
+# This script manage the ~/.latex2html-init LATEX2HTMLSTYLES variable.
+# Its first argument must be a path that will be looked for in the LATEX2HTMLSTYLES
+# variable. If the path is absent, it will be added.
+
+use File::Spec;
+
+if ($^O eq 'MSWin32' || $^O =~ /dos|win/) {
+ $pathsep = ';';
+}
+else {
+ $pathsep = ':';
+}
+
+
+if (@ARGV != 1) {
+ print "Usage: perl setl2hinit.pl <directory to be added to LATEX2HTMLSTYLES variable>\n";
+ exit 1;
+}
+$ARGV[0] = File::Spec->canonpath ($ARGV[0]);
+
+die "$ARGV[0] directory does not exist" if !-x $ARGV[0];
+die "HOME environment variable not set" if !$ENV{'HOME'};
+
+$l2hinit = File::Spec->catfile ($ENV{'HOME'}, '.latex2html-init');
+
+if (!-r $l2hinit) {
+ # Create the file if it does not exist yet.
+ open L2H, ">$l2hinit" or die "Cannot create $l2hinit";
+ print L2H '$LATEX2HTMLSTYLES .= "' . quotepath ($pathsep . $ARGV[0]) . "\";\n";
+ printf L2H "\n1;\n";
+ close L2H;
+ exit 0;
+}
+
+# Read the file (and the LATEX2HTMLSTYLES variable)
+require ($l2hinit);
+
+ at p = split /$pathsep/, $LATEX2HTMLSTYLES;
+foreach my $dir (@p) {
+ $dir = File::Spec->canonpath ($dir);
+ if ($dir eq $ARGV[0]) {
+ # The path was found; nothing to be done
+ exit 0;
+ }
+}
+
+# Since the directory was not found, it must be added.
+# However, the rest of the file must not be altered.
+# The safest way to do it is to add a new line to the init file.
+
+open L2H, "<$l2hinit" or die "Cannot read $l2hinit";
+$contents = join "", <L2H>;
+close L2H;
+
+$contents =~ s/\s*1;\s*$//;
+$contents .= "\n\$LATEX2HTMLSTYLES .= \"" . quotepath ($pathsep . $ARGV[0])
+ . "\";\n\n1;\n";
+
+open L2H, ">$l2hinit" or die "Cannot write to $l2hinit";
+print L2H $contents;
+close L2H;
+
+sub quotepath {
+ # Quotes the backslashes in paths
+ my $p = shift;
+
+ $p =~ s/\\/\\\\/go;
+ return $p;
+}
diff --git a/source/interpreter.properties b/source/interpreter.properties
new file mode 100644
index 0000000..fc59d3f
--- /dev/null
+++ b/source/interpreter.properties
@@ -0,0 +1,23 @@
+# -------------------------------------------------------------------
+# Copyright 2002-2005 Delft University of Technology.
+#
+# Licensed under the Lesser General Purpose, (the "License");
+# The properties in this file control the interpreter
+# -------------------------------------------------------------------
+
+# -------------------------------------------------------------------
+# Copyright 2002-2005 Delft University of Technology.
+#
+# Licensed under the Lesser General Purpose, (the "License");
+# The properties in this file control the interpreter
+# -------------------------------------------------------------------
+
+# Interpreter Factory------------------------------------------------
+#interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.operations.InterpreterFactory
+#interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.operations.reflection.ReflectionFactory
+interpreter.operation.factory=nl.tudelft.simulation.dsol.interpreter.process.ProcessFactory
+interpreter.operation.oracle=umontreal.iro.lecuyer.simprocs.SSJInterpretationOracle
+
+# Interpreter Log Level----------------------------------------------
+interpreter.logLevel=WARNING
+
diff --git a/source/overview.bbl b/source/overview.bbl
new file mode 100644
index 0000000..ad7b4f1
--- /dev/null
+++ b/source/overview.bbl
@@ -0,0 +1,46 @@
+\begin{thebibliography}{1}
+
+\bibitem{fGLA04a}
+P.~Glasserman.
+\newblock {\em Monte {C}arlo Methods in Financial Engineering}.
+\newblock Springer-Verlag, New York, 2004.
+
+\bibitem{iHOS04a}
+Wolfgang Hoschek.
+\newblock {\em The Colt Distribution: Open Source Libraries for High
+ Performance Scientific and Technical Computing in Java}.
+\newblock CERN, Geneva, 2004.
+\newblock Available at \url{http://acs.lbl.gov/software/colt/}.
+
+\bibitem{iMOR80a}
+{J. J. Mor\'e and B. S. Garbow and K. E. Hillstrom}.
+\newblock {\em User Guide for MINPACK-1, Report ANL-80-74}.
+\newblock Argonne, Illinois, USA, 1980.
+\newblock See
+ \url{http://www-fp.mcs.anl.gov/otc/Guide/softwareGuide/Blurbs/minpack.html}.
+
+\bibitem{sLAW00a}
+A.~M. Law and W.~D. Kelton.
+\newblock {\em Simulation Modeling and Analysis}.
+\newblock McGraw-Hill, New York, NY, third edition, 2000.
+
+\bibitem{sLEC02a}
+P.~L'Ecuyer, L.~Meliani, and J.~Vaucher.
+\newblock {SSJ}: A framework for stochastic simulation in {J}ava.
+\newblock In E.~Y\"ucesan, C.-H. Chen, J.~L. Snowdon, and J.~M. Charnes,
+ editors, {\em Proceedings of the 2002 Winter Simulation Conference}, pages
+ 234--242. {IEEE} Press, 2002.
+
+\bibitem{iLEY02a}
+J.~Leydold and W.~H\"ormann.
+\newblock {\em {UNU.RAN}---A Library for Universal Non-Uniform Random Number
+ Generators}, 2002.
+\newblock Available at \url{http://statistik.wu-wien.ac.at/unuran}.
+
+\bibitem{iSCHa}
+R.~B. Schnabel.
+\newblock {\em {UNCMIN}---Unconstrained Optimization Package, FORTRAN}.
+\newblock University of Colorado at Boulder.
+\newblock See \url{http://www.ici.ro/camo/unconstr/uncmin.htm}.
+
+\end{thebibliography}
diff --git a/source/overview.tex b/source/overview.tex
new file mode 100644
index 0000000..818e4f2
--- /dev/null
+++ b/source/overview.tex
@@ -0,0 +1,388 @@
+\documentclass[12pt]{article}
+\usepackage{html}
+\usepackage{url}
+\usepackage{ssj}
+
+\mytwoheads
+\dateheadtrue
+\detailedtrue
+
+\begin{document}
+
+%begin{latexonly}
+\begin{titlepage}
+
+\null\vfill
+\begin {center}
+%{\Large\bf SSJ User's Guide } \\[20pt]
+{\Large SSJ: Stochastic Simulation in Java}\\[15pt]
+{\Large Overview} \\[20pt]
+ Version: \today \\
+\vfill
+\end {center}
+%end{latexonly}
+
+SSJ is a Java library for stochastic simulation, developed in the
+% simulation laboratory of the
+D\'epartement d'Informa\-tique et de Recherche
+Op\'erationnelle (DIRO), at the Universit\'e de Montr\'eal.
+It provides facilities for generating uniform and nonuniform random
+variates, computing different measures related to probability
+distributions, performing goodness-of-fit tests, applying
+quasi-Monte Carlo methods, collecting statistics (elementary),
+and programming discrete-event simulations with both events and processes.
+Additional Java packages are also developed on top of SSJ for simulation
+applications in finance, call centers management, communication networks,
+ etc.
+\bigskip
+
+
+\begin{center}
+\fbox{\parbox{0.94\textwidth}{
+ \ttfamily
+ This file is part of SSJ.
+
+ Copyright (C) 2008 Pierre L'Ecuyer and Universit\'e de Montr\'eal
+\medskip
+
+ SSJ is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ any later version.
+\medskip
+
+ SSJ is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+\medskip
+
+ You should have received a copy of the GNU General Public License
+ along with SSJ. If not, see \url{http://www.gnu.org/licenses}.
+}}
+\end{center}
+%begin{latexonly}
+\vfill
+\end{titlepage}
+%end{latexonly}
+
+%%%%%%%%%%%%%%
+\subsection*{Introduction and overview}
+
+Simulation models can be implemented in many ways \cite{sLAW00a}.
+One can use general-purpose programming languages such as
+FORTRAN, C, C++, Java, or specialized simulation languages such as
+\emph{GPSS}, \emph{SIMAN}, and \emph{SIMSCRIPT}.
+The general-purpose languages may be
+more familiar to the programmer, but usually
+do not have the necessary built-in tools to perform simulation.
+Implementing a model can become complex and tedious.
+Specialized simulation languages must be learned before models
+can be implemented, and they are not as widely available and supported
+as the most popular general-purpose languages.
+
+Over the past few decades, commercial simulation tools with point-and-click
+graphical user interfaces such as \emph{Arena}, \emph{Automod},
+ \emph{Witness},
+and many others, have become by far the most widely used tools to develop
+simulation models. Among their main advantages, these tools do not require
+knowledge of a programming language, provide graphical animation,
+have automatic facilities to collect statistics and perform experiments,
+and can sometimes perform optimization to a certain extent.
+On the other hand, these specialized simulation tools,
+especially the point-and-click tools, are often too restrictive,
+because they are usually targeted at a limited class of models.
+With these tools, simulating a system whose logic is complicated or
+unconventional may become quite difficult.
+All the graphical and automatic devices also tend to slow down the simulation
+significantly. Fast execution times are important for example in a context
+of optimization, where thousands of variants of a base system may have to be
+simulated, or for on-line applications where a fast response time is required.
+
+
+SSJ is an organized set of packages whose purpose is to facilitate
+simulation programming in the Java language.
+A first description was given in \cite{sLEC02a}.
+Some of the tools can also be used for modeling (e.g., selecting
+and fitting distributions).
+\begin{comment}
+A simulation library, such
+as SSJ, extends the Java programming language to equip it with the
+necessary tools for implementing complex models. One can use a
+familiar programming language, such as Java, with high-level tools for
+simulation. A simulation project can be divided in several tasks,
+from modeling to implementation \cite{sLAW00a}. SSJ provides tools
+for model implementation and validation.
+\end{comment}
+As these lines are being written, SSJ is still growing.
+Several new packages, classes, and methods will certainly be added in
+forthcoming years and others will be refined.
+
+The facilities offered are grouped into different packages,
+each one having its own user's guide, in the form of a PDF file.
+There is also a set of commented examples of simulation programs
+in a separate directory with its own guide.
+Programs are given for some of the examples used in the books of
+Law and Kelton \cite{sLAW00a} and Glasserman \cite{fGLA04a}, for instance.
+The best way to learn about SSJ, at the beginning, is probably
+to study these examples and refer to the user guides of the different
+packages when needed.
+The PDF files are the official documentation.
+There is also a simplified on-line documentation in HTML
+format, produced via \texttt{javadoc}.
+
+
+\input{packdep.tex}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{comment}
+
+% Three packages are used for uniform and non-uniform random number
+% generation, one for the uniform random number generators, one for the
+% probability distributions and one for the non-uniform random number
+% generation. It is also possible to replace the streams of uniform
+% random numbers with highly uniform point sets for Quasi-Monte Carlo
+% \cite{vLEC02a} simulation.
+% As opposed to most random number generation libraries, it
+% is also possible to compute densities, distribution functions and
+% inverse distribution functions for all supported distributions.
+% The \externalclass{umontreal.iro.lecuyer}{simevents}
+% and \externalclass{umontreal.iro.lecuyer}{simprocs} packages make the
+% heart of the SSJ library.
+% They provide an efficient framework for stochastic simulation,
+% supporting the event view, proces view, continuous simulation, and arbitrary
+% mixtures of these.
+% The other packages allow one to perform miscellaneous tasks, such as
+% statistical collection and goodness of fit tests.
+
+\subsection*{random number generation}
+
+Random numbers feed simulation models and allow one to compute
+statistics. To generate random numbers from
+any probability distribution, uniform random numbers are required.
+Such numbers are uniformly distributed in the $[0,1)$ interval, i.e.,
+the probability of getting a given number $x$ in that interval is the same
+for all values of $x\in[0,1)$. Any generated number $x$ is also
+independent from any previous or future generated numbers. Although
+the generated uniforms are not truly independent since one uniform is
+obtained from the previous uniforms by a mathematical formula, one can
+consider them independent for simulation purposes. Selection of a
+random number generator is based on several criteria such as
+uniformity, performance, and portability \cite{rLEC01d}.
+The package
+\externalclass{umontreal.iro.lecuyer}{rng} contains the needed tools
+to generate such numbers. It defines an interface called
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} implemented by
+any random number generator supported by SSJ. This interface allows
+one to easily interchange random number generators since they are
+accessed through the same set of methods specified by the interface.
+Only the random number generator setup depends on the type of
+generator that was chosen.
+
+If one wants to replace uniform random numbers with low-discrepancy
+point sets for variance reduction, the package
+\externalclass{umontreal.iro.lecuyer}{hups} contains all the necessary
+facilities. Such highly uniform point sets all inherit from the
+\externalclass{umontreal.iro.lecuyer.hups}{PointSet} which provides a
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetIterator} extending
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}. The
+replacement can be easily done without modifying the model
+implementation, except the setup-time code.
+
+To generate non-uniform random numbers, one must select a probability
+distribution based on the empirical data \cite{sLAW00a}.
+SSJ does not provide
+probability distribution estimation tools, but goodness of fit tests are
+included to help in model validation. The package
+\externalclass{umontreal.iro.lecuyer}{probdist} contains several
+standard, commonly-used, probability distributions. It supports
+discrete and continuous distributions through two different abstract
+base classes:
+\externalclass{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}
+and
+\externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistribution},
+respectively. Again, since the distributions inherit from a common
+class, their access can be independent from the selected distribution,
+except for the setup case. One can compute the density/mass, distribution,
+complementary, and inverse distribution functions. These facilities
+are also accessible through static methods implemented in each
+distribution class if one does not want to create objects or needs
+distributions whose parameters vary in time.
+However, setup-time operations must be performed for each operation,
+which can be inefficient for certain distributions.
+
+To generate non-uniform random numbers, the packages
+\externalclass{umontreal.iro.lecuyer}{rng} (or
+\externalclass{umontreal.iro.lecuyer}{hups}) and
+\externalclass{umontreal.iro.lecuyer}{probdist} must be used
+together. The simplest generation method is to generate a uniform
+random number using a generator implementing
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} (or get a
+coordinate using a point set iterator) and to apply
+inversion by using the selected
+\externalclass{umontreal.iro.lecuyer}{probdist} distribution's
+\externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{inverseF}{} method.
+However, inversion is not the only generation method and sometimes not
+the most efficient. For some distributions, closed-form inverse functions
+or fast inversion algorithms exist.
+For others, inversion is performed using
+binary or even linear search. In such cases, the performance
+and precision depends on the complexity of the distribution function
+which is calculated several times for one inverse.
+The package
+\externalclass{umontreal.iro.lecuyer}{randvar} acts as glue between
+uniform random number generators and probability distributions. Continuous
+or discrete random number generators also inherits from common base
+classes, namely
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} and
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}.
+All generators use a random stream and a probability
+distribution for their construction. As opposed to
+\externalclass{umontreal.iro.lecuyer}{probdist}, one can directly
+instantiate
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} or
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}.
+However, in such cases, only inversion generation method will be
+available. To use an alternate generation method, one must
+instantiate a specialized generator class and switch to the given
+generation algorithm using an object method.
+Each specialized class also provides static method which perform
+the same action. Although they allow one to avoid object creation,
+their signatures are specific to the used distribution and they have
+to perform setup-time operations on each variate generation, which
+can become inefficient.
+The \externalclass{umontreal.iro.lecuyer}{randvar} package also
+provides the class
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateTrans} to
+apply transformations to a generator. This allows, for example, to
+generate variates from a truncated distribution.
+
+\subsection*{Performing simulation}
+
+SSJ supports discrete-event, process-driven, continuous or mixed
+simulation. The discrete-event and continuous simulation are managed
+by the package \externalclass{umontreal.iro.lecuyer}{simevents}. This
+package manages the simulation clock and the event list, two essential
+components for all discrete-event simulations. The simulation clock
+tracks the simulation time whereas the event list stores the
+scheduled events to execute them in the right order.
+Events are user-defined subclasses of
+\externalclass{umontreal.iro.lecuyer.simevents}{Event}. When an event
+occurs, any type of actions can then be taken. The package
+provides a class called
+\externalclass{umontreal.iro.lecuyer.simevents}{List} which implements
+a linked list supporting statistical collection. Continuous
+simulation can be performed using the class
+\externalclass{umontreal.iro.lecuyer.simevents}{Continuous}. It uses
+the event framework to resolve differential equations numerically at
+fixed steps in the simulation time.
+
+Process-driven simulation requires a separate package called
+\externalclass{umontreal.iro.lecuyer}{simprocs}. This package
+provides the base class
+\externalclass{umontreal.iro.lecuyer.simprocs}{SimProcess} which must
+be extended by any process. A simulation process is an autonomous
+object which executes tasks and interacts with other simulation
+processes. To allow such interactions, the package provides some
+synchronization tools such as a resource, a bin, and a condition.
+The process-driven simulation framework relies on the event-driven
+framework. Processes start, resume and wake up on scheduled events.
+One can then easily mix event-driven and process-driven simulation.
+
+\subsection*{Other tools}
+
+The package \externalclass{umontreal.iro.lecuyer}{stat} provides basic
+tools for statistical collection. Statistics are collected using
+statistical probes, i.e, objects implementing the abstract class
+\externalclass{umontreal.iro.lecuyer.stat}{StatProbe}. Two types of
+probes are supported. The
+\externalclass{umontreal.iro.lecuyer.stat}{Tally} allows to collect
+observations of the form $X_1,\dots,X_n$ whereas
+\externalclass{umontreal.iro.lecuyer.simevents}{Accumulate} collects
+statistics for a continuous variable evolving in simulation time.
+During the simulation, one can add observations to such probes. After
+the simulation, measures can be obtained, such as sample average,
+sample standard deviation or confidence interval. A statistical
+report can be obtained for all probes. The package also provides
+a way to detach statistical collection from the model implementation
+by using bound properties.
+
+To test a proposed model against empirical data, goodness of fit tests
+are provided in the package
+\externalclass{umontreal.iro.lecuyer}{gof}.
+Such tests, e.g.\ Kolmogorov-Smirnov
+or Anderson-Darling, compute a statistic using the
+empirical observations and the proposed distribution. The empirical
+observations are given as an array whereas the
+distribution is given as a
+\externalclass{umontreal.iro.lecuyer}{probdist} object. From the computed
+statistic, it is possible to compute the $p$-value which is useful to
+evaluate the significance of the test.
+
+\subsection*{Related documentation}
+
+The \texttt{example.pdf} file, in the \texttt{doc/pdf} subdirectory of
+the SSJ distribution, explains simulation examples implemented using
+SSJ. This may be the best starting point to learn SSJ.
+\begin{htmlonly}
+% This should be removed if we find a way to display blbliographical
+% references in HTML
+% LaTeX2HTML can do it when it is run for a LaTeX document, but it does not
+% work for a single bibliography shared by several documents.
+% Texjava processes each Java class as a separate, standalone, LaTeX document.
+One can find additional information and references in the PDF version
+of this documentation, available in the {\tt doc/pdf} subdirectory
+of the SSJ distribution.
+\end{htmlonly}
+\begin{latexonly}
+% In Javadoc, this is visible to the user as hyperlinks. In LaTeX,
+% this is separated filed.
+Every package introduced here contains its own reference documentation
+as a PDF file, in the \texttt{doc/pdf} subdirectory.
+This documentation describes in more details how to
+use the package and provides a description of each class and method.
+\end{latexonly}
+
+
+\end{comment}
+%%%%%%%%%%%%%%%%%%%%%%
+
+%For overviews, tutorials, examples, guides, and tool documentation,
+% please see:
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection*{Acknowledgments}
+
+SSJ was designed and implemented under the supervision of
+Pierre L'Ecuyer, with the contribution of the following persons
+\begin{verse}
+Mathieu Bague,
+Sylvain Bonnet,
+ \'Eric Buist,
+Maxime Dion,
+ Yves Edel,
+ Regina H.{} S.{} Hong,
+ Alexander Keller,
+ Pierre L'Ecuyer,
+ \'Etienne Marcotte,
+ Lakhdar Meliani,
+ Fran\c{c}ois Panneton,
+Jean-Sebastien Parent-Chartier,
+ Richard Simard,
+ Cl\'ement Teule,
+ Pierre-Alexandre Tremblay,
+ Jean Vaucher.
+\end{verse}
+
+Its development has been supported by NSERC-Canada grant No.\ ODGP0110050,
+NATEQ-Qu\'ebec grant No.\ 02ER3218, a Killam fellowship,
+and a Canada Research Chair to the author.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%begin{latexonly}
+\bibliographystyle{plain}
+\bibliography{simul,ift,random,fin}
+%end{latexonly}
+
+\end{document}
diff --git a/source/packdep.tex b/source/packdep.tex
new file mode 100644
index 0000000..c1e49d3
--- /dev/null
+++ b/source/packdep.tex
@@ -0,0 +1,170 @@
+
+\begin{latexonly} %%%%%%%
+
+The packages currently offered are the following:
+%
+\begin{verse}
+\externalclass{umontreal.iro.lecuyer}{util}
+ contains utility classes used in the implementation of SSJ,
+ and which are often useful elsewhere.
+ For example, there are timers (for CPU usage),
+ utilities to read or format numbers and arrays from/to text,
+ operations on binary vectors and matrices,
+ some mathematical functions and constants,
+ root-finding tools,
+ facilities for SQL database interface, and so on.
+
+\externalclass{umontreal.iro.lecuyer}{probdist}
+ contains a set of Java classes providing methods to compute mass,
+ density, distribution, complementary distribution,
+ and inverse distribution functions for many discrete and continuous
+ probability distributions, as well as estimating the parameters of
+ these distributions.
+
+\externalclass{umontreal.iro.lecuyer}{probdistmulti}
+ contains a set of Java classes providing methods to compute mass,
+ density, distribution, complementary distribution,
+ for some multi-dimensionnal discrete and continuous
+ probability distributions.
+
+\externalclass{umontreal.iro.lecuyer}{rng}
+ provides facilities for generating uniform random numbers over the
+ interval $(0,1)$, or over a given range of integer values, and other
+ types of simple random objects such as random permutations.
+
+\externalclass{umontreal.iro.lecuyer}{hups}
+ provides classes implementing highly uniform point sets and
+ sequences (HUPS), also called low-discrepancy sets and sequences,
+ and tools for their randomization.
+
+\externalclass{umontreal.iro.lecuyer}{randvar}
+ provides a collection of classes for non-uniform random variate
+ generation, primarily from standard distributions.
+
+\externalclass{umontreal.iro.lecuyer}{randvarmulti}
+ provides a collection of classes for
+ random number generators for some multi-dimensional distributions.
+
+\externalclass{umontreal.iro.lecuyer}{gof}
+ contains tools for performing univariate goodness-of-fit
+ (GOF) statistical tests.
+
+\externalclass{umontreal.iro.lecuyer}{stat}
+ provides elementary tools for collecting statistics
+ and computing confidence intervals.
+
+\externalclass{umontreal.iro.lecuyer}{stat.list}
+ this subpackage of \texttt{stat}
+ provides support to manage lists of statistical collectors.
+% Statistical operations may be applied on all the collectors easily.
+
+\externalclass{umontreal.iro.lecuyer}{simevents}
+ provides and manages the event-driven simulation facilities as well
+ as the simulation clock. Can manage several simulations in parallel,
+ in the same program.
+
+\externalclass{umontreal.iro.lecuyer}{simevents.eventlist}
+ this subpackage of \texttt{simevents} offers several kinds of event
+ list implementations.
+
+\externalclass{umontreal.iro.lecuyer}{simprocs}
+ provides and manages the process-driven simulation facilities.
+% This package requires \emph{green threads}, which are available
+% only in JDK version 1.3.1 or earlier (unfortunately).
+%
+% simprocs ne nécessite pas les green threads absolument.
+% Premièrement, si l'interpréteur D-SOL est employé, tout fonctionne #1, même
+% sans green threads. Avec l'implantation par défaut de SimProcess, qui utilise
+% les threads, c'est mieux avec les green threads, mais cela peut fonctionner
+% (quoique pas toujours stable) avec les native threads. Il faudrait donc dire,
+% dans le texte, qu'il est recommandé d'utiliser une JVM avec green threads,
+% comme Sun JRE <=1.3.1.
+
+\externalclass{umontreal.iro.lecuyer}{functions}
+ contains classes that allow one to pass an arbitrary function of one variable
+ as argument to a method
+ and to apply elementary mathematical operations on generic functions.
+
+\externalclass{umontreal.iro.lecuyer}{functionfit}
+provides basic facilities for curve fitting and interpolation
+with polynomials. % as, for example, least square fit and spline interpolation.
+
+\externalclass{umontreal.iro.lecuyer}{charts}
+provides tools for easy construction, visualization, and customization
+of $xy$ plots, histograms, and empirical styled charts
+from a Java program.
+
+\externalclass{umontreal.iro.lecuyer}{stochprocess}
+implements different kinds of stochastic processes.
+%%
+\end{verse}
+\end{latexonly} %%%%%%%%%
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection*{Dependence on other libraries}
+
+SSJ uses some classes from other free Java libraries.
+
+The \htmladdnormallink{Colt library}{http://acs.lbl.gov/software/colt/},
+ developed at the Centre Europ\'een de Recherche
+Nucl\'eaire (CERN) in Geneva \cite{iHOS04a},
+is a large library that provides a wide range of facilities for
+high performance scientific and technical computing in Java.
+SSJ uses the class \externalclass{cern.colt.list}{DoubleArrayList}
+from Colt in a few of its classes, namely in packages
+\externalclass{umontreal.iro.lecuyer}{stat} and
+\externalclass{umontreal.iro.lecuyer}{hups}.
+The reason is that this class provides a very efficient and convenient
+implementation of an (automatically) extensible array of {\tt double},
+together with several methods for computing statistics for the observations
+% Javadoc does not find cern.jet.stat.Descriptive and issues
+% a warning; maybe a bug in Javadoc.
+stored in the array (see, e.g., {\tt Descriptive}).
+% The Colt library must be installed (see the installation instructions
+% of SSJ) if one wishes to use a class relying on it.
+% Otherwise, Colt is not needed.
+The Colt library is distributed with the SSJ package as \textbf{colt.jar}.
+It must be added in the CLASSPATH environment variable.
+
+
+The \textbf{linear\_algebra} library is based on public domain LINPACK routines.
+They were translated from Fortran to Java by Steve Verrill at the
+ USDA Forest Products Laboratory
+ Madison, Wisconsin, USA.
+This software is also in the public domain and is included in the
+SSJ distribution as the \textbf{Blas.jar} archive. It is used only in the
+\texttt{probdist} package to compute maximum likelihood estimators.
+
+
+The optimization package of Steve Verrill includes Java translations of the
+\htmladdnormallink{MINPACK}{http://simul.iro.umontreal.ca/Uncmin_f77/Minpack_f77.html}
+ routines \cite{iMOR80a} for nonlinear least squares problems as well as
+\htmladdnormallink{UNCMIN}{http://simul.iro.umontreal.ca/Uncmin_f77/Uncmin_f77.html}
+ routines \cite{iSCHa} for
+unconstrained optimization. They were translated from Fortran to Java by
+Steve Verrill and are in the public domain. They are included in the SSJ
+distribution as the \textbf{optimization.jar} archive. It is used only in the \texttt{probdist}
+ package to compute maximum likelihood estimators.
+
+
+\htmladdnormallink{JFreeChart}{http://www.jfree.org/jfreechart/index.html} is a free
+Java library that can generate a wide variety of charts and plots for use in
+applications, applets and servlets. \textbf{JFreeChart} currently supports, amongst
+others, bar charts, pie charts, line charts, XY-plots, histograms, scatter plots and
+time series plots. It is distributed with SSJ as \textbf{jfreechart-*.jar}.
+\htmladdnormallink{JCommon}{http://www.jfree.org/jcommon/index.php} is a free
+general purpose Java library containing many useful classes used by JFreeChart and
+ other Java packages. It is distributed with SSJ as \textbf{jcommon-*.jar}.
+JFreeChart (and JCommon) are used in the SSJ package \textbf{charts} to create
+different kinds of charts.
+
+SSJ also provides an interface to the
+\htmladdnormallink{UNURAN}{http://statistik.wu-wien.ac.at/unuran/}
+library for nonuniform random number generation \cite{iLEY02a}, in the
+\externalclass{umontreal.iro.lecuyer}{randvar} package.
+UNURAN does not have to be installed to be used with SSJ, because it is
+linked statically with the appropriate SSJ native library.
+However, the UNURAN documentation will be required
+to take full advantage of the library.
diff --git a/source/ssj.perl b/source/ssj.perl
new file mode 100644
index 0000000..08c462c
--- /dev/null
+++ b/source/ssj.perl
@@ -0,0 +1,558 @@
+&do_require_package ('amsfonts');
+&do_require_package ('alltt');
+&do_require_package ('html');
+&do_require_package ('tcode');
+
+sub do_math_cmd_g {
+ (" <-- ", @_);
+}
+
+# sub do_math_cmd_tbu {
+# ("<B>ũ</B>", @_);
+# }
+
+sub do_math_cmd_bu {
+ ("<B>u</B>", @_);
+}
+
+sub do_math_cmd_ba {
+ ("<B>a</B>", @_);
+}
+
+sub do_math_cmd_be {
+ ("<B>e</B>", @_);
+}
+
+sub do_math_cmd_bg {
+ ("<B>g</B>", @_);
+}
+
+sub do_math_cmd_bq {
+ ("<B>q</B>", @_);
+}
+
+sub do_math_cmd_bv {
+ ("<B>v</B>", @_);
+}
+
+sub do_math_cmd_bA {
+ ("<B>A</B>", @_);
+}
+
+sub do_math_cmd_bB {
+ ("<B>B</B>", @_);
+}
+
+sub do_math_cmd_bC {
+ ("<B>C</B>", @_);
+}
+
+sub do_math_cmd_bM {
+ ("<B>M</B>", @_);
+}
+
+sub do_math_cmd_bP {
+ ("<B>P</B>", @_);
+}
+
+sub do_math_cmd_bU {
+ ("<B>U</B>", @_);
+}
+
+sub do_math_cmd_bV {
+ ("<B>V</B>", @_);
+}
+
+sub do_math_cmd_bX {
+ ("<B>X</B>", @_);
+}
+
+sub do_math_cmd_bR {
+ ("<B>R</B>", @_);
+}
+
+sub do_math_cmd_bS {
+ ("<B>S</B>", @_);
+}
+
+sub do_math_cmd_bZ {
+ ("<B>Z</B>", @_);
+}
+
+sub do_math_cmd_bI {
+ ("<B>I</B>", @_);
+}
+
+sub do_math_cmd_bzero {
+ ("<B>0</B>", @_);
+}
+
+sub do_math_cmd_cS {
+ ("S", @_);
+}
+
+sub do_math_cmd_ZZ {
+ ("<B>Z</B>", @_);
+}
+
+sub do_math_cmd_RR {
+ ("<B>R</B>", @_);
+}
+
+sub do_math_cmd_NN {
+ ("<B>N</B>", @_);
+}
+
+sub do_math_cmd_FF {
+ ("<B>F</B>", @_);
+}
+
+sub do_math_cmd_Ki {
+ ("<I>K</I><SUB>I</SUB>", @_);
+}
+
+sub do_math_cmd_Ko {
+ ("<I>K</I><SUB>O</SUB>", @_);
+}
+
+sub do_math_cmd_RR {
+ ("<B>R</B>", @_);
+}
+
+sub do_math_cmd_NN {
+ ("<B>N</B>", @_);
+}
+
+sub do_math_cmd_boldmu {
+ ("<I><B>μ</B></I>", @_);
+}
+
+sub do_math_cmd_boldnu {
+ ("<I><B>ν</B></I>", @_);
+}
+
+sub do_math_cmd_boldbeta {
+ ("<I><B>β</B></I>", @_);
+}
+
+sub do_math_cmd_boldbetaf {
+ ("<I><B>β</B></I><SUB>f</SUB>", @_);
+}
+
+sub do_math_cmd_betaf {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>β</I><SUB>f" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_barboldmu {
+ ("bar(<I><B>μ</B></I>)", @_);
+}
+
+sub do_math_cmd_barboldnu {
+ ("bar(<I><B>μ</B></I>)", @_);
+}
+
+sub do_math_cmd_barboldX {
+ ("bar(<I><B>X</B></I>)", @_);
+}
+
+sub do_math_cmd_barboldx {
+ ("bar(<I><B>x</B></I>)", @_);
+}
+
+sub do_math_cmd_boldSigma {
+ ("<I><B>Σ</B></I>", @_);
+}
+
+sub do_math_cmd_bsigma {
+ ("<I><B>σ</B></I>", @_);
+}
+
+sub do_math_cmd_boldLambda {
+ ("<I><B>Λ</B></I>", @_);
+}
+
+sub do_math_cmd_boldC {
+ ("<I><B>C</B></I>", @_);
+}
+
+sub do_math_cmd_boldX {
+ ("<I><B>X</B></I>", @_);
+}
+
+sub do_math_cmd_boldY {
+ ("<I><B>Y</B></I>", @_);
+}
+
+sub do_math_cmd_boldV {
+ ("<B><I>V</I></B>", @_);
+}
+
+sub do_math_cmd_boldS {
+ ("<B><I>S</I></B>", @_);
+}
+
+sub do_math_cmd_boldf {
+ ("<I><B>f</B></I>", @_);
+}
+
+sub do_math_cmd_rTG {
+ ("<I>r</I><SUB>TG</SUB>", @_);
+}
+
+sub do_math_cmd_rGT {
+ ("<I>r</I><SUB>GT</SUB>", @_);
+}
+
+sub do_math_cmd_Nf {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>N</I><SUB>f" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_Nb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>N</I><SUB>b" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_Ni {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>N</I><SUB>i" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_Ng {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>N</I><SUB>g" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_Ntb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>N</I><SUB>b" . ($n ? "<I>, $n</I>" : "") . "</SUB><SUP>t</SUP>", $_);
+}
+
+sub do_math_cmd_Ntf {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>N</I><SUB>f" . ($n ? "<I>, $n</I>" : "") . "</SUB><SUP>t</SUP>", $_);
+}
+
+sub do_math_cmd_Ndf {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>N</I><SUB>f" . ($n ? "<I>, $n</I>" : "") . "</SUB><SUP>d</SUP>", $_);
+}
+
+sub do_math_cmd_Xb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>X</I><SUB>b" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_barXb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("bar(<I>X</I><SUB>b" . ($n ? "<I>, $n</I>" : "") . "</SUB>)", $_);
+}
+
+sub do_math_cmd_XbK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>X</I><SUB>b<I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_XbP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>X</I><SUB>b<I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_Xg {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>X</I><SUB>g" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_barXg {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("bar(<I>X</I><SUB>g" . ($n ? "<I>, $n</I>" : "") . "</SUB>)", $_);
+}
+
+sub do_math_cmd_XgK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>X</I><SUB>g<I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_XgP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>X</I><SUB>g<I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_Yb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>Y</I><SUB>b" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_barYb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("bar(<I>Y</I><SUB>b" . ($n ? "<I>, $n</I>" : "") . "</SUB>)", $_);
+}
+
+sub do_math_cmd_YbK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>Y</I><SUB>b<I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_YbP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>Y</I><SUB>b<I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_Yg {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>Y</I><SUB>g" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_barYg {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("bar(<I>Y</I><SUB>g" . ($n ? "<I>, $n</I>" : "") . "</SUB>)", $_);
+}
+
+sub do_math_cmd_YgK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>Y</I><SUB>g<I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_YgP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>Y</I><SUB>g<I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_Var {
+ ("Var", @_);
+}
+
+sub do_math_cmd_Cov {
+ ("Cov", @_);
+}
+
+sub do_math_cmd_sK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>s</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_sP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>s</I><SUB><I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_lK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>l</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_lP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_next_token;
+ ("<I>l</I><SUB><I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_XK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>X</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_XP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>X</I><SUB><I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_YK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>Y</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_YP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>Y</I><SUB><I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_BK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>B</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_BP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>B</I><SUB><I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_AK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>A</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_AP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>A</I><SUB><I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_WK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>W</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_WP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>W</I><SUB><I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_WX {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>W</I><SUB>X" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_WXK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>W</I><SUB>X<I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_WXP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>W</I><SUB>X<I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_WY {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>W</I><SUB>Y" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_WYK {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>W</I><SUB>Y<I>, $n, ." . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_WYP {
+ local($_) = @_;
+ local($m) = &get_next_optional_argument;
+ local($n) = &get_token;
+ ("<I>W</I><SUB>Y<I>, ., $n" . ($m ? ", $m" : "") . "</I></SUB>", $_);
+}
+
+sub do_math_cmd_Ntb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>X</I><SUB>C" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_Ntb {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I><B>X</B></I><SUB>C" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_XC {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I>X</I><SUB>C" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_boldXC {
+ local($_) = @_;
+ local($n) = &get_next_optional_argument;
+ ("<I><B>X</B></I><SUB>C" . ($n ? "<I>, $n</I>" : "") . "</SUB>", $_);
+}
+
+sub do_math_cmd_rmc {
+ ("c", @_);
+}
+
+sub do_math_cmd_rmC {
+ ("C", @_);
+}
+
+sub do_math_cmd_rmCX {
+ ("CX", @_);
+}
+
+sub do_math_cmd_rmX {
+ ("X", @_);
+}
+
+sub do_math_cmd_tr {
+ ("t", @_);
+}
+
+sub do_math_cmd_wTG {
+ ("<I>w</I><SUB>TG</SUB>", @_);
+}
+
+sub do_math_cmd_wGT {
+ ("<I>w</I><SUB>GT</SUB>", @_);
+}
+
+&ignore_commands ("boxnote # {} # {}\npierre # {}\nhpierre # {}\n"
+ . "adam # {}\nhadam # {}\n"
+ . "richard # {}\nhrichard # {}\n"
+ . "eric # {}\nheric # {}\n"
+ . "pierre # {}\nhpierre # {}");
+
+1;
diff --git a/source/ssj.sty b/source/ssj.sty
new file mode 100644
index 0000000..6951466
--- /dev/null
+++ b/source/ssj.sty
@@ -0,0 +1,171 @@
+%%
+%% ssj.sty
+%%
+%% Used for SSJ documentation.
+%% Pierre L'Ecuyer et al.
+%% May 2003
+%%
+
+\RequirePackage{amsfonts}
+\RequirePackage{amsbsy}
+\RequirePackage{lmac}
+\RequirePackage{alltt}
+\RequirePackage{html}
+\RequirePackage{tcode}
+
+\textwidth=6.5 true in\textheight=9.0 true in
+\tolerance=500 \parskip=.08in plus .1in minus .06in
+\mytwoheads
+
+\newif\ifnotes\notestrue
+% \notesfalse %% Uncomment this line to hide footnotes. <----
+%
+\def\boxnote#1#2{\ifnotes\fbox{\footnote{\ }}\ \footnotetext{ From #1: #2}\fi}
+\def\pierre#1{\boxnote{Pierre}{#1}}
+\def\hpierre#1{}
+\def\richard#1{\boxnote{Richard}{#1}}
+\def\hrichard#1{}
+\def\eric#1{\boxnote{\'Eric}{#1}}
+\def\heric#1{}
+\def\adam#1{\boxnote{Adam}{#1}}
+\def\hadam#1{}
+
+% \def\citep{\@ifnextchar [{\@tempswatrue\@citexp}{\@tempswafalse\@citexp[]}}
+
+\def\g {{\;\leftarrow\;}}
+% \def\tbu {\tilde{\mbox{\boldmath $u$}}}
+\catcode`\^^Z 10
+
+
+\newcommand{\ba}{\mathbf{a}}
+\newcommand{\be}{\mathbf{e}}
+\newcommand{\bg}{\mathbf{g}}
+\newcommand{\bq}{\mathbf{q}}
+\newcommand{\bu}{\mathbf{u}}
+\newcommand{\bv}{\mathbf{v}}
+
+\newcommand{\bA}{\mathbf{A}}
+\newcommand{\bB}{\mathbf{B}}
+\newcommand{\bC}{\mathbf{C}}
+\newcommand{\bM}{\mathbf{M}}
+\newcommand{\bP}{\mathbf{P}}
+\newcommand{\bU}{\mathbf{U}}
+\newcommand{\bV}{\mathbf{V}}
+
+\newcommand{\bX}{\mathbf{X}}
+\newcommand{\bR}{\mathbf{R}}
+\newcommand{\bS}{\mathbf{S}}
+\newcommand{\bZ}{\mathbf{Z}}
+\newcommand{\bI}{\mathbf{I}}
+\newcommand{\bmu}{\boldmu}
+\newcommand{\bzero}{\mathbf{0}}
+\newcommand{\bSigma}{\boldSigma}
+
+\newcommand{\cS}{{\cal S}}
+
+\newcommand{\ZZ}{\mathbb{Z}}
+\newcommand{\RR}{\mathbb{R}}
+\newcommand{\NN}{\mathbb{N}}
+\renewcommand{\FF}{\mathbb{F}}
+
+\newcommand{\Ki}{K_{\mathrm{I}}}
+\newcommand{\Ko}{K_{\mathrm{O}}}
+\newcommand{\Nf}[1][\relax]{N_{\mathrm{f}\ifx\relax#1\else, #1\fi}}
+\newcommand{\Nb}[1][\relax]{N_{\mathrm{b}\ifx\relax#1\else, #1\fi}}
+\newcommand{\Ni}[1][\relax]{N_{\mathrm{i}\ifx\relax#1\else, #1\fi}}
+\newcommand{\Ng}[1][\relax]{N_{\mathrm{g}\ifx\relax#1\else, #1\fi}}
+\newcommand{\Ntb}[1][\relax]{N_{\mathrm{b}\ifx\relax#1\else, #1\fi}^{\mathrm{t}}}
+\newcommand{\Ntf}[1][\relax]{N_{\mathrm{f}\ifx\relax#1\else, #1\fi}^{\mathrm{t}}}
+\newcommand{\Ndf}[1][\relax]{N_{\mathrm{f}\ifx\relax#1\else, #1\fi}^{\mathrm{d}}}
+
+% \newcommand{\bsigma}{\mbox{\boldmath $\sigma$}}
+\newcommand{\bsigma}{\boldsymbol{\sigma}}
+
+\newcommand{\barboldmu}{\bar{\boldsymbol{\mu}}}
+\newcommand{\barboldnu}{\bar{\boldsymbol{\nu}}}
+\newcommand{\barboldX}{\bar{\mathbf{X}}}
+\newcommand{\boldmu}{\boldsymbol{\mu}}
+\newcommand{\boldnu}{\boldsymbol{\nu}}
+\newcommand{\boldbeta}{\boldsymbol{\beta}}
+\newcommand{\boldbetaf}{\boldsymbol{\beta}_{\mathrm{f}}}
+\newcommand{\betaf}[1][\relax]{\beta_{\mathrm{f}\ifx\relax#1\else, #1\fi}}
+\newcommand{\boldSigma}{\boldsymbol{\Sigma}}
+\newcommand{\boldLambda}{\boldsymbol{\Lambda}}
+\newcommand{\boldC}{\mathbf{C}}
+\newcommand{\boldX}{\mathbf{X}}
+\newcommand{\boldx}{\mathbf{x}}
+\newcommand{\boldY}{\mathbf{Y}}
+\newcommand{\boldV}{\mathbf{V}}
+\newcommand{\boldS}{\mathbf{S}}
+\newcommand{\boldf}{\mathbf{f}}
+
+\newcommand{\rTG}{r_{\mathrm{TG}}}
+\newcommand{\rGT}{r_{\mathrm{GT}}}
+
+\newcommand{\Xb}[1][\relax]{X_{\mathrm{b}\ifx\relax#1\else, #1\fi}}
+\newcommand{\barXb}[1][\relax]{\bar X_{\mathrm{b}\ifx\relax#1\else, #1\fi}}
+\newcommand{\XbK}[2][\relax]{X_{\mathrm{b}, #2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\XbP}[2][\relax]{X_{\mathrm{b}, \cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\Xg}[1][\relax]{X_{\mathrm{g}\ifx\relax#1\else, #1\fi}}
+\newcommand{\barXg}[1][\relax]{\bar X_{\mathrm{g}\ifx\relax#1\else, #1\fi}}
+\newcommand{\XgK}[2][\relax]{X_{\mathrm{g}, #2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\XgP}[2][\relax]{X_{\mathrm{g}, \cdot, #2\ifx\relax#1\else, #1\fi}}
+
+\newcommand{\Yb}[1][\relax]{Y_{\mathrm{b}\ifx\relax#1\else, #1\fi}}
+\newcommand{\barYb}[1][\relax]{\bar Y_{\mathrm{b}\ifx\relax#1\else, #1\fi}}
+\newcommand{\YbK}[2][\relax]{Y_{\mathrm{b}, #2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\YbP}[2][\relax]{Y_{\mathrm{b}, \cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\Yg}[1][\relax]{Y_{\mathrm{g}\ifx\relax#1\else, #1\fi}}
+\newcommand{\barYg}[1][\relax]{\bar Y_{\mathrm{g}\ifx\relax#1\else, #1\fi}}
+\newcommand{\YgK}[2]{Y_{\mathrm{g}, #2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\YgP}[2]{Y_{\mathrm{g}, \cdot, #2\ifx\relax#1\else, #1\fi}}
+
+% \newcommand{\Var}{\mathrm{Var}}
+% \newcommand{\Cov}{\mathrm{Cov}}
+\newcommand{\sK}[2][\relax]{s_{#2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\sP}[2][\relax]{s_{\cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\lK}[2][\relax]{l_{#2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\lP}[2][\relax]{l_{\cdot, #2\ifx\relax#1\else, #1\fi}}
+
+\newcommand{\XK}[2][\relax]{X_{#2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\XP}[2][\relax]{X_{\cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\YK}[2][\relax]{Y_{#2,\cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\YP}[2][\relax]{Y_{\cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\BK}[2][\relax]{B_{#2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\BP}[2][\relax]{B_{\cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\AK}[2][\relax]{A_{#2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\AP}[2][\relax]{A_{\cdot, #2\ifx\relax#1\else, #1\fi}}
+
+\newcommand{\WK}[2][\relax]{W_{#2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\WP}[2][\relax]{W_{\cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\WX}[1][\relax]{W_{\mathrm{X}\ifx\relax#1\else, #1\fi}}
+\newcommand{\WXK}[2][\relax]{W_{\mathrm{X}, #2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\WXP}[2][\relax]{W_{\mathrm{X}, \cdot, #2\ifx\relax#1\else, #1\fi}}
+\newcommand{\WY}[1][\relax]{W_{\mathrm{Y}\ifx\relax#1\else, #1\fi}}
+\newcommand{\WYK}[2][\relax]{W_{\mathrm{Y}, #2, \cdot\ifx\relax#1\else, #1\fi}}
+\newcommand{\WYP}[2][\relax]{W_{\mathrm{Y}, \cdot, #2\ifx\relax#1\else, #1\fi}}
+
+\newcommand{\XC}[1][\relax]{X_{\mathrm{C}\ifx\relax#1\else, #1\fi}}
+\newcommand{\boldXC}[1][\relax]{\mathbf{X}_{\mathrm{C}\ifx\relax#1\else, #1\fi}}
+\newcommand{\rmc}{\mathrm{c}}
+\newcommand{\rmC}{\mathrm{C}}
+\newcommand{\rmCX}{\mathrm{CX}}
+\newcommand{\rmX}{\mathrm{X}}
+\newcommand{\tr}{\mathsf{t}}
+
+\newcommand{\wTG}{w_{\mathrm{TG}}}
+\newcommand{\wGT}{w_{\mathrm{GT}}}
+
+\def\title#1#2{
+\null\vfill
+\begin {center}
+{\Large\bf SSJ User's Guide } \\[20pt]
+% {\Large\bf A Java Library for Stochastic Simulation }\\[15pt]
+% \centerline{\hbox to 3cm{\hrulefill}}\\[10pt]
+{\Large Package \ \tt #1} \\[15pt]
+{\Large #2} \\[20pt]
+% \centerline{\hbox to 3cm{\hrulefill}}\\[20pt]
+ Version: \today \\
+\vfill\vfill
+\end {center}
+}
diff --git a/source/umontreal/iro/lecuyer/charts/Axis.java b/source/umontreal/iro/lecuyer/charts/Axis.java
new file mode 100644
index 0000000..92f12ee
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/Axis.java
@@ -0,0 +1,500 @@
+
+
+/*
+ * Class: Axis
+ * Description: An axis of a chart and its properties
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
+import java.util.Locale;
+import java.util.Formatter;
+import java.lang.Math;
+
+
+/**
+ * Represents an axis of a chart encapsulated by
+ * an instance of <TT>XYChart</TT>.
+ * <TT>Axis</TT> uses the JFreeChart class <TT>NumberAxis</TT>
+ * to store some axis properties.
+ * This class represents the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis or the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis of a <TT>XYChart</TT>
+ * and, consequently, is drawn when calling the {@link #toLatex toLatex} method.
+ * It provides tools to customize the axis in modifying labels and description.
+ *
+ */
+public class Axis {
+
+
+ public static final boolean ORIENTATION_VERTICAL = false;
+ public static final boolean ORIENTATION_HORIZONTAL = true;
+
+ private NumberAxis axis; // Uses JFreeChart API
+ private boolean orientation; // Vertical or horizontal
+ private double twinAxisPosition = 0;
+
+ private boolean tick0Flag; // if true, label number at origin is shifted
+ // right or above a little to prevent the label from being on the
+ // perpendicular axis.
+
+ private boolean labelsFlag; // true : enable manual labels
+ private String[] labelsName; // Sets manual labels
+ private double[] labelsValue; // Where putting the labels on the axes
+
+ private double fixStep (double minA, double maxA, double step) {
+ // reset step for labels on axes if it is too large or too small
+ final double H = 10;
+ final double inter = maxA - minA;
+ while (step > inter) // step is too large, make it smaller
+ step /= H;
+ while (step < inter / H) // step is too small, make it larger
+ step *= H;
+ if (step > inter/2)
+ step /= 2;
+ return step;
+ }
+
+
+
+ /**
+ * Create a new <TT>Axis</TT> instance from an existing <TT>NumberAxis</TT>
+ * instance with vertical (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) or horizontal (<SPAN CLASS="MATH"><I>x</I></SPAN>-axis) orientation.
+ *
+ * @param inAxis NumberAxis instance associated to the new variable.
+ *
+ * @param orientation axis direction, horizontal or vertical
+ *
+ */
+ public Axis (NumberAxis inAxis, boolean orientation) {
+ this.axis = inAxis;
+ this.orientation = orientation;
+ this.labelsFlag = false;
+ this.labelsValue = null;
+ this.labelsName = null;
+ inAxis.setAutoRangeIncludesZero(false);
+ inAxis.setLowerMargin(0);
+ inAxis.setUpperMargin(0);
+ axis.setTickUnit(new NumberTickUnit(computeAutoTickValue()));
+ }
+
+
+ /**
+ * Returns the <TT>NumberAxis</TT> instance (from JFreeChart) linked with the current variable.
+ *
+ * @return the <TT>NumberAxis</TT> instance (from JFreeChart) linked with the current variable.
+ *
+ */
+ protected NumberAxis getAxis() {
+ return axis;
+ }
+
+
+ /**
+ * Returns the axis description.
+ *
+ * @return the axis description.
+ *
+ */
+ public String getLabel() {
+ return axis.getLabel();
+ }
+
+
+ /**
+ * Sets the axis description. This description will be displayed on the chart, near the axis.
+ *
+ * @param label axis label.
+ *
+ *
+ */
+ public void setLabel (String label) {
+ axis.setLabel(label);
+ }
+
+
+ /**
+ * Sets a periodic label display. Labels will be shown every <TT>tick</TT> unit.
+ * This tick unit replaces the default unit.
+ *
+ * @param tick tick unit.
+ *
+ *
+ */
+ public void setLabels (double tick) {
+ axis.setTickUnit(new NumberTickUnit(tick));
+ labelsFlag = false;
+ }
+
+
+ /**
+ * Calculates and sets an automatic tick unit.
+ *
+ */
+ public void setLabelsAuto() {
+ axis.setTickUnit(new NumberTickUnit(computeAutoTickValue()));
+ labelsFlag = false;
+ }
+
+
+ /**
+ * Not used anymore.
+ *
+ */
+ @Deprecated
+ public void enableCustomLabels() {
+ labelsFlag = true;
+ }
+
+
+ /**
+ * Not used anymore.
+ *
+ */
+ @Deprecated
+ public void disableCustomLabels() {
+ labelsFlag = false;
+ }
+
+
+ /**
+ * Sets the position of each label on this axis. This method requires an array
+ * containing an increasing sequence of numbers representing the positions at
+ * which labels will appear on the axis. It is designed to export the
+ * axis to a LaTeX source code; it has no effect on the chart appearance
+ * displayed with <TT>XYChart.view()</TT>.
+ *
+ * @param position new label positions.
+ *
+ *
+ */
+ public void setLabels (double[] position) {
+ this.labelsName = null;
+ this.labelsValue = position.clone();
+ double max = max(position);
+ if (axis.getUpperBound() < max)
+ axis.setUpperBound(max);
+ double min = min(position);
+ if (axis.getLowerBound() > min)
+ axis.setLowerBound(min);
+ labelsFlag = true;
+ }
+
+
+ /**
+ * Assigns custom labels to user-defined positions on the axis.
+ * This method requires an array of positions as well as an array of labels.
+ * The label <TT>label[i]</TT> will be used at position <TT>position[i]</TT>.
+ * It is designed to export the axis to a LaTeX source code,
+ * and to use <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN>/TikZ commands to write prettier characters; it has no
+ * effect on the chart appearance displayed with <TT>XYChart.view()</TT>.
+ *
+ * @param position label series position on the axis.
+ *
+ * @param label label series name on the axis.
+ *
+ *
+ */
+ public void setLabels (double[] position, String[] label) {
+ if (label.length != position.length)
+ throw new IllegalArgumentException
+ ("A label is required for each given position");
+ this.labelsName = label.clone();
+ this.labelsValue = position.clone();
+ double max = max(position);
+ if (axis.getUpperBound() < max)
+ axis.setUpperBound(max);
+ double min = min(position);
+ if (axis.getLowerBound() > min)
+ axis.setLowerBound(min);
+ labelsFlag = true;
+ }
+
+
+ /**
+ * Returns the drawing position parameter (default equals 0).
+ *
+ * @return drawing position parameter.
+ *
+ */
+ public double getTwinAxisPosition() {
+ return this.twinAxisPosition;
+ }
+
+
+ /**
+ * Defines where the opposite axis must be drawn on the current axis,
+ * where it should appear, and on which label.
+ *
+ * @param position new drawing position.
+ *
+ *
+ */
+ public void setTwinAxisPosition (double position) {
+ this.twinAxisPosition = position;
+ }
+
+
+ /**
+ * Formats and returns a string containing a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN>-compatible
+ * source code which represents this axis and its parameters.
+ *
+ * @return LaTeX source code in a String.
+ * @param scale current axis wished scale.
+ *
+ *
+ */
+ public String toLatex (double scale) {
+ Formatter formatter = new Formatter(Locale.US);
+ double maxAxis = Math.max(axis.getRange().getUpperBound(), twinAxisPosition); //valeur du label le plus grand
+ double minAxis = Math.min(axis.getRange().getLowerBound(), twinAxisPosition); //valeur du label le plus petit
+
+ String precisionAffichageLabel; //determine combien de decimales seront affichees pour les labels
+ double pas = axis.getTickUnit().getSize(); //step d'affichage des labels
+ pas = fixStep (minAxis, maxAxis, pas);
+
+ int puissDix; //echelle des valeurs sur l'axe
+ if (Math.log10(pas) < 0)
+ puissDix = (int)Math.log10(pas) - 1;
+ else
+ puissDix = (int)Math.log10(pas);
+
+ //Placement des fleches, on pourrait facilement les rendre personnalisables...
+ String arrowLeftType = "latex";
+ String arrowRightType = "latex";
+ String arrowLeftMargin = "3mm";
+ String arrowRightMargin = "3mm";
+ if (twinAxisPosition == minAxis) {
+ arrowLeftType = "";
+ arrowLeftMargin = "0mm";
+ } else if (twinAxisPosition == maxAxis) {
+ arrowRightType = "";
+ arrowRightMargin = "0mm";
+ }
+
+ // Label pour l'axe, avec eventuellement l'echelle
+ String label = axis.getLabel();
+ if (label == null)
+ label = " ";
+
+ if (puissDix < -2 || puissDix > 2)
+ label = label + " $(10^{" + puissDix + "})$";
+ else
+ puissDix = 0;
+
+ if (orientation) { //on est sur l'axe des abscisses
+
+ //affichage de l'axe
+ formatter.format("\\draw [%s-%s] ([xshift=-%s] %s,0) -- ([xshift=%s] %s,0) node[right] {%s};%n", arrowLeftType, arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition)*scale, arrowRightMargin, (maxAxis - twinAxisPosition)*scale, label);
+
+ if (labelsFlag) { //labels manuels
+ String name;
+ double value;
+ double labelTemp;
+ for (int i = 0; i < labelsValue.length; i++) {
+ value = labelsValue[i];
+ if (labelsName == null) {
+ labelTemp = (value * 100.0 / Math.pow(10, puissDix));
+ // if(labelTemp == (int)(labelTemp))
+ name = Integer.toString((int)(labelTemp / 100.0));
+ // else
+ // name = Double.toString(Math.round(labelTemp)/100.0);
+ } else
+ name = labelsName[i];
+ if (value == twinAxisPosition && tick0Flag)
+ formatter.format("\\draw (%s,00) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%s};%n", (value - twinAxisPosition)*scale, name);
+ else
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%s};%n", (value - twinAxisPosition)*scale, name);
+ }
+ } else { //labels automatiques
+ double labelTemp;
+ double k = twinAxisPosition;
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ if (tick0Flag)
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ else
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ while (k <= maxAxis) { //cote positif
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ }
+
+ k = twinAxisPosition - pas;
+ while (k >= minAxis) { //cote negatif
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k -= pas;
+ }
+ }
+
+ } else { //On est sur l'axe des ordonnees
+
+ //affichage de l'axe
+ formatter.format("\\draw [%s-%s] ([yshift=-%s] 0,%s) -- ([yshift=%s] 0, %s) node[above] {%s};%n", arrowLeftType, arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition)*scale, arrowRightMargin, (maxAxis - twinAxisPosition)*scale, label);
+
+ if (labelsFlag) {
+ //labels manuels
+ String name;
+ double value;
+ double labelTemp;
+ for (int i = 0; i < labelsValue.length; i++) {
+ value = labelsValue[i];
+ if (labelsName == null) {
+ labelTemp = (value * scale * 100 / Math.pow(10, puissDix));
+ if (labelTemp == (int)(labelTemp))
+ name = Integer.toString((int)(labelTemp / 100.0));
+ else
+ name = Double.toString(Math.round(labelTemp) / 100.0);
+ } else
+ name = labelsName[i];
+ if (value == twinAxisPosition && tick0Flag)
+ formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%s};%n", 0, (value - twinAxisPosition)*scale, name);
+ else
+ formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%s};%n", 0, (value - twinAxisPosition)*scale, name);
+ }
+ } else {
+ //Les labels automatiques
+ double k = twinAxisPosition;
+ double labelTemp;
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ if (tick0Flag)
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ else
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ while (k <= maxAxis) { //cote positif de l'axe
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ }
+ k = twinAxisPosition - pas;
+ while (k >= minAxis) { //cote negatif de l'axe
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k -= pas;
+ }
+ }
+ }
+ return formatter.toString();
+ }
+
+
+ private double computeAutoTickValue() {
+ // Calcule le pas d'affichage par defaut des labels
+ double pas;
+ double[] bounds = new double[2];
+ bounds[0] = Math.min(axis.getRange().getLowerBound(), twinAxisPosition);
+ bounds[1] = Math.max(axis.getRange().getUpperBound(), twinAxisPosition);
+ if ( bounds[1] - bounds[0] >= 1) {
+ int nbMaxDeFois = (int)(bounds[1] - bounds[0]);
+ int puissDix = (int)Math.log10(nbMaxDeFois);
+ nbMaxDeFois = (int)(nbMaxDeFois / Math.pow(10, puissDix)) + 1;
+ if (nbMaxDeFois > 5)
+ pas = Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 3)
+ pas = 0.5 * Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 1)
+ pas = 0.25 * Math.pow(10, puissDix);
+ else //nbMaxDeFois==1
+ pas = 0.1 * Math.pow(10, puissDix);
+ } else {
+ double nbMaxDeFois = bounds[1] - bounds[0];
+ int puissDix = 0;
+ while (nbMaxDeFois < 1) {
+ nbMaxDeFois *= 10;
+ puissDix++;
+ }
+ if (nbMaxDeFois > 5)
+ pas = 1 / Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 3)
+ pas = 0.5 / Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 1)
+ pas = 0.3 / Math.pow(10, puissDix);
+ else //nbMaxDeFois==1
+ pas = 0.1 / Math.pow(10, puissDix);
+ }
+ return pas;
+ }
+
+ private static double max (double[] t) {
+ if (t == null)
+ throw new IllegalArgumentException ("max: null argument.");
+ double aux = t[0];
+ for (int i = 1; i < t.length; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux;
+ }
+
+ private static double min (double[] t) {
+ if (t == null)
+ throw new IllegalArgumentException ("min: null argument.");
+ double aux = t[0];
+ for (int i = 1; i < t.length; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux;
+ }
+
+
+ void setTick0Flag(boolean flag) {
+ tick0Flag = flag;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/Axis.tex b/source/umontreal/iro/lecuyer/charts/Axis.tex
new file mode 100644
index 0000000..4303ca3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/Axis.tex
@@ -0,0 +1,517 @@
+\defmodule {Axis}
+
+Represents an axis of a chart encapsulated by
+an instance of \texttt{XYChart}.
+\texttt{Axis} uses the JFreeChart class \texttt{NumberAxis}
+to store some axis properties.
+This class represents the $x$-axis or the $y$-axis of a \texttt{XYChart}
+and, consequently, is drawn when calling the \method{toLatex}{} method.
+It provides tools to customize the axis in modifying labels and description.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: Axis
+ * Description: An axis of a chart and its properties
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
+import java.util.Locale;
+import java.util.Formatter;
+import java.lang.Math;\end{hide}
+
+
+public class Axis \begin{hide} {
+ \end{hide}
+
+ public static final boolean ORIENTATION_VERTICAL = false;
+ public static final boolean ORIENTATION_HORIZONTAL = true;\begin{hide}
+
+ private NumberAxis axis; // Uses JFreeChart API
+ private boolean orientation; // Vertical or horizontal
+ private double twinAxisPosition = 0;
+
+ private boolean tick0Flag; // if true, label number at origin is shifted
+ // right or above a little to prevent the label from being on the
+ // perpendicular axis.
+
+ private boolean labelsFlag; // true : enable manual labels
+ private String[] labelsName; // Sets manual labels
+ private double[] labelsValue; // Where putting the labels on the axes
+
+ private double fixStep (double minA, double maxA, double step) {
+ // reset step for labels on axes if it is too large or too small
+ final double H = 10;
+ final double inter = maxA - minA;
+ while (step > inter) // step is too large, make it smaller
+ step /= H;
+ while (step < inter / H) // step is too small, make it larger
+ step *= H;
+ if (step > inter/2)
+ step /= 2;
+ return step;
+ }
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+ public Axis (NumberAxis inAxis, boolean orientation) \begin{hide} {
+ this.axis = inAxis;
+ this.orientation = orientation;
+ this.labelsFlag = false;
+ this.labelsValue = null;
+ this.labelsName = null;
+ inAxis.setAutoRangeIncludesZero(false);
+ inAxis.setLowerMargin(0);
+ inAxis.setUpperMargin(0);
+ axis.setTickUnit(new NumberTickUnit(computeAutoTickValue()));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Create a new \texttt{Axis} instance from an existing \texttt{NumberAxis}
+ instance with vertical ($y$-axis) or horizontal ($x$-axis) orientation.
+\end{tabb}
+\begin{htmlonly}
+ \param{inAxis}{NumberAxis instance associated to the new variable.}
+ \param{orientation}{axis direction, horizontal or vertical}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ protected NumberAxis getAxis() \begin{hide} {
+ return axis;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{NumberAxis} instance (from JFreeChart) linked with the current variable.
+\end{tabb}
+\begin{htmlonly}
+ \return{the \texttt{NumberAxis} instance (from JFreeChart) linked with the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public String getLabel() \begin{hide} {
+ return axis.getLabel();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the axis description.
+\end{tabb}
+\begin{htmlonly}
+ \return{the axis description.}
+\end{htmlonly}
+\begin{code}
+
+ public void setLabel (String label) \begin{hide} {
+ axis.setLabel(label);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the axis description. This description will be displayed on the chart, near the axis.
+\end{tabb}
+\begin{htmlonly}
+ \param{label}{axis label.}
+\end{htmlonly}
+\begin{code}
+
+ public void setLabels (double tick) \begin{hide} {
+ axis.setTickUnit(new NumberTickUnit(tick));
+ labelsFlag = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Sets a periodic label display. Labels will be shown every \texttt{tick} unit.
+This tick unit replaces the default unit.
+\end{tabb}
+\begin{htmlonly}
+ \param{tick}{tick unit.}
+\end{htmlonly}
+\begin{code}
+
+ public void setLabelsAuto() \begin{hide} {
+ axis.setTickUnit(new NumberTickUnit(computeAutoTickValue()));
+ labelsFlag = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Calculates and sets an automatic tick unit.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{LaTex-specific methods}
+
+\begin{code}
+
+ @Deprecated
+ public void enableCustomLabels() \begin{hide} {
+ labelsFlag = true;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Not used anymore.
+\end{tabb}
+\begin{code}
+
+ @Deprecated
+ public void disableCustomLabels() \begin{hide} {
+ labelsFlag = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Not used anymore.
+\end{tabb}
+\begin{code}
+
+ public void setLabels (double[] position) \begin{hide} {
+ this.labelsName = null;
+ this.labelsValue = position.clone();
+ double max = max(position);
+ if (axis.getUpperBound() < max)
+ axis.setUpperBound(max);
+ double min = min(position);
+ if (axis.getLowerBound() > min)
+ axis.setLowerBound(min);
+ labelsFlag = true;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Sets the position of each label on this axis. This method requires an array
+containing an increasing sequence of numbers representing the positions at
+ which labels will appear on the axis. It is designed to export the
+ axis to a LaTeX source code; it has no effect on the chart appearance
+ displayed with \texttt{XYChart.view()}.
+\end{tabb}
+\begin{htmlonly}
+ \param{position}{new label positions.}
+\end{htmlonly}
+\begin{code}
+
+ public void setLabels (double[] position, String[] label) \begin{hide} {
+ if (label.length != position.length)
+ throw new IllegalArgumentException
+ ("A label is required for each given position");
+ this.labelsName = label.clone();
+ this.labelsValue = position.clone();
+ double max = max(position);
+ if (axis.getUpperBound() < max)
+ axis.setUpperBound(max);
+ double min = min(position);
+ if (axis.getLowerBound() > min)
+ axis.setLowerBound(min);
+ labelsFlag = true;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Assigns custom labels to user-defined positions on the axis.
+ This method requires an array of positions as well as an array of labels.
+ The label \texttt{label[i]} will be used at position \texttt{position[i]}.
+ It is designed to export the axis to a LaTeX source code,
+ and to use \LaTeX/TikZ commands to write prettier characters; it has no
+ effect on the chart appearance displayed with \texttt{XYChart.view()}.
+\end{tabb}
+\begin{htmlonly}
+ \param{position}{label series position on the axis.}
+ \param{label}{label series name on the axis.}
+\end{htmlonly}
+\begin{code}
+
+ public double getTwinAxisPosition() \begin{hide} {
+ return this.twinAxisPosition;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the drawing position parameter (default equals 0).
+\end{tabb}
+\begin{htmlonly}
+ \return{drawing position parameter.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTwinAxisPosition (double position) \begin{hide} {
+ this.twinAxisPosition = position;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Defines where the opposite axis must be drawn on the current axis,
+ where it should appear, and on which label.
+\end{tabb}
+\begin{htmlonly}
+ \param{position}{new drawing position.}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatex (double scale) \begin{hide} {
+ Formatter formatter = new Formatter(Locale.US);
+ double maxAxis = Math.max(axis.getRange().getUpperBound(), twinAxisPosition); //valeur du label le plus grand
+ double minAxis = Math.min(axis.getRange().getLowerBound(), twinAxisPosition); //valeur du label le plus petit
+
+ String precisionAffichageLabel; //determine combien de decimales seront affichees pour les labels
+ double pas = axis.getTickUnit().getSize(); //step d'affichage des labels
+ pas = fixStep (minAxis, maxAxis, pas);
+
+ int puissDix; //echelle des valeurs sur l'axe
+ if (Math.log10(pas) < 0)
+ puissDix = (int)Math.log10(pas) - 1;
+ else
+ puissDix = (int)Math.log10(pas);
+
+ //Placement des fleches, on pourrait facilement les rendre personnalisables...
+ String arrowLeftType = "latex";
+ String arrowRightType = "latex";
+ String arrowLeftMargin = "3mm";
+ String arrowRightMargin = "3mm";
+ if (twinAxisPosition == minAxis) {
+ arrowLeftType = "";
+ arrowLeftMargin = "0mm";
+ } else if (twinAxisPosition == maxAxis) {
+ arrowRightType = "";
+ arrowRightMargin = "0mm";
+ }
+
+ // Label pour l'axe, avec eventuellement l'echelle
+ String label = axis.getLabel();
+ if (label == null)
+ label = " ";
+
+ if (puissDix < -2 || puissDix > 2)
+ label = label + " $(10^{" + puissDix + "})$";
+ else
+ puissDix = 0;
+
+ if (orientation) { //on est sur l'axe des abscisses
+
+ //affichage de l'axe
+ formatter.format("\\draw [%s-%s] ([xshift=-%s] %s,0) -- ([xshift=%s] %s,0) node[right] {%s};%n", arrowLeftType, arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition)*scale, arrowRightMargin, (maxAxis - twinAxisPosition)*scale, label);
+
+ if (labelsFlag) { //labels manuels
+ String name;
+ double value;
+ double labelTemp;
+ for (int i = 0; i < labelsValue.length; i++) {
+ value = labelsValue[i];
+ if (labelsName == null) {
+ labelTemp = (value * 100.0 / Math.pow(10, puissDix));
+ // if(labelTemp == (int)(labelTemp))
+ name = Integer.toString((int)(labelTemp / 100.0));
+ // else
+ // name = Double.toString(Math.round(labelTemp)/100.0);
+ } else
+ name = labelsName[i];
+ if (value == twinAxisPosition && tick0Flag)
+ formatter.format("\\draw (%s,00) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%s};%n", (value - twinAxisPosition)*scale, name);
+ else
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%s};%n", (value - twinAxisPosition)*scale, name);
+ }
+ } else { //labels automatiques
+ double labelTemp;
+ double k = twinAxisPosition;
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ if (tick0Flag)
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ else
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ while (k <= maxAxis) { //cote positif
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ }
+
+ k = twinAxisPosition - pas;
+ while (k >= minAxis) { //cote negatif
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (%s,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k -= pas;
+ }
+ }
+
+ } else { //On est sur l'axe des ordonnees
+
+ //affichage de l'axe
+ formatter.format("\\draw [%s-%s] ([yshift=-%s] 0,%s) -- ([yshift=%s] 0, %s) node[above] {%s};%n", arrowLeftType, arrowRightType, arrowLeftMargin, (minAxis - twinAxisPosition)*scale, arrowRightMargin, (maxAxis - twinAxisPosition)*scale, label);
+
+ if (labelsFlag) {
+ //labels manuels
+ String name;
+ double value;
+ double labelTemp;
+ for (int i = 0; i < labelsValue.length; i++) {
+ value = labelsValue[i];
+ if (labelsName == null) {
+ labelTemp = (value * scale * 100 / Math.pow(10, puissDix));
+ if (labelTemp == (int)(labelTemp))
+ name = Integer.toString((int)(labelTemp / 100.0));
+ else
+ name = Double.toString(Math.round(labelTemp) / 100.0);
+ } else
+ name = labelsName[i];
+ if (value == twinAxisPosition && tick0Flag)
+ formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%s};%n", 0, (value - twinAxisPosition)*scale, name);
+ else
+ formatter.format("\\draw (%s,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%s};%n", 0, (value - twinAxisPosition)*scale, name);
+ }
+ } else {
+ //Les labels automatiques
+ double k = twinAxisPosition;
+ double labelTemp;
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ if (tick0Flag)
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ else
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ while (k <= maxAxis) { //cote positif de l'axe
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k += pas;
+ }
+ k = twinAxisPosition - pas;
+ while (k >= minAxis) { //cote negatif de l'axe
+ labelTemp = (Math.round(k * 100 / Math.pow(10, puissDix))) / 100.0;
+ if (labelTemp == (int)(labelTemp))
+ precisionAffichageLabel = "0";
+ else if (labelTemp*10 == (int)(labelTemp*10))
+ precisionAffichageLabel = "1";
+ else
+ precisionAffichageLabel = "2";
+ formatter.format("\\draw (0,%s) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {%." + precisionAffichageLabel + "f};%n", (k - twinAxisPosition)*scale, labelTemp);
+ k -= pas;
+ }
+ }
+ }
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Formats and returns a string containing a \LaTeX-compatible
+ source code which represents this axis and its parameters.
+\end{tabb}
+\begin{htmlonly}
+ \return{LaTeX source code in a String.}
+ \param{scale}{current axis wished scale.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ private double computeAutoTickValue() {
+ // Calcule le pas d'affichage par defaut des labels
+ double pas;
+ double[] bounds = new double[2];
+ bounds[0] = Math.min(axis.getRange().getLowerBound(), twinAxisPosition);
+ bounds[1] = Math.max(axis.getRange().getUpperBound(), twinAxisPosition);
+ if ( bounds[1] - bounds[0] >= 1) {
+ int nbMaxDeFois = (int)(bounds[1] - bounds[0]);
+ int puissDix = (int)Math.log10(nbMaxDeFois);
+ nbMaxDeFois = (int)(nbMaxDeFois / Math.pow(10, puissDix)) + 1;
+ if (nbMaxDeFois > 5)
+ pas = Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 3)
+ pas = 0.5 * Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 1)
+ pas = 0.25 * Math.pow(10, puissDix);
+ else //nbMaxDeFois==1
+ pas = 0.1 * Math.pow(10, puissDix);
+ } else {
+ double nbMaxDeFois = bounds[1] - bounds[0];
+ int puissDix = 0;
+ while (nbMaxDeFois < 1) {
+ nbMaxDeFois *= 10;
+ puissDix++;
+ }
+ if (nbMaxDeFois > 5)
+ pas = 1 / Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 3)
+ pas = 0.5 / Math.pow(10, puissDix);
+ else if (nbMaxDeFois > 1)
+ pas = 0.3 / Math.pow(10, puissDix);
+ else //nbMaxDeFois==1
+ pas = 0.1 / Math.pow(10, puissDix);
+ }
+ return pas;
+ }
+
+ private static double max (double[] t) {
+ if (t == null)
+ throw new IllegalArgumentException ("max: null argument.");
+ double aux = t[0];
+ for (int i = 1; i < t.length; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux;
+ }
+
+ private static double min (double[] t) {
+ if (t == null)
+ throw new IllegalArgumentException ("min: null argument.");
+ double aux = t[0];
+ for (int i = 1; i < t.length; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux;
+ }
+
+
+ void setTick0Flag(boolean flag) {
+ tick0Flag = flag;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/BoxChart.java b/source/umontreal/iro/lecuyer/charts/BoxChart.java
new file mode 100644
index 0000000..b81f109
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/BoxChart.java
@@ -0,0 +1,324 @@
+
+
+/*
+ * Class: BoxChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
+import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
+
+import java.util.Locale;
+import java.util.Formatter;
+import javax.swing.JFrame;
+
+
+
+/**
+ * This class provides tools to create and manage box-and-whisker plots.
+ * Each {@link BoxChart} object is linked with a
+ * {@link umontreal.iro.lecuyer.charts.BoxSeriesCollection BoxSeriesCollection} data set.
+ *
+ * <P>
+ * A boxplot is a convenient way of viewing sets of numerical data through their
+ * summaries: the smallest observation, first quartile (
+ * <SPAN CLASS="MATH"><I>Q</I><SUB>1</SUB> = <I>x</I><SUB>.25</SUB></SPAN>), median
+ * (
+ * <SPAN CLASS="MATH"><I>Q</I><SUB>2</SUB> = <I>x</I><SUB>.5</SUB></SPAN>), third quartile (
+ * <SPAN CLASS="MATH"><I>Q</I><SUB>3</SUB> = <I>x</I><SUB>.75</SUB></SPAN>), and largest observation.
+ * Sometimes, the mean and the outliers are also plotted.
+ *
+ * <P>
+ * In the charts created by this class, the box has its lower limit at <SPAN CLASS="MATH"><I>Q</I><SUB>1</SUB></SPAN>
+ * and its upper limit at <SPAN CLASS="MATH"><I>Q</I><SUB>3</SUB></SPAN>. The median is indicated by the line inside the box,
+ * while the mean is at the center of the filled circle inside the box.
+ * Define the interquartile range as (<SPAN CLASS="MATH"><I>Q</I><SUB>3</SUB> - <I>Q</I><SUB>1</SUB></SPAN>).
+ * Any data observation which is more than
+ * <SPAN CLASS="MATH">1.5(<I>Q</I><SUB>3</SUB> - <I>Q</I><SUB>1</SUB>)</SPAN> lower than the first
+ * quartile or
+ * <SPAN CLASS="MATH">1.5(<I>Q</I><SUB>3</SUB> - <I>Q</I><SUB>1</SUB>)</SPAN> higher than the third quartile is considered an
+ * outlier. The smallest and the largest values that are not outliers are connected
+ * to the box with a vertical line or "whisker" which is ended by a horizontal line.
+ * Outliers are indicated by hollow circles outside the whiskers. Triangles
+ * indicate the existence of very far outliers.
+ *
+ */
+public class BoxChart extends CategoryChart {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createBoxAndWhiskerChart (
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ (DefaultBoxAndWhiskerCategoryDataset)dataset.getSeriesCollection(), // data
+ true // include legend
+ );
+
+ ((CategoryPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ initAxis();
+ }
+
+ protected void initAxis(){
+ YAxis = new Axis((NumberAxis)((CategoryPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ setAutoRange();
+ }
+
+
+
+ /**
+ * Initializes a new <TT>BoxChart</TT> instance with an empty data set.
+ *
+ */
+ public BoxChart() {
+ super();
+ dataset = new BoxSeriesCollection();
+ init (null, null, null);
+ }
+
+
+ /**
+ * Initializes a new <TT>BoxChart</TT> instance with data <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input parameter <TT>data</TT> represents a set of plotting data.
+ * Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT> will
+ * be considered for the plot.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data point sets.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public BoxChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) {
+ super();
+ dataset = new BoxSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>BoxChart</TT> instance with data <TT>data</TT>.
+ * <TT>title</TT> sets a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> is a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input parameter <TT>data</TT> represents a set of plotting data.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ */
+ public BoxChart (String title, String XLabel, String YLabel,
+ double[]... data) {
+ super();
+ dataset = new BoxSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>data</TT> represents
+ * a set of plotting data.
+ *
+ * @param data point sets.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>BoxSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] data) {
+ return add(data, data.length);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>data</TT> represents
+ * a set of plotting data. Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of
+ * <TT>data</TT> will be taken into account for the new series.
+ *
+ * @param data point set.
+ *
+ * @param numPoints number of points to add.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>BoxSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] data, int numPoints) {
+ int seriesIndex = getSeriesCollection().add(data, numPoints);
+ initAxis();
+ return seriesIndex;
+ }
+
+
+ /**
+ * Returns the chart's dataset.
+ *
+ * @return the chart's dataset.
+ *
+ */
+ public BoxSeriesCollection getSeriesCollection() {
+ return (BoxSeriesCollection)dataset;
+ }
+
+
+ /**
+ * Links a new dataset to the current chart.
+ *
+ * @param dataset new dataset.
+ *
+ *
+ */
+ public void setSeriesCollection (BoxSeriesCollection dataset) {
+ this.dataset = dataset;
+ }
+
+
+ /**
+ * Sets <TT>fill</TT> to <TT>true</TT>, if the boxes are to be filled.
+ *
+ * @param fill true if the boxes are filled
+ *
+ *
+ */
+ public void setFillBox (boolean fill) {
+ ((BoxAndWhiskerRenderer)dataset.getRenderer()).setFillBox(fill);
+ }
+
+
+ /**
+ * Displays chart on the screen using Swing.
+ * This method creates an application containing a chart panel displaying
+ * the chart. The created frame is positioned on-screen, and displayed before
+ * it is returned. The circle represents the mean, the dark line inside the box
+ * is the median, the box limits are the first and third quartiles,
+ * the lower whisker (the lower line outside the box) is the first decile,
+ * and the upper whisker is the ninth decile. The outliers, if any, are
+ * represented by empty circles, or arrows if outside the range bounds.
+ *
+ * @param width frame width.
+ *
+ * @param height frame height.
+ *
+ * @return frame containing the chart.;
+ *
+ */
+ public JFrame view (int width, int height) {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame ("BoxChart from SSJ : " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame ("BoxChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel (chart);
+ chartPanel.setPreferredSize (new java.awt.Dimension(width, height));
+ myFrame.setContentPane (chartPanel);
+ myFrame.pack ();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible (true);
+ return myFrame;
+ }
+
+
+ /**
+ * NOT IMPLEMENTED.
+ *
+ * @param width Chart's width in centimeters.
+ *
+ * @param height Chart's height in centimeters.
+ *
+ * @return LaTeX source code.
+ *
+ */
+ public String toLatex (double width, double height) {
+ throw new UnsupportedOperationException(" NOT implemented yet");
+/*
+ double yunit;
+ double[] save = new double[4];
+
+ if(dataset.getSeriesCollection().getColumnCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+
+ yunit = height / ( (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale) );
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ // Entete du document
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ : %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% YScale = %s, YShift = %s%n", YScale, YAxis.getTwinAxisPosition());
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[y=%scm]%n", yunit);
+ formatter.format("\\footnotesize%n");
+ if(grid)
+ formatter.format("\\draw[color=lightgray] (%s) grid[ystep=%s] (%s);%n",
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ ystepGrid*YScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(YScale, YAxis.getTwinAxisPosition(), YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+*/
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/BoxChart.tex b/source/umontreal/iro/lecuyer/charts/BoxChart.tex
new file mode 100644
index 0000000..8c2525d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/BoxChart.tex
@@ -0,0 +1,332 @@
+\defmodule {BoxChart}
+
+% Extends \externalclass{umontreal.iro.lecuyer.charts}{CategoryChart}.
+This class provides tools to create and manage box-and-whisker plots.
+Each \class{BoxChart} object is linked with a
+\externalclass{umontreal.iro.lecuyer.charts}{BoxSeriesCollection} data set.
+% It's advised to use more than 3 series.
+
+A boxplot is a convenient way of viewing sets of numerical data through their
+summaries: the smallest observation, first quartile ($Q_1 = x_{.25}$), median
+ ($Q_2 = x_{.5}$), third quartile ($Q_3 = x_{.75}$), and largest observation.
+Sometimes, the mean and the outliers are also plotted.
+
+In the charts created by this class, the box has its lower limit at $Q_1$
+and its upper limit at $Q_3$. The median is indicated by the line inside the box,
+while the mean is at the center of the filled circle inside the box.
+Define the interquartile range as ($Q_3 - Q_1$).
+Any data observation which is more than $1.5(Q_3 - Q_1)$ lower than the first
+quartile or $1.5(Q_3 - Q_1)$ higher than the third quartile is considered an
+outlier. The smallest and the largest values that are not outliers are connected
+to the box with a vertical line or "whisker" which is ended by a horizontal line.
+Outliers are indicated by hollow circles outside the whiskers. Triangles
+indicate the existence of very far outliers.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BoxChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
+import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
+
+import java.util.Locale;
+import java.util.Formatter;
+import javax.swing.JFrame;
+\end{hide}
+
+
+public class BoxChart extends CategoryChart \begin{hide} {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createBoxAndWhiskerChart (
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ (DefaultBoxAndWhiskerCategoryDataset)dataset.getSeriesCollection(), // data
+ true // include legend
+ );
+
+ ((CategoryPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ initAxis();
+ }
+
+ protected void initAxis(){
+ YAxis = new Axis((NumberAxis)((CategoryPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ setAutoRange();
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public BoxChart() \begin{hide} {
+ super();
+ dataset = new BoxSeriesCollection();
+ init (null, null, null);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{BoxChart} instance with an empty data set.
+\end{tabb}
+\begin{code}
+
+ public BoxChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) \begin{hide} {
+ super();
+ dataset = new BoxSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{BoxChart} instance with data \texttt{data}.
+\texttt{title} is a title, \texttt{XLabel} is a short description of the
+$x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+The input parameter \texttt{data} represents a set of plotting data.
+ Only \emph{the first} \texttt{numPoints} of \texttt{data} will
+ be considered for the plot.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{point sets.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public BoxChart (String title, String XLabel, String YLabel,
+ double[]... data) \begin{hide} {
+ super();
+ dataset = new BoxSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{BoxChart} instance with data \texttt{data}.
+\texttt{title} sets a title, \texttt{XLabel} is a short description of the
+$x$-axis, and \texttt{YLabel} is a short description of the $y$-axis.
+The input parameter \texttt{data} represents a set of plotting data.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public int add (double[] data) \begin{hide} {
+ return add(data, data.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{data} represents
+ a set of plotting data.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{point sets.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{BoxSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] data, int numPoints) \begin{hide} {
+ int seriesIndex = getSeriesCollection().add(data, numPoints);
+ initAxis();
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{data} represents
+ a set of plotting data. Only \emph{the first} \texttt{numPoints} of
+ \texttt{data} will be taken into account for the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{point set.}
+ \param{numPoints}{number of points to add.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{BoxSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public BoxSeriesCollection getSeriesCollection() \begin{hide} {
+ return (BoxSeriesCollection)dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's dataset.
+\end{tabb}
+\begin{htmlonly}
+ \return{the chart's dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeriesCollection (BoxSeriesCollection dataset) \begin{hide} {
+ this.dataset = dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Links a new dataset to the current chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{dataset}{new dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setFillBox (boolean fill) \begin{hide} {
+ ((BoxAndWhiskerRenderer)dataset.getRenderer()).setFillBox(fill);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets \texttt{fill} to \texttt{true}, if the boxes are to be filled.
+\end{tabb}
+\begin{htmlonly}
+ \param{fill}{true if the boxes are filled}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame view (int width, int height) \begin{hide} {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame ("BoxChart from SSJ : " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame ("BoxChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel (chart);
+ chartPanel.setPreferredSize (new java.awt.Dimension(width, height));
+ myFrame.setContentPane (chartPanel);
+ myFrame.pack ();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible (true);
+ return myFrame;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays chart on the screen using Swing.
+ This method creates an application containing a chart panel displaying
+ the chart. The created frame is positioned on-screen, and displayed before
+ it is returned. The circle represents the mean, the dark line inside the box
+ is the median, the box limits are the first and third quartiles,
+ the lower whisker (the lower line outside the box) is the first decile,
+ and the upper whisker is the ninth decile. The outliers, if any, are
+ represented by empty circles, or arrows if outside the range bounds.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width.}
+ \param{height}{frame height.}
+ \return{frame containing the chart.};
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Latex-specific method}
+
+\begin{code}
+
+ public String toLatex (double width, double height) \begin{hide} {
+ throw new UnsupportedOperationException(" NOT implemented yet");
+/*
+ double yunit;
+ double[] save = new double[4];
+
+ if(dataset.getSeriesCollection().getColumnCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+
+ yunit = height / ( (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale) );
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ // Entete du document
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ : %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% YScale = %s, YShift = %s%n", YScale, YAxis.getTwinAxisPosition());
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[y=%scm]%n", yunit);
+ formatter.format("\\footnotesize%n");
+ if(grid)
+ formatter.format("\\draw[color=lightgray] (%s) grid[ystep=%s] (%s);%n",
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ ystepGrid*YScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(YScale, YAxis.getTwinAxisPosition(), YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+*/
+ }\end{hide}
+\end{code}
+\begin{tabb}
+NOT IMPLEMENTED.
+% Exports the chart to a \LaTeX\ source code using PGF/TikZ.
+% This method constructs and returns a string that can be written to
+% a \LaTeX\ document to render the plot. \texttt{width} and \texttt{height}
+% represents the width and the height of the produced chart. These dimensions
+% do not take into account the axes and labels extra space.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{Chart's width in centimeters.}
+ \param{height}{Chart's height in centimeters.}
+ \return{LaTeX source code.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/BoxSeriesCollection.java b/source/umontreal/iro/lecuyer/charts/BoxSeriesCollection.java
new file mode 100644
index 0000000..06e0c36
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/BoxSeriesCollection.java
@@ -0,0 +1,326 @@
+
+
+/*
+ * Class: BoxSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
+import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Formatter;
+
+
+/**
+ * This class stores data used in a
+ * {@link umontreal.iro.lecuyer.charts.CategoryChart CategoryChart}.
+ * It also provides complementary tools to draw
+ * box-and-whisker plots; for example, one may
+ * add or remove plots series and modify plot style. This class is linked with
+ * the JFreeChart <TT>DefaultBoxAndWhiskerCategoryDataset</TT> class to store
+ * data plots, and linked with the JFreeChart
+ * <TT>BoxAndWhiskerRenderer</TT> to render the plots.
+ *
+ */
+public class BoxSeriesCollection extends SSJCategorySeriesCollection {
+ final double BARWIDTH = 0.1;
+
+
+ /**
+ * Creates a new <TT>BoxSeriesCollection</TT> instance with an empty dataset.
+ *
+ */
+ public BoxSeriesCollection () {
+ renderer = new BoxAndWhiskerRenderer();
+ seriesCollection = new DefaultBoxAndWhiskerCategoryDataset ();
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ }
+
+
+ /**
+ * Creates a new <TT>BoxSeriesCollection</TT> instance with default parameters
+ * and input series <TT>data</TT>. Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT>
+ * of <TT>data</TT> will taken into account.
+ *
+ * @param data point sets.
+ *
+ * @param numPoints Number of points
+ *
+ *
+ */
+ public BoxSeriesCollection (double[] data, int numPoints) {
+ renderer = new BoxAndWhiskerRenderer();
+
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ seriesCollection = new DefaultBoxAndWhiskerCategoryDataset ();
+
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ final List<Double> list = new ArrayList<Double>();
+ for (int i = 0; i < numPoints; i ++)
+ list.add(data[i]);
+
+ tempSeriesCollection.add(list, 0, 0);
+ }
+
+
+ /**
+ * Creates a new <TT>BoxSeriesCollection</TT> instance with default
+ * parameters and given data series. The input parameter represents series
+ * of point sets.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public BoxSeriesCollection (double[]... data) {
+ renderer = new BoxAndWhiskerRenderer();
+ seriesCollection = new DefaultBoxAndWhiskerCategoryDataset ();
+
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ for (int i = 0; i < data.length; i ++) {
+ if (data[i].length == 0)
+ throw new IllegalArgumentException("Unable to render the plot. data["
+ + i +"] contains no row");
+ final List<Double> list = new ArrayList<Double>();
+ for (int j = 0; j < data[i].length-1; j ++)
+ list.add(data[i][j]);
+ tempSeriesCollection.add(list, 0, "Serie " + i);
+ list.clear();
+ }
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ }
+
+
+ /**
+ * Creates a new <TT>BoxSeriesCollection</TT> instance with default parameters and given data series.
+ * The input parameter represents a <TT>DefaultBoxAndWhiskerCategoryDataset</TT>.
+ *
+ * @param data series of point sets.
+ *
+ */
+ public BoxSeriesCollection (DefaultBoxAndWhiskerCategoryDataset data) {
+ renderer = new BoxAndWhiskerRenderer();
+ ((BoxAndWhiskerRenderer)renderer).setFillBox(false);
+ seriesCollection = data;
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>data</TT> represents
+ * a point set.
+ *
+ * @param data point sets.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>DefaultBoxAndWhiskerXYDataset</TT> object.
+ *
+ */
+ public int add (double[] data) {
+ return add(data, data.length);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>data</TT> represents
+ * a point set. Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of
+ * <TT>data</TT> will be added to the new series.
+ *
+ * @param data Point set
+ *
+ * @param numPoints Number of points to add
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>DefaultBoxAndWhiskerXYDataset</TT> object.
+ *
+ */
+ public int add (double[] data, int numPoints) {
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ final List<Double> list = new ArrayList<Double>();
+ for (int i = 0; i < numPoints; i ++)
+ list.add(data[i]);
+
+ int count = tempSeriesCollection.getColumnCount();
+ tempSeriesCollection.add(list, 0, "Serie " + count);
+ return count;
+ }
+
+
+ /**
+ * Gets the current name of the selected series.
+ *
+ * @param series series index.
+ *
+ * @return current name of the series.
+ *
+ */
+ public String getName (int series) {
+ return (String)((DefaultBoxAndWhiskerCategoryDataset)seriesCollection).getColumnKey(series);
+ }
+
+
+ /**
+ * Returns the range (<SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates) min and max values.
+ *
+ * @return range min and max values.
+ *
+ */
+ public double[] getRangeBounds() {
+ double max=0, min=0;
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ if(tempSeriesCollection.getColumnCount() != 0 && tempSeriesCollection.getRowCount() != 0) {
+ max = tempSeriesCollection.getItem(0, 0).getMaxOutlier().doubleValue() ;
+ min = tempSeriesCollection.getItem(0, 0).getMinOutlier().doubleValue() ;
+ }
+
+ for(int i = 0; i < tempSeriesCollection.getRowCount(); i++) {
+ for( int j = 0; j < tempSeriesCollection.getColumnCount(); j++) {
+ max = Math.max(max, tempSeriesCollection.getItem(i, j).getMaxOutlier().doubleValue() );
+ min = Math.min(min, tempSeriesCollection.getItem(i, j).getMinOutlier().doubleValue() );
+ }
+ }
+
+ double[] retour = {min, max};
+ return retour;
+ }
+
+
+ /**
+ * Returns in a <TT>String</TT> all data contained in the current object.
+ *
+ * @return All data contained in the current object as a {@link String}.
+ *
+ */
+ public String toString() {
+ Formatter formatter = new Formatter(Locale.US);
+ for(int i = 0; i < seriesCollection.getRowCount(); i++) {
+ formatter.format(" Series " + i + " : %n");
+ for(int j = 0; j < seriesCollection.getColumnCount(); j++)
+ formatter.format(",%15e%n", seriesCollection.getValue(i, j));
+ }
+ return formatter.toString();
+ }
+
+
+ /**
+ * NOT IMPLEMENTED: To do.
+ *
+ * @param ymin
+ *
+ * @param ymax
+ *
+ * @return LaTeX source code
+ *
+ */
+ public String toLatex (double YScale, double YShift,
+ double ymin, double ymax) {
+ throw new UnsupportedOperationException(" NOT implemented yet");
+/*
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection = (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+ Formatter formatter = new Formatter(Locale.US);
+// double var;
+// double margin = ((BoxAndWhiskerRenderer)renderer).getMargin();
+
+// for (int i = tempSeriesCollection.getColumnCount() - 1; i >= 0; i--) {
+// List temp = tempSeriesCollection.getBins(i);
+// ListIterator iter = temp.listIterator();
+//
+// Color color = (Color)renderer.getSeriesPaint(i);
+// String colorString = detectXColorClassic(color);
+// if (colorString == null) {
+// colorString = "color"+i;
+// formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+// colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
+// }
+//
+// HistogramBin currentBin=null;
+// while(iter.hasNext()) {
+// double currentMargin;
+// currentBin = (HistogramBin)iter.next();
+// currentMargin = ((margin*(currentBin.getEndBoundary()-currentBin.getStartBoundary()))-XShift)*XScale;
+// if ((currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+// && (currentBin.getCount() >= ymin && currentBin.getCount() <= ymax) )
+// {
+// var = Math.min( currentBin.getEndBoundary(), xmax);
+// if (filled[i]) {
+// formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], (color.getAlpha()/255.0), colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+// }
+// else {
+// formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+// }
+// }
+// else if ( (currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+// && (currentBin.getCount() >= ymin && currentBin.getCount() > ymax) )
+// { // Cas ou notre rectangle ne peut pas etre affiche en entier (trop haut)
+// var = Math.min( currentBin.getEndBoundary(), xmax);
+// if (filled[i]) {
+// formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], (color.getAlpha()/255.0), colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+// formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+// }
+// else {
+// formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+//
+// formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+// }
+// }
+// }
+// }
+ return formatter.toString();
+*/
+}
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/BoxSeriesCollection.tex b/source/umontreal/iro/lecuyer/charts/BoxSeriesCollection.tex
new file mode 100644
index 0000000..4e8d483
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/BoxSeriesCollection.tex
@@ -0,0 +1,341 @@
+\defmodule {BoxSeriesCollection}
+
+This class stores data used in a
+\externalclass{umontreal.iro.lecuyer.charts}{CategoryChart}.
+It also provides complementary tools to draw
+ box-and-whisker plots; for example, one may
+add or remove plots series and modify plot style. This class is linked with
+the JFreeChart \texttt{DefaultBoxAndWhiskerCategoryDataset} class to store
+data plots, and linked with the JFreeChart
+\texttt{BoxAndWhiskerRenderer} to render the plots.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: BoxSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
+import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Formatter;
+\end{hide}
+
+public class BoxSeriesCollection extends SSJCategorySeriesCollection \begin{hide} {
+ final double BARWIDTH = 0.1;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+ public BoxSeriesCollection () \begin{hide} {
+ renderer = new BoxAndWhiskerRenderer();
+ seriesCollection = new DefaultBoxAndWhiskerCategoryDataset ();
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{BoxSeriesCollection} instance with an empty dataset.
+\end{tabb}
+\begin{code}
+
+ public BoxSeriesCollection (double[] data, int numPoints) \begin{hide} {
+ renderer = new BoxAndWhiskerRenderer();
+
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ seriesCollection = new DefaultBoxAndWhiskerCategoryDataset ();
+
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ final List<Double> list = new ArrayList<Double>();
+ for (int i = 0; i < numPoints; i ++)
+ list.add(data[i]);
+
+ tempSeriesCollection.add(list, 0, 0);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{BoxSeriesCollection} instance with default parameters
+ and input series \texttt{data}. Only \emph{the first} \texttt{numPoints}
+ of \texttt{data} will taken into account.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{point sets.}
+ \param{numPoints}{Number of points}
+\end{htmlonly}
+\begin{code}
+
+ public BoxSeriesCollection (double[]... data) \begin{hide} {
+ renderer = new BoxAndWhiskerRenderer();
+ seriesCollection = new DefaultBoxAndWhiskerCategoryDataset ();
+
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ for (int i = 0; i < data.length; i ++) {
+ if (data[i].length == 0)
+ throw new IllegalArgumentException("Unable to render the plot. data["
+ + i +"] contains no row");
+ final List<Double> list = new ArrayList<Double>();
+ for (int j = 0; j < data[i].length-1; j ++)
+ list.add(data[i][j]);
+ tempSeriesCollection.add(list, 0, "Serie " + i);
+ list.clear();
+ }
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{BoxSeriesCollection} instance with default
+ parameters and given data series. The input parameter represents series
+ of point sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public BoxSeriesCollection (DefaultBoxAndWhiskerCategoryDataset data) \begin{hide} {
+ renderer = new BoxAndWhiskerRenderer();
+ ((BoxAndWhiskerRenderer)renderer).setFillBox(false);
+ seriesCollection = data;
+ ((BoxAndWhiskerRenderer)renderer).setMaximumBarWidth(BARWIDTH);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{BoxSeriesCollection} instance with default parameters and given data series.
+ The input parameter represents a \texttt{DefaultBoxAndWhiskerCategoryDataset}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data control methods}
+
+\begin{code}
+
+ public int add (double[] data) \begin{hide} {
+ return add(data, data.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{data} represents
+ a point set.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{point sets.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{DefaultBoxAndWhiskerXYDataset} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] data, int numPoints) \begin{hide} {
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ final List<Double> list = new ArrayList<Double>();
+ for (int i = 0; i < numPoints; i ++)
+ list.add(data[i]);
+
+ int count = tempSeriesCollection.getColumnCount();
+ tempSeriesCollection.add(list, 0, "Serie " + count);
+ return count;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{data} represents
+ a point set. Only \emph{the first} \texttt{numPoints} of
+ \texttt{data} will be added to the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{Point set}
+ \param{numPoints}{Number of points to add}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{DefaultBoxAndWhiskerXYDataset} object.}
+\end{htmlonly}
+\begin{code}
+
+ public String getName (int series) \begin{hide} {
+ return (String)((DefaultBoxAndWhiskerCategoryDataset)seriesCollection).getColumnKey(series);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current name of the selected series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{current name of the series.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getRangeBounds() \begin{hide} {
+ double max=0, min=0;
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection =
+ (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+
+ if(tempSeriesCollection.getColumnCount() != 0 && tempSeriesCollection.getRowCount() != 0) {
+ max = tempSeriesCollection.getItem(0, 0).getMaxOutlier().doubleValue() ;
+ min = tempSeriesCollection.getItem(0, 0).getMinOutlier().doubleValue() ;
+ }
+
+ for(int i = 0; i < tempSeriesCollection.getRowCount(); i++) {
+ for( int j = 0; j < tempSeriesCollection.getColumnCount(); j++) {
+ max = Math.max(max, tempSeriesCollection.getItem(i, j).getMaxOutlier().doubleValue() );
+ min = Math.min(min, tempSeriesCollection.getItem(i, j).getMinOutlier().doubleValue() );
+ }
+ }
+
+ double[] retour = {min, max};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the range ($y$-coordinates) min and max values.
+\end{tabb}
+\begin{htmlonly}
+ \return{range min and max values.}
+\end{htmlonly}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ Formatter formatter = new Formatter(Locale.US);
+ for(int i = 0; i < seriesCollection.getRowCount(); i++) {
+ formatter.format(" Series " + i + " : %n");
+ for(int j = 0; j < seriesCollection.getColumnCount(); j++)
+ formatter.format(",%15e%n", seriesCollection.getValue(i, j));
+ }
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns in a \texttt{String} all data contained in the current object.
+\end{tabb}
+\begin{htmlonly}
+ \return{All data contained in the current object as a \class{String}.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection*{Rendering methods}
+\begin{code}
+
+ public String toLatex (double YScale, double YShift,
+ double ymin, double ymax) \begin{hide} {
+ throw new UnsupportedOperationException(" NOT implemented yet");
+/*
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ DefaultBoxAndWhiskerCategoryDataset tempSeriesCollection = (DefaultBoxAndWhiskerCategoryDataset)seriesCollection;
+ Formatter formatter = new Formatter(Locale.US);
+// double var;
+// double margin = ((BoxAndWhiskerRenderer)renderer).getMargin();
+
+// for (int i = tempSeriesCollection.getColumnCount() - 1; i >= 0; i--) {
+// List temp = tempSeriesCollection.getBins(i);
+// ListIterator iter = temp.listIterator();
+//
+// Color color = (Color)renderer.getSeriesPaint(i);
+// String colorString = detectXColorClassic(color);
+// if (colorString == null) {
+// colorString = "color"+i;
+// formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+// colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
+// }
+//
+// HistogramBin currentBin=null;
+// while(iter.hasNext()) {
+// double currentMargin;
+// currentBin = (HistogramBin)iter.next();
+// currentMargin = ((margin*(currentBin.getEndBoundary()-currentBin.getStartBoundary()))-XShift)*XScale;
+// if ((currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+// && (currentBin.getCount() >= ymin && currentBin.getCount() <= ymax) )
+// {
+// var = Math.min( currentBin.getEndBoundary(), xmax);
+// if (filled[i]) {
+// formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], (color.getAlpha()/255.0), colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+// }
+// else {
+// formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+// }
+// }
+// else if ( (currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+// && (currentBin.getCount() >= ymin && currentBin.getCount() > ymax) )
+// { // Cas ou notre rectangle ne peut pas etre affiche en entier (trop haut)
+// var = Math.min( currentBin.getEndBoundary(), xmax);
+// if (filled[i]) {
+// formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], (color.getAlpha()/255.0), colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+// formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+// }
+// else {
+// formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+//
+// formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+// lineWidth[i], colorString,
+// currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+// currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+// }
+// }
+// }
+// }
+ return formatter.toString();
+*/
+}\end{hide}
+\end{code}
+\begin{tabb}
+ NOT IMPLEMENTED: To do.
+\end{tabb}
+\begin{htmlonly}
+ \param{ymin}{}
+ \param{ymax}{}
+ \return{LaTeX source code}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/CategoryChart.java b/source/umontreal/iro/lecuyer/charts/CategoryChart.java
new file mode 100644
index 0000000..e5ea3da
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/CategoryChart.java
@@ -0,0 +1,230 @@
+
+
+/*
+ * Class: CategoryChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.chart.JFreeChart;
+import javax.swing.JFrame;
+
+/**
+ * This class provides tools to create charts from data in a simple way. Its main
+ * feature is to produce TikZ/PGF (see WWW link <TT><A NAME="tex2html1"
+ * HREF="http://sourceforge.net/projects/pgf/">http://sourceforge.net/projects/pgf/</A></TT>) compatible source code which can be included
+ * in <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> documents, but it can also produce charts in other formats.
+ * One can easily create a new chart, and customize its appearance using methods
+ * of this class, with the encapsulated
+ * {@link umontreal.iro.lecuyer.charts.SSJCategorySeriesCollection SSJCategorySeriesCollection} object
+ * representing the data, and an {@link umontreal.iro.lecuyer.charts.Axis Axis}
+ * object representing the axis.
+ * All these classes depend on the <TT>JFreeChart</TT> API (see WWW link
+ * <TT><A NAME="tex2html2"
+ * HREF="http://www.jfree.org/jfreechart/">http://www.jfree.org/jfreechart/</A></TT>) which provides tools to build charts with
+ * Java, to draw them, and export them to files. However, only basic features are
+ * used here.
+ *
+ * <P>
+ * Moreover, <TT>CategoryChart</TT> provides methods to plot data using a MATLAB friendly
+ * syntax. None of these methods provides new features; they just propose a
+ * different syntax to create charts. Therefore some features are unavailable
+ * when using these methods only.
+ *
+ */
+public abstract class CategoryChart {
+
+ protected Axis YAxis;
+ protected SSJCategorySeriesCollection dataset;
+ protected JFreeChart chart;
+ protected boolean latexDocFlag = true;
+
+ protected boolean autoRange;
+ protected double[] manualRange;
+
+ protected boolean grid = false;
+ protected double ystepGrid;
+
+ final protected double BOR = 0.1;
+
+
+ /**
+ * Returns the <TT>JFreeChart</TT> object associated with this chart.
+ *
+ * @return the associated JFreeChart object.
+ *
+ */
+ public JFreeChart getJFreeChart() {
+ return chart;
+ }
+
+
+ /**
+ * Returns the chart's range axis (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) object.
+ *
+ * @return chart's range axis (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) object.
+ *
+ */
+ public Axis getYAxis() {
+ return YAxis;
+ }
+
+
+ public abstract JFrame view (int width, int height);
+
+
+ /**
+ * Gets the current chart title.
+ *
+ * @return Chart title.
+ *
+ */
+ public String getTitle() {
+ return chart.getTitle().getText();
+ }
+
+
+ /**
+ * Sets a title to this chart. This title will appear on the chart displayed
+ * by method {@link #view view}.
+ *
+ * @param title chart title.
+ *
+ *
+ */
+ public void setTitle (String title) {
+ chart.setTitle(title);
+ }
+
+
+ /**
+ * Sets chart <SPAN CLASS="MATH"><I>y</I></SPAN> range to automatic values.
+ *
+ */
+ public void setAutoRange () {
+ autoRange = true;
+
+ double BorneMin = Math.abs((dataset.getRangeBounds())[0]);
+ double BorneMax = Math.abs((dataset.getRangeBounds())[1]);
+
+ double max = Math.max(BorneMin,BorneMax) * BOR;
+ YAxis.getAxis().setLowerBound(BorneMin - max);
+ YAxis.getAxis().setUpperBound(BorneMax + max);
+ YAxis.setLabelsAuto();
+ }
+
+
+ /**
+ * Sets new <SPAN CLASS="MATH"><I>y</I></SPAN>-axis bounds, using the format:
+ * <TT>range</TT> = [<TT>ymin, ymax</TT>].
+ *
+ * @param range new axis ranges.
+ *
+ *
+ */
+ private void setManualRange (double[] range) {
+ if(range.length != 2)
+ throw new IllegalArgumentException (
+ "range must have the format: [ymin, ymax]");
+ autoRange = false;
+ YAxis.getAxis().setLowerBound(Math.min(range[0],range[1]));
+ YAxis.getAxis().setUpperBound(Math.max(range[0],range[1]));
+ }
+
+
+ /**
+ * Puts a grid on the background. It is important to note that the grid is
+ * always shifted in such a way that it contains the axes. Thus, the grid does
+ * not always have an intersection at the corner points; this occurs
+ * only if the corner points are multiples of the steps: <TT>xstep</TT>
+ * and <TT>ystep</TT> sets the step in each direction.
+ *
+ * @param xstep sets the step in the x-direction.
+ *
+ * @param ystep sets the step in the y-direction.
+ *
+ *
+ */
+ public void enableGrid (double xstep, double ystep) {
+ this.grid = true;
+ this.ystepGrid = ystep;
+ }
+
+
+ /**
+ * Disables the background grid.
+ *
+ */
+ public void disableGrid() {
+ this.grid = false;
+ }
+
+
+ /**
+ * Transforms the chart into <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> form and returns it as a <TT>String</TT>.
+ *
+ */
+ public abstract String toLatex (double width, double height);
+
+
+ /**
+ * Same as in {@link XYChart}.
+ *
+ */
+ public void setLatexDocFlag (boolean flag) {
+ latexDocFlag = flag;
+ }
+
+
+
+ protected double computeYScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = YAxis.getAxis().getLowerBound();
+ bounds[1] = YAxis.getAxis().getUpperBound();
+
+ if (position < bounds[0])
+ bounds[0] = position;
+ if (position > bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale (bounds);
+ }
+
+ protected double computeScale (double[] bounds) {
+ int tenPowerRatio = 0;
+ // echelle < 1 si les valeurs sont grandes
+ while (bounds[1] > 1000 || bounds[0] < -1000) {
+ bounds[1] /= 10;
+ bounds[0] /= 10;
+ tenPowerRatio++;
+ }
+ // echelle > 1 si les valeurs sont petites
+ while (bounds[1] < 100 && bounds[0] > -100) {
+ bounds[1] *= 10;
+ bounds[0] *= 10;
+ tenPowerRatio--;
+ }
+ return 1/Math.pow(10, tenPowerRatio);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/CategoryChart.tex b/source/umontreal/iro/lecuyer/charts/CategoryChart.tex
new file mode 100644
index 0000000..b1be751
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/CategoryChart.tex
@@ -0,0 +1,242 @@
+\defmodule {CategoryChart}
+
+This class provides tools to create charts from data in a simple way. Its main
+feature is to produce TikZ/PGF (see WWW link \url{http://sourceforge.net/projects/pgf/}) compatible source code which can be included
+in \LaTeX\ documents, but it can also produce charts in other formats.
+One can easily create a new chart, and customize its appearance using methods
+of this class, with the encapsulated
+\externalclass{umontreal.iro.lecuyer.charts}{SSJCategorySeriesCollection} object
+representing the data, and an \externalclass{umontreal.iro.lecuyer.charts}{Axis}
+object representing the axis.
+All these classes depend on the \texttt{JFreeChart} API (see WWW link
+\url{http://www.jfree.org/jfreechart/}) which provides tools to build charts with
+Java, to draw them, and export them to files. However, only basic features are
+used here.
+
+Moreover, \texttt{CategoryChart} provides methods to plot data using a MATLAB friendly
+syntax. None of these methods provides new features; they just propose a
+different syntax to create charts. Therefore some features are unavailable
+when using these methods only.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: CategoryChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.chart.JFreeChart;
+import javax.swing.JFrame;\end{hide}
+
+public abstract class CategoryChart \begin{hide} {
+
+ protected Axis YAxis;
+ protected SSJCategorySeriesCollection dataset;
+ protected JFreeChart chart;
+ protected boolean latexDocFlag = true;
+
+ protected boolean autoRange;
+ protected double[] manualRange;
+
+ protected boolean grid = false;
+ protected double ystepGrid;
+
+ final protected double BOR = 0.1;\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public JFreeChart getJFreeChart() \begin{hide} {
+ return chart;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{JFreeChart} object associated with this chart.
+\end{tabb}
+\begin{htmlonly}
+ \return{the associated JFreeChart object.}
+\end{htmlonly}
+\begin{code}
+
+ public Axis getYAxis() \begin{hide} {
+ return YAxis;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's range axis ($y$-axis) object.
+\end{tabb}
+\begin{htmlonly}
+ \return{chart's range axis ($y$-axis) object.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract JFrame view (int width, int height);
+\end{code}
+\begin{code}
+
+ public String getTitle() \begin{hide} {
+ return chart.getTitle().getText();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current chart title.
+\end{tabb}
+\begin{htmlonly}
+ \return{Chart title.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTitle (String title) \begin{hide} {
+ chart.setTitle(title);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets a title to this chart. This title will appear on the chart displayed
+ by method \method{view}{}.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+\end{htmlonly}
+\begin{code}
+
+ public void setAutoRange () \begin{hide} {
+ autoRange = true;
+
+ double BorneMin = Math.abs((dataset.getRangeBounds())[0]);
+ double BorneMax = Math.abs((dataset.getRangeBounds())[1]);
+
+ double max = Math.max(BorneMin,BorneMax) * BOR;
+ YAxis.getAxis().setLowerBound(BorneMin - max);
+ YAxis.getAxis().setUpperBound(BorneMax + max);
+ YAxis.setLabelsAuto();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets chart $y$ range to automatic values.
+\end{tabb}
+\begin{code}
+
+ private void setManualRange (double[] range) \begin{hide} {
+ if(range.length != 2)
+ throw new IllegalArgumentException (
+ "range must have the format: [ymin, ymax]");
+ autoRange = false;
+ YAxis.getAxis().setLowerBound(Math.min(range[0],range[1]));
+ YAxis.getAxis().setUpperBound(Math.max(range[0],range[1]));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets new $y$-axis bounds, using the format:
+\texttt{range} = [\texttt{ymin, ymax}].
+\end{tabb}
+\begin{htmlonly}
+ \param{range}{new axis ranges.}
+\end{htmlonly}
+\begin{code}
+
+ public void enableGrid (double xstep, double ystep) \begin{hide} {
+ this.grid = true;
+ this.ystepGrid = ystep;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Puts a grid on the background. It is important to note that the grid is
+ always shifted in such a way that it contains the axes. Thus, the grid does
+ not always have an intersection at the corner points; this occurs
+ only if the corner points are multiples of the steps: \texttt{xstep}
+ and \texttt{ystep} sets the step in each direction.
+\end{tabb}
+\begin{htmlonly}
+ \param{xstep}{sets the step in the x-direction.}
+ \param{ystep}{sets the step in the y-direction.}
+\end{htmlonly}
+\begin{code}
+
+ public void disableGrid() \begin{hide} {
+ this.grid = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Disables the background grid.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Latex-specific methods}
+
+\begin{code}
+
+ public abstract String toLatex (double width, double height);
+\end{code}
+\begin{tabb}
+ Transforms the chart into \LaTeX{} form and returns it as a \texttt{String}.
+\end{tabb}
+\begin{code}
+
+ public void setLatexDocFlag (boolean flag) \begin{hide} {
+ latexDocFlag = flag;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Same as in \class{XYChart}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ protected double computeYScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = YAxis.getAxis().getLowerBound();
+ bounds[1] = YAxis.getAxis().getUpperBound();
+
+ if (position < bounds[0])
+ bounds[0] = position;
+ if (position > bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale (bounds);
+ }
+
+ protected double computeScale (double[] bounds) {
+ int tenPowerRatio = 0;
+ // echelle < 1 si les valeurs sont grandes
+ while (bounds[1] > 1000 || bounds[0] < -1000) {
+ bounds[1] /= 10;
+ bounds[0] /= 10;
+ tenPowerRatio++;
+ }
+ // echelle > 1 si les valeurs sont petites
+ while (bounds[1] < 100 && bounds[0] > -100) {
+ bounds[1] *= 10;
+ bounds[0] *= 10;
+ tenPowerRatio--;
+ }
+ return 1/Math.pow(10, tenPowerRatio);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/ContinuousDistChart.java b/source/umontreal/iro/lecuyer/charts/ContinuousDistChart.java
new file mode 100644
index 0000000..84ba541
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/ContinuousDistChart.java
@@ -0,0 +1,199 @@
+
+
+/*
+ * Class: ContinuousDistChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;
+
+import org.jfree.chart.JFreeChart;
+import javax.swing.JFrame;
+
+
+
+/**
+ * This class provides tools to plot the density and the cumulative probability
+ * of a continuous probability distribution.
+ *
+ */
+public class ContinuousDistChart {
+ protected ContinuousDistribution dist;
+ protected double a,b;
+ protected int m;
+ protected XYLineChart cdfChart;
+ protected XYLineChart densityChart;
+
+ private void init() {
+ double[][] cdf = new double[2][m+1];
+ double[][] density = new double[2][m+1];
+ double h = (b - a) / m;
+ double x;
+ int coex = 0;
+
+ try {
+ for (int i = 0; i <= m; i++) {
+ x = a + i*h;
+ cdf[0][i] = x;
+ cdf[1][i] = dist.cdf (x);
+ }
+ cdfChart = new XYLineChart("cdf: " + dist.toString(), "", "", cdf);
+ } catch (UnsupportedOperationException e) {
+ coex++;
+ System.err.println (e);
+// e.printStackTrace();
+ }
+
+ try {
+ for (int i = 0; i <= m; i++) {
+ x = a + i*h;
+ density[0][i] = x;
+ density[1][i] = dist.density (x);
+ }
+ densityChart = new XYLineChart("density: " + dist.toString(),
+ "", "", density);
+ } catch (UnsupportedOperationException e) {
+ System.err.println (e);
+ if (coex == 1)
+ throw e;
+ }
+ cdfChart.setprobFlag (true);
+ densityChart.setprobFlag (true);
+ }
+
+
+ /**
+ * Constructor for a new <TT>ContinuousDistChart</TT> instance. It will plot the
+ * continuous distribution <TT>dist</TT> over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>,
+ * using <SPAN CLASS="MATH"><I>m</I> + 1</SPAN> equidistant sample points.
+ *
+ * @param dist continuous distribution to plot
+ *
+ * @param a lower bound of interval
+ *
+ * @param b upper bound of interval
+ *
+ * @param m number of steps
+ *
+ */
+ public ContinuousDistChart (ContinuousDistribution dist, double a,
+ double b, int m) {
+ this.dist = dist;
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ init();
+ }
+
+
+ /**
+ * Displays a chart of the cumulative distribution function (cdf) on the screen
+ * using Swing. This method creates an application containing a chart panel
+ * displaying the chart. The created frame is positioned on-screen, and
+ * displayed before it is returned. The <TT>width</TT> and the <TT>height</TT>
+ * of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels
+ *
+ * @param height frame height in pixels
+ *
+ * @return frame containing the chart
+ *
+ */
+ public JFrame viewCdf (int width, int height) {
+ return cdfChart.view(width, height);
+ }
+
+
+ /**
+ * Similar to {@link #viewCdf viewCdf}, but for the probability density instead
+ * of the cdf.
+ *
+ * @param width frame width in pixels
+ *
+ * @param height frame height in pixels
+ *
+ * @return frame containing the chart
+ *
+ */
+ public JFrame viewDensity (int width, int height) {
+ return densityChart.view(width, height);
+ }
+
+
+ /**
+ * Exports a chart of the cdf to a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> source code using PGF/TikZ.
+ * This method constructs and returns a string that can be written to
+ * a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> document to render the plot. <TT>width</TT> and <TT>height</TT>
+ * represents the width and the height of the produced chart. These dimensions
+ * do not take into account the axes and labels extra space. The <TT>width</TT>
+ * and the <TT>height</TT> of the chart are measured in centimeters.
+ *
+ * @param width Chart's width in centimeters
+ *
+ * @param height Chart's height in centimeters
+ *
+ * @return LaTeX source code
+ *
+ */
+ public String toLatexCdf (int width, int height) {
+ return cdfChart.toLatex(width, height);
+ }
+
+
+ /**
+ * Similar to {@link #toLatexCdf toLatexCdf}, but for the probability density instead
+ * of the cdf.
+ *
+ * @param width Chart's width in centimeters
+ *
+ * @param height Chart's height in centimeters
+ *
+ * @return LaTeX source code
+ *
+ */
+ public String toLatexDensity (int width, int height) {
+ return densityChart.toLatex(width, height);
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> for this object.
+ *
+ * @param a lower bound of interval
+ *
+ * @param b upper bound of interval
+ *
+ * @param m number of points in the plot minus one
+ *
+ *
+ */
+ public void setParam (double a, double b, int m) {
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ init();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/ContinuousDistChart.tex b/source/umontreal/iro/lecuyer/charts/ContinuousDistChart.tex
new file mode 100644
index 0000000..4033c7b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/ContinuousDistChart.tex
@@ -0,0 +1,204 @@
+\defmodule {ContinuousDistChart}
+
+This class provides tools to plot the density and the cumulative probability
+ of a continuous probability distribution.
+
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: ContinuousDistChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;\begin{hide}
+
+import org.jfree.chart.JFreeChart;
+import javax.swing.JFrame;
+\end{hide}
+
+
+public class ContinuousDistChart \begin{hide} {
+ protected ContinuousDistribution dist;
+ protected double a,b;
+ protected int m;
+ protected XYLineChart cdfChart;
+ protected XYLineChart densityChart;
+
+ private void init() {
+ double[][] cdf = new double[2][m+1];
+ double[][] density = new double[2][m+1];
+ double h = (b - a) / m;
+ double x;
+ int coex = 0;
+
+ try {
+ for (int i = 0; i <= m; i++) {
+ x = a + i*h;
+ cdf[0][i] = x;
+ cdf[1][i] = dist.cdf (x);
+ }
+ cdfChart = new XYLineChart("cdf: " + dist.toString(), "", "", cdf);
+ } catch (UnsupportedOperationException e) {
+ coex++;
+ System.err.println (e);
+// e.printStackTrace();
+ }
+
+ try {
+ for (int i = 0; i <= m; i++) {
+ x = a + i*h;
+ density[0][i] = x;
+ density[1][i] = dist.density (x);
+ }
+ densityChart = new XYLineChart("density: " + dist.toString(),
+ "", "", density);
+ } catch (UnsupportedOperationException e) {
+ System.err.println (e);
+ if (coex == 1)
+ throw e;
+ }
+ cdfChart.setprobFlag (true);
+ densityChart.setprobFlag (true);
+ }
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection*{Constructor}
+\begin{code}
+
+ public ContinuousDistChart (ContinuousDistribution dist, double a,
+ double b, int m) \begin{hide} {
+ this.dist = dist;
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor for a new \texttt{ContinuousDistChart} instance. It will plot the
+% \externalclass{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}
+ continuous distribution \texttt{dist} over the interval $[a,b]$,
+ using $m+1$ equidistant sample points.
+\end{tabb}
+\begin{htmlonly}
+ \param{dist}{continuous distribution to plot}
+ \param{a}{lower bound of interval}
+ \param{b}{upper bound of interval}
+ \param{m}{number of steps}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public JFrame viewCdf (int width, int height) \begin{hide} {
+ return cdfChart.view(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays a chart of the cumulative distribution function (cdf) on the screen
+ using Swing. This method creates an application containing a chart panel
+ displaying the chart. The created frame is positioned on-screen, and
+ displayed before it is returned. The \texttt{width} and the \texttt{height}
+ of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels}
+ \param{height}{frame height in pixels}
+ \return{frame containing the chart}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame viewDensity (int width, int height) \begin{hide} {
+ return densityChart.view(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to \method{viewCdf}{}, but for the probability density instead
+ of the cdf.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels}
+ \param{height}{frame height in pixels}
+ \return{frame containing the chart}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatexCdf (int width, int height) \begin{hide} {
+ return cdfChart.toLatex(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Exports a chart of the cdf to a \LaTeX\ source code using PGF/TikZ.
+ This method constructs and returns a string that can be written to
+ a \LaTeX\ document to render the plot. \texttt{width} and \texttt{height}
+ represents the width and the height of the produced chart. These dimensions
+ do not take into account the axes and labels extra space. The \texttt{width}
+ and the \texttt{height} of the chart are measured in centimeters.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{Chart's width in centimeters}
+ \param{height}{Chart's height in centimeters}
+ \return{LaTeX source code}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatexDensity (int width, int height) \begin{hide} {
+ return densityChart.toLatex(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to \method{toLatexCdf}{}, but for the probability density instead
+ of the cdf.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{Chart's width in centimeters}
+ \param{height}{Chart's height in centimeters}
+ \return{LaTeX source code}
+\end{htmlonly}
+\begin{code}
+
+ public void setParam (double a, double b, int m) \begin{hide} {
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $a$, $b$ and $m$ for this object.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{lower bound of interval}
+ \param{b}{upper bound of interval}
+ \param{m}{number of points in the plot minus one}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/CustomHistogramDataset.java b/source/umontreal/iro/lecuyer/charts/CustomHistogramDataset.java
new file mode 100644
index 0000000..a53b580
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/CustomHistogramDataset.java
@@ -0,0 +1,711 @@
+
+package umontreal.iro.lecuyer.charts;
+
+import java.util.*;
+import org.jfree.data.general.DatasetChangeEvent;
+import org.jfree.data.statistics.HistogramBin;
+import org.jfree.data.statistics.HistogramType;
+import org.jfree.data.xy.AbstractIntervalXYDataset;
+import org.jfree.data.xy.IntervalXYDataset;
+import org.jfree.util.ObjectUtilities;
+import org.jfree.util.PublicCloneable;
+
+/*
+ * Class: CustomHistogramDataset
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+
+/**
+ * A dataset that can be used for creating histograms.
+ * This class is inspired from JFreeChart HistogramDataset class,
+ * and provides tools to customize histogram bins.
+ *
+ */
+public class CustomHistogramDataset extends AbstractIntervalXYDataset
+ implements IntervalXYDataset, Cloneable, PublicCloneable
+{
+
+ /** A list of maps. */
+ private List list;
+
+ /** The histogram type. */
+ public HistogramType type;
+
+ /**
+ * Creates a new (empty) dataset with a default type of
+ * {@link HistogramType}.FREQUENCY.
+ */
+ public CustomHistogramDataset()
+ {
+ list = new ArrayList();
+ type = HistogramType.FREQUENCY;
+ }
+
+ /**
+ * Returns the histogram type.
+ */
+ public HistogramType getType()
+ {
+ return type;
+ }
+
+ /**
+ * Sets the histogram type and sends a {@link DatasetChangeEvent} to all
+ * registered listeners.
+ *
+ * @param type the type (<code>null</code> not permitted).
+ */
+ public void setType(HistogramType type)
+ {
+ if (type == null) {
+ throw new IllegalArgumentException("Null 'type' argument");
+ } else {
+ this.type = type;
+ notifyListeners(new DatasetChangeEvent(this, this));
+ return ;
+ }
+ }
+
+ /**
+ * Adds a series to the dataset, using the specified number of bins.
+ *
+ * @param key the series key (<code>null</code> not permitted).
+ * @param values the values (<code>null</code> not permitted).
+ * @param bins the number of bins (must be at least 1).
+ */
+ public void addSeries(Comparable key, double values[], int bins)
+ {
+ double minimum = getMinimum(values);
+ double maximum = getMaximum(values);
+ addSeries(key, values, bins, minimum, maximum);
+ }
+
+ /**
+ * Adds a series to the dataset, using the specified number of bins.
+ *
+ * @param key the series key (<code>null</code> not permitted).
+ * @param values the values (<code>null</code> not permitted).
+ * @param numPoints only the first numPoints values are used.
+ * @param bins the number of bins (must be at least 1).
+ */
+ public void addSeries(Comparable key, double values[], int numPoints, int bins)
+ {
+ double minimum = getMinimum(values);
+ double maximum = getMaximum(values);
+ addSeries(key, values, numPoints, bins, minimum, maximum);
+ }
+
+ /**
+ * Adds a series to the dataset. Any data value less than minimum will be
+ * assigned to the first bin, and any data value greater than maximum will
+ * be assigned to the last bin. Values falling on the boundary of
+ * adjacent bins will be assigned to the higher indexed bin.
+ *
+ * @param key the series key (<code>null</code> not permitted).
+ * @param values the raw observations.
+ * @param bins the number of bins (must be at least 1).
+ * @param minimum the lower bound of the bin range.
+ * @param maximum the upper bound of the bin range.
+ */
+ public void addSeries(Comparable key, double values[], int bins,
+ double minimum, double maximum)
+ {
+ addSeries(key, values, values.length, bins, minimum, maximum);
+ }
+
+ /**
+ * Adds a series to the dataset. Any data value less than minimum will be
+ * assigned to the first bin, and any data value greater than maximum will
+ * be assigned to the last bin. Values falling on the boundary of
+ * adjacent bins will be assigned to the higher indexed bin.
+ * Only the first numPoints values are used.
+ *
+ * @param key the series key (<code>null</code> not permitted).
+ * @param values the raw observations.
+ * @param numPoints only the first numPoints values are used.
+ * @param bins the number of bins (must be at least 1).
+ * @param minimum the lower bound of the bin range.
+ * @param maximum the upper bound of the bin range.
+ */
+ public void addSeries(Comparable key, double values[], int numPoints,
+ int bins, double minimum, double maximum)
+ {
+ if (key == null)
+ throw new IllegalArgumentException("Null 'key' argument.");
+ if (values == null)
+ throw new IllegalArgumentException("Null 'values' argument.");
+ if (bins < 1)
+ throw new IllegalArgumentException("The 'bins' value must be at least 1.");
+ double binWidth = (maximum - minimum) / (double)bins;
+ double lower = minimum;
+ final double EPS = 1.0e-15;
+ List binList = new ArrayList(bins);
+ for (int i = 0; i < bins; i++) {
+ HistogramBin bin;
+ if (i == bins - 1) {
+ bin = new HistogramBin(lower, maximum*(1.0 + EPS));
+ } else {
+ double upper = minimum + (double)(i + 1) * binWidth;
+ bin = new HistogramBin(lower, upper);
+ lower = upper;
+ }
+ binList.add(bin);
+ }
+
+ ArrayList valuesList = new ArrayList(numPoints);
+ for (int i = 0; i < numPoints; i++)
+ valuesList.add(new Double(values[i]));
+
+ synchronizeValuesAndBins(binList, valuesList);
+ Map map = new HashMap();
+ map.put("key", key);
+ map.put("values", valuesList);
+ map.put("bins", binList);
+ map.put("numPoints", new Integer(numPoints));
+ map.put("bin width", new Double(binWidth));
+ list.add(map);
+ }
+
+ /**
+ * Adds a series to the dataset. Values falling on the boundary of
+ * adjacent bins will be assigned to the higher indexed bin.
+ *
+ * @param key the series key (<code>null</code> not permitted).
+ * @param values the raw observations.
+ * @param bins new bins (size must be at least 1).
+ */
+ public void addSeries(Comparable key, double values[], HistogramBin bins[])
+ {
+ addSeries(key, values, values.length, bins);
+ }
+
+ /**
+ * Adds a series to the dataset. Values falling on the boundary of
+ * adjacent bins will be assigned to the higher indexed bin.
+ *
+ * @param key the series key (<code>null</code> not permitted).
+ * @param values the raw observations.
+ * @param numPoints only the first numPoints values are used.
+ * @param bins new bins (size must be at least 1).
+ */
+ public void addSeries(Comparable key, double values[], int numPoints,
+ HistogramBin bins[])
+ {
+ if (key == null)
+ throw new IllegalArgumentException("Null 'key' argument.");
+ if (values == null)
+ throw new IllegalArgumentException("Null 'values' argument.");
+ if (bins == null || bins.length < 2)
+ throw new IllegalArgumentException
+ ("The 'bins' table must contain at least 1 org.jfree.data.statistics.HistogramBin.");
+ List binList = new ArrayList(bins.length);
+ for (int i = 0; i < bins.length; i++)
+ binList.add(bins[i]);
+
+ ArrayList valuesList = new ArrayList(numPoints);
+ for (int i = 0; i < numPoints; i++)
+ valuesList.add(new Double(values[i]));
+
+ synchronizeValuesAndBins(binList, valuesList);
+ Map map = new HashMap();
+ map.put("key", key);
+ map.put("values", valuesList);
+ map.put("bins", binList);
+ map.put("numPoints", new Integer(numPoints));
+ map.put("bin width", new Double( -1D));
+ list.add(map);
+ }
+
+ /**
+ * Returns the minimum value in an array of values.
+ *
+ * @param values the values (<code>null</code> not permitted and
+ * zero-length array not permitted).
+ *
+ * @return The minimum value.
+ */
+ private double getMinimum(double values[])
+ {
+ if (values == null || values.length < 1)
+ throw new IllegalArgumentException
+ ("Null or zero length 'values' argument.");
+ double min = 1.7E+308D;
+ for (int i = 0; i < values.length; i++)
+ if (values[i] < min)
+ min = values[i];
+
+ return min;
+ }
+
+ /**
+ * Returns the maximum value in an array of values.
+ *
+ * @param values the values (<code>null</code> not permitted and
+ * zero-length array not permitted).
+ *
+ * @return The maximum value.
+ */
+ private double getMaximum(double values[])
+ {
+ if (values == null || values.length < 1)
+ throw new IllegalArgumentException
+ ("Null or zero length 'values' argument.");
+ double max = -1.7E+308D;
+ for (int i = 0; i < values.length; i++)
+ if (values[i] > max)
+ max = values[i];
+
+ return max;
+ }
+
+ /**
+ * Returns the bins for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ *
+ * @return A list of bins.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public List getBins(int series)
+ {
+ Map map = (Map)list.get(series);
+ return (List)map.get("bins");
+ }
+
+ /**
+ * Sets the bins for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param bins the number of bins (must be at least 1).
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public void setBins(int series, int bins)
+ {
+ double minimum = getMinimum(getValues(series));
+ double maximum = getMaximum(getValues(series));
+ setBins(series, bins, minimum, maximum);
+ }
+
+ /**
+ * Sets the bins for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param bins the number of bins (must be at least 1).
+ * @param minimum the lower bound of the bin range.
+ * @param maximum the upper bound of the bin range.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public void setBins(int series, int bins, double minimum, double maximum)
+ {
+ Map map = (Map)list.get(series);
+ List currentValues = (List)map.get("values");
+ double binWidth = (maximum - minimum) / (double)bins;
+ double lower = minimum;
+ List binList = new ArrayList(bins);
+ double EPS = 1.0e-15;
+ for (int i = 0; i < bins; i++) {
+ HistogramBin bin;
+ if (i == bins - 1) {
+ bin = new HistogramBin(lower, maximum*(1.0 + EPS));
+ } else {
+ double upper = minimum + (double)(i + 1) * binWidth;
+ bin = new HistogramBin(lower, upper);
+ lower = upper;
+ }
+ binList.add(bin);
+ }
+
+ synchronizeValuesAndBins(binList, currentValues);
+ map.put("values", currentValues);
+ map.put("bins", binList);
+ }
+
+ /**
+ * Sets the bins for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param bins the number of bins (must be at least 1).
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public void setBins(int series, HistogramBin bins[])
+ {
+ Map map = (Map)list.get(series);
+ List currentValues = (List)map.get("values");
+ ArrayList binList = new ArrayList(bins.length);
+ for (int i = 0; i < bins.length; i++)
+ binList.add(bins[i]);
+
+ synchronizeValuesAndBins(binList, currentValues);
+ map.put("values", currentValues);
+ map.put("bins", binList);
+ }
+
+ /**
+ * Returns the values for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ *
+ * @return A list of values.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public List getValuesList(int series)
+ {
+ Map map = (Map)list.get(series);
+ return (List)map.get("values");
+ }
+
+ /**
+ * Returns the values for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ *
+ * @return A table of values.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public double[] getValues(int series)
+ {
+ List valuesList = (List)((Map)list.get(series)).get("values");
+ ListIterator iter = valuesList.listIterator();
+ double retour[] = new double[valuesList.size()];
+ for (int i = 0; iter.hasNext(); i++)
+ retour[i] = ((Double)iter.next()).doubleValue();
+
+ return retour;
+ }
+
+ /**
+ * Sets the values for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param valuesList List of new values.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public void setValues(int series, List valuesList)
+ {
+ Map map = (Map)list.get(series);
+ List currentBins = (List)map.get("bins");
+ synchronizeValuesAndBins(currentBins, valuesList);
+ map.put("values", valuesList);
+ map.put("bins", currentBins);
+ }
+
+ /**
+ * Sets the values for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param values Table of new values.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public void setValues(int series, double values[])
+ {
+ ArrayList valuesList = new ArrayList(values.length);
+ for (int i = 0; i < values.length; i++)
+ valuesList.add(new Double(values[i]));
+
+ setValues(series, ((List) (valuesList)));
+ }
+
+ /**
+ * Synchronize values to bins. Compute bins values.
+ *
+ * @param bins List of bins.
+ * @param values List of values.
+ */
+ private void synchronizeValuesAndBins(List bins, List values)
+ {
+ ListIterator iterBins = bins.listIterator(0);
+ ListIterator iterValues = values.listIterator();
+ HistogramBin bin;
+ for (; iterBins.hasNext(); iterBins.set(
+ new HistogramBin(bin.getStartBoundary(), bin.getEndBoundary())))
+ bin = (HistogramBin)iterBins.next();
+
+ iterBins = bins.listIterator(0);
+ while (iterValues.hasNext()) {
+ double currentValue = ((Double)iterValues.next()).doubleValue();
+ boolean continu = true;
+ iterBins = bins.listIterator(0);
+ while (continu && iterBins.hasNext()) {
+ HistogramBin tempBin = (HistogramBin)iterBins.next();
+ if (currentValue >= tempBin.getStartBoundary() &&
+ currentValue < tempBin.getEndBoundary()) {
+ tempBin.incrementCount();
+ continu = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the total number of observations for a series.
+ *
+ * @param series the series index.
+ *
+ * @return The total.
+ */
+ public int getTotal(int series)
+ {
+ Map map = (Map)list.get(series);
+ return ((Integer)map.get("numPoints")).intValue();
+ }
+
+ /**
+ * Returns the bin width for a series.
+ *
+ * @param series the series index (zero based).
+ *
+ * @return The bin width.
+ */
+ public double getBinWidth(int series)
+ {
+ Map map = (Map)list.get(series);
+ return ((Double)map.get("bin width")).doubleValue();
+ }
+
+ /**
+ * Returns the number of series in the dataset.
+ *
+ * @return The series count.
+ */
+ public int getSeriesCount()
+ {
+ return list.size();
+ }
+
+ /**
+ * Returns the key for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ *
+ * @return The series key.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public Comparable getSeriesKey(int series)
+ {
+ Map map = (Map)list.get(series);
+ return (Comparable)map.get("key");
+ }
+
+ /**
+ * Returns the number of data items for a series.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ *
+ * @return The item count.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public int getItemCount(int series)
+ {
+ return getBins(series).size();
+ }
+
+ /**
+ * Returns the X value for a bin. This value won't be used for plotting
+ * histograms, since the renderer will ignore it. But other renderers can
+ * use it (for example, you could use the dataset to create a line
+ * chart).
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param item the item index (zero based).
+ *
+ * @return The start value.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public Number getX(int series, int item)
+ {
+ List bins = getBins(series);
+ HistogramBin bin = (HistogramBin)bins.get(item);
+ double x = (bin.getStartBoundary() + bin.getEndBoundary()) / 2D;
+ return new Double(x);
+ }
+
+ /**
+ * Returns the y-value for a bin (calculated to take into account the
+ * histogram type).
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param item the item index (zero based).
+ *
+ * @return The y-value.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public Number getY(int series, int item)
+ {
+ List bins = getBins(series);
+ HistogramBin bin = (HistogramBin)bins.get(item);
+ double total = getTotal(series);
+ double binWidth = getBinWidth(series);
+ if (type == HistogramType.FREQUENCY)
+ return new Double(bin.getCount());
+ if (type == HistogramType.RELATIVE_FREQUENCY)
+ return new Double((double)bin.getCount() / total);
+ if (type == HistogramType.SCALE_AREA_TO_1)
+ return new Double((double)bin.getCount() / (binWidth * total));
+ else
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Returns the start value for a bin.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param item the item index (zero based).
+ *
+ * @return The start value.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public Number getStartX(int series, int item)
+ {
+ List bins = getBins(series);
+ HistogramBin bin = (HistogramBin)bins.get(item);
+ return new Double(bin.getStartBoundary());
+ }
+
+ /**
+ * Returns the end value for a bin.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param item the item index (zero based).
+ *
+ * @return The end value.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public Number getEndX(int series, int item)
+ {
+ List bins = getBins(series);
+ HistogramBin bin = (HistogramBin)bins.get(item);
+ return new Double(bin.getEndBoundary());
+ }
+
+ /**
+ * Returns the start y-value for a bin (which is the same as the y-value).
+ * This method exists only to support the general form of the
+ * {IntervalXYDataset} interface.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param item the item index (zero based).
+ *
+ * @return The y-value.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public Number getStartY(int series, int item)
+ {
+ return getY(series, item);
+ }
+
+ /**
+ * Returns the end y-value for a bin (which is the same as the y-value).
+ * This method exists only to support the general form of the
+ * IntervalXYDataset} interface.
+ *
+ * @param series the series index (in the range <code>0</code> to
+ * <code>getSeriesCount() - 1</code>).
+ * @param item the item index (zero based).
+ *
+ * @return The Y value.
+ *
+ * @throws IndexOutOfBoundsException if <code>series</code> is outside the
+ * specified range.
+ */
+ public Number getEndY(int series, int item)
+ {
+ return getY(series, item);
+ }
+
+ /**
+ * Tests this dataset for equality with an arbitrary object.
+ *
+ * @param obj the object to test against (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof CustomHistogramDataset))
+ return false;
+ CustomHistogramDataset that = (CustomHistogramDataset)obj;
+ if (!ObjectUtilities.equal(type, that.type))
+ return false;
+ return ObjectUtilities.equal(list, that.list);
+ }
+
+ /**
+ * Returns a clone of the dataset.
+ *
+ * @return A clone of the dataset.
+ *
+ * @throws CloneNotSupportedException if the object cannot be cloned.
+ */
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ return super.clone();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/DiscreteDistIntChart.java b/source/umontreal/iro/lecuyer/charts/DiscreteDistIntChart.java
new file mode 100644
index 0000000..66ffc0a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/DiscreteDistIntChart.java
@@ -0,0 +1,306 @@
+
+
+/*
+ * Class: DiscreteDistIntChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.DiscreteDistributionInt;
+
+import org.jfree.chart.JFreeChart;
+import javax.swing.JFrame;
+import java.awt.*;
+
+
+
+/**
+ * This class provides tools to plot the mass function and the cumulative
+ * probability of a discrete probability distribution over the integers.
+ *
+ */
+public class DiscreteDistIntChart {
+ protected DiscreteDistributionInt dist;
+ protected int a,b;
+ protected XYLineChart cdfChart;
+ protected XYLineChart probChart;
+
+
+ /**
+ * Constructor for a new <TT>DiscreteDistIntChart</TT> instance used to plot the
+ * probabilities of the
+ * discrete distribution <TT>dist</TT> over the integers.
+ *
+ * @param dist discrete distribution to plot
+ *
+ *
+ */
+ public DiscreteDistIntChart (DiscreteDistributionInt dist) {
+ this.dist = dist;
+ this.a = 0;
+ this.b = 0;
+ }
+
+
+ /**
+ * Constructor for a new <TT>DiscreteDistIntChart</TT> instance used to plot the
+ * probabilities of the
+ * discrete distribution <TT>dist</TT> over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ *
+ * @param dist continuous distribution to plot
+ *
+ * @param a lower bound of interval
+ *
+ * @param b upper bound of interval
+ *
+ */
+ public DiscreteDistIntChart (DiscreteDistributionInt dist, int a, int b) {
+ this.dist = dist;
+ if (a>=b) throw new IllegalArgumentException ("a is bigger than b");
+ this.a = a;
+ this.b = b;
+ init();
+ }
+
+
+ private void init() {
+ int m = b-a+1;
+ double[][] cdf = new double[2][m];
+ double[][] probability = new double[2][m];
+
+ for (int i = 0; i < m; i++) {
+ cdf[0][i] = i+a;
+ cdf[1][i] = dist.cdf (i+a);
+ probability[0][i] = i+a;
+ probability[1][i] = dist.prob (i+a);
+ }
+
+ double[][] cdfFinal = new double[2][2*(m-1)];
+ for (int i = 0; i < m-1; i++) {
+ cdfFinal[0][2*i] = cdf[0][i];
+ cdfFinal[0][2*i+1] = cdf[0][i+1];
+ cdfFinal[1][2*i] = cdf[1][i];
+ cdfFinal[1][2*i+1] = cdf[1][i];
+ }
+
+ cdfChart = new XYLineChart("cdf: " + dist.toString(), "", "", cdfFinal);
+ probChart = new XYLineChart("probability: " + dist.toString(), "",
+ "", probability);
+ cdfChart.setprobFlag (true);
+ probChart.setprobFlag (true);
+
+ // Only for tikZ
+ XYListSeriesCollection collec = cdfChart.getSeriesCollection();
+ collec.setColor(0, Color.BLUE);
+ collec.setPlotStyle(0, "thick");
+ collec.setMarksType(0, "only marks");
+
+ collec = probChart.getSeriesCollection();
+ collec.setColor(0, Color.ORANGE);
+ collec.setPlotStyle(0, "ycomb");
+ collec.setMarksType(0, "*");
+ collec.setDashPattern(0, "solid");
+ }
+
+ private void testParam() {
+ if (a==0 && b==0) {
+ double mean = dist.getMean();
+ double sd = dist.getStandardDeviation();
+ int xa = (int)Math.round(mean - 3.0*sd);
+ if (xa < dist.getXinf())
+ xa = dist.getXinf();
+ int xb = (int)Math.round(mean + 3.0*sd);
+ if (xb > dist.getXsup())
+ xb = dist.getXsup();
+ setParam(xa, xb);
+ }
+ }
+
+
+ /**
+ * Displays a chart of the cumulative distribution function (cdf) over the
+ * interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> on the screen using Swing. This method creates an
+ * application containing a chart panel displaying the chart. The created
+ * frame is positioned on-screen, and displayed before it is returned. The
+ * <TT>width</TT> and the <TT>height</TT> of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @param a lower bound of interval
+ *
+ * @param b upper bound of interval
+ *
+ * @return frame containing the chart
+ *
+ */
+ public JFrame viewCdf (int width, int height, int a, int b) {
+ setParam(a,b);
+ return cdfChart.view(width, height);
+ }
+
+
+ /**
+ * Similar to method {@link #viewCdf viewCdf} above.
+ * If the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> for the graph is
+ * not defined, it will be set automatically to
+ * <SPAN CLASS="MATH">[<I>μ</I> -3<I>σ</I>, <I>μ</I> +3<I>σ</I>]</SPAN>,
+ * where <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are the mean and the variance of the distribution.
+ *
+ * @param width frame width in pixels
+ *
+ * @param height frame height in pixels
+ *
+ * @return frame containing the chart
+ *
+ */
+ public JFrame viewCdf (int width, int height) {
+ testParam();
+ return cdfChart.view(width, height);
+ }
+
+
+ /**
+ * Displays a chart of the probability mass function over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>
+ * on the screen using Swing. This method creates an application containing
+ * a chart panel displaying the chart. The created frame is positioned
+ * on-screen, and displayed before it is returned. The <TT>width</TT> and
+ * the <TT>height</TT> of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @param a lower bound of interval
+ *
+ * @param b upper bound of interval
+ *
+ * @return frame containing the chart
+ *
+ */
+ public JFrame viewProb (int width, int height, int a, int b) {
+ setParam(a,b);
+ return probChart.viewBar(width, height);
+ }
+
+
+ /**
+ * Similar to method {@link #viewProb viewProb} above.
+ * If the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> for the graph is
+ * not defined, it will be set automatically to
+ * <SPAN CLASS="MATH">[<I>μ</I> -3<I>σ</I>, <I>μ</I> +3<I>σ</I>]</SPAN>,
+ * where <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are the mean and the variance of the distribution.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @return frame containing the chart
+ *
+ */
+ public JFrame viewProb (int width, int height) {
+ testParam();
+ return probChart.viewBar(width, height);
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> for this object.
+ *
+ * @param a lower bound of interval
+ *
+ * @param b upper bound of interval
+ *
+ *
+ */
+ public void setParam (int a, int b) {
+ if (a >= b) throw new IllegalArgumentException
+ ("a is bigger than b" + a + " " + b);
+ this.a = a;
+ this.b = b;
+ init();
+ }
+
+
+ /**
+ * Exports a chart of the cumulative probability to a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> source code using
+ * PGF/TikZ.
+ * This method constructs and returns a string that can be written to
+ * a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> document to render the plot. <TT>width</TT> and <TT>height</TT>
+ * represents the width and the height of the produced chart. These dimensions
+ * do not take into account the axes and labels extra space. The <TT>width</TT>
+ * and the <TT>height</TT> of the chart are measured in centimeters.
+ *
+ * @param width Chart's width in centimeters
+ *
+ * @param height Chart's height in centimeters
+ *
+ * @return LaTeX source code
+ *
+ */
+ public String toLatexCdf (int width, int height) {
+ testParam();
+ return cdfChart.toLatex(width, height);
+ }
+
+
+ /**
+ * Similar to {@link #toLatexCdf toLatexCdf}, but for the probability instead
+ * of the cdf.
+ *
+ * @param width Chart's width in centimeters
+ *
+ * @param height Chart's height in centimeters
+ *
+ * @return LaTeX source code
+ *
+ */
+ public String toLatexProb (int width, int height) {
+ testParam();
+ return probChart.toLatex(width, height);
+ }
+
+
+ /**
+ * Returns the chart of the cdf.
+ *
+ * @return the chart of the cdf.
+ *
+ */
+ public XYLineChart getCdf () {
+ return cdfChart;
+ }
+
+
+ /**
+ * Returns the chart of the probability.
+ *
+ * @return the chart of the probability.
+ *
+ */
+ public XYLineChart getProb () {
+ return probChart;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/DiscreteDistIntChart.tex b/source/umontreal/iro/lecuyer/charts/DiscreteDistIntChart.tex
new file mode 100644
index 0000000..d6a5ff9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/DiscreteDistIntChart.tex
@@ -0,0 +1,308 @@
+\defmodule {DiscreteDistIntChart}
+
+This class provides tools to plot the mass function and the cumulative
+probability of a discrete probability distribution over the integers.
+
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: DiscreteDistIntChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.DiscreteDistributionInt;\begin{hide}
+
+import org.jfree.chart.JFreeChart;
+import javax.swing.JFrame;
+import java.awt.*;
+\end{hide}
+
+
+public class DiscreteDistIntChart \begin{hide} {
+ protected DiscreteDistributionInt dist;
+ protected int a,b;
+ protected XYLineChart cdfChart;
+ protected XYLineChart probChart;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection*{Constructors}
+\begin{code}
+
+ public DiscreteDistIntChart (DiscreteDistributionInt dist) \begin{hide} {
+ this.dist = dist;
+ this.a = 0;
+ this.b = 0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor for a new \texttt{DiscreteDistIntChart} instance used to plot the
+probabilities of the
+% \externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt}
+discrete distribution \texttt{dist} over the integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{dist}{discrete distribution to plot}
+\end{htmlonly}
+\begin{code}
+
+ public DiscreteDistIntChart (DiscreteDistributionInt dist, int a, int b) \begin{hide} {
+ this.dist = dist;
+ if (a>=b) throw new IllegalArgumentException ("a is bigger than b");
+ this.a = a;
+ this.b = b;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor for a new \texttt{DiscreteDistIntChart} instance used to plot the
+probabilities of the
+% \externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt}
+discrete distribution \texttt{dist} over the interval $[a,b]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{dist}{continuous distribution to plot}
+ \param{a}{lower bound of interval}
+ \param{b}{upper bound of interval}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}\begin{hide}
+
+ private void init() {
+ int m = b-a+1;
+ double[][] cdf = new double[2][m];
+ double[][] probability = new double[2][m];
+
+ for (int i = 0; i < m; i++) {
+ cdf[0][i] = i+a;
+ cdf[1][i] = dist.cdf (i+a);
+ probability[0][i] = i+a;
+ probability[1][i] = dist.prob (i+a);
+ }
+
+ double[][] cdfFinal = new double[2][2*(m-1)];
+ for (int i = 0; i < m-1; i++) {
+ cdfFinal[0][2*i] = cdf[0][i];
+ cdfFinal[0][2*i+1] = cdf[0][i+1];
+ cdfFinal[1][2*i] = cdf[1][i];
+ cdfFinal[1][2*i+1] = cdf[1][i];
+ }
+
+ cdfChart = new XYLineChart("cdf: " + dist.toString(), "", "", cdfFinal);
+ probChart = new XYLineChart("probability: " + dist.toString(), "",
+ "", probability);
+ cdfChart.setprobFlag (true);
+ probChart.setprobFlag (true);
+
+ // Only for tikZ
+ XYListSeriesCollection collec = cdfChart.getSeriesCollection();
+ collec.setColor(0, Color.BLUE);
+ collec.setPlotStyle(0, "thick");
+ collec.setMarksType(0, "only marks");
+
+ collec = probChart.getSeriesCollection();
+ collec.setColor(0, Color.ORANGE);
+ collec.setPlotStyle(0, "ycomb");
+ collec.setMarksType(0, "*");
+ collec.setDashPattern(0, "solid");
+ }
+
+ private void testParam() {
+ if (a==0 && b==0) {
+ double mean = dist.getMean();
+ double sd = dist.getStandardDeviation();
+ int xa = (int)Math.round(mean - 3.0*sd);
+ if (xa < dist.getXinf())
+ xa = dist.getXinf();
+ int xb = (int)Math.round(mean + 3.0*sd);
+ if (xb > dist.getXsup())
+ xb = dist.getXsup();
+ setParam(xa, xb);
+ }
+ }
+\end{hide}
+
+ public JFrame viewCdf (int width, int height, int a, int b) \begin{hide} {
+ setParam(a,b);
+ return cdfChart.view(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays a chart of the cumulative distribution function (cdf) over the
+ interval $[a,b]$ on the screen using Swing. This method creates an
+ application containing a chart panel displaying the chart. The created
+ frame is positioned on-screen, and displayed before it is returned. The
+ \texttt{width} and the \texttt{height} of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \param{a}{lower bound of interval}
+ \param{b}{upper bound of interval}
+ \return{frame containing the chart}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame viewCdf (int width, int height) \begin{hide} {
+ testParam();
+ return cdfChart.view(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to method \method{viewCdf}{} above.
+ If the interval $[a,b]$ for the graph is
+ not defined, it will be set automatically to $[\mu - 3\sigma, \mu + 3\sigma]$,
+ where $\mu$ and $\sigma$ are the mean and the variance of the distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels}
+ \param{height}{frame height in pixels}
+ \return{frame containing the chart}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame viewProb (int width, int height, int a, int b) \begin{hide} {
+ setParam(a,b);
+ return probChart.viewBar(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays a chart of the probability mass function over the interval $[a,b]$
+ on the screen using Swing. This method creates an application containing
+ a chart panel displaying the chart. The created frame is positioned
+ on-screen, and displayed before it is returned. The \texttt{width} and
+ the \texttt{height} of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \param{a}{lower bound of interval}
+ \param{b}{upper bound of interval}
+ \return{frame containing the chart}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame viewProb (int width, int height) \begin{hide} {
+ testParam();
+ return probChart.viewBar(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to method \method{viewProb}{} above.
+ If the interval $[a,b]$ for the graph is
+ not defined, it will be set automatically to $[\mu - 3\sigma, \mu + 3\sigma]$,
+ where $\mu$ and $\sigma$ are the mean and the variance of the distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \return{frame containing the chart}
+\end{htmlonly}
+\begin{code}
+
+ public void setParam (int a, int b) \begin{hide} {
+ if (a >= b) throw new IllegalArgumentException
+ ("a is bigger than b" + a + " " + b);
+ this.a = a;
+ this.b = b;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $a$ and $b$ for this object.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{lower bound of interval}
+ \param{b}{upper bound of interval}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatexCdf (int width, int height) \begin{hide} {
+ testParam();
+ return cdfChart.toLatex(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Exports a chart of the cumulative probability to a \LaTeX\ source code using
+ PGF/TikZ.
+ This method constructs and returns a string that can be written to
+ a \LaTeX\ document to render the plot. \texttt{width} and \texttt{height}
+ represents the width and the height of the produced chart. These dimensions
+ do not take into account the axes and labels extra space. The \texttt{width}
+ and the \texttt{height} of the chart are measured in centimeters.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{Chart's width in centimeters}
+ \param{height}{Chart's height in centimeters}
+ \return{LaTeX source code}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatexProb (int width, int height) \begin{hide} {
+ testParam();
+ return probChart.toLatex(width, height);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to \method{toLatexCdf}{}, but for the probability instead
+ of the cdf.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{Chart's width in centimeters}
+ \param{height}{Chart's height in centimeters}
+ \return{LaTeX source code}
+\end{htmlonly}
+\begin{code}
+
+ public XYLineChart getCdf () \begin{hide} {
+ return cdfChart;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart of the cdf.
+\end{tabb}
+\begin{htmlonly}
+ \return{the chart of the cdf.}
+\end{htmlonly}
+\begin{code}
+
+ public XYLineChart getProb () \begin{hide} {
+ return probChart;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart of the probability.
+\end{tabb}
+\begin{htmlonly}
+ \return{the chart of the probability.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/EmpiricalChart.java b/source/umontreal/iro/lecuyer/charts/EmpiricalChart.java
new file mode 100644
index 0000000..9096a27
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/EmpiricalChart.java
@@ -0,0 +1,368 @@
+
+
+/*
+ * Class: EmpiricalChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import umontreal.iro.lecuyer.stat.TallyStore;
+
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.xy.XYSeries;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Formatter;
+import javax.swing.JFrame;
+
+/**
+ * This class provides additional tools to create and manage empirical
+ * plots. Empirical plots are used to plot empirical distributions. The
+ * {@link EmpiricalChart} class is the simplest way to produce empirical
+ * plots only. Each {@link EmpiricalChart} object is linked with an
+ * {@link umontreal.iro.lecuyer.charts.EmpiricalSeriesCollection EmpiricalSeriesCollection}
+ * data set.
+ *
+ */
+public class EmpiricalChart extends XYChart {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tool tips
+ false // urls
+ );
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ fixZeroPoint();
+ }
+
+
+ private void fixZeroPoint() {
+ // reset the first point (x0, 0) with x0 at the beginning of x-axis
+ double xmin = Math.min (XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition());
+ XYSeriesCollection col = (XYSeriesCollection)dataset.getSeriesCollection();
+ for (int i = 0; i < col.getSeriesCount(); i++) {
+ XYSeries ser = col.getSeries (i);
+ ser.remove(0); // remove temporary 0-point
+ ser.add(xmin, 0); // replace
+ }
+ }
+
+ /**
+ * Initializes a new <TT>EmpiricalChart</TT> instance with an empty data set.
+ *
+ */
+ public EmpiricalChart() {
+ super();
+ dataset = new EmpiricalSeriesCollection();
+ init (null, null, null);
+ }
+
+
+ /**
+ * Initializes a new <TT>EmpiricalChart</TT> instance with data <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input vectors <TT>data</TT> represents a collection of observation sets.
+ * Each vector of <TT>data</TT> represents a <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates set.
+ * Therefore <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 0,…, <I>n</I> - 1</SPAN>, is used to draw the <SPAN CLASS="MATH"><I>i</I></SPAN>-th plot.
+ * The values of each observation set <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>]</SPAN> <SPAN CLASS="textit">must be sorted</SPAN>
+ * in increasing order.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ double[]... data) {
+ super();
+ dataset = new EmpiricalSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>EmpiricalChart</TT> instance with a set of points
+ * <TT>data</TT>. <TT>title</TT> is a title, <TT>XLabel</TT> is a short
+ * description of the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis and <TT>YLabel</TT> a short description of the
+ * <SPAN CLASS="MATH"><I>y</I></SPAN>-axis. Vector <TT>data</TT> represents a <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates set. The values
+ * of this observation set <SPAN CLASS="textit">must be sorted</SPAN> in increasing order. Only
+ * <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT> will
+ * be considered to plot.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints number of points to plot
+ *
+ *
+ */
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) {
+ super();
+ dataset = new EmpiricalSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Similar to the above constructor, but with <TT>DoubleArrayList</TT>.
+ * A {@link cern.colt.list.DoubleArrayList DoubleArrayList} from the Colt library is
+ * used to store data. The values of each observation set <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>]</SPAN>
+ * <SPAN CLASS="textit">must be sorted</SPAN> in increasing order.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) {
+ super();
+ dataset = new EmpiricalSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>EmpiricalChart</TT> instance with data arrays
+ * contained in each
+ * {@link umontreal.iro.lecuyer.stat.TallyStore TallyStore} object. The input
+ * parameter <TT>tallies</TT> represents a collection of observation sets.
+ * Therefore, the <SPAN CLASS="MATH"><I>i</I></SPAN>-th <TT>tallies</TT> is used to draw
+ * the <SPAN CLASS="MATH"><I>i</I></SPAN>-th plot.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param tallies series of observation sets.
+ *
+ *
+ */
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ TallyStore... tallies) {
+ super();
+ dataset = new EmpiricalSeriesCollection(tallies);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>EmpiricalChart</TT> instance with data <TT>data</TT>.
+ * The input parameter <TT>data</TT> represents a set of plotting data.
+ * <TT>XYSeriesCollection</TT> is a <TT>JFreeChart</TT>-like container class
+ * used to store and manage observation sets.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series collection.
+ *
+ */
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ XYSeriesCollection data) {
+ super();
+ dataset = new EmpiricalSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Returns the chart's dataset.
+ *
+ * @return the chart's dataset.
+ *
+ */
+ public EmpiricalSeriesCollection getSeriesCollection() {
+ return (EmpiricalSeriesCollection)dataset;
+ }
+
+
+ /**
+ * Links a new dataset to the current chart.
+ *
+ * @param dataset new dataset.
+ *
+ *
+ */
+ public void setSeriesCollection (EmpiricalSeriesCollection dataset) {
+ this.dataset = dataset;
+ }
+
+
+ /**
+ * Synchronizes <SPAN CLASS="MATH"><I>x</I></SPAN>-axis ticks to the <SPAN CLASS="MATH"><I>s</I></SPAN>-th series <SPAN CLASS="MATH"><I>x</I></SPAN>-values.
+ *
+ * @param s series used to define ticks.
+ *
+ *
+ */
+ public void setTicksSynchro (int s) {
+ XYSeriesCollection seriesCollection = (XYSeriesCollection)this.dataset.getSeriesCollection();
+ double[] values = new double[seriesCollection.getItemCount(s)];
+
+ for(int i = 0; i < seriesCollection.getItemCount(s); i++)
+ values[i] = seriesCollection.getXValue(s, i);
+
+ XAxis.setLabels(values);
+ }
+
+
+ /**
+ * Displays chart on the screen using Swing.
+ * This method creates an application containing a chart panel displaying
+ * the chart. The created frame is positioned on-screen, and displayed before
+ * it is returned. The <TT>width</TT> and the <TT>height</TT>
+ * of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @return frame containing the chart.
+ *
+ */
+ public JFrame view (int width, int height) {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame("EmpiricalChart from SSJ : " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("EmpiricalChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }
+
+
+ public String toLatex (double width, double height) {
+ double xunit, yunit;
+ double[] save = new double[4];
+
+ if(dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ xunit = width / ((Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale));
+ //taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ yunit = height / ((Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale));
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ : %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if (grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale);
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale));
+ formatter.format("%s", YAxis.toLatex(YScale));
+
+ formatter.format("%s", dataset.toLatex(XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/EmpiricalChart.tex b/source/umontreal/iro/lecuyer/charts/EmpiricalChart.tex
new file mode 100644
index 0000000..61b3722
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/EmpiricalChart.tex
@@ -0,0 +1,372 @@
+\defmodule {EmpiricalChart}
+
+% Extends \externalclass{umontreal.iro.lecuyer}{Chart}.
+This class provides additional tools to create and manage empirical
+plots. Empirical plots are used to plot empirical distributions. The
+\class{EmpiricalChart} class is the simplest way to produce empirical
+ plots only. Each \class{EmpiricalChart} object is linked with an
+\externalclass{umontreal.iro.lecuyer.charts}{EmpiricalSeriesCollection}
+data set.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: EmpiricalChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import umontreal.iro.lecuyer.stat.TallyStore;
+
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.xy.XYSeries;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Formatter;
+import javax.swing.JFrame;\end{hide}
+
+public class EmpiricalChart extends XYChart \begin{hide} {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tool tips
+ false // urls
+ );
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ fixZeroPoint();
+ }
+
+
+ private void fixZeroPoint() {
+ // reset the first point (x0, 0) with x0 at the beginning of x-axis
+ double xmin = Math.min (XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition());
+ XYSeriesCollection col = (XYSeriesCollection)dataset.getSeriesCollection();
+ for (int i = 0; i < col.getSeriesCount(); i++) {
+ XYSeries ser = col.getSeries (i);
+ ser.remove(0); // remove temporary 0-point
+ ser.add(xmin, 0); // replace
+ }
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+
+\begin{code}
+ public EmpiricalChart() \begin{hide} {
+ super();
+ dataset = new EmpiricalSeriesCollection();
+ init (null, null, null);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{EmpiricalChart} instance with an empty data set.
+\end{tabb}
+\begin{code}
+
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ double[]... data) \begin{hide} {
+ super();
+ dataset = new EmpiricalSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{EmpiricalChart} instance with data \texttt{data}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis and \texttt{YLabel} a short description of the $y$-axis.
+ The input vectors \texttt{data} represents a collection of observation sets.
+ Each vector of \texttt{data} represents a $x$-coordinates set.
+ Therefore \texttt{data}$[i], i = 0,\ldots,n-1$, is used to draw the $i$-th plot.
+ The values of each observation set \texttt{data}$[i]$ \emph{must be sorted}
+ in increasing order.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) \begin{hide} {
+ super();
+ dataset = new EmpiricalSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{EmpiricalChart} instance with a set of points
+ \texttt{data}. \texttt{title} is a title, \texttt{XLabel} is a short
+ description of the $x$-axis and \texttt{YLabel} a short description of the
+ $y$-axis. Vector \texttt{data} represents a $x$-coordinates set. The values
+ of this observation set \emph{must be sorted} in increasing order. Only
+ \emph{the first} \texttt{numPoints} of \texttt{data} will
+ be considered to plot.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+ \param{numPoints}{number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) \begin{hide} {
+ super();
+ dataset = new EmpiricalSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to the above constructor, but with \texttt{DoubleArrayList}.
+ A \externalclass{cern.colt.list}{DoubleArrayList} from the Colt library is
+ used to store data. The values of each observation set \texttt{data}$[i]$
+ \emph{must be sorted} in increasing order.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ TallyStore... tallies) \begin{hide} {
+ super();
+ dataset = new EmpiricalSeriesCollection(tallies);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{EmpiricalChart} instance with data arrays
+ contained in each
+ \externalclass{umontreal.iro.lecuyer.stat}{TallyStore} object. The input
+ parameter \texttt{tallies} represents a collection of observation sets.
+ Therefore, the $i$-th \texttt{tallies} is used to draw
+ the $i$-th plot.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{tallies}{series of observation sets.}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalChart (String title, String XLabel, String YLabel,
+ XYSeriesCollection data) \begin{hide} {
+ super();
+ dataset = new EmpiricalSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{EmpiricalChart} instance with data \texttt{data}.
+ The input parameter \texttt{data} represents a set of plotting data.
+ \texttt{XYSeriesCollection} is a \texttt{JFreeChart}-like container class
+ used to store and manage observation sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series collection.}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public EmpiricalSeriesCollection getSeriesCollection() \begin{hide} {
+ return (EmpiricalSeriesCollection)dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's dataset.
+\end{tabb}
+\begin{htmlonly}
+ \return{the chart's dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeriesCollection (EmpiricalSeriesCollection dataset) \begin{hide} {
+ this.dataset = dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Links a new dataset to the current chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{dataset}{new dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTicksSynchro (int s) \begin{hide} {
+ XYSeriesCollection seriesCollection = (XYSeriesCollection)this.dataset.getSeriesCollection();
+ double[] values = new double[seriesCollection.getItemCount(s)];
+
+ for(int i = 0; i < seriesCollection.getItemCount(s); i++)
+ values[i] = seriesCollection.getXValue(s, i);
+
+ XAxis.setLabels(values);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Synchronizes $x$-axis ticks to the $s$-th series $x$-values.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{series used to define ticks.}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame view (int width, int height) \begin{hide} {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame("EmpiricalChart from SSJ : " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("EmpiricalChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays chart on the screen using Swing.
+ This method creates an application containing a chart panel displaying
+ the chart. The created frame is positioned on-screen, and displayed before
+ it is returned. The \texttt{width} and the \texttt{height}
+ of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \return{frame containing the chart.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{\LaTeX-specific method}
+
+\begin{code}
+
+ public String toLatex (double width, double height) \begin{hide} {
+ double xunit, yunit;
+ double[] save = new double[4];
+
+ if(dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ xunit = width / ((Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale));
+ //taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ yunit = height / ((Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale));
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ : %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if (grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale);
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale));
+ formatter.format("%s", YAxis.toLatex(YScale));
+
+ formatter.format("%s", dataset.toLatex(XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/EmpiricalRenderer.java b/source/umontreal/iro/lecuyer/charts/EmpiricalRenderer.java
new file mode 100644
index 0000000..3b7e7b5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/EmpiricalRenderer.java
@@ -0,0 +1,217 @@
+package umontreal.iro.lecuyer.charts;
+
+import java.awt.Graphics2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.Shape;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.entity.XYItemEntity;
+import org.jfree.chart.labels.XYToolTipGenerator;
+import org.jfree.chart.urls.XYURLGenerator;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRendererState;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotRenderingInfo;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.CrosshairState;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.util.PublicCloneable;
+import org.jfree.util.ShapeUtilities;
+
+/*
+ * Class: EmpiricalRenderer
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+
+/**
+ * A renderer that draws horizontal lines between points and/or draws shapes
+ * at each data point to provide an empirical style chart. This renderer is
+ * designed for use with the {XYPlot} class.
+ */
+public class EmpiricalRenderer extends XYLineAndShapeRenderer
+ implements XYItemRenderer, Cloneable, PublicCloneable
+{
+
+ /**
+ * Creates a new renderer.
+ */
+ public EmpiricalRenderer()
+ {
+ this(null, null);
+ }
+
+
+ /**
+ * Creates a new renderer with selected tool tip and url generators.
+ *
+ * @param toolTipGenerator Tool tip generator.
+ * @param urlGenerator Url generator.
+ */
+ public EmpiricalRenderer(XYToolTipGenerator toolTipGenerator, XYURLGenerator urlGenerator)
+ {
+ setBaseToolTipGenerator(toolTipGenerator);
+ setURLGenerator(urlGenerator);
+ setShapesFilled(true);
+ setShapesVisible(true);
+ }
+
+ /**
+ * Draws the visual representation of a single data item.
+ *
+ * @param g2 the graphics device.
+ * @param state the renderer state.
+ * @param dataArea the area within which the data is being drawn.
+ * @param info collects information about the drawing.
+ * @param plot the plot (can be used to obtain standard color
+ * information etc).
+ * @param domainAxis the domain axis.
+ * @param rangeAxis the range axis.
+ * @param dataset the dataset.
+ * @param series the series index (zero-based).
+ * @param item the item index (zero-based).
+ * @param crosshairState crosshair information for the plot
+ * (<code>null</code> permitted).
+ * @param pass the pass index.
+ */
+ public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea,
+ PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis,
+ XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass)
+ {
+
+ if (!getItemVisible(series, item))
+ return ;
+ PlotOrientation orientation = plot.getOrientation();
+ java.awt.Paint seriesPaint = getItemPaint(series, item);
+ java.awt.Stroke seriesStroke = getItemStroke(series, item);
+ g2.setPaint(seriesPaint);
+ g2.setStroke(seriesStroke);
+ double x0 = dataset.getXValue(series, item);
+ double y0 = dataset.getYValue(series, item);
+ if (java.lang.Double.isNaN(y0))
+ return ;
+ org.jfree.ui.RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
+ org.jfree.ui.RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
+ double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation);
+ double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation);
+
+ double x1 = 0, y1 = 0;
+ if (item < dataset.getItemCount(series) - 1) {
+ x1 = dataset.getXValue(series, item + 1);
+ y1 = dataset.getYValue(series, item + 1);
+ } else {
+ x1 = dataArea.getMaxX();
+ y1 = dataArea.getMaxY();
+ }
+
+ boolean useFillPaint = getUseFillPaint();
+ ;
+ boolean drawOutlines = getDrawOutlines();
+ if (!java.lang.Double.isNaN(y0)) {
+ double transX1;
+ double transY1;
+ if (item < dataset.getItemCount(series) - 1) {
+ transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
+ transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
+ } else {
+ transX1 = x1;
+ transY1 = y1;
+ }
+ Line2D line = state.workingLine;
+ if (orientation == PlotOrientation.HORIZONTAL) {
+ line.setLine(transY0, transX0, transY0, transX1);
+ g2.draw(line);
+ } else if (orientation == PlotOrientation.VERTICAL) {
+ line.setLine(transX0, transY0, transX1, transY0);
+ g2.draw(line);
+ }
+ }
+ if (getItemShapeVisible(series, item)) {
+ Shape shape = getItemShape(series, item);
+ if (orientation == PlotOrientation.HORIZONTAL)
+ shape = ShapeUtilities.createTranslatedShape(shape, transY0, transX0);
+ else if (orientation == PlotOrientation.VERTICAL)
+ shape = ShapeUtilities.createTranslatedShape(shape, transX0, transY0);
+ if (shape.intersects(dataArea)) {
+ if (getItemShapeFilled(series, item)) {
+ if (useFillPaint)
+ g2.setPaint(getItemFillPaint(series, item));
+ else
+ g2.setPaint(getItemPaint(series, item));
+ g2.fill(shape);
+ }
+ if (drawOutlines) {
+ if (getUseOutlinePaint())
+ g2.setPaint(getItemOutlinePaint(series, item));
+ else
+ g2.setPaint(getItemPaint(series, item));
+ g2.setStroke(getItemOutlineStroke(series, item));
+ g2.draw(shape);
+ }
+ }
+ }
+ if (isItemLabelVisible(series, item)) {
+ double xx = transX0;
+ double yy = transY0;
+ if (orientation == PlotOrientation.HORIZONTAL) {
+ xx = transY0;
+ yy = transX0;
+ }
+ drawItemLabel(g2, orientation, dataset, series, item, xx, yy, y0 < 0.0D);
+ }
+ int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
+ int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
+ updateCrosshairValues(crosshairState, x0, y0, domainAxisIndex, rangeAxisIndex, transX0, transY0, orientation);
+ if (state.getInfo() != null) {
+ EntityCollection entities = state.getEntityCollection();
+ if (entities != null) {
+ int r = getDefaultEntityRadius();
+ java.awt.Shape shape = orientation != PlotOrientation.VERTICAL ? ((java.awt.Shape) (new java.awt.geom.Rectangle2D.Double(transY0 - (double)r, transX0 - (double)r, 2 * r, 2 * r))) : ((java.awt.Shape) (new java.awt.geom.Rectangle2D.Double(transX0 - (double)r, transY0 - (double)r, 2 * r, 2 * r)));
+ if (shape != null) {
+ String tip = null;
+ XYToolTipGenerator generator = getToolTipGenerator(series, item);
+ if (generator != null)
+ tip = generator.generateToolTip(dataset, series, item);
+ String url = null;
+ if (getURLGenerator() != null)
+ url = getURLGenerator().generateURL(dataset, series, item);
+ XYItemEntity entity = new XYItemEntity(shape, dataset, series, item, tip, url);
+ entities.add(entity);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a clone of the renderer.
+ *
+ * @return A clone.
+ *
+ * @throws CloneNotSupportedException if the clone cannot be created.
+ */
+ public Object clone() throws CloneNotSupportedException
+ {
+ return super.clone();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/EmpiricalSeriesCollection.java b/source/umontreal/iro/lecuyer/charts/EmpiricalSeriesCollection.java
new file mode 100644
index 0000000..9fd69c8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/EmpiricalSeriesCollection.java
@@ -0,0 +1,443 @@
+
+
+/*
+ * Class: EmpiricalSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import umontreal.iro.lecuyer.stat.TallyStore;
+
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.xy.XYSeries;
+import cern.colt.list.DoubleArrayList;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.util.List;
+import java.util.ListIterator;
+import java.awt.Color;
+
+/**
+ * Stores data used in a <TT>EmpiricalChart</TT>. <TT>EmpiricalSeriesCollection</TT> provides complementary tools to draw
+ * empirical distribution charts, like add plots series.
+ * This class is linked with <TT>XYSeriesCollection</TT> class from JFreeChart
+ * to store data plots, and linked with JFreeChart <TT>EmpiricalRenderer</TT> to
+ * render the plot. <TT>EmpiricalRenderer</TT> has been developed at the
+ * Université de Montréal to extend the JFreeChart API, and is used to render
+ * charts with an empirical chart style in a JFreeChart chart.
+ *
+ */
+public class EmpiricalSeriesCollection extends SSJXYSeriesCollection {
+
+ // for the zero point: fix its x a little smaller than the first point;
+ // later when we know where x-axis begins, reset its x at the
+ // beginning of x-axis; its y is always 0.
+ private final double EMPIR_EPS = 0.015625;
+
+ private String[] marksType; //marks on points (+, x, *...)
+ private String[] dashPattern; //line dashing (solid, dotted, densely dotted, loosely dotted,
+ // dashed, densely dashed, loosely dashed)
+
+ private double setZeroPoint (double x1) {
+ // set temporary 0-point with x EPS smaller than x1
+ return x1 - EMPIR_EPS*Math.abs(x1);
+ }
+
+ /**
+ * Creates a new <TT>EmpiricalSeriesCollection</TT> instance with empty dataset.
+ *
+ */
+ public EmpiricalSeriesCollection() {
+ renderer = new EmpiricalRenderer();
+ seriesCollection = new XYSeriesCollection();
+ }
+
+
+ /**
+ * Creates a new <TT>EmpiricalSeriesCollection</TT> instance with default
+ * parameters and given data series. Each input parameter represents an
+ * observation set. The values of this observation set <SPAN CLASS="textit">must be
+ * sorted</SPAN> in increasing order.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public EmpiricalSeriesCollection (double[]... data) {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ for (int j = 0; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(data[j][0]), 0); // correct x-value of 0-point will be set later
+ for (int k = 0; k < data[j].length; k++)
+ serie.add(data[j][k], (double)(k + 1)/data[j].length);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ /*set default colors*/
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ /* set default plot style*/
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>EmpiricalSeriesCollection</TT> instance with default
+ * parameters and a given series <TT>data</TT>. The values of <TT>data</TT>
+ * <SPAN CLASS="textit">must be sorted</SPAN> in increasing order. However, only <SPAN CLASS="textit">the first</SPAN>
+ * <TT>numPoints</TT> of <TT>data</TT> will be considered for the series.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public EmpiricalSeriesCollection (double[] data, int numPoints) {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(data[0]), 0); // correct x-value of zero point will be set later
+ for (int k = 0; k < numPoints; k++)
+ serie.add(data[k], (double)(k + 1) / numPoints);
+ tempSeriesCollection.addSeries(serie);
+
+ // set default colors
+ renderer.setSeriesPaint(0, getDefaultColor(0));
+
+ // set default plot style
+ marksType = new String[1];
+ dashPattern = new String[1];
+ marksType[0] = "*";
+ dashPattern[0] = "solid";
+ }
+
+
+ /**
+ * Creates a new <TT>EmpiricalSeriesCollection</TT> instance with default
+ * parameters and given data.
+ * The input parameter represents a collection of data observation sets.
+ * Each <TT>DoubleArrayList</TT>
+ * input parameter represents an observation set. The values of this
+ * observation set <SPAN CLASS="textit">must be sorted in increasing order</SPAN>.
+ * Each {@link cern.colt.list.DoubleArrayList DoubleArrayList} variable corresponds
+ * to an observation set.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public EmpiricalSeriesCollection (DoubleArrayList... data) {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+
+ for (int j = 0; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(data[j].get(0)), 0); // correct x-value of zero point will be set later
+ for (int k = 0; k < data[j].size(); k++)
+ serie.add(data[j].get(k), (double)(k + 1)/data[j].size());
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ /*set default colors*/
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ /* set default plot style*/
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>EmpiricalSeriesCollection</TT> instance with default
+ * parameters and given data. The input parameter represents a collection of data
+ * observation sets. Each <TT>TallyStore</TT> input parameter represents an
+ * observation set.
+ *
+ * @param tallies series of point sets.
+ *
+ *
+ */
+ public EmpiricalSeriesCollection (TallyStore... tallies) {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+
+ for (int j = 0; j < tallies.length; j++) {
+ TallyStore temp = tallies[j];
+ temp.quickSort();
+ double[] array = temp.getArray();
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(array[0]), 0); // correct x-value of zero point will be set later
+ for (int k = 0; k < tallies[j].numberObs(); k++)
+ serie.add(array[k], (double)(k + 1)/tallies[j].numberObs());
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ /*set default colors*/
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ /* set default plot style*/
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>EmpiricalSeriesCollection</TT> instance with default parameters and given data series.
+ * The input parameter represents a set of plotting data.
+ * Each series of the given collection corresponds to a plot on the chart.
+ *
+ * @param data series of point sets.
+ *
+ */
+ public EmpiricalSeriesCollection (XYSeriesCollection data) {
+ renderer = new EmpiricalRenderer();
+ seriesCollection = data;
+
+ /*set default colors*/
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ XYSeries ser = data.getSeries(i);
+ ser.add(setZeroPoint(ser.getX(0).doubleValue()), 0); // add zero point; will set its correct x-value later
+ }
+
+ /* set default plot style*/
+ marksType = new String[data.getSeriesCount()];
+ dashPattern = new String[data.getSeriesCount()];
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Adds a data series into the series collection.
+ *
+ * @param observationSet new series values.
+ *
+ * @return Integer that represent the new point set's position in the <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] observationSet) {
+ return add(observationSet, observationSet.length);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Only <SPAN CLASS="textit">the first</SPAN>
+ * <TT>numPoints</TT> of <TT>observationSet</TT>
+ * will be added to the new series.
+ *
+ * @param observationSet new series values.
+ *
+ * @param numPoints number of points to add.
+ *
+ * @return Integer that represent the new point set's position in the <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] observationSet, int numPoints) {
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(observationSet[0]), 0); // coordinates of first point will be reset later
+ for (int k = 0; k < numPoints; k++)
+ serie.add(observationSet[k], (double)(k + 1)/numPoints);
+ tempSeriesCollection.addSeries(serie);
+
+ // color
+ int j = seriesCollection.getSeriesCount()-1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ String[] newMarksType = new String[seriesCollection.getSeriesCount()];
+ String[] newDashPattern = new String[seriesCollection.getSeriesCount()];
+ for (j = 0; j < seriesCollection.getSeriesCount()-1; j++) {
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ newMarksType[j] = "*";
+ newDashPattern[j] = "solid";
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return seriesCollection.getSeriesCount()-1;
+ }
+
+
+ /**
+ * Adds a data series into the series collection.
+ *
+ * @param observationSet new series values.
+ *
+ * @return Integer that represent the new point set's position in the <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (DoubleArrayList observationSet) {
+ return add(observationSet.elements(), observationSet.size());
+ }
+
+
+ /**
+ * Adds a data series into the series collection.
+ *
+ * @param tally {@link umontreal.iro.lecuyer.stat.TallyStore TallyStore} to add values.
+ *
+ * @return Integer that represent the new point set's position in the <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (TallyStore tally) {
+ tally.quickSort();
+ return add(tally.getArray(), tally.numberObs());
+ }
+
+
+ /**
+ * Returns the mark type associated with the <TT>series</TT>-th data series.
+ *
+ * @param series series index.
+ *
+ * @return mark type.
+ *
+ */
+ public String getMarksType (int series) {
+ return marksType[series];
+ }
+
+
+ /**
+ * Adds marks on points to a data series.
+ * It is possible to use all the marks provided by the TikZ package,
+ * some of which are <TT>*</TT>, <TT>+</TT> and <TT>x</TT>.
+ * A blank character, used by default, will disable marks. The PGF/TikZ
+ * documentation provides more information about placing marks on plots.
+ *
+ * @param series series index.
+ *
+ * @param marksType mark type.
+ *
+ *
+ */
+ public void setMarksType (int series, String marksType) {
+ this.marksType[series] = marksType;
+ }
+
+
+ /**
+ * Returns the dash pattern associated with the <TT>series</TT>-th data series.
+ *
+ * @param series series index.
+ *
+ * @return dash style.
+ *
+ */
+ public String getDashPattern (int series) {
+ return dashPattern[series];
+ }
+
+
+ /**
+ * Selects dash pattern for a data series.
+ * It is possible to use all the dash options provided by
+ * the TikZ package: solid, dotted, densely dotted,
+ * loosely dotted, dashed, densely dashed and loosely dashed.
+ *
+ * @param series series index.
+ *
+ * @param dashPattern dash style.
+ *
+ *
+ */
+ public void setDashPattern (int series, String dashPattern) {
+ this.dashPattern[series] = dashPattern;
+ }
+
+
+ public String toLatex (double XScale, double YScale,
+ double XShift, double YShift,
+ double xmin, double xmax,
+ double ymin, double ymax) {
+
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+ xmin = Math.min(XShift, xmin);
+ xmax = Math.max(XShift, xmax);
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ Formatter formatter = new Formatter(Locale.US);
+ double var;
+ for (int i = tempSeriesCollection.getSeriesCount()-1; i >= 0; i--) {
+
+ Color color = (Color)renderer.getSeriesPaint(i);
+ String colorString = detectXColorClassic(color);
+ if (colorString == null) {
+ colorString = "color"+i;
+ formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+ colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
+ }
+
+ double currentX, currentY, nextX, nextY;
+ for (int j = 0; j < tempSeriesCollection.getItemCount(i); j++) {
+ currentX = tempSeriesCollection.getXValue(i, j);
+ if (j == tempSeriesCollection.getItemCount(i)-1)
+ nextX = xmax;
+ else
+ nextX = Math.min(tempSeriesCollection.getXValue(i, j+1), xmax);
+
+ if ((currentX >= xmin && currentX <= xmax) )
+ {
+ formatter.format("\\draw [color=%s] plot[mark=%s] (%.4f, %.4f) --plot[style=%s] (%.4f, %.4f); %%%n",
+ colorString, marksType[i],
+ (currentX-XShift)*XScale, (tempSeriesCollection.getYValue(i, j)-YShift)*YScale,
+ dashPattern[i],
+ (nextX-XShift)*XScale, (tempSeriesCollection.getYValue(i, j)-YShift)*YScale);
+ }
+ }
+ }
+ return formatter.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/EmpiricalSeriesCollection.tex b/source/umontreal/iro/lecuyer/charts/EmpiricalSeriesCollection.tex
new file mode 100644
index 0000000..81aad79
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/EmpiricalSeriesCollection.tex
@@ -0,0 +1,458 @@
+\defmodule {EmpiricalSeriesCollection}
+
+Stores data used in a \texttt{EmpiricalChart}. % This class extends
+%\externalclass{umontreal.iro.lecuyer.charts}{SSJXYSeriesCollection}.
+\texttt{EmpiricalSeriesCollection} provides complementary tools to draw
+empirical distribution charts, like add plots series.
+This class is linked with \texttt{XYSeriesCollection} class from JFreeChart
+to store data plots, and linked with JFreeChart \texttt{EmpiricalRenderer} to
+render the plot. \texttt{EmpiricalRenderer} has been developed at the
+Universit\'e de Montr\'eal to extend the JFreeChart API, and is used to render
+charts with an empirical chart style in a JFreeChart chart.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: EmpiricalSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import umontreal.iro.lecuyer.stat.TallyStore;
+
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.xy.XYSeries;
+import cern.colt.list.DoubleArrayList;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.util.List;
+import java.util.ListIterator;
+import java.awt.Color;\end{hide}
+
+public class EmpiricalSeriesCollection extends SSJXYSeriesCollection \begin{hide} {
+
+ // for the zero point: fix its x a little smaller than the first point;
+ // later when we know where x-axis begins, reset its x at the
+ // beginning of x-axis; its y is always 0.
+ private final double EMPIR_EPS = 0.015625;
+
+ private String[] marksType; //marks on points (+, x, *...)
+ private String[] dashPattern; //line dashing (solid, dotted, densely dotted, loosely dotted,
+ // dashed, densely dashed, loosely dashed)
+
+ private double setZeroPoint (double x1) {
+ // set temporary 0-point with x EPS smaller than x1
+ return x1 - EMPIR_EPS*Math.abs(x1);
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+ public EmpiricalSeriesCollection() \begin{hide} {
+ renderer = new EmpiricalRenderer();
+ seriesCollection = new XYSeriesCollection();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{EmpiricalSeriesCollection} instance with empty dataset.
+\end{tabb}
+\begin{code}
+
+ public EmpiricalSeriesCollection (double[]... data) \begin{hide} {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ for (int j = 0; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(data[j][0]), 0); // correct x-value of 0-point will be set later
+ for (int k = 0; k < data[j].length; k++)
+ serie.add(data[j][k], (double)(k + 1)/data[j].length);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ /*set default colors*/
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ /* set default plot style*/
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{EmpiricalSeriesCollection} instance with default
+ parameters and given data series. Each input parameter represents an
+ observation set. The values of this observation set \emph{must be
+ sorted} in increasing order.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalSeriesCollection (double[] data, int numPoints) \begin{hide} {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(data[0]), 0); // correct x-value of zero point will be set later
+ for (int k = 0; k < numPoints; k++)
+ serie.add(data[k], (double)(k + 1) / numPoints);
+ tempSeriesCollection.addSeries(serie);
+
+ // set default colors
+ renderer.setSeriesPaint(0, getDefaultColor(0));
+
+ // set default plot style
+ marksType = new String[1];
+ dashPattern = new String[1];
+ marksType[0] = "*";
+ dashPattern[0] = "solid";
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{EmpiricalSeriesCollection} instance with default
+ parameters and a given series \texttt{data}. The values of \texttt{data}
+ \emph{must be sorted} in increasing order. However, only \emph{the first}
+ \texttt{numPoints} of \texttt{data} will be considered for the series.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalSeriesCollection (DoubleArrayList... data) \begin{hide} {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+
+ for (int j = 0; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(data[j].get(0)), 0); // correct x-value of zero point will be set later
+ for (int k = 0; k < data[j].size(); k++)
+ serie.add(data[j].get(k), (double)(k + 1)/data[j].size());
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ /*set default colors*/
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ /* set default plot style*/
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{EmpiricalSeriesCollection} instance with default
+parameters and given data.
+ The input parameter represents a collection of data observation sets.
+ Each \texttt{Double\-Array\-List}
+ input parameter represents an observation set. The values of this
+ observation set \textit{must be sorted in increasing order}.
+ Each \externalclass{cern.colt.list}{DoubleArrayList} variable corresponds
+ to an observation set.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalSeriesCollection (TallyStore... tallies) \begin{hide} {
+ seriesCollection = new XYSeriesCollection();
+ renderer = new EmpiricalRenderer();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+
+ for (int j = 0; j < tallies.length; j++) {
+ TallyStore temp = tallies[j];
+ temp.quickSort();
+ double[] array = temp.getArray();
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(array[0]), 0); // correct x-value of zero point will be set later
+ for (int k = 0; k < tallies[j].numberObs(); k++)
+ serie.add(array[k], (double)(k + 1)/tallies[j].numberObs());
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ /*set default colors*/
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ /* set default plot style*/
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{EmpiricalSeriesCollection} instance with default
+ parameters and given data. The input parameter represents a collection of data
+ observation sets. Each \texttt{TallyStore} input parameter represents an
+ observation set.
+\end{tabb}
+\begin{htmlonly}
+ \param{tallies}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public EmpiricalSeriesCollection (XYSeriesCollection data) \begin{hide} {
+ renderer = new EmpiricalRenderer();
+ seriesCollection = data;
+
+ /*set default colors*/
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ XYSeries ser = data.getSeries(i);
+ ser.add(setZeroPoint(ser.getX(0).doubleValue()), 0); // add zero point; will set its correct x-value later
+ }
+
+ /* set default plot style*/
+ marksType = new String[data.getSeriesCount()];
+ dashPattern = new String[data.getSeriesCount()];
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ marksType[i] = "*";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{EmpiricalSeriesCollection} instance with default parameters and given data series.
+ The input parameter represents a set of plotting data.
+ Each series of the given collection corresponds to a plot on the chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data control methods}
+
+\begin{code}
+
+ public int add (double[] observationSet) \begin{hide} {
+ return add(observationSet, observationSet.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection.
+\end{tabb}
+\begin{htmlonly}
+ \param{observationSet}{new series values.}
+ \return{Integer that represent the new point set's position in the \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] observationSet, int numPoints) \begin{hide} {
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+
+ XYSeries serie = new XYSeries(" ");
+ serie.add(setZeroPoint(observationSet[0]), 0); // coordinates of first point will be reset later
+ for (int k = 0; k < numPoints; k++)
+ serie.add(observationSet[k], (double)(k + 1)/numPoints);
+ tempSeriesCollection.addSeries(serie);
+
+ // color
+ int j = seriesCollection.getSeriesCount()-1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ String[] newMarksType = new String[seriesCollection.getSeriesCount()];
+ String[] newDashPattern = new String[seriesCollection.getSeriesCount()];
+ for (j = 0; j < seriesCollection.getSeriesCount()-1; j++) {
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ newMarksType[j] = "*";
+ newDashPattern[j] = "solid";
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return seriesCollection.getSeriesCount()-1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Only \emph{the first}
+ \texttt{numPoints} of \texttt{observationSet}
+ will be added to the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{observationSet}{new series values.}
+ \param{numPoints}{number of points to add.}
+ \return{Integer that represent the new point set's position in the \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (DoubleArrayList observationSet) \begin{hide} {
+ return add(observationSet.elements(), observationSet.size());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection.
+\end{tabb}
+\begin{htmlonly}
+ \param{observationSet}{new series values.}
+ \return{Integer that represent the new point set's position in the \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (TallyStore tally) \begin{hide} {
+ tally.quickSort();
+ return add(tally.getArray(), tally.numberObs());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection.
+\end{tabb}
+\begin{htmlonly}
+ \param{tally}{\externalclass{umontreal.iro.lecuyer.stat}{TallyStore} to add values.}
+ \return{Integer that represent the new point set's position in the \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Rendering methods}
+
+\begin{code}
+
+ public String getMarksType (int series) \begin{hide} {
+ return marksType[series];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the mark type associated with the \texttt{series}-th data series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{mark type.}
+\end{htmlonly}
+\begin{code}
+
+ public void setMarksType (int series, String marksType) \begin{hide} {
+ this.marksType[series] = marksType;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds marks on points to a data series.
+ It is possible to use all the marks provided by the TikZ package,
+ some of which are \texttt{*}, \texttt{+} and \texttt{x}.
+ A blank character, used by default, will disable marks. The PGF/TikZ
+ documentation provides more information about placing marks on plots.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{marksType}{mark type.}
+\end{htmlonly}
+\begin{code}
+
+ public String getDashPattern (int series) \begin{hide} {
+ return dashPattern[series];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dash pattern associated with the \texttt{series}-th data series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{dash style.}
+\end{htmlonly}
+\begin{code}
+
+ public void setDashPattern (int series, String dashPattern) \begin{hide} {
+ this.dashPattern[series] = dashPattern;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Selects dash pattern for a data series.
+ It is possible to use all the dash options provided by
+ the TikZ package: solid, dotted, densely dotted,
+ loosely dotted, dashed, densely dashed and loosely dashed.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{dashPattern}{dash style.}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatex (double XScale, double YScale,
+ double XShift, double YShift,
+ double xmin, double xmax,
+ double ymin, double ymax) \begin{hide} {
+
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+ xmin = Math.min(XShift, xmin);
+ xmax = Math.max(XShift, xmax);
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ Formatter formatter = new Formatter(Locale.US);
+ double var;
+ for (int i = tempSeriesCollection.getSeriesCount()-1; i >= 0; i--) {
+
+ Color color = (Color)renderer.getSeriesPaint(i);
+ String colorString = detectXColorClassic(color);
+ if (colorString == null) {
+ colorString = "color"+i;
+ formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+ colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
+ }
+
+ double currentX, currentY, nextX, nextY;
+ for (int j = 0; j < tempSeriesCollection.getItemCount(i); j++) {
+ currentX = tempSeriesCollection.getXValue(i, j);
+ if (j == tempSeriesCollection.getItemCount(i)-1)
+ nextX = xmax;
+ else
+ nextX = Math.min(tempSeriesCollection.getXValue(i, j+1), xmax);
+
+ if ((currentX >= xmin && currentX <= xmax) )
+ {
+ formatter.format("\\draw [color=%s] plot[mark=%s] (%.4f, %.4f) --plot[style=%s] (%.4f, %.4f); %%%n",
+ colorString, marksType[i],
+ (currentX-XShift)*XScale, (tempSeriesCollection.getYValue(i, j)-YShift)*YScale,
+ dashPattern[i],
+ (nextX-XShift)*XScale, (tempSeriesCollection.getYValue(i, j)-YShift)*YScale);
+ }
+ }
+ }
+ return formatter.toString();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/HistogramChart.java b/source/umontreal/iro/lecuyer/charts/HistogramChart.java
new file mode 100644
index 0000000..17b9897
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/HistogramChart.java
@@ -0,0 +1,472 @@
+
+
+/*
+ * Class: HistogramChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import umontreal.iro.lecuyer.stat.*;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.data.statistics.HistogramBin;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Formatter;
+import javax.swing.JFrame;
+
+/**
+ * This class provides tools to create and manage histograms.
+ * The {@link HistogramChart} class is the simplest way to produce histograms.
+ * Each {@link HistogramChart} object is linked with an
+ * {@link umontreal.iro.lecuyer.charts.HistogramSeriesCollection HistogramSeriesCollection} dataset.
+ *
+ */
+public class HistogramChart extends XYChart {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tooltips
+ false // urls
+ );
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ //Initialize axis variables
+ XAxis = new Axis( (NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL );
+ YAxis = new Axis( (NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis() ,
+ Axis.ORIENTATION_VERTICAL );
+ setAutoRange(false, true, true, true);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with an empty data set.
+ *
+ */
+ public HistogramChart () {
+ super();
+ dataset = new HistogramSeriesCollection();
+ init (null, null, null);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with input <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input parameter <TT>data</TT> represents a collection of observation sets.
+ * Therefore <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 0,…, <I>n</I> - 1</SPAN>, is used to plot the
+ * <SPAN CLASS="MATH"><I>i</I></SPAN>th histogram.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public HistogramChart (String title, String XLabel, String YLabel,
+ double[]... data) {
+ super();
+ dataset = new HistogramSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with input <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input parameter <TT>data</TT> represents an observation set.
+ * Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT> will
+ * be considered to plot the histogram.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public HistogramChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) {
+ super();
+ double[] datan = new double[numPoints];
+ System.arraycopy (data, 0, datan, 0, numPoints);
+ dataset = new HistogramSeriesCollection(datan);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with data <TT>data</TT>.
+ * Each <TT>DoubleArrayList</TT> input parameter represents a collection of
+ * observation sets.
+ * {@link cern.colt.list.DoubleArrayList DoubleArrayList} is from the Colt library
+ * and is used to store data.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of observation sets.
+ *
+ *
+ */
+ public HistogramChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) {
+ super();
+ dataset = new HistogramSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with data arrays contained in each
+ * {@link umontreal.iro.lecuyer.stat.TallyStore TallyStore} object.
+ * The input parameter <TT>tallies</TT> represents a collection of observation sets.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param tallies series of observation sets.
+ *
+ *
+ */
+ public HistogramChart (String title, String XLabel, String YLabel,
+ TallyStore... tallies) {
+ super();
+ dataset = new HistogramSeriesCollection(tallies);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with data <TT>data</TT>.
+ * The input parameter <TT>data</TT> represents a set of plotting data.
+ * {@link umontreal.iro.lecuyer.charts.CustomHistogramDataset CustomHistogramDataset} is a
+ * <TT>JFreeChart</TT>-like container class that stores and manages
+ * observation sets.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series collection.
+ *
+ *
+ */
+ public HistogramChart (String title, String XLabel, String YLabel,
+ CustomHistogramDataset data) {
+ super();
+ dataset = new HistogramSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with data <TT>count</TT>
+ * and <TT>bound</TT>. The adjacent categories (or bins) are specified as
+ * non-overlapping intervals: bin[j] contains the values in the interval
+ * [<TT>bound[j]</TT>, <TT>bound[j+1]</TT>], and <TT>count[j]</TT> is the
+ * number of such values. Thus the length of <TT>bound</TT> must be equal to
+ * the length of <TT>count</TT> plus one: the last value of <TT>bound</TT>
+ * is the right boundary of the last bin.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param count the number of observation between each bound.
+ *
+ * @param bound the bounds of the observations
+ *
+ *
+ */
+ public HistogramChart (String title, String XLabel, String YLabel,
+ int[] count, double[] bound) {
+ super();
+ if (bound.length != count.length + 1)
+ throw new IllegalArgumentException (
+ "bound.length must be equal to count.length + 1");
+ final int nb = count.length;
+ int sum = 0;
+ for (int i = 0 ; i < nb; i++) sum +=count[i];
+ double[] data = new double [sum];
+
+ int k = 0;
+ double h;
+ for (int i = 0 ; i < nb; i++) {
+ h = bound[i + 1] - bound[i];
+ if (count[i] > 0)
+ h /= count[i];
+ if (i == nb - 1) {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = bound[i + 1] - j*h;
+ } else {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = bound[i] + j*h;
+ }
+ }
+
+ dataset = new HistogramSeriesCollection(data, sum);
+ init (title, XLabel, YLabel);
+ ((HistogramSeriesCollection) dataset).setBins(0, nb);
+ }
+
+
+ /**
+ * Initializes a new <TT>HistogramChart</TT> instance with data arrays
+ * contained in each
+ * {@link umontreal.iro.lecuyer.stat.TallyHistogram TallyHistogram} object.
+ * The input parameter <TT>tallies</TT> represents a collection
+ * of observation sets. The 2 extra bins at the beginning and at the end of the
+ * tallies are not counted nor represented in the chart.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param tallies series of observation sets.
+ *
+ */
+ public HistogramChart (String title, String XLabel, String YLabel,
+ TallyHistogram... tallies) {
+ super();
+ dataset = new HistogramSeriesCollection(tallies);
+ init (title, XLabel, YLabel);
+ }
+
+
+ public void setAutoRange (boolean right, boolean top) {
+ throw new UnsupportedOperationException(
+ "You can't use setAutoRange with HistogramChart class, use setAutoRange().");
+ }
+ public void setManuelRange (double [] range, boolean right, boolean top) {
+ throw new UnsupportedOperationException(
+ "You can't use setManuelRange with HistogramChart class, use setManuelRange(range).");
+ }
+
+
+ /**
+ * Returns the chart's dataset.
+ *
+ * @return the chart's dataset.
+ *
+ */
+ public HistogramSeriesCollection getSeriesCollection() {
+ return (HistogramSeriesCollection)dataset;
+ }
+
+
+ /**
+ * Links a new dataset to the current chart.
+ *
+ * @param dataset new dataset.
+ *
+ *
+ */
+ public void setSeriesCollection (HistogramSeriesCollection dataset) {
+ this.dataset = dataset;
+ }
+
+
+ /**
+ * Synchronizes <SPAN CLASS="MATH"><I>x</I></SPAN>-axis ticks to the <SPAN CLASS="MATH"><I>s</I></SPAN>-th histogram bins if the number
+ * of bins is not larger than 10;
+ * otherwise, choose approximately 10 ticks.
+ *
+ * @param s selects histogram used to define ticks.
+ *
+ *
+ */
+ public void setTicksSynchro (int s) {
+ if (((CustomHistogramDataset)this.dataset.getSeriesCollection()).getBinWidth(s) == -1){
+ DoubleArrayList newTicks = new DoubleArrayList();
+ ListIterator binsIter = ((HistogramSeriesCollection)this.dataset).getBins(s).listIterator();
+
+ int i = 0;
+ HistogramBin prec = (HistogramBin)binsIter.next();
+ double var;
+ newTicks.add(prec.getStartBoundary());
+ newTicks.add(var = prec.getEndBoundary());
+ HistogramBin temp;
+ while(binsIter.hasNext()) {
+ temp = (HistogramBin)binsIter.next();
+ if(temp.getStartBoundary() != var) {
+ newTicks.add(var = temp.getStartBoundary());
+ } else if(temp.getEndBoundary() != var) {
+ newTicks.add(var = temp.getEndBoundary());
+ }
+ }
+ XAxis.setLabels(newTicks.elements());
+ }
+ else {
+ // set a label-tick for each bin, if num bins is <= 10
+ int n = ((HistogramSeriesCollection)this.dataset).getBins(s).size();
+ if (n > 10) {
+ // number of bins is too large, set ~10 labels-ticks for histogram
+ n = 10;
+ double[] B = ((HistogramSeriesCollection)this.dataset).getDomainBounds();
+ double w = (B[1] - B[0]) / n;
+ XAxis.setLabels(w);
+ } else {
+ XAxis.setLabels(((CustomHistogramDataset)this.dataset.getSeriesCollection()).getBinWidth(s));
+ }
+ }
+ }
+
+
+ /**
+ * Displays chart on the screen using Swing.
+ * This method creates an application containing a chart panel displaying
+ * the chart. The created frame is positioned on-screen, and displayed before
+ * it is returned. The <TT>width</TT> and the <TT>height</TT>
+ * of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @return frame containing the chart.
+ *
+ */
+ public JFrame view (int width, int height) {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame("HistogramChart from SSJ: " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("HistogramChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }
+
+
+ public String toLatex (double width, double height) {
+ double xunit, yunit;
+ double[] save = new double[4];
+
+ if (dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+ if (YAxis.getTwinAxisPosition() < 0)
+ YAxis.setTwinAxisPosition(0);
+
+ // Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ // taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ xunit = width / ( (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale) );
+ // taille d'une unite en y et en cm dans l'objet "tikzpicture"
+ yunit = height / ( (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale) );
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if (chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ: %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if (grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(
+ XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/HistogramChart.tex b/source/umontreal/iro/lecuyer/charts/HistogramChart.tex
new file mode 100644
index 0000000..1a6fcb3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/HistogramChart.tex
@@ -0,0 +1,470 @@
+\defmodule {HistogramChart}
+
+% Extends \externalclass{umontreal.iro.lecuyer.charts}{XYChart}.
+This class provides tools to create and manage histograms.
+The \class{HistogramChart} class is the simplest way to produce histograms.
+Each \class{HistogramChart} object is linked with an
+\externalclass{umontreal.iro.lecuyer.charts}{HistogramSeriesCollection} dataset.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: HistogramChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import umontreal.iro.lecuyer.stat.*;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.data.statistics.HistogramBin;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Formatter;
+import javax.swing.JFrame;\end{hide}
+
+public class HistogramChart extends XYChart \begin{hide} {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tooltips
+ false // urls
+ );
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ //Initialize axis variables
+ XAxis = new Axis( (NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL );
+ YAxis = new Axis( (NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis() ,
+ Axis.ORIENTATION_VERTICAL );
+ setAutoRange(false, true, true, true);
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+ public HistogramChart () \begin{hide} {
+ super();
+ dataset = new HistogramSeriesCollection();
+ init (null, null, null);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with an empty data set.
+\end{tabb}
+\begin{code}
+
+ public HistogramChart (String title, String XLabel, String YLabel,
+ double[]... data) \begin{hide} {
+ super();
+ dataset = new HistogramSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with input \texttt{data}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The input parameter \texttt{data} represents a collection of observation sets.
+ Therefore \texttt{data}$[i], i = 0,\ldots,n-1$, is used to plot the
+ $i$th histogram.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) \begin{hide} {
+ super();
+ double[] datan = new double[numPoints];
+ System.arraycopy (data, 0, datan, 0, numPoints);
+ dataset = new HistogramSeriesCollection(datan);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with input \texttt{data}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The input parameter \texttt{data} represents an observation set.
+ Only \emph{the first} \texttt{numPoints} of \texttt{data} will
+ be considered to plot the histogram.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) \begin{hide} {
+ super();
+ dataset = new HistogramSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with data \texttt{data}.
+ Each \texttt{DoubleArrayList} input parameter represents a collection of
+ observation sets.
+ \externalclass{cern.colt.list}{DoubleArrayList} is from the Colt library
+ and is used to store data.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of observation sets.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramChart (String title, String XLabel, String YLabel,
+ TallyStore... tallies) \begin{hide} {
+ super();
+ dataset = new HistogramSeriesCollection(tallies);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with data arrays contained in each
+ \externalclass{umontreal.iro.lecuyer.stat}{TallyStore} object.
+ The input parameter \texttt{tallies} represents a collection of observation sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{tallies}{series of observation sets.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramChart (String title, String XLabel, String YLabel,
+ CustomHistogramDataset data) \begin{hide} {
+ super();
+ dataset = new HistogramSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with data \texttt{data}.
+ The input parameter \texttt{data} represents a set of plotting data.
+ \externalclass{umontreal.iro.lecuyer.charts}{CustomHistogramDataset} is a
+ \texttt{JFreeChart}-like container class that stores and manages
+ observation sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series collection.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramChart (String title, String XLabel, String YLabel,
+ int[] count, double[] bound) \begin{hide} {
+ super();
+ if (bound.length != count.length + 1)
+ throw new IllegalArgumentException (
+ "bound.length must be equal to count.length + 1");
+ final int nb = count.length;
+ int sum = 0;
+ for (int i = 0 ; i < nb; i++) sum +=count[i];
+ double[] data = new double [sum];
+
+ int k = 0;
+ double h;
+ for (int i = 0 ; i < nb; i++) {
+ h = bound[i + 1] - bound[i];
+ if (count[i] > 0)
+ h /= count[i];
+ if (i == nb - 1) {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = bound[i + 1] - j*h;
+ } else {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = bound[i] + j*h;
+ }
+ }
+
+ dataset = new HistogramSeriesCollection(data, sum);
+ init (title, XLabel, YLabel);
+ ((HistogramSeriesCollection) dataset).setBins(0, nb);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with data \texttt{count}
+ and \texttt{bound}. The adjacent categories (or bins) are specified as
+ non-overlapping intervals: bin[j] contains the values in the interval
+ [\texttt{bound[j]}, \texttt{bound[j+1]}], and \texttt{count[j]} is the
+ number of such values. % \texttt{bound}$_{j+1}$ is the right boundary of
+ % bin$_j$ and also the left boundary of bin$_{j+1}$.
+ Thus the length of \texttt{bound} must be equal to
+ the length of \texttt{count} plus one: the last value of \texttt{bound}
+ is the right boundary of the last bin.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{count}{the number of observation between each bound.}
+ \param{bound}{the bounds of the observations}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramChart (String title, String XLabel, String YLabel,
+ TallyHistogram... tallies) \begin{hide} {
+ super();
+ dataset = new HistogramSeriesCollection(tallies);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{HistogramChart} instance with data arrays
+ contained in each
+ \externalclass{umontreal.iro.lecuyer.stat}{TallyHistogram} object.
+ The input parameter \texttt{tallies} represents a collection
+ of observation sets. The 2 extra bins at the beginning and at the end of the
+ tallies are not counted nor represented in the chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{tallies}{series of observation sets.}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+\begin{hide}
+ public void setAutoRange (boolean right, boolean top) {
+ throw new UnsupportedOperationException(
+ "You can't use setAutoRange with HistogramChart class, use setAutoRange().");
+ }
+ public void setManuelRange (double [] range, boolean right, boolean top) {
+ throw new UnsupportedOperationException(
+ "You can't use setManuelRange with HistogramChart class, use setManuelRange(range).");
+ }
+\end{hide}
+
+ public HistogramSeriesCollection getSeriesCollection() \begin{hide} {
+ return (HistogramSeriesCollection)dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's dataset.
+\end{tabb}
+\begin{htmlonly}
+ \return{the chart's dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeriesCollection (HistogramSeriesCollection dataset) \begin{hide} {
+ this.dataset = dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Links a new dataset to the current chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{dataset}{new dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTicksSynchro (int s) \begin{hide} {
+ if (((CustomHistogramDataset)this.dataset.getSeriesCollection()).getBinWidth(s) == -1){
+ DoubleArrayList newTicks = new DoubleArrayList();
+ ListIterator binsIter = ((HistogramSeriesCollection)this.dataset).getBins(s).listIterator();
+
+ int i = 0;
+ HistogramBin prec = (HistogramBin)binsIter.next();
+ double var;
+ newTicks.add(prec.getStartBoundary());
+ newTicks.add(var = prec.getEndBoundary());
+ HistogramBin temp;
+ while(binsIter.hasNext()) {
+ temp = (HistogramBin)binsIter.next();
+ if(temp.getStartBoundary() != var) {
+ newTicks.add(var = temp.getStartBoundary());
+ } else if(temp.getEndBoundary() != var) {
+ newTicks.add(var = temp.getEndBoundary());
+ }
+ }
+ XAxis.setLabels(newTicks.elements());
+ }
+ else {
+ // set a label-tick for each bin, if num bins is <= 10
+ int n = ((HistogramSeriesCollection)this.dataset).getBins(s).size();
+ if (n > 10) {
+ // number of bins is too large, set ~10 labels-ticks for histogram
+ n = 10;
+ double[] B = ((HistogramSeriesCollection)this.dataset).getDomainBounds();
+ double w = (B[1] - B[0]) / n;
+ XAxis.setLabels(w);
+ } else {
+ XAxis.setLabels(((CustomHistogramDataset)this.dataset.getSeriesCollection()).getBinWidth(s));
+ }
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Synchronizes $x$-axis ticks to the $s$-th histogram bins if the number
+ of bins is not larger than 10;
+ otherwise, choose approximately 10 ticks.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{selects histogram used to define ticks.}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame view (int width, int height) \begin{hide} {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame("HistogramChart from SSJ: " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("HistogramChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays chart on the screen using Swing.
+ This method creates an application containing a chart panel displaying
+ the chart. The created frame is positioned on-screen, and displayed before
+ it is returned. The \texttt{width} and the \texttt{height}
+ of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \return{frame containing the chart.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{\LaTeX-specific method}
+
+\begin{code}
+
+ public String toLatex (double width, double height) \begin{hide} {
+ double xunit, yunit;
+ double[] save = new double[4];
+
+ if (dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+ if (YAxis.getTwinAxisPosition() < 0)
+ YAxis.setTwinAxisPosition(0);
+
+ // Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ // taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ xunit = width / ( (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale) );
+ // taille d'une unite en y et en cm dans l'objet "tikzpicture"
+ yunit = height / ( (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale) );
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if (chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ: %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if (grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(
+ XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/HistogramSeriesCollection.java b/source/umontreal/iro/lecuyer/charts/HistogramSeriesCollection.java
new file mode 100644
index 0000000..9edf8b1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/HistogramSeriesCollection.java
@@ -0,0 +1,686 @@
+
+
+/*
+ * Class: HistogramSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import umontreal.iro.lecuyer.stat.*;
+import umontreal.iro.lecuyer.util.Num;
+
+import org.jfree.chart.renderer.xy.XYBarRenderer;
+import org.jfree.data.statistics.HistogramBin;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.util.List;
+import java.util.ListIterator;
+import java.awt.Color;
+
+/**
+ * Stores data used in a <TT>HistogramChart</TT>. <TT>HistogramSeriesCollection</TT> provides complementary tools to draw histograms.
+ * One may add observation sets, define histogram bins, set plot color and plot style,
+ * enable/disable filled shapes, and set margin between shapes for each series.
+ * This class is linked with class <TT>CustomHistogramDataset</TT> to store data plots,
+ * and linked with JFreeChart <TT>XYBarRenderer</TT> to render the plot.
+ * <TT>CustomHistogramDataset</TT> has been developed at the Université de Montréal to extend the JFreeChart API,
+ * and is used to manage histogram datasets in a JFreeChart chart.
+ *
+ */
+public class HistogramSeriesCollection extends SSJXYSeriesCollection {
+ protected boolean[] filled; // fill flag for each series
+ protected double[] lineWidth; // sets line width
+ protected int numBin = 20; // default number of bins
+
+ private int calcNumBins (int n) {
+ // set the number of bins
+ int numbins = (int) Math.ceil (1.0 + Num.log2(n));
+ if (n > 5000)
+ numbins *= 2;
+ return numbins;
+ }
+
+ private double[] countersToArray (TallyHistogram hist) {
+ final int nb = hist.getNumBins();
+ final double binwidth = (hist.getB() - hist.getA()) / nb;
+ int[] count = hist.getCounters();
+ int sum = 0;
+ for (int i = 1 ; i <= nb; i++)
+ sum += count[i];
+ double[] data = new double [sum];
+
+ double h, base;
+ int k = 0;
+ for (int i = 1 ; i <= nb; i++) {
+ h = binwidth;
+ base = hist.getA() + (i-1)*binwidth;
+ if (count[i] > 0)
+ h /= count[i];
+ if (i == nb) {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = hist.getB() - j*h;
+ } else {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = base + j*h;
+ }
+ }
+ return data;
+ }
+
+ /**
+ * Creates a new <TT>HistogramSeriesCollection</TT> instance with empty dataset.
+ *
+ */
+ public HistogramSeriesCollection() {
+ renderer = new XYBarRenderer();
+ seriesCollection = new CustomHistogramDataset();
+ }
+
+
+ /**
+ * Creates a new <TT>HistogramSeriesCollection</TT> instance with given data series.
+ * Bins the elements of data in equally spaced containers (the number of bins
+ * can be changed using the method {@link #setBins setBins}).
+ * Each input parameter represents a data series.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public HistogramSeriesCollection (double[]... data) {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection =
+ (CustomHistogramDataset)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+ numBin = calcNumBins(data[i].length);
+ tempSeriesCollection.addSeries(i, data[i], numBin);
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>HistogramSeriesCollection</TT> instance with the given data
+ * series <TT>data</TT>. Bins the elements of data in equally spaced containers
+ * (the number of bins can be changed using the method {@link #setBins setBins}).
+ * Only the first <TT>numPoints</TT> of <TT>data</TT> will be taken into account.
+ *
+ * @param data Point set
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public HistogramSeriesCollection (double[] data, int numPoints) {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection =
+ (CustomHistogramDataset)seriesCollection;
+ numBin = calcNumBins(numPoints);
+ tempSeriesCollection.addSeries(0, data, numPoints, numBin);
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>HistogramSeriesCollection</TT>.
+ * Bins the elements of data in equally spaced containers (the number of bins
+ * can be changed using the method {@link #setBins setBins}).
+ * The input parameter represents a set of data plots. Each
+ * {@link cern.colt.list.DoubleArrayList DoubleArrayList} variable corresponds
+ * to a histogram on the chart.
+ *
+ * @param data series of observation sets.
+ *
+ *
+ */
+ public HistogramSeriesCollection (DoubleArrayList... data) {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection =
+ (CustomHistogramDataset)seriesCollection;
+
+ for (int i = 0; i < data.length; i++) {
+ numBin = calcNumBins(data[i].size());
+ tempSeriesCollection.addSeries(i, data[i].elements(), data[i].size(), numBin);
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>HistogramSeriesCollection</TT> instance with default
+ * parameters and given data. The input parameter represents a collection of
+ * data observation sets. Each <TT>TallyStore</TT> input parameter represents
+ * an observation set.
+ *
+ * @param tallies series of point sets.
+ *
+ *
+ */
+ public HistogramSeriesCollection (TallyStore... tallies) {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+
+ double h;
+ for (int i = 0; i < tallies.length; i++) {
+ // Scott's formula
+ h = 3.5*tallies[i].standardDeviation() /
+ Math.pow(tallies[i].numberObs(), 1.0/3.0);
+ numBin = (int) ((tallies[i].max() - tallies[i].min()) / (1.5*h));
+ tempSeriesCollection.addSeries (i, tallies[i].getArray(),
+ tallies[i].numberObs(), numBin, tallies[i].min(), tallies[i].max());
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>HistogramSeriesCollection</TT> instance with default
+ * parameters and given data. The input parameter represents a collection of
+ * data observation sets. Each <TT>TallyHistogram</TT> input parameter represents
+ * an observation set. The 2 extra bins at the beginning and at the end of the
+ * tallies are not counted nor represented in the chart.
+ *
+ * @param tallies series of point sets.
+ *
+ *
+ */
+ public HistogramSeriesCollection (TallyHistogram... tallies) {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+
+ double[] data;
+ for (int i = 0; i < tallies.length; i++) {
+ data = countersToArray(tallies[i]);
+ tempSeriesCollection.addSeries (i, data, data.length,
+ tallies[i].getNumBins(), tallies[i].getA(), tallies[i].getB());
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>HistogramSeriesCollection</TT> instance.
+ * The input parameter represents a set of plotting data.
+ * Each series of the given collection corresponds to a histogram.
+ *
+ * @param data series of point sets.
+ *
+ */
+ public HistogramSeriesCollection (CustomHistogramDataset data) {
+ renderer = new XYBarRenderer();
+ seriesCollection = data;
+
+ // set default colors
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }
+
+
+ /**
+ * Adds a data series into the series collection.
+ *
+ * @param data new series.
+ *
+ * @return Integer that represent the new point set.
+ *
+ */
+ public int add (double[] data) {
+ return add (data, data.length);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Only <SPAN CLASS="textit">the first</SPAN>
+ * <TT>numPoints</TT> of <TT>data</TT> will be added to the new series.
+ *
+ * @param data new series.
+ *
+ * @param numPoints Number of points to add
+ *
+ * @return Integer that represent the new point set.
+ *
+ */
+ public int add (double[] data, int numPoints) {
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+ tempSeriesCollection.addSeries(tempSeriesCollection.getSeriesCount(),
+ data, numPoints, numBin);
+
+ boolean[] newFilled = new boolean[seriesCollection.getSeriesCount()];
+ double[] newLineWidth = new double[seriesCollection.getSeriesCount()];
+
+ // color
+ int j = seriesCollection.getSeriesCount() - 1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+ newFilled[j] = false;
+ newLineWidth[j] = 0.5;
+
+ for (j = 0; j < seriesCollection.getSeriesCount() - 1; j++) {
+ newFilled[j] = filled[j];
+ newLineWidth[j] = lineWidth[j];
+ }
+
+ filled = newFilled;
+ lineWidth = newLineWidth;
+
+ return seriesCollection.getSeriesCount() - 1;
+ }
+
+
+ /**
+ * Adds a data series into the series collection.
+ *
+ * @param observationSet new series values.
+ *
+ * @return Integer that represent the new point set's position in the <TT>CustomHistogramDataset</TT> object.
+ *
+ */
+ public int add (DoubleArrayList observationSet) {
+ return add(observationSet.elements(), observationSet.size());
+ }
+
+
+ /**
+ * Adds a data series into the series collection.
+ *
+ * @param tally {@link umontreal.iro.lecuyer.stat.TallyStore TallyStore} to add values.
+ *
+ * @return Integer that represent the new point set's position in the <TT>CustomHistogramDataset</TT> object.
+ *
+ */
+ public int add (TallyStore tally) {
+ return add(tally.getArray(), tally.numberObs());
+ }
+
+
+ /**
+ * Returns the <TT>CustomHistogramDataset</TT> object associated with the current variable.
+ *
+ * @return <TT>CustomHistogramDataset</TT> object associated with the current variable.
+ *
+ */
+ public CustomHistogramDataset getSeriesCollection() {
+ return (CustomHistogramDataset)seriesCollection;
+ }
+
+
+ /**
+ * Returns the bins for a series.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @return A list of bins.
+ * IndexOutOfBoundsExceptionif <TT>series</TT> is outside the specified range.
+ *
+ */
+ public List getBins (int series) {
+ return ((CustomHistogramDataset)seriesCollection).getBins(series);
+ }
+
+
+ /**
+ * Sets <TT>bins</TT> periodic bins from the observation minimum values to the observation maximum value for a series.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @param bins the number of periodic bins.
+ *
+ * IndexOutOfBoundsExceptionif <TT>series</TT> is outside the specified range.
+ *
+ */
+ public void setBins (int series, int bins) {
+ ((CustomHistogramDataset)seriesCollection).setBins(series, bins);
+ }
+
+
+ /**
+ * Sets <TT>bins</TT> periodic bins from <TT>minimum</TT> to <TT>maximum</TT> for a series.
+ * Values falling on the boundary of adjacent bins will be assigned to the higher indexed bin.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @param bins the number of periodic bins.
+ *
+ * @param minimum minimum value.
+ *
+ * @param maximum maximum value.
+ *
+ * IndexOutOfBoundsExceptionif <TT>series</TT> is outside the specified range.
+ *
+ */
+ public void setBins (int series, int bins, double minimum, double maximum) {
+ ((CustomHistogramDataset)seriesCollection).setBins(series, bins, minimum, maximum);
+ }
+
+
+ /**
+ * Links bins given by table <TT>binsTable</TT> to a series.
+ * Values falling on the boundary of adjacent bins will be assigned to the higher indexed bin.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @param binsTable new bins table.
+ *
+ * IndexOutOfBoundsExceptionif <TT>series</TT> is outside the specified range.
+ *
+ */
+ public void setBins (int series, HistogramBin[] binsTable) {
+ ((CustomHistogramDataset)seriesCollection).setBins(series, binsTable);
+ }
+
+
+ /**
+ * Returns the values for a series.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @return A list of values.
+ * (IndexOutOfBoundsExceptionif <code>series</code> is outside the specified range.
+ *
+ */
+ public List getValuesList (int series) {
+ return ((CustomHistogramDataset)seriesCollection).getValuesList(series);
+ }
+
+
+ /**
+ * Returns the values for a series.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @return A list of values.
+ * (IndexOutOfBoundsExceptionif <code>series</code> is outside the specified range.
+ *
+ */
+ public double[] getValues (int series) {
+ return ((CustomHistogramDataset)seriesCollection).getValues(series);
+ }
+
+
+ /**
+ * Sets a new values set to a series from a <TT>List</TT> variable.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @param valuesList new values list.
+ *
+ *
+ */
+ public void setValues (int series, List valuesList) {
+ ((CustomHistogramDataset)seriesCollection).setValues(series, valuesList);
+ }
+
+
+ /**
+ * Sets a new values set to a series from a table.
+ *
+ * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>).
+ *
+ * @param values new values table.
+ *
+ */
+ public void setValues (int series, double[] values) {
+ ((CustomHistogramDataset)seriesCollection).setValues(series, values);
+ }
+
+
+ /**
+ * Returns the <TT>filled</TT> flag associated with the <TT>series</TT>-th
+ * data series.
+ *
+ * @param series series index.
+ *
+ * @return fill flag.
+ *
+ */
+ public boolean getFilled (int series) {
+ return filled[series];
+ }
+
+
+ /**
+ * Sets the <TT>filled</TT> flag. This option fills histogram rectangular.
+ * The fill color is the current series color, alpha's color parameter will
+ * be used to draw transparency.
+ *
+ * @param series series index.
+ *
+ * @param filled fill flag.
+ *
+ *
+ */
+ public void setFilled (int series, boolean filled) {
+ this.filled[series] = filled;
+ }
+
+
+ /**
+ * Returns the margin which is a percentage amount by which the bars are trimmed.
+ *
+ */
+ public double getMargin() {
+ return ((XYBarRenderer)renderer).getMargin();
+ }
+
+
+ /**
+ * Sets the margin which is a percentage amount by which the bars are trimmed for all series.
+ *
+ * @param margin margin percentage amount.
+ *
+ *
+ */
+ public void setMargin (double margin) {
+ ((XYBarRenderer)renderer).setMargin(margin);
+ }
+
+
+ /**
+ * Returns the outline width in pt.
+ *
+ * @param series series index.
+ *
+ *
+ */
+ public double getOutlineWidth (int series) {
+ return lineWidth[series];
+ }
+
+
+ /**
+ * Sets the outline width in pt.
+ *
+ * @param series series index.
+ *
+ * @param outline outline width.
+ *
+ *
+ */
+ public void setOutlineWidth (int series, double outline) {
+ this.lineWidth[series] = outline;
+ }
+
+
+ public String toLatex (double XScale, double YScale, double XShift,
+ double YShift, double xmin, double xmax,
+ double ymin, double ymax) {
+
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+ xmin = Math.min(XShift, xmin);
+ xmax = Math.max(XShift, xmax);
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+ Formatter formatter = new Formatter(Locale.US);
+ double var;
+ double margin = ((XYBarRenderer)renderer).getMargin();
+
+ for (int i = tempSeriesCollection.getSeriesCount() - 1; i >= 0; i--) {
+ List temp = tempSeriesCollection.getBins(i);
+ ListIterator iter = temp.listIterator();
+
+ Color color = (Color)renderer.getSeriesPaint(i);
+ String colorString = detectXColorClassic(color);
+ if (colorString == null) {
+ colorString = "color"+i;
+ formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+ colorString, color.getRed()/255.0, color.getGreen()/255.0,
+ color.getBlue()/255.0);
+ }
+
+ HistogramBin currentBin=null;
+ while(iter.hasNext()) {
+ double currentMargin;
+ currentBin = (HistogramBin)iter.next();
+ currentMargin = ((margin*(currentBin.getEndBoundary()-currentBin.getStartBoundary())))*XScale;
+ if ((currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+ && (currentBin.getCount() >= ymin && currentBin.getCount() <= ymax) )
+ {
+ var = Math.min( currentBin.getEndBoundary(), xmax);
+ if (filled[i]) {
+ formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], (color.getAlpha()/255.0), colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+ }
+ else {
+ formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+ }
+ }
+ else if ( (currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+ && (currentBin.getCount() >= ymin && currentBin.getCount() > ymax) )
+ { // Cas ou notre rectangle ne peut pas etre affiche en entier (trop haut)
+ var = Math.min( currentBin.getEndBoundary(), xmax);
+ if (filled[i]) {
+ formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], (color.getAlpha()/255.0), colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+ formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+ }
+ else {
+ formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+
+ formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+ }
+ }
+ }
+ }
+ return formatter.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/HistogramSeriesCollection.tex b/source/umontreal/iro/lecuyer/charts/HistogramSeriesCollection.tex
new file mode 100644
index 0000000..e59767b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/HistogramSeriesCollection.tex
@@ -0,0 +1,701 @@
+\defmodule {HistogramSeriesCollection}
+
+Stores data used in a \texttt{HistogramChart}. %This class extends
+%\externalclass{umontreal.iro.lecuyer.charts}{SSJXYSeriesCollection}.
+\texttt{HistogramSeriesCollection} provides complementary tools to draw histograms.
+One may add observation sets, define histogram bins, set plot color and plot style,
+enable/disable filled shapes, and set margin between shapes for each series.
+%
+This class is linked with class \texttt{CustomHistogramDataset} to store data plots,
+and linked with JFreeChart \texttt{XYBarRenderer} to render the plot.
+\texttt{CustomHistogramDataset} has been developed at the Universit\'
+e de Montr\'eal to extend the JFreeChart API,
+and is used to manage histogram datasets in a JFreeChart chart.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: HistogramSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import umontreal.iro.lecuyer.stat.*;
+import umontreal.iro.lecuyer.util.Num;
+
+import org.jfree.chart.renderer.xy.XYBarRenderer;
+import org.jfree.data.statistics.HistogramBin;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.util.List;
+import java.util.ListIterator;
+import java.awt.Color;\end{hide}
+
+public class HistogramSeriesCollection extends SSJXYSeriesCollection \begin{hide} {
+ protected boolean[] filled; // fill flag for each series
+ protected double[] lineWidth; // sets line width
+ protected int numBin = 20; // default number of bins
+
+ private int calcNumBins (int n) {
+ // set the number of bins
+ int numbins = (int) Math.ceil (1.0 + Num.log2(n));
+ if (n > 5000)
+ numbins *= 2;
+ return numbins;
+ }
+
+ private double[] countersToArray (TallyHistogram hist) {
+ final int nb = hist.getNumBins();
+ final double binwidth = (hist.getB() - hist.getA()) / nb;
+ int[] count = hist.getCounters();
+ int sum = 0;
+ for (int i = 1 ; i <= nb; i++)
+ sum += count[i];
+ double[] data = new double [sum];
+
+ double h, base;
+ int k = 0;
+ for (int i = 1 ; i <= nb; i++) {
+ h = binwidth;
+ base = hist.getA() + (i-1)*binwidth;
+ if (count[i] > 0)
+ h /= count[i];
+ if (i == nb) {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = hist.getB() - j*h;
+ } else {
+ for (int j = 0 ; j < count[i] ; j++)
+ data[k++] = base + j*h;
+ }
+ }
+ return data;
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+ public HistogramSeriesCollection() \begin{hide} {
+ renderer = new XYBarRenderer();
+ seriesCollection = new CustomHistogramDataset();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{HistogramSeriesCollection} instance with empty dataset.
+\end{tabb}
+\begin{code}
+
+ public HistogramSeriesCollection (double[]... data) \begin{hide} {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection =
+ (CustomHistogramDataset)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+ numBin = calcNumBins(data[i].length);
+ tempSeriesCollection.addSeries(i, data[i], numBin);
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{HistogramSeriesCollection} instance with given data series.
+ Bins the elements of data in equally spaced containers (the number of bins
+ can be changed using the method \method{setBins}{}).
+ Each input parameter % (or matrix column)
+ represents a data series.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramSeriesCollection (double[] data, int numPoints) \begin{hide} {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection =
+ (CustomHistogramDataset)seriesCollection;
+ numBin = calcNumBins(numPoints);
+ tempSeriesCollection.addSeries(0, data, numPoints, numBin);
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{HistogramSeriesCollection} instance with the given data
+ series \texttt{data}. Bins the elements of data in equally spaced containers
+ (the number of bins can be changed using the method \method{setBins}{}).
+ Only the first \texttt{numPoints} of \texttt{data} will be taken into account.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{Point set}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramSeriesCollection (DoubleArrayList... data) \begin{hide} {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection =
+ (CustomHistogramDataset)seriesCollection;
+
+ for (int i = 0; i < data.length; i++) {
+ numBin = calcNumBins(data[i].size());
+ tempSeriesCollection.addSeries(i, data[i].elements(), data[i].size(), numBin);
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{HistogramSeriesCollection}.
+ Bins the elements of data in equally spaced containers (the number of bins
+ can be changed using the method \method{setBins}{}).
+ The input parameter represents a set of data plots. Each
+ \externalclass{cern.colt.list}{DoubleArrayList} variable corresponds
+ to a histogram on the chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of observation sets.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramSeriesCollection (TallyStore... tallies) \begin{hide} {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+
+ double h;
+ for (int i = 0; i < tallies.length; i++) {
+ // Scott's formula
+ h = 3.5*tallies[i].standardDeviation() /
+ Math.pow(tallies[i].numberObs(), 1.0/3.0);
+ numBin = (int) ((tallies[i].max() - tallies[i].min()) / (1.5*h));
+ tempSeriesCollection.addSeries (i, tallies[i].getArray(),
+ tallies[i].numberObs(), numBin, tallies[i].min(), tallies[i].max());
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{HistogramSeriesCollection} instance with default
+ parameters and given data. The input parameter represents a collection of
+ data observation sets. Each \texttt{TallyStore} input parameter represents
+ an observation set.
+\end{tabb}
+\begin{htmlonly}
+ \param{tallies}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramSeriesCollection (TallyHistogram... tallies) \begin{hide} {
+ seriesCollection = new CustomHistogramDataset();
+ renderer = new XYBarRenderer();
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+
+ double[] data;
+ for (int i = 0; i < tallies.length; i++) {
+ data = countersToArray(tallies[i]);
+ tempSeriesCollection.addSeries (i, data, data.length,
+ tallies[i].getNumBins(), tallies[i].getA(), tallies[i].getB());
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{HistogramSeriesCollection} instance with default
+ parameters and given data. The input parameter represents a collection of
+ data observation sets. Each \texttt{TallyHistogram} input parameter represents
+ an observation set. The 2 extra bins at the beginning and at the end of the
+ tallies are not counted nor represented in the chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{tallies}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public HistogramSeriesCollection (CustomHistogramDataset data) \begin{hide} {
+ renderer = new XYBarRenderer();
+ seriesCollection = data;
+
+ // set default colors
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ filled = new boolean[seriesCollection.getSeriesCount()];
+ lineWidth = new double[seriesCollection.getSeriesCount()];
+ for (int i = 0; i < data.getSeriesCount(); i++) {
+ filled[i] = false;
+ lineWidth[i] = 0.5;
+ setFilled(i, false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{HistogramSeriesCollection} instance.
+ The input parameter represents a set of plotting data.
+ Each series of the given collection corresponds to a histogram.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data control methods}
+
+\begin{code}
+
+ public int add (double[] data) \begin{hide} {
+ return add (data, data.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{new series.}
+ \return{Integer that represent the new point set.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] data, int numPoints) \begin{hide} {
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+ tempSeriesCollection.addSeries(tempSeriesCollection.getSeriesCount(),
+ data, numPoints, numBin);
+
+ boolean[] newFilled = new boolean[seriesCollection.getSeriesCount()];
+ double[] newLineWidth = new double[seriesCollection.getSeriesCount()];
+
+ // color
+ int j = seriesCollection.getSeriesCount() - 1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+ newFilled[j] = false;
+ newLineWidth[j] = 0.5;
+
+ for (j = 0; j < seriesCollection.getSeriesCount() - 1; j++) {
+ newFilled[j] = filled[j];
+ newLineWidth[j] = lineWidth[j];
+ }
+
+ filled = newFilled;
+ lineWidth = newLineWidth;
+
+ return seriesCollection.getSeriesCount() - 1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Only \emph{the first}
+ \texttt{numPoints} of \texttt{data} will be added to the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{new series.}
+ \param{numPoints}{Number of points to add}
+ \return{Integer that represent the new point set.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (DoubleArrayList observationSet) \begin{hide} {
+ return add(observationSet.elements(), observationSet.size());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection.
+\end{tabb}
+\begin{htmlonly}
+ \param{observationSet}{new series values.}
+ \return{Integer that represent the new point set's position in the \texttt{CustomHistogramDataset} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (TallyStore tally) \begin{hide} {
+ return add(tally.getArray(), tally.numberObs());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection.
+\end{tabb}
+\begin{htmlonly}
+ \param{tally}{\externalclass{umontreal.iro.lecuyer.stat}{TallyStore} to add values.}
+ \return{Integer that represent the new point set's position in the \texttt{CustomHistogramDataset} object.}
+\end{htmlonly}
+\begin{code}
+
+ public CustomHistogramDataset getSeriesCollection() \begin{hide} {
+ return (CustomHistogramDataset)seriesCollection;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{CustomHistogramDataset} object associated with the current variable.
+\end{tabb}
+\begin{htmlonly}
+ \return{\texttt{CustomHistogramDataset} object associated with the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public List getBins (int series) \begin{hide} {
+ return ((CustomHistogramDataset)seriesCollection).getBins(series);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the bins for a series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \return{A list of bins.}
+ \throws{IndexOutOfBoundsException}{if \texttt{series} is outside the specified range.}
+\end{htmlonly}
+\begin{code}
+
+ public void setBins (int series, int bins) \begin{hide} {
+ ((CustomHistogramDataset)seriesCollection).setBins(series, bins);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets \texttt{bins} periodic bins from the observation minimum values to the observation maximum value for a series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \param{bins}{the number of periodic bins.}
+ \throws{IndexOutOfBoundsException}{if \texttt{series} is outside the specified range.}
+\end{htmlonly}
+\begin{code}
+
+ public void setBins (int series, int bins, double minimum, double maximum) \begin{hide} {
+ ((CustomHistogramDataset)seriesCollection).setBins(series, bins, minimum, maximum);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets \texttt{bins} periodic bins from \texttt{minimum} to \texttt{maximum} for a series.
+ Values falling on the boundary of adjacent bins will be assigned to the higher indexed bin.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \param{bins}{the number of periodic bins.}
+ \param{minimum}{minimum value.}
+ \param{maximum}{maximum value.}
+ \throws{IndexOutOfBoundsException}{if \texttt{series} is outside the specified range.}
+\end{htmlonly}
+\begin{code}
+
+ public void setBins (int series, HistogramBin[] binsTable) \begin{hide} {
+ ((CustomHistogramDataset)seriesCollection).setBins(series, binsTable);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Links bins given by table \texttt{binsTable} to a series.
+ Values falling on the boundary of adjacent bins will be assigned to the higher indexed bin.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \param{binsTable}{new bins table.}
+ \throws{IndexOutOfBoundsException}{if \texttt{series} is outside the specified range.}
+\end{htmlonly}
+\begin{code}
+
+ public List getValuesList (int series) \begin{hide} {
+ return ((CustomHistogramDataset)seriesCollection).getValuesList(series);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values for a series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \return{A list of values.}
+ \throws(IndexOutOfBoundsException}{if <code>series</code> is outside the specified range.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getValues (int series) \begin{hide} {
+ return ((CustomHistogramDataset)seriesCollection).getValues(series);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values for a series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \return{A list of values.}
+ \throws(IndexOutOfBoundsException}{if <code>series</code> is outside the specified range.}
+\end{htmlonly}
+\begin{code}
+
+ public void setValues (int series, List valuesList) \begin{hide} {
+ ((CustomHistogramDataset)seriesCollection).setValues(series, valuesList);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets a new values set to a series from a \texttt{List} variable.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \param{valuesList}{new values list.}
+\end{htmlonly}
+\begin{code}
+
+ public void setValues (int series, double[] values) \begin{hide} {
+ ((CustomHistogramDataset)seriesCollection).setValues(series, values);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets a new values set to a series from a table.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{the series index (in the range \texttt{0} to \texttt{getSeriesCount() - 1}).}
+ \param{values}{new values table.}
+\end{htmlonly}
+
+
+\newpage
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Rendering methods}
+
+\begin{code}
+
+ public boolean getFilled (int series) \begin{hide} {
+ return filled[series];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{filled} flag associated with the \texttt{series}-th
+ data series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{fill flag.}
+\end{htmlonly}
+\begin{code}
+
+ public void setFilled (int series, boolean filled) \begin{hide} {
+ this.filled[series] = filled;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the \texttt{filled} flag. This option fills histogram rectangular.
+ The fill color is the current series color, alpha's color parameter will
+ be used to draw transparency.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{filled}{fill flag.}
+\end{htmlonly}
+\begin{code}
+
+ public double getMargin() \begin{hide} {
+ return ((XYBarRenderer)renderer).getMargin();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the margin which is a percentage amount by which the bars are trimmed.
+\end{tabb}
+\begin{code}
+
+ public void setMargin (double margin) \begin{hide} {
+ ((XYBarRenderer)renderer).setMargin(margin);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the margin which is a percentage amount by which the bars are trimmed for all series.
+\end{tabb}
+\begin{htmlonly}
+ \param{margin}{margin percentage amount.}
+\end{htmlonly}
+\begin{code}
+
+ public double getOutlineWidth (int series) \begin{hide} {
+ return lineWidth[series];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the outline width in pt.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+\end{htmlonly}
+\begin{code}
+
+ public void setOutlineWidth (int series, double outline) \begin{hide} {
+ this.lineWidth[series] = outline;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the outline width in pt.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{outline}{outline width.}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatex (double XScale, double YScale, double XShift,
+ double YShift, double xmin, double xmax,
+ double ymin, double ymax) \begin{hide} {
+
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+ xmin = Math.min(XShift, xmin);
+ xmax = Math.max(XShift, xmax);
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
+ Formatter formatter = new Formatter(Locale.US);
+ double var;
+ double margin = ((XYBarRenderer)renderer).getMargin();
+
+ for (int i = tempSeriesCollection.getSeriesCount() - 1; i >= 0; i--) {
+ List temp = tempSeriesCollection.getBins(i);
+ ListIterator iter = temp.listIterator();
+
+ Color color = (Color)renderer.getSeriesPaint(i);
+ String colorString = detectXColorClassic(color);
+ if (colorString == null) {
+ colorString = "color"+i;
+ formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+ colorString, color.getRed()/255.0, color.getGreen()/255.0,
+ color.getBlue()/255.0);
+ }
+
+ HistogramBin currentBin=null;
+ while(iter.hasNext()) {
+ double currentMargin;
+ currentBin = (HistogramBin)iter.next();
+ currentMargin = ((margin*(currentBin.getEndBoundary()-currentBin.getStartBoundary())))*XScale;
+ if ((currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+ && (currentBin.getCount() >= ymin && currentBin.getCount() <= ymax) )
+ {
+ var = Math.min( currentBin.getEndBoundary(), xmax);
+ if (filled[i]) {
+ formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], (color.getAlpha()/255.0), colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+ }
+ else {
+ formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
+ }
+ }
+ else if ( (currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
+ && (currentBin.getCount() >= ymin && currentBin.getCount() > ymax) )
+ { // Cas ou notre rectangle ne peut pas etre affiche en entier (trop haut)
+ var = Math.min( currentBin.getEndBoundary(), xmax);
+ if (filled[i]) {
+ formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], (color.getAlpha()/255.0), colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+ formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+ }
+ else {
+ formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+
+ formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
+ lineWidth[i], colorString,
+ currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
+ currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
+ }
+ }
+ }
+ }
+ return formatter.toString();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/MultipleDatasetChart.java b/source/umontreal/iro/lecuyer/charts/MultipleDatasetChart.java
new file mode 100644
index 0000000..94c2597
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/MultipleDatasetChart.java
@@ -0,0 +1,518 @@
+
+
+/*
+ * Class: MultipleDatasetChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.util.ArrayList;
+import javax.swing.JFrame;
+
+/**
+ * Provides tools to plot many datasets on the same chart.
+ * This class is mainly used to draw plots with different styles.
+ * Class {@link umontreal.iro.lecuyer.charts.XYChart XYChart} and
+ * its subclasses are to be preferred to draw simple charts with one style.
+ * Datasets are stored in an <TT>ArrayList</TT>. The first dataset is called
+ * as the <SPAN CLASS="textit">primary dataset</SPAN>.
+ *
+ */
+public class MultipleDatasetChart {
+
+ protected ArrayList<SSJXYSeriesCollection> datasetList;
+ protected Axis XAxis;
+ protected Axis YAxis;
+ protected JFreeChart chart;
+ protected boolean latexDocFlag = true;
+
+ protected boolean autoRange = true;
+ protected double[] manualRange;
+
+ protected boolean grid = false;
+ protected double xstepGrid;
+ protected double ystepGrid;
+
+
+
+ /**
+ * Initializes a new <TT>MultipleDatasetChart</TT>.
+ *
+ */
+ public MultipleDatasetChart() {
+ super();
+
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ null, // chart title
+ null, // x axis label
+ null, // y axis label
+ null, // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tool tips
+ false // urls
+ );
+
+ datasetList = new ArrayList<SSJXYSeriesCollection>();
+ // Initialize axis variables
+ XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ }
+
+
+ /**
+ * Initializes a new <TT>MultipleDatasetChart</TT> instance.
+ * <TT>title</TT> sets a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> is a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ */
+ public MultipleDatasetChart (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ null, // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tool tips
+ false // urls
+ );
+
+ datasetList = new ArrayList<SSJXYSeriesCollection>();
+ //Initialize axis variables
+ XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ }
+
+
+ /**
+ * Returns the <TT>JFreeChart</TT> variable associated with this chart.
+ *
+ * @return the associated JFreeChart variable.
+ *
+ */
+ public JFreeChart getJFreeChart() {
+ return chart;
+ }
+
+
+ /**
+ * Returns the chart's domain axis (<SPAN CLASS="MATH"><I>x</I></SPAN>-axis) object.
+ *
+ * @return chart's domain axis (<SPAN CLASS="MATH"><I>x</I></SPAN>-axis) object.
+ *
+ */
+ public Axis getXAxis() {
+ return XAxis;
+ }
+
+
+ /**
+ * Returns the chart's range axis (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) object.
+ *
+ * @return chart's range axis (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) object.
+ *
+ */
+ public Axis getYAxis() {
+ return YAxis;
+ }
+
+
+ /**
+ * Gets the current chart title.
+ *
+ * @return Chart title.
+ *
+ */
+ public String getTitle() {
+ return chart.getTitle().getText();
+ }
+
+
+ /**
+ * Sets a title to the chart. This title will appear on the chart displayed by method {@link #view view}.
+ *
+ * @param title chart title.
+ *
+ *
+ */
+ public void setTitle (String title) {
+ chart.setTitle(title);
+ }
+
+
+ /**
+ * Sets chart range to automatic values.
+ *
+ */
+ public void setAutoRange() {
+ autoRange = true;
+ double[][] temp = new double[2][datasetList.size()];
+ for(int i = 0; i<datasetList.size(); i++) {
+ temp[0][i] = (datasetList.get(i).getDomainBounds())[0];
+ temp[1][i] = (datasetList.get(i).getDomainBounds())[1];
+ }
+ XAxis.getAxis().setLowerBound(min(temp[0]));
+ XAxis.getAxis().setUpperBound(max(temp[1]));
+
+ for(int i = 0; i<datasetList.size(); i++) {
+ temp[0][i] = (datasetList.get(i).getRangeBounds())[0];
+ temp[1][i] = (datasetList.get(i).getRangeBounds())[1];
+ }
+ YAxis.getAxis().setLowerBound(min(temp[0]));
+ YAxis.getAxis().setUpperBound(max(temp[1]));
+ }
+
+
+ /**
+ * Sets new <SPAN CLASS="MATH"><I>x</I></SPAN>-axis and <SPAN CLASS="MATH"><I>y</I></SPAN>-axis bounds, with format: <TT>axisRange</TT> = [xmin, xmax, ymin, ymax].
+ *
+ * @param axisRange new axis ranges.
+ *
+ *
+ */
+ public void setManualRange (double[] axisRange) {
+ if(axisRange.length != 4)
+ throw new IllegalArgumentException("axisRange must share the format: [xmin, xmax, ymin, ymax]");
+ autoRange = false;
+ XAxis.getAxis().setLowerBound(axisRange[0]);
+ XAxis.getAxis().setUpperBound(axisRange[1]);
+ YAxis.getAxis().setLowerBound(axisRange[2]);
+ YAxis.getAxis().setUpperBound(axisRange[3]);
+ }
+
+
+ /**
+ * Adds a new dataset to the chart at the end of the list and returns its position.
+ *
+ * @param dataset dataset to add.
+ *
+ * @return the dataset position in the list.
+ *
+ */
+ public int add (SSJXYSeriesCollection dataset) {
+ ((XYPlot)chart.getPlot()).setDataset(datasetList.size(),
+ dataset.getSeriesCollection());
+ ((XYPlot)chart.getPlot()).setRenderer(datasetList.size(),
+ dataset.getRenderer());
+ datasetList.add(dataset);
+ if(datasetList.size() == 1) {
+ XAxis.setLabelsAuto();
+ YAxis.setLabelsAuto();
+ }
+ return datasetList.size()-1;
+ }
+
+
+ /**
+ * Gets the primary dataset.
+ *
+ * @return dataset.
+ *
+ */
+ public SSJXYSeriesCollection get() {
+ return datasetList.get(0);
+ }
+
+
+ /**
+ * Sets the primary dataset for the plot, replacing the existing dataset if there is one.
+ *
+ * @param dataset the new primary dataset.
+ *
+ *
+ */
+ public void set (SSJXYSeriesCollection dataset) {
+ ((XYPlot)chart.getPlot()).setDataset(dataset.getSeriesCollection());
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ datasetList.set(0, dataset);
+ }
+
+
+ /**
+ * Gets the element at the specified position in the dataset list.
+ *
+ * @param datasetNum position in the dataset list.
+ *
+ * @return dataset.
+ *
+ */
+ public SSJXYSeriesCollection get (int datasetNum) {
+ return datasetList.get(datasetNum);
+ }
+
+
+ /**
+ * Replaces the element at the specified position in the dataset list with the specified element.
+ *
+ * @param datasetNum position in the dataset list.
+ *
+ * @param dataset dataset list.
+ *
+ *
+ */
+ public void set (int datasetNum, SSJXYSeriesCollection dataset) {
+ ((XYPlot)chart.getPlot()).setDataset(datasetNum, dataset.getSeriesCollection());
+ ((XYPlot)chart.getPlot()).setRenderer(datasetNum, dataset.getRenderer());
+ datasetList.add(datasetNum, dataset);
+ }
+
+
+ /**
+ * Returns the dataset list.
+ *
+ * @return dataset list.
+ *
+ */
+ public ArrayList<SSJXYSeriesCollection> getList() {
+ return datasetList;
+ }
+
+
+ /**
+ * Displays chart on the screen using Swing.
+ * This method creates an application containing a chart panel displaying
+ * the chart. The created frame is positioned on-screen, and displayed before
+ * it is returned. The <TT>width</TT> and the <TT>height</TT>
+ * of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ *
+ */
+ public JFrame view (int width, int height) {
+ JFrame myFrame;
+ if (chart.getTitle() != null)
+ myFrame = new JFrame("MultipleDatasetChart from SSJ: " +
+ chart.getTitle().getText());
+ else
+ myFrame = new JFrame("MultipleDatasetChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }
+
+
+ /**
+ * Puts grid on the background. It is important to note that the grid is
+ * always placed in such a way that it contains the axes. Thus, the grid does
+ * not always have an intersection at the corner points; this occurs
+ * only if the corner points are multiples of the stepping. <TT>xstep</TT>
+ * and <TT>ystep</TT> sets the stepping in each direction.
+ *
+ * @param xstep sets the stepping in the <SPAN CLASS="MATH"><I>x</I></SPAN>-direction.
+ *
+ * @param ystep sets the stepping in the <SPAN CLASS="MATH"><I>y</I></SPAN>-direction.
+ *
+ *
+ */
+ public void enableGrid (double xstep, double ystep) {
+ this.grid = true;
+ this.xstepGrid = xstep;
+ this.ystepGrid = ystep;
+ }
+
+
+ /**
+ * Disables the background grid.
+ *
+ */
+ public void disableGrid () {
+ this.grid = false;
+ }
+
+
+ /**
+ * Same as in {@link XYChart}.
+ *
+ * @param width Chart's width in centimeters.
+ *
+ * @param height Chart's height in centimeters.
+ *
+ *
+ */
+ public String toLatex (double width, double height) {
+ double xunit, yunit;
+ double[] save = new double[4];
+
+ if(datasetList.size() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ xunit = width / ( (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale) );
+ //taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ yunit = height / ( (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale) );
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ : %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if(grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ for(int i = 0; i < datasetList.size(); i++)
+ formatter.format("%s", datasetList.get(i).toLatex(XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }
+
+
+ /**
+ * Same as in {@link XYChart}.
+ *
+ */
+ public void setLatexDocFlag (boolean flag) {
+ latexDocFlag = flag;
+ }
+
+
+
+ protected double computeXScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = XAxis.getAxis().getLowerBound();
+ bounds[1] = XAxis.getAxis().getUpperBound();
+
+ if(position<bounds[0])
+ bounds[0] = position;
+ if(position>bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale(bounds);
+ }
+
+ protected double computeYScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = YAxis.getAxis().getLowerBound();
+ bounds[1] = YAxis.getAxis().getUpperBound();
+
+ if(position<bounds[0])
+ bounds[0] = position;
+ if(position>bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale(bounds);
+ }
+
+
+ protected double computeScale (double[] bounds) {
+ int tenPowerRatio=0;
+ //echelle < 1 si les valeurs sont grandes
+ while(bounds[1] > 1000 || bounds[0] < -1000) {
+ bounds[1] /= 10;
+ bounds[0] /= 10;
+ tenPowerRatio++;
+ }
+ //echelle > 1 si les valeurs sont petites
+ while(bounds[1]<100 && bounds[0] > -100) {
+ bounds[1] *= 10;
+ bounds[0] *= 10;
+ tenPowerRatio--;
+ }
+ return 1/Math.pow(10, tenPowerRatio);
+ }
+
+ private static double max (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux ;
+ }
+
+ private static double min (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux ;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/MultipleDatasetChart.tex b/source/umontreal/iro/lecuyer/charts/MultipleDatasetChart.tex
new file mode 100644
index 0000000..be10608
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/MultipleDatasetChart.tex
@@ -0,0 +1,536 @@
+\defmodule {MultipleDatasetChart}
+
+Provides tools to plot many datasets on the same chart.
+This class is mainly used to draw plots with different styles.
+Class \externalclass{umontreal.iro.lecuyer.charts}{XYChart} and
+its subclasses are to be preferred to draw simple charts with one style.
+Datasets are stored in an \texttt{ArrayList}. The first dataset is called
+as the \textit{primary dataset}.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: MultipleDatasetChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.util.ArrayList;
+import javax.swing.JFrame;\end{hide}
+
+public class MultipleDatasetChart \begin{hide} {
+
+ protected ArrayList<SSJXYSeriesCollection> datasetList;
+ protected Axis XAxis;
+ protected Axis YAxis;
+ protected JFreeChart chart;
+ protected boolean latexDocFlag = true;
+
+ protected boolean autoRange = true;
+ protected double[] manualRange;
+
+ protected boolean grid = false;
+ protected double xstepGrid;
+ protected double ystepGrid;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+ public MultipleDatasetChart() \begin{hide} {
+ super();
+
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ null, // chart title
+ null, // x axis label
+ null, // y axis label
+ null, // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tool tips
+ false // urls
+ );
+
+ datasetList = new ArrayList<SSJXYSeriesCollection>();
+ // Initialize axis variables
+ XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{MultipleDatasetChart}.
+\end{tabb}
+\begin{code}
+
+ public MultipleDatasetChart (String title, String XLabel, String YLabel) \begin{hide} {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart(
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ null, // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tool tips
+ false // urls
+ );
+
+ datasetList = new ArrayList<SSJXYSeriesCollection>();
+ //Initialize axis variables
+ XAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot) chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{MultipleDatasetChart} instance.
+ \texttt{title} sets a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} is a short description of the $y$-axis.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public JFreeChart getJFreeChart() \begin{hide} {
+ return chart;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{JFreeChart} variable associated with this chart.
+\end{tabb}
+\begin{htmlonly}
+ \return{the associated JFreeChart variable.}
+\end{htmlonly}
+\begin{code}
+
+ public Axis getXAxis() \begin{hide} {
+ return XAxis;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's domain axis ($x$-axis) object.
+\end{tabb}
+\begin{htmlonly}
+ \return{chart's domain axis ($x$-axis) object.}
+\end{htmlonly}
+\begin{code}
+
+ public Axis getYAxis() \begin{hide} {
+ return YAxis;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's range axis ($y$-axis) object.
+\end{tabb}
+\begin{htmlonly}
+ \return{chart's range axis ($y$-axis) object.}
+\end{htmlonly}
+\begin{code}
+
+ public String getTitle() \begin{hide} {
+ return chart.getTitle().getText();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current chart title.
+\end{tabb}
+\begin{htmlonly}
+ \return{Chart title.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTitle (String title) \begin{hide} {
+ chart.setTitle(title);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets a title to the chart. This title will appear on the chart displayed by method \method{view}{}.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+\end{htmlonly}
+\begin{code}
+
+ public void setAutoRange() \begin{hide} {
+ autoRange = true;
+ double[][] temp = new double[2][datasetList.size()];
+ for(int i = 0; i<datasetList.size(); i++) {
+ temp[0][i] = (datasetList.get(i).getDomainBounds())[0];
+ temp[1][i] = (datasetList.get(i).getDomainBounds())[1];
+ }
+ XAxis.getAxis().setLowerBound(min(temp[0]));
+ XAxis.getAxis().setUpperBound(max(temp[1]));
+
+ for(int i = 0; i<datasetList.size(); i++) {
+ temp[0][i] = (datasetList.get(i).getRangeBounds())[0];
+ temp[1][i] = (datasetList.get(i).getRangeBounds())[1];
+ }
+ YAxis.getAxis().setLowerBound(min(temp[0]));
+ YAxis.getAxis().setUpperBound(max(temp[1]));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets chart range to automatic values.
+\end{tabb}
+\begin{code}
+
+ public void setManualRange (double[] axisRange) \begin{hide} {
+ if(axisRange.length != 4)
+ throw new IllegalArgumentException("axisRange must share the format: [xmin, xmax, ymin, ymax]");
+ autoRange = false;
+ XAxis.getAxis().setLowerBound(axisRange[0]);
+ XAxis.getAxis().setUpperBound(axisRange[1]);
+ YAxis.getAxis().setLowerBound(axisRange[2]);
+ YAxis.getAxis().setUpperBound(axisRange[3]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets new $x$-axis and $y$-axis bounds, with format: \texttt{axisRange} = [xmin, xmax, ymin, ymax].
+\end{tabb}
+\begin{htmlonly}
+ \param{axisRange}{new axis ranges.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (SSJXYSeriesCollection dataset) \begin{hide} {
+ ((XYPlot)chart.getPlot()).setDataset(datasetList.size(),
+ dataset.getSeriesCollection());
+ ((XYPlot)chart.getPlot()).setRenderer(datasetList.size(),
+ dataset.getRenderer());
+ datasetList.add(dataset);
+ if(datasetList.size() == 1) {
+ XAxis.setLabelsAuto();
+ YAxis.setLabelsAuto();
+ }
+ return datasetList.size()-1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a new dataset to the chart at the end of the list and returns its position.
+\end{tabb}
+\begin{htmlonly}
+ \param{dataset}{dataset to add.}
+ \return{the dataset position in the list.}
+\end{htmlonly}
+\begin{code}
+
+ public SSJXYSeriesCollection get() \begin{hide} {
+ return datasetList.get(0);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the primary dataset.
+\end{tabb}
+\begin{htmlonly}
+ \return{dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void set (SSJXYSeriesCollection dataset) \begin{hide} {
+ ((XYPlot)chart.getPlot()).setDataset(dataset.getSeriesCollection());
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ datasetList.set(0, dataset);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the primary dataset for the plot, replacing the existing dataset if there is one.
+\end{tabb}
+\begin{htmlonly}
+ \param{dataset}{the new primary dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public SSJXYSeriesCollection get (int datasetNum) \begin{hide} {
+ return datasetList.get(datasetNum);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the element at the specified position in the dataset list.
+\end{tabb}
+\begin{htmlonly}
+ \param{datasetNum}{position in the dataset list.}
+ \return{dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void set (int datasetNum, SSJXYSeriesCollection dataset) \begin{hide} {
+ ((XYPlot)chart.getPlot()).setDataset(datasetNum, dataset.getSeriesCollection());
+ ((XYPlot)chart.getPlot()).setRenderer(datasetNum, dataset.getRenderer());
+ datasetList.add(datasetNum, dataset);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Replaces the element at the specified position in the dataset list with the specified element.
+\end{tabb}
+\begin{htmlonly}
+ \param{datasetNum}{position in the dataset list.}
+ \param{dataset}{dataset list.}
+\end{htmlonly}
+\begin{code}
+
+ public ArrayList<SSJXYSeriesCollection> getList() \begin{hide} {
+ return datasetList;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dataset list.
+\end{tabb}
+\begin{htmlonly}
+ \return{dataset list.}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame view (int width, int height) \begin{hide} {
+ JFrame myFrame;
+ if (chart.getTitle() != null)
+ myFrame = new JFrame("MultipleDatasetChart from SSJ: " +
+ chart.getTitle().getText());
+ else
+ myFrame = new JFrame("MultipleDatasetChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays chart on the screen using Swing.
+ This method creates an application containing a chart panel displaying
+ the chart. The created frame is positioned on-screen, and displayed before
+ it is returned. The \texttt{width} and the \texttt{height}
+ of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+\end{htmlonly}
+\begin{code}
+
+ public void enableGrid (double xstep, double ystep) \begin{hide} {
+ this.grid = true;
+ this.xstepGrid = xstep;
+ this.ystepGrid = ystep;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Puts grid on the background. It is important to note that the grid is
+always placed in such a way that it contains the axes. Thus, the grid does
+ not always have an intersection at the corner points; this occurs
+ only if the corner points are multiples of the stepping. \texttt{xstep}
+ and \texttt{ystep} sets the stepping in each direction.
+\end{tabb}
+\begin{htmlonly}
+ \param{xstep}{sets the stepping in the $x$-direction.}
+ \param{ystep}{sets the stepping in the $y$-direction.}
+\end{htmlonly}
+\begin{code}
+
+ public void disableGrid () \begin{hide} {
+ this.grid = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Disables the background grid.
+\end{tabb}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{\LaTeX-specific method}
+
+\begin{code}
+
+ public String toLatex (double width, double height) \begin{hide} {
+ double xunit, yunit;
+ double[] save = new double[4];
+
+ if(datasetList.size() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ xunit = width / ( (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale) );
+ //taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ yunit = height / ( (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale) );
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ : %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if(grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ for(int i = 0; i < datasetList.size(); i++)
+ formatter.format("%s", datasetList.get(i).toLatex(XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Same as in \class{XYChart}.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{Chart's width in centimeters.}
+ \param{height}{Chart's height in centimeters.}
+\end{htmlonly}
+\begin{code}
+
+ public void setLatexDocFlag (boolean flag) \begin{hide} {
+ latexDocFlag = flag;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Same as in \class{XYChart}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ protected double computeXScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = XAxis.getAxis().getLowerBound();
+ bounds[1] = XAxis.getAxis().getUpperBound();
+
+ if(position<bounds[0])
+ bounds[0] = position;
+ if(position>bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale(bounds);
+ }
+
+ protected double computeYScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = YAxis.getAxis().getLowerBound();
+ bounds[1] = YAxis.getAxis().getUpperBound();
+
+ if(position<bounds[0])
+ bounds[0] = position;
+ if(position>bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale(bounds);
+ }
+
+
+ protected double computeScale (double[] bounds) {
+ int tenPowerRatio=0;
+ //echelle < 1 si les valeurs sont grandes
+ while(bounds[1] > 1000 || bounds[0] < -1000) {
+ bounds[1] /= 10;
+ bounds[0] /= 10;
+ tenPowerRatio++;
+ }
+ //echelle > 1 si les valeurs sont petites
+ while(bounds[1]<100 && bounds[0] > -100) {
+ bounds[1] *= 10;
+ bounds[0] *= 10;
+ tenPowerRatio--;
+ }
+ return 1/Math.pow(10, tenPowerRatio);
+ }
+
+ private static double max (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux ;
+ }
+
+ private static double min (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux ;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/PPPlot.java b/source/umontreal/iro/lecuyer/charts/PPPlot.java
new file mode 100644
index 0000000..dd06233
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/PPPlot.java
@@ -0,0 +1,167 @@
+
+
+/*
+ * Class: PPPlot
+ * Description: pp-plot
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;
+ import java.util.Arrays;
+
+
+
+/**
+ * This class implements <SPAN CLASS="textit">PP-plot</SPAN> (or probability-probability plot)
+ * objects that compare two probability distributions.
+ * The data is given as a list of <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>n</SUB>)</SPAN>,
+ * and one is given a reference continuous probability distribution <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ * One first sorts the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> in ascending order, then noted <SPAN CLASS="MATH"><I>x</I><SUB>(i)</SUB></SPAN>, and
+ * plots the points
+ * <SPAN CLASS="MATH">(<I>i</I>/<I>n</I>, <I>F</I>(<I>x</I><SUB>(i)</SUB>))</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1, 2,…, <I>n</I></SPAN>, to see if
+ * the data <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> comes from the reference distribution <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ * The graph of the straight line <SPAN CLASS="MATH"><I>y</I> = <I>x</I></SPAN> is also plotted for comparison.
+ *
+ */
+public class PPPlot extends XYLineChart {
+ private double[][] U; // u_i = cdf(x_i)
+ private double[][] Lin; // line y = x
+
+ private void initLinear ()
+ {
+ // line y = x in [0, 1] by steps of h
+ int m = 100;
+ double h = 1.0 / m;
+ Lin = new double[2][m+1];
+ for (int i = 0; i <= m; i++)
+ Lin[0][i] = Lin[1][i] = h * i;
+ }
+
+
+ private void initPoints (ContinuousDistribution dist, double[] data,
+ int numPoints)
+ {
+ int i;
+ U = new double[2][numPoints];
+
+ for (i = 0; i < numPoints; i++)
+ U[1][i] = dist.cdf(data[i]);
+ Arrays.sort(U[1]);
+ for (i = 0; i < numPoints; i++)
+ U[0][i] = (double) (i+1)/numPoints;
+ }
+
+
+ /**
+ * Initializes a new <TT>PPPlot</TT> instance using the points <TT>X</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The plot is a PP-plot of the points
+ *
+ * <SPAN CLASS="MATH">(<I>i</I>/<I>n</I>, <I>F</I>(<I>x</I><SUB>(i)</SUB>)</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1, 2,…, <I>n</I></SPAN>, where <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB> =</SPAN><TT> X[<SPAN CLASS="MATH"><I>i</I></SPAN>-1]</TT>,
+ * <SPAN CLASS="MATH"><I>x</I><SUB>(i)</SUB></SPAN> are the sorted points, and <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) =</SPAN><TT> dist.cdf(<SPAN CLASS="MATH"><I>x</I></SPAN>)</TT>.
+ * The points <TT>X</TT> are not sorted.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param dist Reference distribution
+ *
+ * @param X points.
+ *
+ *
+ */
+ public PPPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X) {
+ this (title, XLabel, YLabel, dist, X, X.length);
+ }
+
+
+ /**
+ * Similar to the constructor
+ * {@link #PPPlot(String,String,String,ContinuousDistribution,double[]) PPPlot}
+ * <TT>(title, XLabel, YLabel, dist, X)</TT>
+ * above, except that only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>X</TT>
+ * are plotted.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param dist Reference distribution
+ *
+ * @param X point set.
+ *
+ * @param numPoints number of points to plot
+ *
+ *
+ */
+ public PPPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X, int numPoints) {
+ super();
+ initPoints (dist, X, numPoints);
+ initLinear ();
+ dataset = new XYListSeriesCollection(U, Lin);
+ // --- dashed line for y = x
+ ((XYListSeriesCollection)dataset).setDashPattern(1, "dashed");
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>PPPlot</TT> instance.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input vectors in <TT>data</TT> represents several sets of <SPAN CLASS="MATH"><I>x</I></SPAN>-points.
+ * <SPAN CLASS="MATH"><I>r</I></SPAN> determine the set of points to be plotted in the PP-plot, that is,
+ * one will plot only the points <TT>data[r][i]</TT>,
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…,(<I>n</I> - 1)</SPAN> and a given <SPAN CLASS="MATH"><I>r</I></SPAN>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number
+ * of points in set <SPAN CLASS="MATH"><I>r</I></SPAN>. The points are assumed to follow the distribution
+ * <TT>dist</TT>.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param dist Reference distribution
+ *
+ * @param data series of point sets.
+ *
+ * @param r set of points to plot
+ *
+ */
+ public PPPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[][] data, int r) {
+ this (title, XLabel, YLabel, dist, data[r], data[r].length);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/PPPlot.tex b/source/umontreal/iro/lecuyer/charts/PPPlot.tex
new file mode 100644
index 0000000..847cf0e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/PPPlot.tex
@@ -0,0 +1,155 @@
+\defmodule {PPPlot}
+
+This class implements \emph{PP-plot} (or probability-probability plot)
+objects that compare two probability distributions.
+The data is given as a list of $x$-coordinates $(x_1, x_2, \ldots, x_{n})$,
+and one is given a reference continuous probability distribution $F(x)$.
+One first sorts the $x_i$ in ascending order, then noted $x_{(i)}$, and
+plots the points $(i/n, F(x_{(i)}))$, $i= 1, 2, \ldots, n$, to see if
+the data $x_i$ comes from the reference distribution $F(x)$.
+The graph of the straight line $y=x$ is also plotted for comparison.
+
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: PPPlot
+ * Description: pp-plot
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;\begin{hide}
+ import java.util.Arrays;
+\end{hide}
+
+
+public class PPPlot extends XYLineChart \begin{hide} {
+ private double[][] U; // u_i = cdf(x_i)
+ private double[][] Lin; // line y = x
+
+ private void initLinear ()
+ {
+ // line y = x in [0, 1] by steps of h
+ int m = 100;
+ double h = 1.0 / m;
+ Lin = new double[2][m+1];
+ for (int i = 0; i <= m; i++)
+ Lin[0][i] = Lin[1][i] = h * i;
+ }
+
+
+ private void initPoints (ContinuousDistribution dist, double[] data,
+ int numPoints)
+ {
+ int i;
+ U = new double[2][numPoints];
+
+ for (i = 0; i < numPoints; i++)
+ U[1][i] = dist.cdf(data[i]);
+ Arrays.sort(U[1]);
+ for (i = 0; i < numPoints; i++)
+ U[0][i] = (double) (i+1)/numPoints;
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public PPPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X) \begin{hide} {
+ this (title, XLabel, YLabel, dist, X, X.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{PPPlot} instance using the points \texttt{X}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The plot is a PP-plot of the points
+ $(i/n, F(x_{(i)})$, $i= 1, 2, \ldots, n$, where $x_i = $\texttt{ X[$i$-1]},
+ $x_{(i)}$ are the sorted points, and $F(x) = $\texttt{ dist.cdf($x$)}.
+ The points \texttt{X} are not sorted.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{dist}{Reference distribution}
+ \param{X}{points.}
+\end{htmlonly}
+\begin{code}
+
+ public PPPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X, int numPoints) \begin{hide} {
+ super();
+ initPoints (dist, X, numPoints);
+ initLinear ();
+ dataset = new XYListSeriesCollection(U, Lin);
+ // --- dashed line for y = x
+ ((XYListSeriesCollection)dataset).setDashPattern(1, "dashed");
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to the constructor
+ \method{PPPlot}{String,String,String,ContinuousDistribution,double[]}
+ \texttt{(title, XLabel, YLabel, dist, X)}
+ above, except that only \emph{the first} \texttt{numPoints} of \texttt{X}
+ are plotted.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{dist}{Reference distribution}
+ \param{X}{point set.}
+ \param{numPoints}{number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public PPPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[][] data, int r) \begin{hide} {
+ this (title, XLabel, YLabel, dist, data[r], data[r].length);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{PPPlot} instance.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The input vectors in \texttt{data} represents several sets of $x$-points.
+ $r$ determine the set of points to be plotted in the PP-plot, that is,
+ one will plot only the points \texttt{data[r][i]},
+ for $i=0, 1, \ldots, (n-1)$ and a given $r$, where $n$ is the number
+ of points in set $r$. The points are assumed to follow the distribution
+ \texttt{dist}.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{dist}{Reference distribution}
+ \param{data}{series of point sets.}
+ \param{r}{set of points to plot}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/charts/PlotFormat.java b/source/umontreal/iro/lecuyer/charts/PlotFormat.java
new file mode 100644
index 0000000..1edced0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/PlotFormat.java
@@ -0,0 +1,489 @@
+
+
+/*
+ * Class: PlotFormat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.IOException;
+
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.category.CategoryDataset;
+import org.jfree.data.io.CSV;
+
+/**
+ * Provide tools to import and export data set tables
+ * to and from Gnuplot, MATLAB and Mathematica compatible
+ * formats, or customized format.
+ *
+ */
+public class PlotFormat {
+ private PlotFormat() {}
+
+ /**
+ * Parses <TT>data</TT> according to the standard GNUPlot format and stores
+ * the extracted values in the returned table. All unrecognized characters
+ * and empty lines will be cut. Deleting comments inside the input
+ * <TT>String</TT> is recommended.
+ *
+ * @param data String to parse.
+ *
+ * @return Table that represent data contained in <TT>data</TT>.
+ * @exception IllegalArgumentException if each line of the input <TT>String</TT> doesn't share the same number of values
+ *
+ *
+ */
+ public static double[][] fromGNUPlot (String data) {
+ int maxLineNumber = 16;
+ int columnNumber = 0;
+ int lineNumber = 0;
+ double[][] retour = new double[1][1]; //initialisation temporaire
+
+ Pattern p = Pattern.compile("\\n\\n*");
+ String[] string = p.split(data); // A chaque String correspond une ligne
+
+ for (int j = 0; j < string.length; j++) {
+ if (string[j].charAt(0) != '#' && string[j].length() != 0) {
+ //On ne traite pas les lignes de commentaires ni les lignes
+
+ p = Pattern.compile("\\s\\s*");
+ String[] result = p.split(string[j]);
+ double[] line = new double[result.length];
+
+ int k = 0; // Compte le nombre de valeurs sur la ligne courante
+ for (int i = 0; i < result.length; i++) {
+ try {
+ line[k] = Double.valueOf(result[i]); // les chaines de caracteres qui ne sont pas des nombres sont ignorees
+ k++;
+ } catch (NumberFormatException nfe) {}
+ }
+
+ if (k != 0) { // il y a des valeurs sur la ligne
+ //La premiere ligne determine combien de colonnes aura notre tableau de retour
+ if (lineNumber == 0) {
+ columnNumber = k;
+ retour = new double[columnNumber][maxLineNumber]; // Allocation du tableau de retour
+ } else if ( columnNumber != k ) // Toutes les lignes de la String n'ont pas le meme nombre de valeurs
+ throw new IllegalArgumentException("Each line must have the same number of values");
+
+ if (lineNumber >= maxLineNumber) { // Cas ou notre tableau initial n'est plus assez grand
+ maxLineNumber = maxLineNumber * 2;
+ double[][] temp = new double[columnNumber][maxLineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < retour[0].length; l++)
+ temp[i][l] = retour[i][l];
+ retour = temp;
+ }
+
+ for (int i = 0; i < columnNumber; i++)
+ retour[i][lineNumber] = line[i];
+ lineNumber++;
+ }
+ }
+ }
+ // Cree un tableau aux dimensions exactes
+ double[][] temp;
+ if (columnNumber == 0)
+ temp = null;
+ else
+ temp = new double[columnNumber][lineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < lineNumber; l++)
+ temp[i][l] = retour[i][l];
+ retour = null;
+ return temp;
+ }
+
+
+ /**
+ * Parses <TT>data</TT> according to the standard CSV format
+ * and stores the extracted values in the returned table.
+ *
+ * @param data String to parse.
+ *
+ * @return Table that represent data contained in <TT>data</TT>.
+ * @exception IllegalArgumentException if each line of the input <TT>String</TT> doesn't share the same number of values
+ *
+ *
+ */
+ public static double[][] fromCSV (String data) {
+ int maxLineNumber = 16;
+ int columnNumber = 0;
+ int lineNumber = 0;
+ double[][] retour = new double[1][1]; //initialisation temporaire
+
+ Pattern p = Pattern.compile("\\n\\n*");
+ String[] string = p.split(data); // A chaque String correspond une ligne
+
+ for (int j = 0; j < string.length; j++) {
+ if (string[j].length() != 0) {
+ //On ne traite pas les lignes vides
+
+ p = Pattern.compile(",,*");
+ String[] result = p.split(string[j]);
+ double[] line = new double[result.length];
+
+ int k = 0; // Compte le nombre de valeurs sur la ligne courante
+ for (int i = 0; i < result.length; i++) {
+ try {
+ line[k] = Double.valueOf(result[i]); // les chaines de caracteres qui ne sont pas des nombres sont ignorees
+ k++;
+ } catch (NumberFormatException nfe) {}
+ }
+
+ if (k != 0) { // il y a des valeurs sur la ligne
+ //La premiere ligne determine combien de colonnes aura notre tableau de retour
+ if (lineNumber == 0) {
+ columnNumber = k;
+ retour = new double[columnNumber][maxLineNumber]; // Allocation du tableau de retour
+ } else if ( columnNumber != k ) // Toutes les lignes de la String n'ont pas le meme nombre de valeurs
+ throw new IllegalArgumentException("Each line must have the same number of values");
+
+ if (lineNumber >= maxLineNumber) { // Cas ou notre tableau initial n'est plus assez grand
+ maxLineNumber = maxLineNumber * 2;
+ double[][] temp = new double[columnNumber][maxLineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < retour[0].length; l++)
+ temp[i][l] = retour[i][l];
+ retour = temp;
+ }
+
+ for (int i = 0; i < columnNumber; i++)
+ retour[i][lineNumber] = line[i];
+ lineNumber++;
+ }
+ }
+ }
+ // Cree un tableau aux dimensions exactes
+ double[][] temp;
+ if (columnNumber == 0)
+ temp = null;
+ else
+ temp = new double[columnNumber][lineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < lineNumber; l++)
+ temp[i][l] = retour[i][l];
+ retour = null;
+ return temp;
+ }
+
+
+ /**
+ * Parses <TT>data</TT> according to a user defined format and stores the
+ * extracted values in the returned table. <TT>betweenValues</TT> sets
+ * characters between values on the same line and <TT>endLine</TT> sets
+ * characters which separates each line of the input data set. Usually, this
+ * parameter contains the classic end of line character <TT>%n</TT>.
+ * This method uses regular expressions, so it is possible to use regular
+ * characters as defined in the standard java API, specially in the class
+ * {@link Pattern} (package java.util.regex).
+ *
+ * @param betweenValues <TT>String</TT> which separates values on the same line.
+ *
+ * @param endLine <TT>String</TT> which separates lines.
+ *
+ * @param data String to parse.
+ *
+ * @return Table that represent data contained in <TT>data</TT>.
+ * @exception IllegalArgumentException if each line of the input <TT>String</TT> doesn't share the same number of values
+ *
+ */
+ public static double[][] fromCustomizedFormat (String betweenValues,
+ String endLine, String data) {
+ int maxLineNumber = 16;
+ int columnNumber = 0;
+ int lineNumber = 0;
+ double[][] retour = new double[1][1]; //initialisation temporaire
+
+ Pattern p = Pattern.compile("(" + endLine + ")(" + endLine + ")*");
+ String[] string = p.split(data); // A chaque String correspond une ligne
+
+ for (int j = 0; j < string.length; j++) {
+ if (string[j].length() != 0) {
+ //On ne traite pas les lignes vides
+
+ p = Pattern.compile("(" + betweenValues + ")(" + betweenValues + ")*");
+ String[] result = p.split(string[j]);
+ double[] line = new double[result.length];
+
+ int k = 0; // Compte le nombre de valeurs sur la ligne courante
+ for (int i = 0; i < result.length; i++) {
+ try {
+ line[k] = Double.valueOf(result[i]); // les chaines de caracteres qui ne sont pas des nombres sont ignorees
+ k++;
+ } catch (NumberFormatException nfe) {}
+ }
+
+ if (k != 0) { // il y a des valeurs sur la ligne
+ //La premiere ligne determine combien de colonnes aura notre tableau de retour
+ if (lineNumber == 0) {
+ columnNumber = k;
+ retour = new double[columnNumber][maxLineNumber]; // Allocation du tableau de retour
+ } else if ( columnNumber != k ) // Toutes les lignes de la String n'ont pas le meme nombre de valeurs
+ throw new IllegalArgumentException("Each line must have the same number of values");
+
+ if (lineNumber >= maxLineNumber) { // Cas ou notre tableau initial n'est plus assez grand
+ maxLineNumber = maxLineNumber * 2;
+ double[][] temp = new double[columnNumber][maxLineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < retour[0].length; l++)
+ temp[i][l] = retour[i][l];
+ retour = temp;
+ }
+
+ for (int i = 0; i < columnNumber; i++)
+ retour[i][lineNumber] = line[i];
+ lineNumber++;
+ }
+ }
+ }
+ // Cree un tableau aux dimensions exactes
+ double[][] temp;
+ if (columnNumber == 0)
+ temp = null;
+ else
+ temp = new double[columnNumber][lineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < lineNumber; l++)
+ temp[i][l] = retour[i][l];
+ retour = null;
+ return temp;
+ }
+
+
+ /**
+ * Stores data tables <TT>data</TT> into a <TT>String</TT>,
+ * with format understandable by GNUPlot.
+ *
+ * @param data data tables.
+ *
+ * @return String that represent data tables in GNUPlot format.
+ *
+ */
+ public static String toGNUPlot (double[]... data) {
+ checkData(data);
+ Formatter formatter = new Formatter(Locale.US);
+
+ for (int i = 0; i < data[0].length; i++) {
+ for (int j = 0; j < data.length; j++) {
+ formatter.format("%20f", data[j][i]);
+ }
+ formatter.format("%n");
+ }
+ formatter.format("%n%n");
+ return formatter.toString();
+ }
+
+
+ /**
+ * Stores series collection <TT>data</TT> into a <TT>String</TT>,
+ * with format understandable by GNUPlot.
+ *
+ * @param data data tables.
+ *
+ * @return String that represent data tables in GNUPlot format.
+ *
+ */
+ public static String toGNUPlot (XYSeriesCollection data) {
+ return toGNUPlot(toTable(data));
+ }
+
+
+ /**
+ * Stores data tables <TT>data</TT> into a <TT>String</TT>
+ * with format CSV (comma-separated value tabular data).
+ * The output string could be imported from a file to a matrix
+ * into Mathematica with Mathematica's function <TT>Import["fileName", "CSV"]</TT>,
+ * or into MATLAB with MATLAB's function <TT>csvread('fileName')</TT>.
+ *
+ * @param data data tables.
+ *
+ * @return String that represent data tables in CSV format.
+ *
+ */
+ public static String toCSV (double[]...data) {
+ checkData(data);
+ Formatter formatter = new Formatter(Locale.US);
+
+ for (int i = 0; i < data[0].length; i++) {
+ for (int j = 0; j < data.length - 1; j++) {
+ formatter.format("%-20f, ", data[j][i]);
+ }
+ formatter.format("%-20f%n", data[data.length-1][i]); //le dernier
+ }
+ formatter.format("%n");
+
+ return formatter.toString();
+ }
+
+
+ /**
+ * Stores series collection <TT>data</TT> into a <TT>String</TT>
+ * with format CSV (comma-separated value tabular data).
+ *
+ * @param data data tables.
+ *
+ * @return String that represent data tables in CSV format.
+ *
+ */
+ public static String toCSV (XYSeriesCollection data) {
+ return toCSV(toTable(data));
+ }
+
+
+ /**
+ * Stores data tables <TT>data</TT> into a <TT>String</TT> with customized format.
+ * <TT>heading</TT> sets the head of the returned <TT>String</TT>, <TT>footer</TT>
+ * sets its end, <TT>betweenValues</TT> sets characters between values on the same
+ * line and finally <TT>endLine</TT> sets characters which separates each line of
+ * the input data set. Normally, this parameter contains the classic end of line
+ * character `%n'.
+ *
+ * @param heading head of the returned <TT>String</TT>.
+ *
+ * @param footer end of the returned <TT>String</TT>.
+ *
+ * @param betweenValues <TT>String</TT> which separates values on the same line.
+ *
+ * @param endLine <TT>String</TT> which separates lines.
+ *
+ * @param data data tables.
+ *
+ * @return String that represent data tables in customized format.
+ *
+ */
+ public static String toCustomizedFormat (String heading, String footer,
+ String betweenValues, String endLine,
+ int precision, double[]...data) {
+ checkData(data);
+ Formatter formatter = new Formatter(Locale.US);
+ String myString = "%20."+ precision +"f";
+
+ formatter.format("%s", heading);
+ for (int i = 0; i < data[0].length; i++) {
+ for (int j = 0; j < data.length - 1; j++) {
+ formatter.format(myString+"%s", data[j][i], betweenValues);
+ }
+ formatter.format(myString+"%s", data[data.length-1][i], endLine); //le dernier de la ligne
+ }
+ formatter.format("%s", footer);
+ return formatter.toString();
+ }
+
+
+ /**
+ * Stores data tables <TT>data</TT> into a <TT>String</TT>
+ * with customized format from an <TT>XYSeriesCollection</TT> variable.
+ *
+ * @param heading head of the returned <TT>String</TT>.
+ *
+ * @param footer end of the returned <TT>String</TT>.
+ *
+ * @param betweenValues <TT>String</TT> which separates values on the same line.
+ *
+ * @param endLine <TT>String</TT> which separates lines.
+ *
+ * @param data data tables.
+ *
+ * @return String that represent data tables in customized format.
+ *
+ */
+ public static String toCustomizedFormat (String heading, String footer,
+ String betweenValues, String endLine,
+ int precision, XYSeriesCollection data) {
+ return toCustomizedFormat (heading, footer, betweenValues, endLine,
+ precision, toTable(data));
+ }
+
+
+ /* *
+ * Converts a <TT>XYSeriesCollection</TT> object into a <TT>double[][]</TT>.
+ */
+ private static double[][] toTable (XYSeriesCollection data) {
+ double[][] transform = new double[data.getSeriesCount()*2][];
+
+ for(int i = 0; i<data.getSeriesCount(); i++) {
+ double[][] temp = data.getSeries(i).toArray();
+ transform[2*i] = temp[0];
+ transform[2*i+1] = temp[1];
+ }
+ return transform;
+ }
+
+
+ /* *
+ * Check the data table <TT>data</TT>.
+ *
+ * @exception IllegalArgumentException If the input tables doesn't have the same length.
+ */
+ private static void checkData(double[]...data) {
+ for(int i = 0; i < data.length-1; i++) {
+ if(data[i].length != data[i+1].length)
+ throw new IllegalArgumentException("Data tables " + i + " and " + (i+1) + " must share the same length");
+ }
+ }
+
+/* //Inutile si on exporte en CSV
+ private static String mathematicaFormatPoint (double x, double y) {
+ // Writes the pair (x, y) in returned string, in a format understood
+ // by Mathematica
+ StringBuffer sb = new StringBuffer();
+ String S;
+
+ sb.append (" { ");
+ if ((x != 0.0) && (x < 0.1 || x > 1.0)) {
+ S = PrintfFormat.E (16, 7, x);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, x);
+
+ sb.append (S + ", ");
+
+ if (y != 0.0 && (y < 0.1 || y > 1.0)) {
+ S = PrintfFormat.E (16, 7, y);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, y);
+
+ sb.append (S + " }");
+ return sb.toString();
+ }*/
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/PlotFormat.tex b/source/umontreal/iro/lecuyer/charts/PlotFormat.tex
new file mode 100644
index 0000000..ff1b336
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/PlotFormat.tex
@@ -0,0 +1,491 @@
+\defmodule {PlotFormat}
+
+Provide tools to import and export data set tables
+to and from Gnuplot, MATLAB and Mathematica compatible
+formats, or customized format.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: PlotFormat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.IOException;
+
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.category.CategoryDataset;
+import org.jfree.data.io.CSV;\end{hide}
+
+public class PlotFormat\begin{hide} {
+ private PlotFormat() {}\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data import functions}
+
+\begin{code}
+ public static double[][] fromGNUPlot (String data) \begin{hide} {
+ int maxLineNumber = 16;
+ int columnNumber = 0;
+ int lineNumber = 0;
+ double[][] retour = new double[1][1]; //initialisation temporaire
+
+ Pattern p = Pattern.compile("\\n\\n*");
+ String[] string = p.split(data); // A chaque String correspond une ligne
+
+ for (int j = 0; j < string.length; j++) {
+ if (string[j].charAt(0) != '#' && string[j].length() != 0) {
+ //On ne traite pas les lignes de commentaires ni les lignes
+
+ p = Pattern.compile("\\s\\s*");
+ String[] result = p.split(string[j]);
+ double[] line = new double[result.length];
+
+ int k = 0; // Compte le nombre de valeurs sur la ligne courante
+ for (int i = 0; i < result.length; i++) {
+ try {
+ line[k] = Double.valueOf(result[i]); // les chaines de caracteres qui ne sont pas des nombres sont ignorees
+ k++;
+ } catch (NumberFormatException nfe) {}
+ }
+
+ if (k != 0) { // il y a des valeurs sur la ligne
+ //La premiere ligne determine combien de colonnes aura notre tableau de retour
+ if (lineNumber == 0) {
+ columnNumber = k;
+ retour = new double[columnNumber][maxLineNumber]; // Allocation du tableau de retour
+ } else if ( columnNumber != k ) // Toutes les lignes de la String n'ont pas le meme nombre de valeurs
+ throw new IllegalArgumentException("Each line must have the same number of values");
+
+ if (lineNumber >= maxLineNumber) { // Cas ou notre tableau initial n'est plus assez grand
+ maxLineNumber = maxLineNumber * 2;
+ double[][] temp = new double[columnNumber][maxLineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < retour[0].length; l++)
+ temp[i][l] = retour[i][l];
+ retour = temp;
+ }
+
+ for (int i = 0; i < columnNumber; i++)
+ retour[i][lineNumber] = line[i];
+ lineNumber++;
+ }
+ }
+ }
+ // Cree un tableau aux dimensions exactes
+ double[][] temp;
+ if (columnNumber == 0)
+ temp = null;
+ else
+ temp = new double[columnNumber][lineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < lineNumber; l++)
+ temp[i][l] = retour[i][l];
+ retour = null;
+ return temp;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Parses \texttt{data} according to the standard GNUPlot format and stores
+ the extracted values in the returned table. All unrecognized characters
+ and empty lines will be cut. Deleting comments inside the input
+ \texttt{String} is recommended.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{String to parse.}
+ \return{Table that represent data contained in \texttt{data}.}
+ \exception{IllegalArgumentException}{if each line of the input \texttt{String} doesn't share the same number of values}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] fromCSV (String data) \begin{hide} {
+ int maxLineNumber = 16;
+ int columnNumber = 0;
+ int lineNumber = 0;
+ double[][] retour = new double[1][1]; //initialisation temporaire
+
+ Pattern p = Pattern.compile("\\n\\n*");
+ String[] string = p.split(data); // A chaque String correspond une ligne
+
+ for (int j = 0; j < string.length; j++) {
+ if (string[j].length() != 0) {
+ //On ne traite pas les lignes vides
+
+ p = Pattern.compile(",,*");
+ String[] result = p.split(string[j]);
+ double[] line = new double[result.length];
+
+ int k = 0; // Compte le nombre de valeurs sur la ligne courante
+ for (int i = 0; i < result.length; i++) {
+ try {
+ line[k] = Double.valueOf(result[i]); // les chaines de caracteres qui ne sont pas des nombres sont ignorees
+ k++;
+ } catch (NumberFormatException nfe) {}
+ }
+
+ if (k != 0) { // il y a des valeurs sur la ligne
+ //La premiere ligne determine combien de colonnes aura notre tableau de retour
+ if (lineNumber == 0) {
+ columnNumber = k;
+ retour = new double[columnNumber][maxLineNumber]; // Allocation du tableau de retour
+ } else if ( columnNumber != k ) // Toutes les lignes de la String n'ont pas le meme nombre de valeurs
+ throw new IllegalArgumentException("Each line must have the same number of values");
+
+ if (lineNumber >= maxLineNumber) { // Cas ou notre tableau initial n'est plus assez grand
+ maxLineNumber = maxLineNumber * 2;
+ double[][] temp = new double[columnNumber][maxLineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < retour[0].length; l++)
+ temp[i][l] = retour[i][l];
+ retour = temp;
+ }
+
+ for (int i = 0; i < columnNumber; i++)
+ retour[i][lineNumber] = line[i];
+ lineNumber++;
+ }
+ }
+ }
+ // Cree un tableau aux dimensions exactes
+ double[][] temp;
+ if (columnNumber == 0)
+ temp = null;
+ else
+ temp = new double[columnNumber][lineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < lineNumber; l++)
+ temp[i][l] = retour[i][l];
+ retour = null;
+ return temp;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Parses \texttt{data} according to the standard CSV format
+ and stores the extracted values in the returned table.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{String to parse.}
+ \return{Table that represent data contained in \texttt{data}.}
+ \exception{IllegalArgumentException}{if each line of the input \texttt{String} doesn't share the same number of values}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] fromCustomizedFormat (String betweenValues,
+ String endLine, String data) \begin{hide} {
+ int maxLineNumber = 16;
+ int columnNumber = 0;
+ int lineNumber = 0;
+ double[][] retour = new double[1][1]; //initialisation temporaire
+
+ Pattern p = Pattern.compile("(" + endLine + ")(" + endLine + ")*");
+ String[] string = p.split(data); // A chaque String correspond une ligne
+
+ for (int j = 0; j < string.length; j++) {
+ if (string[j].length() != 0) {
+ //On ne traite pas les lignes vides
+
+ p = Pattern.compile("(" + betweenValues + ")(" + betweenValues + ")*");
+ String[] result = p.split(string[j]);
+ double[] line = new double[result.length];
+
+ int k = 0; // Compte le nombre de valeurs sur la ligne courante
+ for (int i = 0; i < result.length; i++) {
+ try {
+ line[k] = Double.valueOf(result[i]); // les chaines de caracteres qui ne sont pas des nombres sont ignorees
+ k++;
+ } catch (NumberFormatException nfe) {}
+ }
+
+ if (k != 0) { // il y a des valeurs sur la ligne
+ //La premiere ligne determine combien de colonnes aura notre tableau de retour
+ if (lineNumber == 0) {
+ columnNumber = k;
+ retour = new double[columnNumber][maxLineNumber]; // Allocation du tableau de retour
+ } else if ( columnNumber != k ) // Toutes les lignes de la String n'ont pas le meme nombre de valeurs
+ throw new IllegalArgumentException("Each line must have the same number of values");
+
+ if (lineNumber >= maxLineNumber) { // Cas ou notre tableau initial n'est plus assez grand
+ maxLineNumber = maxLineNumber * 2;
+ double[][] temp = new double[columnNumber][maxLineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < retour[0].length; l++)
+ temp[i][l] = retour[i][l];
+ retour = temp;
+ }
+
+ for (int i = 0; i < columnNumber; i++)
+ retour[i][lineNumber] = line[i];
+ lineNumber++;
+ }
+ }
+ }
+ // Cree un tableau aux dimensions exactes
+ double[][] temp;
+ if (columnNumber == 0)
+ temp = null;
+ else
+ temp = new double[columnNumber][lineNumber];
+ for (int i = 0; i < columnNumber; i++)
+ for (int l = 0; l < lineNumber; l++)
+ temp[i][l] = retour[i][l];
+ retour = null;
+ return temp;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Parses \texttt{data} according to a user defined format and stores the
+ extracted values in the returned table. \texttt{betweenValues} sets
+ characters between values on the same line and \texttt{endLine} sets
+ characters which separates each line of the input data set. Usually, this
+ parameter contains the classic end of line character \texttt{\%n}.
+ This method uses regular expressions, so it is possible to use regular
+ characters as defined in the standard java API, specially in the class
+ \class{Pattern} (package java.util.regex).
+\end{tabb}
+\begin{tabb}
+\end{tabb}
+\begin{htmlonly}
+ \param{betweenValues}{\texttt{String} which separates values on the same line.}
+ \param{endLine}{\texttt{String} which separates lines.}
+ \param{data}{String to parse.}
+ \return{Table that represent data contained in \texttt{data}.}
+ \exception{IllegalArgumentException}{if each line of the input \texttt{String} doesn't share the same number of values}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data export functions}
+
+\begin{code}
+
+ public static String toGNUPlot (double[]... data) \begin{hide} {
+ checkData(data);
+ Formatter formatter = new Formatter(Locale.US);
+
+ for (int i = 0; i < data[0].length; i++) {
+ for (int j = 0; j < data.length; j++) {
+ formatter.format("%20f", data[j][i]);
+ }
+ formatter.format("%n");
+ }
+ formatter.format("%n%n");
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Stores data tables \texttt{data} into a \texttt{String},
+ with format understandable by GNUPlot.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{data tables.}
+ \return{String that represent data tables in GNUPlot format.}
+\end{htmlonly}
+\begin{code}
+
+ public static String toGNUPlot (XYSeriesCollection data) \begin{hide} {
+ return toGNUPlot(toTable(data));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Stores series collection \texttt{data} into a \texttt{String},
+ with format understandable by GNUPlot.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{data tables.}
+ \return{String that represent data tables in GNUPlot format.}
+\end{htmlonly}
+\begin{code}
+
+ public static String toCSV (double[]...data) \begin{hide} {
+ checkData(data);
+ Formatter formatter = new Formatter(Locale.US);
+
+ for (int i = 0; i < data[0].length; i++) {
+ for (int j = 0; j < data.length - 1; j++) {
+ formatter.format("%-20f, ", data[j][i]);
+ }
+ formatter.format("%-20f%n", data[data.length-1][i]); //le dernier
+ }
+ formatter.format("%n");
+
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Stores data tables \texttt{data} into a \texttt{String}
+ with format CSV (comma-separated value tabular data).
+ The output string could be imported from a file to a matrix
+ into Mathematica with Mathematica's function \texttt{Import["fileName", "CSV"]},
+ or into MATLAB with MATLAB's function \texttt{csvread('fileName')}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{data tables.}
+ \return{String that represent data tables in CSV format.}
+\end{htmlonly}
+\begin{code}
+
+ public static String toCSV (XYSeriesCollection data) \begin{hide} {
+ return toCSV(toTable(data));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Stores series collection \texttt{data} into a \texttt{String}
+ with format CSV (comma-separated value tabular data).
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{data tables.}
+ \return{String that represent data tables in CSV format.}
+\end{htmlonly}
+\begin{code}
+
+ public static String toCustomizedFormat (String heading, String footer,
+ String betweenValues, String endLine,
+ int precision, double[]...data) \begin{hide} {
+ checkData(data);
+ Formatter formatter = new Formatter(Locale.US);
+ String myString = "%20."+ precision +"f";
+
+ formatter.format("%s", heading);
+ for (int i = 0; i < data[0].length; i++) {
+ for (int j = 0; j < data.length - 1; j++) {
+ formatter.format(myString+"%s", data[j][i], betweenValues);
+ }
+ formatter.format(myString+"%s", data[data.length-1][i], endLine); //le dernier de la ligne
+ }
+ formatter.format("%s", footer);
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Stores data tables \texttt{data} into a \texttt{String} with customized format.
+\texttt{heading} sets the head of the returned \texttt{String}, \texttt{footer}
+sets its end, \texttt{betweenValues} sets characters between values on the same
+ line and finally \texttt{endLine} sets characters which separates each line of
+ the input data set. Normally, this parameter contains the classic end of line
+ character `\%n'.
+\end{tabb}
+\begin{htmlonly}
+ \param{heading}{head of the returned \texttt{String}.}
+ \param{footer}{end of the returned \texttt{String}.}
+ \param{betweenValues}{\texttt{String} which separates values on the same line.}
+ \param{endLine}{\texttt{String} which separates lines.}
+ \param{data}{data tables.}
+ \return{String that represent data tables in customized format.}
+\end{htmlonly}
+\begin{code}
+
+ public static String toCustomizedFormat (String heading, String footer,
+ String betweenValues, String endLine,
+ int precision, XYSeriesCollection data) \begin{hide} {
+ return toCustomizedFormat (heading, footer, betweenValues, endLine,
+ precision, toTable(data));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Stores data tables \texttt{data} into a \texttt{String}
+ with customized format from an \texttt{XYSeriesCollection} variable.
+\end{tabb}
+\begin{htmlonly}
+ \param{heading}{head of the returned \texttt{String}.}
+ \param{footer}{end of the returned \texttt{String}.}
+ \param{betweenValues}{\texttt{String} which separates values on the same line.}
+ \param{endLine}{\texttt{String} which separates lines.}
+ \param{data}{data tables.}
+ \return{String that represent data tables in customized format.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ /**
+ * Converts a <TT>XYSeriesCollection</TT> object into a <TT>double[][]</TT>.
+ */
+ private static double[][] toTable (XYSeriesCollection data) {
+ double[][] transform = new double[data.getSeriesCount()*2][];
+
+ for(int i = 0; i<data.getSeriesCount(); i++) {
+ double[][] temp = data.getSeries(i).toArray();
+ transform[2*i] = temp[0];
+ transform[2*i+1] = temp[1];
+ }
+ return transform;
+ }
+
+
+ /**
+ * Check the data table <TT>data</TT>.
+ *
+ * @exception IllegalArgumentException If the input tables doesn't have the same length.
+ */
+ private static void checkData(double[]...data) {
+ for(int i = 0; i < data.length-1; i++) {
+ if(data[i].length != data[i+1].length)
+ throw new IllegalArgumentException("Data tables " + i + " and " + (i+1) + " must share the same length");
+ }
+ }
+
+/* //Inutile si on exporte en CSV
+ private static String mathematicaFormatPoint (double x, double y) {
+ // Writes the pair (x, y) in returned string, in a format understood
+ // by Mathematica
+ StringBuffer sb = new StringBuffer();
+ String S;
+
+ sb.append (" { ");
+ if ((x != 0.0) && (x < 0.1 || x > 1.0)) {
+ S = PrintfFormat.E (16, 7, x);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, x);
+
+ sb.append (S + ", ");
+
+ if (y != 0.0 && (y < 0.1 || y > 1.0)) {
+ S = PrintfFormat.E (16, 7, y);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, y);
+
+ sb.append (S + " }");
+ return sb.toString();
+ }*/\end{hide}
+\end{code}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/QQPlot.java b/source/umontreal/iro/lecuyer/charts/QQPlot.java
new file mode 100644
index 0000000..9f1d8c4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/QQPlot.java
@@ -0,0 +1,171 @@
+
+
+/*
+ * Class: QQPlot
+ * Description: qq-plot
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;
+ import java.util.Arrays;
+
+
+
+/**
+ * This class implements <SPAN CLASS="textit">QQ-plot</SPAN> (or quantile-quantile plot)
+ * objects that compare two probability distributions.
+ * The data is given as a list of <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>n</SUB>)</SPAN>,
+ * and one is given a reference continuous probability distribution <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ * One first sorts the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> in ascending order, then noted <SPAN CLASS="MATH"><I>x</I><SUB>(i)</SUB></SPAN>, and
+ * plots the points
+ * <SPAN CLASS="MATH">(<I>F</I><SUP>-1</SUP>(<I>p</I><SUB>i</SUB>), <I>x</I><SUB>(i)</SUB>)</SPAN>, where
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 1, 2,…, <I>n</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>p</I><SUB>i</SUB> = (<I>i</I> - 1/2)/<I>n</I></SPAN>,
+ * to see if the data <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> comes from the reference distribution <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ * The graph of the straight line <SPAN CLASS="MATH"><I>y</I> = <I>x</I></SPAN> is also plotted for comparison.
+ *
+ */
+public class QQPlot extends XYLineChart {
+ private double[][] Q; // data points
+ private double[][] Lin; // line y = x
+
+ private void initLinear (double a, double b)
+ {
+ // line y = x in [a, b] by steps of h
+ int m = 100;
+ double h = (b - a)/ m;
+ Lin = new double[2][m+1];
+ for (int i = 0; i <= m; i++)
+ Lin[0][i] = Lin[1][i] = a + h * i;
+ }
+
+
+ private void initPoints (ContinuousDistribution dist, double[] data,
+ int numPoints)
+ {
+ int i;
+ double p;
+ Q = new double[2][numPoints]; // q_i = cdf^(-1)(p_i)
+
+ for (i = 0; i < numPoints; i++)
+ Q[1][i] = data[i];
+ Arrays.sort(Q[1]);
+ for (i = 0; i < numPoints; i++) {
+ p = (i + 0.5)/numPoints;
+ Q[0][i] = dist.inverseF(p);
+ }
+ }
+
+
+ /**
+ * Constructs a new <TT>QQPlot</TT> instance using the points <TT>X</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The plot is a QQ-plot of the points
+ *
+ * <SPAN CLASS="MATH">(<I>F</I><SUP>-1</SUP>(<I>p</I><SUB>i</SUB>), <I>x</I><SUB>(i)</SUB>)</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1, 2,…, <I>n</I></SPAN>, where
+ * <SPAN CLASS="MATH"><I>p</I><SUB>i</SUB> = (<I>i</I> - 1/2)/<I>n</I></SPAN>,
+ * <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB> =</SPAN><TT> X[<SPAN CLASS="MATH"><I>i</I></SPAN>-1]</TT>, <SPAN CLASS="MATH"><I>x</I><SUB>(i)</SUB></SPAN> are the sorted points,
+ * and
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>p</I>) =</SPAN><TT> dist.inverseF(<SPAN CLASS="MATH"><I>p</I></SPAN>)</TT>. The points <TT>X</TT> are not sorted.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param dist Reference distribution
+ *
+ * @param X points.
+ *
+ *
+ */
+ public QQPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X) {
+ this (title, XLabel, YLabel, dist, X, X.length);
+ }
+
+
+ /**
+ * Similar to the constructor {@link #QQPlot(String,String,String,ContinuousDistribution,double[]) QQPlot}<TT>(title, XLabel, YLabel, dist, X)</TT> above, except that only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>X</TT>
+ * are plotted.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param dist Reference distribution
+ *
+ * @param X point set.
+ *
+ * @param numPoints number of points to plot
+ *
+ *
+ */
+ public QQPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X, int numPoints) {
+ super();
+ initPoints (dist, X, numPoints);
+ initLinear (Q[1][0], Q[1][numPoints-1]);
+ dataset = new XYListSeriesCollection(Q, Lin);
+ // --- dashed line for y = x
+ ((XYListSeriesCollection)dataset).setDashPattern(1, "dashed");
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Constructs a new <TT>QQPlot</TT> instance.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input vectors in <TT>data</TT> represents several sets of <SPAN CLASS="MATH"><I>x</I></SPAN>-points.
+ * <SPAN CLASS="MATH"><I>r</I></SPAN> determine the set of points to be plotted in the QQ-plot, that is,
+ * one will plot only the points <TT>data[r][i]</TT>,
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…,(<I>n</I> - 1)</SPAN> and a given <SPAN CLASS="MATH"><I>r</I></SPAN>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number
+ * of points in set <SPAN CLASS="MATH"><I>r</I></SPAN>. The points are assumed to follow the distribution
+ * <TT>dist</TT>.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param dist Reference distribution
+ *
+ * @param data series of point sets.
+ *
+ * @param r set of points to plot
+ *
+ */
+ public QQPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[][] data, int r) {
+ this (title, XLabel, YLabel, dist, data[r], data[r].length);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/QQPlot.tex b/source/umontreal/iro/lecuyer/charts/QQPlot.tex
new file mode 100644
index 0000000..c6a7803
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/QQPlot.tex
@@ -0,0 +1,161 @@
+\defmodule {QQPlot}
+
+This class implements \emph{QQ-plot} (or quantile-quantile plot)
+objects that compare two probability distributions.
+The data is given as a list of $x$-coordinates $(x_1, x_2, \ldots, x_{n})$,
+and one is given a reference continuous probability distribution $F(x)$.
+One first sorts the $x_i$ in ascending order, then noted $x_{(i)}$, and
+plots the points $(F^{-1}(p_i), x_{(i)})$, where
+$i= 1, 2, \ldots, n$ and $p_i = (i- 1/2)/n$,
+to see if the data $x_i$ comes from the reference distribution $F(x)$.
+The graph of the straight line $y=x$ is also plotted for comparison.
+
+
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: QQPlot
+ * Description: qq-plot
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;\begin{hide}
+ import java.util.Arrays;
+\end{hide}
+
+
+public class QQPlot extends XYLineChart \begin{hide} {
+ private double[][] Q; // data points
+ private double[][] Lin; // line y = x
+
+ private void initLinear (double a, double b)
+ {
+ // line y = x in [a, b] by steps of h
+ int m = 100;
+ double h = (b - a)/ m;
+ Lin = new double[2][m+1];
+ for (int i = 0; i <= m; i++)
+ Lin[0][i] = Lin[1][i] = a + h * i;
+ }
+
+
+ private void initPoints (ContinuousDistribution dist, double[] data,
+ int numPoints)
+ {
+ int i;
+ double p;
+ Q = new double[2][numPoints]; // q_i = cdf^(-1)(p_i)
+
+ for (i = 0; i < numPoints; i++)
+ Q[1][i] = data[i];
+ Arrays.sort(Q[1]);
+ for (i = 0; i < numPoints; i++) {
+ p = (i + 0.5)/numPoints;
+ Q[0][i] = dist.inverseF(p);
+ }
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public QQPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X) \begin{hide} {
+ this (title, XLabel, YLabel, dist, X, X.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new \texttt{QQPlot} instance using the points \texttt{X}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The plot is a QQ-plot of the points
+ $(F^{-1}(p_i), x_{(i)})$, $i= 1, 2, \ldots, n$, where $p_i = (i- 1/2)/n$,
+ $x_i = $\texttt{ X[$i$-1]}, $x_{(i)}$ are the sorted points,
+ and $x = F^{-1}(p) =
+ $\texttt{ dist.inverseF($p$)}. The points \texttt{X} are not sorted.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{dist}{Reference distribution}
+ \param{X}{points.}
+\end{htmlonly}
+\begin{code}
+
+ public QQPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[] X, int numPoints) \begin{hide} {
+ super();
+ initPoints (dist, X, numPoints);
+ initLinear (Q[1][0], Q[1][numPoints-1]);
+ dataset = new XYListSeriesCollection(Q, Lin);
+ // --- dashed line for y = x
+ ((XYListSeriesCollection)dataset).setDashPattern(1, "dashed");
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to the constructor %
+ \method{QQPlot}{String,String,String,ContinuousDistribution,double[]}%
+ \texttt{(title, XLabel, YLabel, dist, X)} %
+ above, except that only \emph{the first} \texttt{numPoints} of \texttt{X}
+ are plotted.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{dist}{Reference distribution}
+ \param{X}{point set.}
+ \param{numPoints}{number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public QQPlot (String title, String XLabel, String YLabel,
+ ContinuousDistribution dist, double[][] data, int r) \begin{hide} {
+ this (title, XLabel, YLabel, dist, data[r], data[r].length);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new \texttt{QQPlot} instance.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The input vectors in \texttt{data} represents several sets of $x$-points.
+ $r$ determine the set of points to be plotted in the QQ-plot, that is,
+ one will plot only the points \texttt{data[r][i]},
+ for $i=0, 1, \ldots, (n-1)$ and a given $r$, where $n$ is the number
+ of points in set $r$. The points are assumed to follow the distribution
+ \texttt{dist}.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{dist}{Reference distribution}
+ \param{data}{series of point sets.}
+ \param{r}{set of points to plot}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/charts/SSJCategorySeriesCollection.java b/source/umontreal/iro/lecuyer/charts/SSJCategorySeriesCollection.java
new file mode 100644
index 0000000..1876132
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/SSJCategorySeriesCollection.java
@@ -0,0 +1,294 @@
+
+
+/*
+ * Class: SSJCategorySeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.data.category.CategoryDataset;
+import org.jfree.chart.renderer.category.CategoryItemRenderer;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.awt.Color;
+
+/**
+ * Stores data used in a <TT>CategoryChart</TT>.
+ * This class provides tools to manage data sets and rendering options, and modify
+ * plot color, plot style, and marks on points for each series.
+ *
+ */
+public abstract class SSJCategorySeriesCollection {
+ protected CategoryItemRenderer renderer;
+ protected CategoryDataset seriesCollection;
+
+
+
+ /**
+ * Returns the category-value in the specified series.
+ *
+ * @param series required series value.
+ *
+ * @return <SPAN CLASS="MATH"><I>x</I></SPAN>-value at the specified index in the specified series.
+ *
+ */
+ public String getCategory (int series) {
+ return seriesCollection.getColumnKey(series).toString();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>y</I></SPAN>-value at the specified index in the specified series.
+ *
+ * @param series required series value.
+ *
+ * @param index value's index.
+ *
+ * @return <SPAN CLASS="MATH"><I>y</I></SPAN>-value at the specified index in the specified series.
+ *
+ */
+ public double getValue (int series, int index) {
+ return (Double)seriesCollection.getValue(series, index);
+ }
+
+
+ /**
+ * Returns the <TT>CategoryDataset</TT> object associated with the current object.
+ *
+ * @return <TT>CategoryDataset</TT> object associated with the current variable.
+ *
+ */
+ public CategoryDataset getSeriesCollection() {
+ return seriesCollection;
+ }
+
+
+ /**
+ * Returns range (<SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates) min and max values.
+ *
+ * @return range min and max values.
+ *
+ */
+ public abstract double[] getRangeBounds();
+
+
+ /**
+ * Returns in a <TT>String</TT> all data contained in the current object.
+ *
+ * @return All data contained in the current object as a {@link String}.
+ *
+ */
+ public abstract String toString();
+
+
+ /**
+ * Returns the <TT>CategoryItemRenderer</TT> object associated with the current object.
+ *
+ * @return <TT>CategoryItemRenderer</TT> object associated with the current variable.
+ *
+ */
+ public CategoryItemRenderer getRenderer() {
+ return renderer;
+ }
+
+
+ /**
+ * Sets the <TT>CategoryItemRenderer</TT> object associated with the current variable.
+ * This object determines the chart JFreeChart look, produced by method
+ * <TT>view</TT> in class
+ * {@link umontreal.iro.lecuyer.charts.XYChart XYChart}.
+ *
+ * @param renderer new <TT>CategoryItemRenderer</TT> object.
+ *
+ *
+ */
+ public void setRenderer (CategoryItemRenderer renderer) {
+ this.renderer = renderer;
+ }
+
+
+ /**
+ * Gets the current plotting color of the selected series.
+ *
+ * @return current plotting color.
+ *
+ */
+ public Color getColor (int series) {
+ return (Color)renderer.getSeriesPaint(series);
+ }
+
+
+ /**
+ * Sets a new plotting color to the series <SPAN CLASS="MATH"><I>series</I></SPAN>.
+ *
+ * @param series series index.
+ *
+ * @param color plotting color.
+ *
+ *
+ */
+ public void setColor (int series, Color color) {
+ renderer.setSeriesPaint(series, color);
+ }
+
+
+ /**
+ * Formats and returns a string containing a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN>-compatible source
+ * code which represents this data series collection. The original datasets are shifted and scaled with the
+ * <TT>YShift</TT> and <TT>YScale</TT> parameters.
+ * <TT>ymin</TT> and <TT>ymax</TT> represent the chart bounds.
+ *
+ * @param YScale Range original data scale.
+ *
+ * @param YShift Range original data shift value.
+ *
+ * @param ymin Range min bound.
+ *
+ * @param ymax Range max bound.
+ *
+ * @return Latex code.
+ *
+ */
+ public abstract String toLatex (double YScale, double YShift,
+ double ymin, double ymax);
+
+
+
+ /* *
+ * Converts a java Color object into a friendly and readable LaTeX/xcolor string.
+ *
+ * @param color in color.
+ * @return friendly color with string format as possible, null otherwise.
+ */
+ protected static String detectXColorClassic(Color color) {
+ String retour = null;
+
+ int red = color.getRed();
+ int green = color.getGreen();
+ int blue = color.getBlue();
+
+ // On utilise pas la method Color.equals(Color ) car on ne veut pas tester le parametre de transparence : Alpha
+ if ( red == Color.GREEN.getRed()
+ && blue == Color.GREEN.getBlue()
+ && green == Color.GREEN.getGreen())
+ return "green";
+ else if ( red == Color.RED.getRed()
+ && blue == Color.RED.getBlue()
+ && green == Color.RED.getGreen())
+ return "red";
+ else if ( red == Color.WHITE.getRed()
+ && blue == Color.WHITE.getBlue()
+ && green == Color.WHITE.getGreen())
+ return "white";
+ else if ( red == Color.GRAY.getRed()
+ && blue == Color.GRAY.getBlue()
+ && green == Color.GRAY.getGreen())
+ return "gray";
+ else if ( red == Color.BLACK.getRed()
+ && blue == Color.BLACK.getBlue()
+ && green == Color.BLACK.getGreen())
+ return "black";
+ else if ( red == Color.YELLOW.getRed()
+ && blue == Color.YELLOW.getBlue()
+ && green == Color.YELLOW.getGreen())
+ return "yellow";
+ else if ( red == Color.MAGENTA.getRed()
+ && blue == Color.MAGENTA.getBlue()
+ && green == Color.MAGENTA.getGreen())
+ return "magenta";
+ else if ( red == Color.CYAN.getRed()
+ && blue == Color.CYAN.getBlue()
+ && green == Color.CYAN.getGreen())
+ return "cyan";
+ else if ( red == Color.BLUE.getRed()
+ && blue == Color.BLUE.getBlue()
+ && green == Color.BLUE.getGreen())
+ return "blue";
+ else if ( red == Color.DARK_GRAY.getRed()
+ && blue == Color.DARK_GRAY.getBlue()
+ && green == Color.DARK_GRAY.getGreen())
+ return "darkgray";
+ else if ( red == Color.LIGHT_GRAY.getRed()
+ && blue == Color.LIGHT_GRAY.getBlue()
+ && green == Color.LIGHT_GRAY.getGreen())
+ return "lightgray";
+ else if ( red == Color.ORANGE.getRed()
+ && blue == Color.ORANGE.getBlue()
+ && green == Color.ORANGE.getGreen())
+ return "orange";
+ else if ( red == Color.PINK.getRed()
+ && blue == Color.PINK.getBlue()
+ && green == Color.PINK.getGreen())
+ return "pink";
+
+
+ if (red == 192 && blue == 128 && green == 64)
+ return "brown";
+ else if (red == 128 && blue == 128 && green == 0)
+ return "olive";
+ else if (red == 128 && blue == 0 && green == 128)
+ return "violet";
+ else if (red == 192 && blue == 0 && green ==64)
+ return "purple";
+ else return null;
+ }
+
+ /* *
+ * Gives the default color associated with a series
+ *
+ * @param index Index of the series in the CategoryDataset object.
+ * @return default color object.
+ */
+ protected static Color getDefaultColor(int index) {
+ if(index%6 == 0)
+ return Color.RED;
+ else if(index%6 == 1)
+ return Color.BLUE;
+ else if(index%6 == 2)
+ return Color.GREEN;
+ else if(index%6 == 3)
+ return Color.YELLOW;
+ else if(index%6 == 4)
+ return Color.MAGENTA;
+ else
+ return Color.CYAN;
+ }
+
+ // Returns maximum value in table t
+ protected static double max (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux ;
+ }
+ // Returns minimum value in table t
+ protected static double min (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux ;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/SSJCategorySeriesCollection.tex b/source/umontreal/iro/lecuyer/charts/SSJCategorySeriesCollection.tex
new file mode 100644
index 0000000..0da10b6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/SSJCategorySeriesCollection.tex
@@ -0,0 +1,306 @@
+\defmodule {SSJCategorySeriesCollection}
+
+Stores data used in a \texttt{CategoryChart}.
+This class provides tools to manage data sets and rendering options, and modify
+plot color, plot style, and marks on points for each series.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: SSJCategorySeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.data.category.CategoryDataset;
+import org.jfree.chart.renderer.category.CategoryItemRenderer;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.awt.Color;\end{hide}
+
+public abstract class SSJCategorySeriesCollection \begin{hide} {
+ protected CategoryItemRenderer renderer;
+ protected CategoryDataset seriesCollection;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data control methods}
+
+\begin{code}
+
+ public String getCategory (int series) \begin{hide} {
+ return seriesCollection.getColumnKey(series).toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the category-value in the specified series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{required series value.}
+ \return{$x$-value at the specified index in the specified series.}
+\end{htmlonly}
+\begin{code}
+
+ public double getValue (int series, int index) \begin{hide} {
+ return (Double)seriesCollection.getValue(series, index);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $y$-value at the specified index in the specified series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{required series value.}
+ \param{index}{value's index.}
+ \return{$y$-value at the specified index in the specified series.}
+\end{htmlonly}
+\begin{code}
+
+ public CategoryDataset getSeriesCollection() \begin{hide} {
+ return seriesCollection;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{CategoryDataset} object associated with the current object.
+\end{tabb}
+\begin{htmlonly}
+ \return{\texttt{CategoryDataset} object associated with the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract double[] getRangeBounds();
+\end{code}
+\begin{tabb}
+ Returns range ($y$-coordinates) min and max values.
+\end{tabb}
+\begin{htmlonly}
+ \return{range min and max values.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract String toString();
+\end{code}
+\begin{tabb}
+ Returns in a \texttt{String} all data contained in the current object.
+\end{tabb}
+\begin{htmlonly}
+ \return{All data contained in the current object as a \class{String}.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Rendering methods}
+
+\begin{code}
+
+ public CategoryItemRenderer getRenderer() \begin{hide} {
+ return renderer;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{CategoryItemRenderer} object associated with the current object.
+\end{tabb}
+\begin{htmlonly}
+ \return{\texttt{CategoryItemRenderer} object associated with the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public void setRenderer (CategoryItemRenderer renderer) \begin{hide} {
+ this.renderer = renderer;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the \texttt{CategoryItemRenderer} object associated with the current variable.
+ This object determines the chart JFreeChart look, produced by method
+ \texttt{view} in class
+ \externalclass{umontreal.iro.lecuyer.charts}{XYChart}.
+\end{tabb}
+\begin{htmlonly}
+ \param{renderer}{ new \texttt{CategoryItemRenderer} object.}
+\end{htmlonly}
+\begin{code}
+
+ public Color getColor (int series) \begin{hide} {
+ return (Color)renderer.getSeriesPaint(series);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current plotting color of the selected series.
+\end{tabb}
+\begin{htmlonly}
+ \return{current plotting color.}
+\end{htmlonly}
+\begin{code}
+
+ public void setColor (int series, Color color) \begin{hide} {
+ renderer.setSeriesPaint(series, color);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets a new plotting color to the series $series$.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{color}{plotting color.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract String toLatex (double YScale, double YShift,
+ double ymin, double ymax);
+\end{code}
+\begin{tabb}
+ Formats and returns a string containing a \LaTeX-compatible source
+ code which represents this data series collection. % and its parameters.
+ The original datasets are shifted and scaled with the
+ \texttt{YShift} and \texttt{YScale} parameters.
+ \texttt{ymin} and \texttt{ymax} represent the chart bounds.
+\end{tabb}
+\begin{htmlonly}
+ \param{YScale}{Range original data scale.}
+ \param{YShift}{Range original data shift value.}
+ \param{ymin}{Range min bound.}
+ \param{ymax}{Range max bound.}
+ \return{Latex code.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+
+ /**
+ * Converts a java Color object into a friendly and readable LaTeX/xcolor string.
+ *
+ * @param color in color.
+ * @return friendly color with string format as possible, null otherwise.
+ */
+ protected static String detectXColorClassic(Color color) {
+ String retour = null;
+
+ int red = color.getRed();
+ int green = color.getGreen();
+ int blue = color.getBlue();
+
+ // On utilise pas la method Color.equals(Color ) car on ne veut pas tester le parametre de transparence : Alpha
+ if ( red == Color.GREEN.getRed()
+ && blue == Color.GREEN.getBlue()
+ && green == Color.GREEN.getGreen())
+ return "green";
+ else if ( red == Color.RED.getRed()
+ && blue == Color.RED.getBlue()
+ && green == Color.RED.getGreen())
+ return "red";
+ else if ( red == Color.WHITE.getRed()
+ && blue == Color.WHITE.getBlue()
+ && green == Color.WHITE.getGreen())
+ return "white";
+ else if ( red == Color.GRAY.getRed()
+ && blue == Color.GRAY.getBlue()
+ && green == Color.GRAY.getGreen())
+ return "gray";
+ else if ( red == Color.BLACK.getRed()
+ && blue == Color.BLACK.getBlue()
+ && green == Color.BLACK.getGreen())
+ return "black";
+ else if ( red == Color.YELLOW.getRed()
+ && blue == Color.YELLOW.getBlue()
+ && green == Color.YELLOW.getGreen())
+ return "yellow";
+ else if ( red == Color.MAGENTA.getRed()
+ && blue == Color.MAGENTA.getBlue()
+ && green == Color.MAGENTA.getGreen())
+ return "magenta";
+ else if ( red == Color.CYAN.getRed()
+ && blue == Color.CYAN.getBlue()
+ && green == Color.CYAN.getGreen())
+ return "cyan";
+ else if ( red == Color.BLUE.getRed()
+ && blue == Color.BLUE.getBlue()
+ && green == Color.BLUE.getGreen())
+ return "blue";
+ else if ( red == Color.DARK_GRAY.getRed()
+ && blue == Color.DARK_GRAY.getBlue()
+ && green == Color.DARK_GRAY.getGreen())
+ return "darkgray";
+ else if ( red == Color.LIGHT_GRAY.getRed()
+ && blue == Color.LIGHT_GRAY.getBlue()
+ && green == Color.LIGHT_GRAY.getGreen())
+ return "lightgray";
+ else if ( red == Color.ORANGE.getRed()
+ && blue == Color.ORANGE.getBlue()
+ && green == Color.ORANGE.getGreen())
+ return "orange";
+ else if ( red == Color.PINK.getRed()
+ && blue == Color.PINK.getBlue()
+ && green == Color.PINK.getGreen())
+ return "pink";
+
+
+ if (red == 192 && blue == 128 && green == 64)
+ return "brown";
+ else if (red == 128 && blue == 128 && green == 0)
+ return "olive";
+ else if (red == 128 && blue == 0 && green == 128)
+ return "violet";
+ else if (red == 192 && blue == 0 && green ==64)
+ return "purple";
+ else return null;
+ }
+
+ /**
+ * Gives the default color associated with a series
+ *
+ * @param index Index of the series in the CategoryDataset object.
+ * @return default color object.
+ */
+ protected static Color getDefaultColor(int index) {
+ if(index%6 == 0)
+ return Color.RED;
+ else if(index%6 == 1)
+ return Color.BLUE;
+ else if(index%6 == 2)
+ return Color.GREEN;
+ else if(index%6 == 3)
+ return Color.YELLOW;
+ else if(index%6 == 4)
+ return Color.MAGENTA;
+ else
+ return Color.CYAN;
+ }
+
+ // Returns maximum value in table t
+ protected static double max (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux ;
+ }
+ // Returns minimum value in table t
+ protected static double min (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux ;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/SSJXYSeriesCollection.java b/source/umontreal/iro/lecuyer/charts/SSJXYSeriesCollection.java
new file mode 100644
index 0000000..aa35688
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/SSJXYSeriesCollection.java
@@ -0,0 +1,353 @@
+
+
+/*
+ * Class: SSJXYSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.data.xy.XYDataset;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.awt.Color;
+
+/**
+ * Stores data used in a <TT>XYChart</TT>.
+ * This class provides tools to manage data sets and rendering options, and modify
+ * plot color, plot style, and marks on points for each series.
+ *
+ */
+public abstract class SSJXYSeriesCollection {
+ protected XYItemRenderer renderer;
+ protected XYDataset seriesCollection;
+
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>x</I></SPAN>-value at the specified index in the specified series.
+ *
+ * @param series required series value.
+ *
+ * @param index value's index.
+ *
+ * @return <SPAN CLASS="MATH"><I>x</I></SPAN>-value at the specified index in the specified series.
+ *
+ */
+ public double getX (int series, int index) {
+ return seriesCollection.getXValue(series, index);
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>y</I></SPAN>-value at the specified index in the specified series.
+ *
+ * @param series required series value.
+ *
+ * @param index value's index.
+ *
+ * @return <SPAN CLASS="MATH"><I>y</I></SPAN>-value at the specified index in the specified series.
+ *
+ */
+ public double getY (int series, int index) {
+ return seriesCollection.getYValue(series, index);
+ }
+
+
+ /**
+ * Returns the <TT>XYDataset</TT> object associated with the current object.
+ *
+ * @return <TT>XYDataset</TT> object associated with the current variable.
+ *
+ */
+ public XYDataset getSeriesCollection() {
+ return seriesCollection;
+ }
+
+
+ /**
+ * Returns domain (<SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates) min and max values.
+ *
+ * @return domain min and max values.
+ *
+ */
+ public double[] getDomainBounds() {
+ double max=-1.0e307, min=1.0e307;
+
+ if(seriesCollection.getSeriesCount() != 0 && seriesCollection.getItemCount(0) != 0)
+ max = min = seriesCollection.getXValue(0, 0);
+
+ for(int i = 0; i < seriesCollection.getSeriesCount(); i++) {
+ for( int j = 0; j < seriesCollection.getItemCount(i); j++) {
+ max = Math.max(max, seriesCollection.getXValue(i, j));
+ min = Math.min(min, seriesCollection.getXValue(i, j));
+ }
+ }
+
+ double[] retour = {min, max};
+ return retour;
+ }
+
+
+ /**
+ * Returns range (<SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates) min and max values.
+ *
+ * @return range min and max values.
+ *
+ */
+ public double[] getRangeBounds() {
+ double max=-1.7e307, min=1.7e307;
+
+ if(seriesCollection.getSeriesCount() != 0 && seriesCollection.getItemCount(0) != 0)
+ max = min = seriesCollection.getYValue(0, 0);
+
+ for(int i = 0; i < seriesCollection.getSeriesCount(); i++) {
+ for( int j = 0; j < seriesCollection.getItemCount(i); j++) {
+ max = Math.max(max, seriesCollection.getYValue(i, j));
+ min = Math.min(min, seriesCollection.getYValue(i, j));
+ }
+ }
+
+ double[] retour = {min, max};
+ return retour;
+ }
+
+
+ /**
+ * Returns in a <TT>String</TT> all data contained in the current object.
+ *
+ * @return All data contained in the current object as a {@link String}.
+ *
+ */
+ public String toString() {
+ Formatter formatter = new Formatter(Locale.US);
+ for(int i = 0; i < seriesCollection.getSeriesCount(); i++) {
+ formatter.format(" Series " + i + " : %n");
+ for(int j = 0; j < seriesCollection.getItemCount(i); j++)
+ formatter.format("%15e,%15e%n", getX(i, j), getY(i, j));
+ }
+ return formatter.toString();
+ }
+
+
+ /**
+ * Returns the <TT>XYItemRenderer</TT> object associated with the current object.
+ *
+ * @return <TT>XYItemRenderer</TT> object associated with the current variable.
+ *
+ */
+ public XYItemRenderer getRenderer() {
+ return renderer;
+ }
+
+
+ /**
+ * Sets the <TT>XYItemRenderer</TT> object associated with the current variable.
+ * This object determines the chart JFreeChart look, produced by method
+ * <TT>view</TT> in class
+ * {@link umontreal.iro.lecuyer.charts.XYChart XYChart}.
+ *
+ * @param renderer new <TT>XYItemRenderer</TT> object.
+ *
+ *
+ */
+ public void setRenderer (XYItemRenderer renderer) {
+ this.renderer = renderer;
+ }
+
+
+ /**
+ * Gets the current plotting color of the selected series.
+ *
+ * @return current plotting color.
+ *
+ */
+ public Color getColor (int series) {
+ return (Color)renderer.getSeriesPaint(series);
+ }
+
+
+ /**
+ * Sets a new plotting color to the series <SPAN CLASS="MATH"><I>series</I></SPAN>.
+ *
+ * @param series series index.
+ *
+ * @param color plotting color.
+ *
+ *
+ */
+ public void setColor (int series, Color color) {
+ renderer.setSeriesPaint(series, color);
+ }
+
+
+ /**
+ * Formats and returns a string containing a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN>-compatible source
+ * code which represents this data series collection. The original datasets are shifted and scaled with the <TT>XShift</TT>,
+ * <TT>YShift</TT>, <TT>XScale</TT> and <TT>YScale</TT> parameters.
+ * <TT>xmin</TT>, <TT>xmax</TT>, <TT>ymin</TT> and <TT>ymax</TT> represent the chart bounds.
+ *
+ * @param XScale Domain original data scale.
+ *
+ * @param YScale Range original data scale.
+ *
+ * @param XShift Domain original data shift value.
+ *
+ * @param YShift Range original data shift value.
+ *
+ * @param xmin Domain min bound.
+ *
+ * @param xmax Domain nax bound.
+ *
+ * @param ymin Range min bound.
+ *
+ * @param ymax Range nax bound.
+ *
+ * @return TikZ code.
+ *
+ */
+ public abstract String toLatex (double XScale, double YScale,
+ double XShift, double YShift,
+ double xmin, double xmax,
+ double ymin, double ymax);
+
+
+
+ /* *
+ * Converts a java Color object into a friendly and readable LaTeX/xcolor string.
+ *
+ * @param color in color.
+ * @return friendly color with string format as possible, null otherwise.
+ */
+ protected static String detectXColorClassic(Color color) {
+ String retour = null;
+
+ int red = color.getRed();
+ int green = color.getGreen();
+ int blue = color.getBlue();
+
+ // On utilise pas la method Color.equals(Color ) car on ne veut pas tester le parametre de transparence : Alpha
+ if ( red == Color.GREEN.getRed()
+ && blue == Color.GREEN.getBlue()
+ && green == Color.GREEN.getGreen())
+ return "green";
+ else if ( red == Color.RED.getRed()
+ && blue == Color.RED.getBlue()
+ && green == Color.RED.getGreen())
+ return "red";
+ else if ( red == Color.WHITE.getRed()
+ && blue == Color.WHITE.getBlue()
+ && green == Color.WHITE.getGreen())
+ return "white";
+ else if ( red == Color.GRAY.getRed()
+ && blue == Color.GRAY.getBlue()
+ && green == Color.GRAY.getGreen())
+ return "gray";
+ else if ( red == Color.BLACK.getRed()
+ && blue == Color.BLACK.getBlue()
+ && green == Color.BLACK.getGreen())
+ return "black";
+ else if ( red == Color.YELLOW.getRed()
+ && blue == Color.YELLOW.getBlue()
+ && green == Color.YELLOW.getGreen())
+ return "yellow";
+ else if ( red == Color.MAGENTA.getRed()
+ && blue == Color.MAGENTA.getBlue()
+ && green == Color.MAGENTA.getGreen())
+ return "magenta";
+ else if ( red == Color.CYAN.getRed()
+ && blue == Color.CYAN.getBlue()
+ && green == Color.CYAN.getGreen())
+ return "cyan";
+ else if ( red == Color.BLUE.getRed()
+ && blue == Color.BLUE.getBlue()
+ && green == Color.BLUE.getGreen())
+ return "blue";
+ else if ( red == Color.DARK_GRAY.getRed()
+ && blue == Color.DARK_GRAY.getBlue()
+ && green == Color.DARK_GRAY.getGreen())
+ return "darkgray";
+ else if ( red == Color.LIGHT_GRAY.getRed()
+ && blue == Color.LIGHT_GRAY.getBlue()
+ && green == Color.LIGHT_GRAY.getGreen())
+ return "lightgray";
+ else if ( red == Color.ORANGE.getRed()
+ && blue == Color.ORANGE.getBlue()
+ && green == Color.ORANGE.getGreen())
+ return "orange";
+ else if ( red == Color.PINK.getRed()
+ && blue == Color.PINK.getBlue()
+ && green == Color.PINK.getGreen())
+ return "pink";
+
+
+ if (red == 192 && blue == 128 && green == 64)
+ return "brown";
+ else if (red == 128 && blue == 128 && green == 0)
+ return "olive";
+ else if (red == 128 && blue == 0 && green == 128)
+ return "violet";
+ else if (red == 192 && blue == 0 && green ==64)
+ return "purple";
+ else return null;
+ }
+
+ /* *
+ * Gives the default color associated with a series
+ *
+ * @param index Index of the series in the XYDataset object.
+ * @return default color object.
+ */
+ protected static Color getDefaultColor(int index) {
+ if(index%6 == 0)
+ return Color.RED;
+ else if(index%6 == 1)
+ return Color.BLUE;
+ else if(index%6 == 2)
+ return Color.GREEN;
+ else if(index%6 == 3)
+ return Color.YELLOW;
+ else if(index%6 == 4)
+ return Color.MAGENTA;
+ else
+ return Color.CYAN;
+ }
+
+ // Returns maximum value in table t
+ protected static double max (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux ;
+ }
+ // Returns minimum value in table t
+ protected static double min (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux ;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/SSJXYSeriesCollection.tex b/source/umontreal/iro/lecuyer/charts/SSJXYSeriesCollection.tex
new file mode 100644
index 0000000..20205ad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/SSJXYSeriesCollection.tex
@@ -0,0 +1,361 @@
+\defmodule {SSJXYSeriesCollection}
+
+Stores data used in a \texttt{XYChart}.
+This class provides tools to manage data sets and rendering options, and modify
+plot color, plot style, and marks on points for each series.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: SSJXYSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.data.xy.XYDataset;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.awt.Color;\end{hide}
+
+public abstract class SSJXYSeriesCollection \begin{hide} {
+ protected XYItemRenderer renderer;
+ protected XYDataset seriesCollection;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data control methods}
+
+\begin{code}
+
+ public double getX (int series, int index) \begin{hide} {
+ return seriesCollection.getXValue(series, index);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $x$-value at the specified index in the specified series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{required series value.}
+ \param{index}{value's index.}
+ \return{$x$-value at the specified index in the specified series.}
+\end{htmlonly}
+\begin{code}
+
+ public double getY (int series, int index) \begin{hide} {
+ return seriesCollection.getYValue(series, index);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $y$-value at the specified index in the specified series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{required series value.}
+ \param{index}{value's index.}
+ \return{$y$-value at the specified index in the specified series.}
+\end{htmlonly}
+\begin{code}
+
+ public XYDataset getSeriesCollection() \begin{hide} {
+ return seriesCollection;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{XYDataset} object associated with the current object.
+\end{tabb}
+\begin{htmlonly}
+ \return{\texttt{XYDataset} object associated with the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getDomainBounds() \begin{hide} {
+ double max=-1.0e307, min=1.0e307;
+
+ if(seriesCollection.getSeriesCount() != 0 && seriesCollection.getItemCount(0) != 0)
+ max = min = seriesCollection.getXValue(0, 0);
+
+ for(int i = 0; i < seriesCollection.getSeriesCount(); i++) {
+ for( int j = 0; j < seriesCollection.getItemCount(i); j++) {
+ max = Math.max(max, seriesCollection.getXValue(i, j));
+ min = Math.min(min, seriesCollection.getXValue(i, j));
+ }
+ }
+
+ double[] retour = {min, max};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns domain ($x$-coordinates) min and max values.
+\end{tabb}
+\begin{htmlonly}
+ \return{domain min and max values.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getRangeBounds() \begin{hide} {
+ double max=-1.7e307, min=1.7e307;
+
+ if(seriesCollection.getSeriesCount() != 0 && seriesCollection.getItemCount(0) != 0)
+ max = min = seriesCollection.getYValue(0, 0);
+
+ for(int i = 0; i < seriesCollection.getSeriesCount(); i++) {
+ for( int j = 0; j < seriesCollection.getItemCount(i); j++) {
+ max = Math.max(max, seriesCollection.getYValue(i, j));
+ min = Math.min(min, seriesCollection.getYValue(i, j));
+ }
+ }
+
+ double[] retour = {min, max};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns range ($y$-coordinates) min and max values.
+\end{tabb}
+\begin{htmlonly}
+ \return{range min and max values.}
+\end{htmlonly}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ Formatter formatter = new Formatter(Locale.US);
+ for(int i = 0; i < seriesCollection.getSeriesCount(); i++) {
+ formatter.format(" Series " + i + " : %n");
+ for(int j = 0; j < seriesCollection.getItemCount(i); j++)
+ formatter.format("%15e,%15e%n", getX(i, j), getY(i, j));
+ }
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns in a \texttt{String} all data contained in the current object.
+\end{tabb}
+\begin{htmlonly}
+ \return{All data contained in the current object as a \class{String}.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Rendering methods}
+
+\begin{code}
+
+ public XYItemRenderer getRenderer() \begin{hide} {
+ return renderer;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{XYItemRenderer} object associated with the current object.
+\end{tabb}
+\begin{htmlonly}
+ \return{\texttt{XYItemRenderer} object associated with the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public void setRenderer (XYItemRenderer renderer) \begin{hide} {
+ this.renderer = renderer;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the \texttt{XYItemRenderer} object associated with the current variable.
+ This object determines the chart JFreeChart look, produced by method
+ \texttt{view} in class
+ \externalclass{umontreal.iro.lecuyer.charts}{XYChart}.
+\end{tabb}
+\begin{htmlonly}
+ \param{renderer}{ new \texttt{XYItemRenderer} object.}
+\end{htmlonly}
+\begin{code}
+
+ public Color getColor (int series) \begin{hide} {
+ return (Color)renderer.getSeriesPaint(series);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current plotting color of the selected series.
+\end{tabb}
+\begin{htmlonly}
+ \return{current plotting color.}
+\end{htmlonly}
+\begin{code}
+
+ public void setColor (int series, Color color) \begin{hide} {
+ renderer.setSeriesPaint(series, color);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets a new plotting color to the series $series$.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{color}{plotting color.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract String toLatex (double XScale, double YScale,
+ double XShift, double YShift,
+ double xmin, double xmax,
+ double ymin, double ymax);
+\end{code}
+\begin{tabb}
+ Formats and returns a string containing a \LaTeX-compatible source
+ code which represents this data series collection. % and its parameters.
+ The original datasets are shifted and scaled with the \texttt{XShift},
+ \texttt{YShift}, \texttt{XScale} and \texttt{YScale} parameters.
+ \texttt{xmin}, \texttt{xmax}, \texttt{ymin} and \texttt{ymax} represent the chart bounds.
+\end{tabb}
+\begin{htmlonly}
+ \param{XScale}{Domain original data scale.}
+ \param{YScale}{Range original data scale.}
+ \param{XShift}{Domain original data shift value.}
+ \param{YShift}{Range original data shift value.}
+ \param{xmin}{Domain min bound.}
+ \param{xmax}{Domain nax bound.}
+ \param{ymin}{Range min bound.}
+ \param{ymax}{Range nax bound.}
+ \return{TikZ code.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+
+ /**
+ * Converts a java Color object into a friendly and readable LaTeX/xcolor string.
+ *
+ * @param color in color.
+ * @return friendly color with string format as possible, null otherwise.
+ */
+ protected static String detectXColorClassic(Color color) {
+ String retour = null;
+
+ int red = color.getRed();
+ int green = color.getGreen();
+ int blue = color.getBlue();
+
+ // On utilise pas la method Color.equals(Color ) car on ne veut pas tester le parametre de transparence : Alpha
+ if ( red == Color.GREEN.getRed()
+ && blue == Color.GREEN.getBlue()
+ && green == Color.GREEN.getGreen())
+ return "green";
+ else if ( red == Color.RED.getRed()
+ && blue == Color.RED.getBlue()
+ && green == Color.RED.getGreen())
+ return "red";
+ else if ( red == Color.WHITE.getRed()
+ && blue == Color.WHITE.getBlue()
+ && green == Color.WHITE.getGreen())
+ return "white";
+ else if ( red == Color.GRAY.getRed()
+ && blue == Color.GRAY.getBlue()
+ && green == Color.GRAY.getGreen())
+ return "gray";
+ else if ( red == Color.BLACK.getRed()
+ && blue == Color.BLACK.getBlue()
+ && green == Color.BLACK.getGreen())
+ return "black";
+ else if ( red == Color.YELLOW.getRed()
+ && blue == Color.YELLOW.getBlue()
+ && green == Color.YELLOW.getGreen())
+ return "yellow";
+ else if ( red == Color.MAGENTA.getRed()
+ && blue == Color.MAGENTA.getBlue()
+ && green == Color.MAGENTA.getGreen())
+ return "magenta";
+ else if ( red == Color.CYAN.getRed()
+ && blue == Color.CYAN.getBlue()
+ && green == Color.CYAN.getGreen())
+ return "cyan";
+ else if ( red == Color.BLUE.getRed()
+ && blue == Color.BLUE.getBlue()
+ && green == Color.BLUE.getGreen())
+ return "blue";
+ else if ( red == Color.DARK_GRAY.getRed()
+ && blue == Color.DARK_GRAY.getBlue()
+ && green == Color.DARK_GRAY.getGreen())
+ return "darkgray";
+ else if ( red == Color.LIGHT_GRAY.getRed()
+ && blue == Color.LIGHT_GRAY.getBlue()
+ && green == Color.LIGHT_GRAY.getGreen())
+ return "lightgray";
+ else if ( red == Color.ORANGE.getRed()
+ && blue == Color.ORANGE.getBlue()
+ && green == Color.ORANGE.getGreen())
+ return "orange";
+ else if ( red == Color.PINK.getRed()
+ && blue == Color.PINK.getBlue()
+ && green == Color.PINK.getGreen())
+ return "pink";
+
+
+ if (red == 192 && blue == 128 && green == 64)
+ return "brown";
+ else if (red == 128 && blue == 128 && green == 0)
+ return "olive";
+ else if (red == 128 && blue == 0 && green == 128)
+ return "violet";
+ else if (red == 192 && blue == 0 && green ==64)
+ return "purple";
+ else return null;
+ }
+
+ /**
+ * Gives the default color associated with a series
+ *
+ * @param index Index of the series in the XYDataset object.
+ * @return default color object.
+ */
+ protected static Color getDefaultColor(int index) {
+ if(index%6 == 0)
+ return Color.RED;
+ else if(index%6 == 1)
+ return Color.BLUE;
+ else if(index%6 == 2)
+ return Color.GREEN;
+ else if(index%6 == 3)
+ return Color.YELLOW;
+ else if(index%6 == 4)
+ return Color.MAGENTA;
+ else
+ return Color.CYAN;
+ }
+
+ // Returns maximum value in table t
+ protected static double max (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] > aux)
+ aux = t[i];
+ return aux ;
+ }
+ // Returns minimum value in table t
+ protected static double min (double[] t) {
+ double aux = t[0];
+ for (int i=1 ; i < t.length ; i++)
+ if (t[i] < aux)
+ aux = t[i];
+ return aux ;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/ScatterChart.java b/source/umontreal/iro/lecuyer/charts/ScatterChart.java
new file mode 100644
index 0000000..2ce665a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/ScatterChart.java
@@ -0,0 +1,486 @@
+
+
+/*
+ * Class: ScatterChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.chart.renderer.xy.XYDotRenderer;
+import org.jfree.data.xy.XYSeriesCollection;
+import java.util.Locale;
+import java.util.Formatter;
+import cern.colt.list.DoubleArrayList;
+import javax.swing.JFrame;
+
+
+/**
+ * This class provides tools to create and manage scatter plots. Using the
+ * {@link ScatterChart} class is the simplest way to produce scatter plots only.
+ * Each {@link ScatterChart} object is linked with a
+ * {@link umontreal.iro.lecuyer.charts.XYListSeriesCollection XYListSeriesCollection} data set.
+ *
+ */
+public class ScatterChart extends XYChart {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createScatterPlot (
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tooltips
+ false // urls
+ );
+
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ initAxis();
+
+ int nb = getSeriesCollection().getSeriesCollection().getSeriesCount();
+ for (int i = 0 ; i < nb ; i++) {
+ getSeriesCollection().setDashPattern(i, "only marks");
+ getSeriesCollection().setMarksType(i, "+");
+ }
+ }
+
+ protected void initAxis(){
+ XAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ setAutoRange(true, true);
+ }
+
+
+
+ /**
+ * Initializes a new <TT>ScatterChart</TT> instance with an empty data set.
+ *
+ */
+ public ScatterChart() {
+ super();
+ dataset = new XYListSeriesCollection();
+ init (null, null, null);
+ }
+
+
+ /**
+ * Initializes a new <TT>ScatterChart</TT> instance with data <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input parameter <TT>data</TT> represents sets of plotting data.
+ * For example, if one <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix <TT>data1</TT> is given as argument
+ * <TT>data</TT>, then the first row <TT>data1</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data1</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of a set of points.
+ * Therefore matrix <TT>data1</TT> corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> sets of points, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * However, one may want to plot sets of points with different <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * In that case, one should give the points as matrices with two rows.
+ * For examples, if the argument <TT>data</TT> is made of three 2-row matrices
+ * <TT>data1</TT>, <TT>data2</TT> and <TT>data3</TT>, then they represents
+ * three different sets of points, <TT>data*</TT><SPAN CLASS="MATH">[0]</SPAN> giving the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates,
+ * and <TT>data*</TT><SPAN CLASS="MATH">[1]</SPAN> the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of the points.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public ScatterChart (String title, String XLabel, String YLabel,
+ double[][]... data) {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>ScatterChart</TT> instance with sets of points <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * If <TT>data</TT> is a <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix,
+ * then the first row <TT>data</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate vector.
+ * Therefore matrix <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][ ]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> sets of points, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * However, only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of each set <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>]</SPAN>
+ * (i.e. the first <TT>numPoints</TT> columns of each row)
+ * will be plotted.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public ScatterChart (String title, String XLabel, String YLabel,
+ double[][] data, int numPoints) {
+ super();
+ dataset = new XYListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>ScatterChart</TT> instance using subsets of <TT>data</TT>.
+ * <TT>data[x][.]</TT> will form the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and
+ * <TT>data[y][.]</TT> will form the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of the chart.
+ * <TT>title</TT> sets a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> is a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * Warning: if the new <SPAN CLASS="MATH"><I>x</I></SPAN>-axis coordinates are not monotone increasing, then
+ * they will automatically be sorted in increasing order so the points will
+ * be reordered, but the original <TT>data</TT> is not changed.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ * @param x Index of data forming the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ *
+ * @param y Index of data forming the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates
+ *
+ *
+ */
+ public ScatterChart (String title, String XLabel, String YLabel,
+ double[][] data, int x, int y) {
+ super();
+ int len = data[0].length;
+ double[][] proj = new double[2][len];
+ for (int i = 0; i < len; i++) {
+ proj[0][i] = data[x][i];
+ proj[1][i] = data[y][i];
+ }
+ dataset = new XYListSeriesCollection(proj);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>ScatterChart</TT> instance with data <TT>data</TT>.
+ * The input parameter <TT>data</TT> represents a set of plotting data. A
+ * {@link cern.colt.list.DoubleArrayList DoubleArrayList} from the Colt library is
+ * used to store the data. The description is similar to the above
+ * constructor with <TT>double[]... data</TT>.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public ScatterChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>ScatterChart</TT> instance with data <TT>data</TT>.
+ * The input parameter <TT>data</TT> represents a set of plotting data.
+ * {@link org.jfree.data.xy.XYSeriesCollection XYSeriesCollection} is a
+ * <TT>JFreeChart</TT> container class to store <SPAN CLASS="MATH"><I>XY</I></SPAN> plots.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series collection.
+ *
+ */
+ public ScatterChart (String title, String XLabel, String YLabel,
+ XYSeriesCollection data) {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series. <TT>name</TT> and <TT>plotStyle</TT> are the name and the plot
+ * style associated to the series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param name Name of the series.
+ *
+ * @param plotStyle Plot style of the series.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y, String name, String plotStyle) {
+ int seriesIndex = add(x,y);
+ getSeriesCollection().setName(seriesIndex, name);
+ getSeriesCollection().setPlotStyle (seriesIndex, plotStyle);
+ return seriesIndex;
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y) {
+ return add (x, y, x.length);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series. Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>x</TT> and
+ * <TT>y</TT> will be taken into account for the new series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param numPoints Number of points to add.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y, int numPoints) {
+ int seriesIndex = getSeriesCollection().add(x, y, numPoints);
+ initAxis();
+ getSeriesCollection().setMarksType(seriesIndex, "+");
+ getSeriesCollection().setDashPattern(seriesIndex, "only marks");
+ return seriesIndex;
+ }
+
+
+ /**
+ * Returns the chart's dataset.
+ *
+ * @return the chart's dataset.
+ *
+ */
+ public XYListSeriesCollection getSeriesCollection() {
+ return (XYListSeriesCollection)dataset;
+ }
+
+
+ /**
+ * Links a new dataset to the current chart.
+ *
+ * @param dataset new dataset.
+ *
+ *
+ */
+ public void setSeriesCollection (XYListSeriesCollection dataset) {
+ this.dataset = dataset;
+ }
+
+
+ /**
+ * Synchronizes <SPAN CLASS="MATH"><I>X</I></SPAN>-axis ticks to the <SPAN CLASS="MATH"><I>s</I></SPAN>-th series <SPAN CLASS="MATH"><I>x</I></SPAN>-values.
+ *
+ * @param s series used to define ticks.
+ *
+ *
+ */
+ public void setTicksSynchro (int s) {
+ XYSeriesCollection seriesCollection =
+ (XYSeriesCollection)this.dataset.getSeriesCollection();
+ double[] values = new double[seriesCollection.getItemCount(s)];
+
+ for(int i = 0; i < seriesCollection.getItemCount(s); i++)
+ values[i] = seriesCollection.getXValue(s, i);
+
+ XAxis.setLabels(values);
+ }
+
+
+ /**
+ * Displays chart on the screen using Swing.
+ * This method creates an application containing a chart panel displaying
+ * the chart. The created frame is positioned on-screen, and displayed before
+ * it is returned. The <TT>width</TT> and the <TT>height</TT>
+ * of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @return frame containing the chart.;
+ *
+ */
+ public JFrame view (int width, int height) {
+ JFrame myFrame;
+ if (chart.getTitle () != null)
+ myFrame = new JFrame ("ScatterChart from SSJ: " + chart.getTitle ().getText ());
+ else
+ myFrame = new JFrame ("ScatterChart from SSJ");
+ XYPlot plot = chart.getXYPlot ();
+
+/* // The drawn points are somewhat big, of different shapes, unfilled
+ XYLineAndShapeRenderer shape = new XYLineAndShapeRenderer(false, true);
+ int nb = getSeriesCollection().getSeriesCollection().getSeriesCount();
+ for (int i = 0 ; i < nb ; i++) {
+ shape.setSeriesShapesFilled(i, false);
+ plot.setRenderer(i, shape);
+ }
+*/
+ // The drawn points are all square, filled
+ XYDotRenderer shape = new XYDotRenderer();
+ final int dotSize = 3;
+ shape.setDotWidth(dotSize);
+ shape.setDotHeight(dotSize);
+ int nb = getSeriesCollection().getSeriesCollection().getSeriesCount();
+ for (int i = 0 ; i < nb ; i++)
+ plot.setRenderer(i, shape);
+
+ ChartPanel chartPanel = new ChartPanel (chart);
+ chartPanel.setPreferredSize (new java.awt.Dimension(width, height));
+ myFrame.setContentPane (chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible (true);
+ return myFrame;
+ }
+
+
+ public String toLatex (double width, double height) {
+ double xunit=0, yunit=0;
+ double[] save = new double[4];
+
+ if(dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ //taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ xunit = width / ((Math.max(XAxis.getAxis().getRange().getUpperBound(),
+ XAxis.getTwinAxisPosition()) * XScale)
+ - (Math.min(XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition()) * XScale));
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+ yunit = height / ((Math.max(YAxis.getAxis().getRange().getUpperBound(),
+ YAxis.getTwinAxisPosition()) * YScale)
+ - (Math.min(YAxis.getAxis().getRange().getLowerBound(),
+ YAxis.getTwinAxisPosition()) * YScale));
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ: %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if(grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(),
+ YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(),
+ XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(),
+ YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/ScatterChart.tex b/source/umontreal/iro/lecuyer/charts/ScatterChart.tex
new file mode 100644
index 0000000..4b604ec
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/ScatterChart.tex
@@ -0,0 +1,485 @@
+\defmodule {ScatterChart}
+
+% Extends \externalclass{umontreal.iro.lecuyer.charts}{XYChart}.
+This class provides tools to create and manage scatter plots. Using the
+\class{ScatterChart} class is the simplest way to produce scatter plots only.
+Each \class{ScatterChart} object is linked with a
+\externalclass{umontreal.iro.lecuyer.charts}{XYListSeriesCollection} data set.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: ScatterChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.chart.renderer.xy.XYDotRenderer;
+import org.jfree.data.xy.XYSeriesCollection;
+import java.util.Locale;
+import java.util.Formatter;
+import cern.colt.list.DoubleArrayList;
+import javax.swing.JFrame;
+\end{hide}
+
+public class ScatterChart extends XYChart \begin{hide} {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createScatterPlot (
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ true, // include legend
+ true, // tooltips
+ false // urls
+ );
+
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ initAxis();
+
+ int nb = getSeriesCollection().getSeriesCollection().getSeriesCount();
+ for (int i = 0 ; i < nb ; i++) {
+ getSeriesCollection().setDashPattern(i, "only marks");
+ getSeriesCollection().setMarksType(i, "+");
+ }
+ }
+
+ protected void initAxis(){
+ XAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ setAutoRange(true, true);
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public ScatterChart() \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection();
+ init (null, null, null);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{ScatterChart} instance with an empty data set.
+\end{tabb}
+\begin{code}
+
+ public ScatterChart (String title, String XLabel, String YLabel,
+ double[][]... data) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{ScatterChart} instance with data \texttt{data}.
+\texttt{title} is a title, \texttt{XLabel} is a short description of the
+$x$-axis and \texttt{YLabel} a short description of the $y$-axis.
+The input parameter \texttt{data} represents sets of plotting data.
+%
+ For example, if one $n$-row matrix \texttt{data1} is given as argument
+ \texttt{data}, then the first row \texttt{data1}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data1}$[i],
+ i=1,\ldots, n-1$, represents the $y$-coordinates of a set of points.
+ Therefore matrix \texttt{data1} corresponds
+ to $n-1$ sets of points, all with the same $x$-coordinates.
+%
+ However, one may want to plot sets of points with different $x$-coordinates.
+ In that case, one should give the points as matrices with two rows.
+For examples, if the argument \texttt{data} is made of three 2-row matrices
+\texttt{data1}, \texttt{data2} and \texttt{data3}, then they represents
+ three different sets of points, \texttt{data*}$[0]$ giving the $x$-coordinates,
+ and \texttt{data*}$[1]$ the $y$-coordinates of the points.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public ScatterChart (String title, String XLabel, String YLabel,
+ double[][] data, int numPoints) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{ScatterChart} instance with sets of points \texttt{data}.
+\texttt{title} is a title, \texttt{XLabel} is a short description of the
+$x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ If \texttt{data} is a $n$-row matrix,
+ then the first row \texttt{data}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data}$[i],
+ i=1,\ldots, n-1$, represents a $y$-coordinate vector.
+ Therefore matrix \texttt{data}$[i][\ ]$, $i=0,\ldots, n-1$, corresponds
+ to $n-1$ sets of points, all with the same $x$-coordinates.
+ However, only \emph{the first} \texttt{numPoints} of each set \texttt{data}$[i]$
+ (i.e. the first \texttt{numPoints} columns of each row)
+ will be plotted.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public ScatterChart (String title, String XLabel, String YLabel,
+ double[][] data, int x, int y) \begin{hide} {
+ super();
+ int len = data[0].length;
+ double[][] proj = new double[2][len];
+ for (int i = 0; i < len; i++) {
+ proj[0][i] = data[x][i];
+ proj[1][i] = data[y][i];
+ }
+ dataset = new XYListSeriesCollection(proj);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{ScatterChart} instance using subsets of \texttt{data}.
+\texttt{data[x][.]} will form the $x$-coordinates and
+\texttt{data[y][.]} will form the $y$-coordinates of the chart.
+\texttt{title} sets a title, \texttt{XLabel} is a short description of the
+$x$-axis, and \texttt{YLabel} is a short description of the $y$-axis.
+Warning: if the new $x$-axis coordinates are not monotone increasing, then
+they will automatically be sorted in increasing order so the points will
+be reordered, but the original \texttt{data} is not changed.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+ \param{x}{Index of data forming the $x$-coordinates}
+ \param{y}{Index of data forming the $y$-coordinates}
+\end{htmlonly}
+\begin{code}
+
+ public ScatterChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{ScatterChart} instance with data \texttt{data}.
+ The input parameter \texttt{data} represents a set of plotting data. A
+ \externalclass{cern.colt.list}{DoubleArrayList} from the Colt library is
+ used to store the data. The description is similar to the above
+ constructor with \texttt{double[]... data}.
+% Therefore \texttt{data}$[0]$ will form the first
+ % curve, \texttt{data}$[1]$ forms the second and in general,
+% \texttt{data}$[i], i = 0, \ldots,n,$ forms a point set.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public ScatterChart (String title, String XLabel, String YLabel,
+ XYSeriesCollection data) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{ScatterChart} instance with data \texttt{data}.
+ The input parameter \texttt{data} represents a set of plotting data.
+ \externalclass{org.jfree.data.xy}{XYSeriesCollection} is a
+ \texttt{JFreeChart} container class to store $XY$ plots.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series collection.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public int add (double[] x, double[] y, String name, String plotStyle) \begin{hide} {
+ int seriesIndex = add(x,y);
+ getSeriesCollection().setName(seriesIndex, name);
+ getSeriesCollection().setPlotStyle (seriesIndex, plotStyle);
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series. \texttt{name} and \texttt{plotStyle} are the name and the plot
+ style associated to the series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \param{name}{Name of the series.}
+ \param{plotStyle}{Plot style of the series.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] x, double[] y) \begin{hide} {
+ return add (x, y, x.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] x, double[] y, int numPoints) \begin{hide} {
+ int seriesIndex = getSeriesCollection().add(x, y, numPoints);
+ initAxis();
+ getSeriesCollection().setMarksType(seriesIndex, "+");
+ getSeriesCollection().setDashPattern(seriesIndex, "only marks");
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series. Only \emph{the first} \texttt{numPoints} of \texttt{x} and
+ \texttt{y} will be taken into account for the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \param{numPoints}{Number of points to add.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public XYListSeriesCollection getSeriesCollection() \begin{hide} {
+ return (XYListSeriesCollection)dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's dataset.
+\end{tabb}
+\begin{htmlonly}
+ \return{the chart's dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeriesCollection (XYListSeriesCollection dataset) \begin{hide} {
+ this.dataset = dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Links a new dataset to the current chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{dataset}{new dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTicksSynchro (int s) \begin{hide} {
+ XYSeriesCollection seriesCollection =
+ (XYSeriesCollection)this.dataset.getSeriesCollection();
+ double[] values = new double[seriesCollection.getItemCount(s)];
+
+ for(int i = 0; i < seriesCollection.getItemCount(s); i++)
+ values[i] = seriesCollection.getXValue(s, i);
+
+ XAxis.setLabels(values);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Synchronizes $X$-axis ticks to the $s$-th series $x$-values.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{series used to define ticks.}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame view (int width, int height) \begin{hide} {
+ JFrame myFrame;
+ if (chart.getTitle () != null)
+ myFrame = new JFrame ("ScatterChart from SSJ: " + chart.getTitle ().getText ());
+ else
+ myFrame = new JFrame ("ScatterChart from SSJ");
+ XYPlot plot = chart.getXYPlot ();
+
+/* // The drawn points are somewhat big, of different shapes, unfilled
+ XYLineAndShapeRenderer shape = new XYLineAndShapeRenderer(false, true);
+ int nb = getSeriesCollection().getSeriesCollection().getSeriesCount();
+ for (int i = 0 ; i < nb ; i++) {
+ shape.setSeriesShapesFilled(i, false);
+ plot.setRenderer(i, shape);
+ }
+*/
+ // The drawn points are all square, filled
+ XYDotRenderer shape = new XYDotRenderer();
+ final int dotSize = 3;
+ shape.setDotWidth(dotSize);
+ shape.setDotHeight(dotSize);
+ int nb = getSeriesCollection().getSeriesCollection().getSeriesCount();
+ for (int i = 0 ; i < nb ; i++)
+ plot.setRenderer(i, shape);
+
+ ChartPanel chartPanel = new ChartPanel (chart);
+ chartPanel.setPreferredSize (new java.awt.Dimension(width, height));
+ myFrame.setContentPane (chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible (true);
+ return myFrame;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays chart on the screen using Swing.
+ This method creates an application containing a chart panel displaying
+ the chart. The created frame is positioned on-screen, and displayed before
+ it is returned. The \texttt{width} and the \texttt{height}
+ of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \return{frame containing the chart.};
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{LaTex-specific method}
+
+\begin{code}
+
+ public String toLatex (double width, double height) \begin{hide} {
+ double xunit=0, yunit=0;
+ double[] save = new double[4];
+
+ if(dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ //taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ xunit = width / ((Math.max(XAxis.getAxis().getRange().getUpperBound(),
+ XAxis.getTwinAxisPosition()) * XScale)
+ - (Math.min(XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition()) * XScale));
+ //taille d'une unite en y et en cm dans l'objet "tikzpicture"
+ yunit = height / ((Math.max(YAxis.getAxis().getRange().getUpperBound(),
+ YAxis.getTwinAxisPosition()) * YScale)
+ - (Math.min(YAxis.getAxis().getRange().getLowerBound(),
+ YAxis.getTwinAxisPosition()) * YScale));
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if(chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ: %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if(grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(),
+ YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(),
+ XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(),
+ YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/XYChart.java b/source/umontreal/iro/lecuyer/charts/XYChart.java
new file mode 100644
index 0000000..ca75c99
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/XYChart.java
@@ -0,0 +1,592 @@
+
+
+/*
+ * Class: XYChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+import java.io.*;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.annotations.*;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.ui.TextAnchor;
+import javax.swing.JFrame;
+
+/**
+ * This class provides tools to create charts from data in a simple way. Its main
+ * feature is to produce
+ * TikZ/PGF (see WWW link <TT><A NAME="tex2html1"
+ * HREF="http://sourceforge.net/projects/pgf/">http://sourceforge.net/projects/pgf/</A></TT>)
+ * compatible source code which can be included
+ * in <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> documents, but it can also produce charts in other formats.
+ * One can easily create a new chart, and customize its appearance using methods
+ * of this class, with the encapsulated
+ * {@link umontreal.iro.lecuyer.charts.SSJXYSeriesCollection SSJXYSeriesCollection} object
+ * representing the data, and the two
+ * {@link umontreal.iro.lecuyer.charts.Axis Axis} objects representing the axes.
+ * All these classes depend on the <TT>JFreeChart</TT> API (see WWW link
+ * <TT><A NAME="tex2html2"
+ * HREF="http://www.jfree.org/jfreechart/">http://www.jfree.org/jfreechart/</A></TT>) which provides tools to build charts with
+ * Java, to draw them, and export them to files. However, only basic features are
+ * used here.
+ *
+ * <P>
+ * Moreover, <TT>XYChart</TT> provides methods to plot data using a MATLAB friendly
+ * syntax. None of these methods provides new features; they just propose a
+ * different syntax to create charts. Therefore some features are unavailable
+ * when using these methods only.
+ *
+ */
+public abstract class XYChart {
+ protected Axis XAxis;
+ protected Axis YAxis;
+
+ protected SSJXYSeriesCollection dataset;
+ protected JFreeChart chart;
+ protected boolean latexDocFlag = true;
+
+ protected boolean autoRange;
+ protected double[] manualRange;
+
+ protected boolean grid = false;
+ protected double xstepGrid;
+ protected double ystepGrid;
+
+ // this flag is set true when plotting probabilities. In that case,
+ // y is always >= 0.
+ protected boolean probFlag = false;
+
+ protected double chartMargin = 0.02; // margin around the chart
+
+
+
+ /**
+ * Returns the <TT>JFreeChart</TT> object associated with this chart.
+ *
+ * @return the associated JFreeChart object.
+ *
+ */
+ public JFreeChart getJFreeChart() {
+ return chart;
+ }
+
+
+ /**
+ * Returns the chart's domain axis (<SPAN CLASS="MATH"><I>x</I></SPAN>-axis) object.
+ *
+ * @return chart's domain axis (<SPAN CLASS="MATH"><I>x</I></SPAN>-axis) object.
+ *
+ */
+ public Axis getXAxis() {
+ return XAxis;
+ }
+
+
+ /**
+ * Returns the chart's range axis (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) object.
+ *
+ * @return chart's range axis (<SPAN CLASS="MATH"><I>y</I></SPAN>-axis) object.
+ *
+ */
+ public Axis getYAxis() {
+ return YAxis;
+ }
+
+
+ public abstract JFrame view (int width, int height);
+
+
+ /**
+ * Gets the current chart title.
+ *
+ * @return Chart title.
+ *
+ */
+ public String getTitle() {
+ return chart.getTitle().getText();
+ }
+
+
+ /**
+ * Sets a title to this chart. This title will appear on the chart displayed
+ * by method {@link #view view}.
+ *
+ * @param title chart title.
+ *
+ *
+ */
+ public void setTitle (String title) {
+ chart.setTitle(title);
+ }
+
+
+ /**
+ * Must be set <TT>true</TT> when plotting probabilities,
+ * <TT>false</TT> otherwise.
+ *
+ * @param flag <TT>true</TT> for plotting probabilities
+ *
+ *
+ */
+ public void setprobFlag (boolean flag) {
+ probFlag = flag;
+ }
+
+
+ /**
+ * The <SPAN CLASS="MATH"><I>x</I></SPAN> and the <SPAN CLASS="MATH"><I>y</I></SPAN> ranges of the chart are set automatically.
+ *
+ */
+ public void setAutoRange() {
+ setAutoRange (false, false, true, true);
+ }
+
+
+ /**
+ * The <SPAN CLASS="MATH"><I>x</I></SPAN> and the <SPAN CLASS="MATH"><I>y</I></SPAN> ranges of the chart are set automatically.
+ * If <TT>right</TT> is <TT>true</TT>, the vertical axis will be on the left of
+ * the points, otherwise on the right. If <TT>top</TT> is <TT>true</TT>,
+ * the horizontal axis will be under the points, otherwise above the points.
+ *
+ * @param right true if the x-values on the right of axis.
+ *
+ * @param top true if the y-values on the top of axis.
+ *
+ *
+ */
+ public void setAutoRange (boolean right, boolean top) {
+ setAutoRange (false, false, right, top);
+ }
+
+ private double[] adjustRangeBounds (double bmin, double bmax) {
+ // resets chart lower and upper bounds to round values.
+ // Returns corrected [lowerBound, upperBound]
+
+ double del = (bmax - bmin)/20.0; // Choose 20 intervals to round
+ int a = (int) Math.floor(0.5 + Math.log10(del));
+ double d = Math.pow(10.0, (double) a); // power of 10
+ double lower = d*Math.ceil((bmin - del)/d);
+ if (lower > bmin)
+ lower -= d;
+ if (0 == Math.abs(bmin))
+ lower = 0;
+ double upper = d*Math.floor((bmax + del)/d);
+ if (upper < bmax)
+ upper += d;
+ double [] range = new double[2];
+ range[0] = lower;
+ range[1] = upper;
+ return range;
+ }
+
+ protected void setAutoRange (boolean xZero, boolean yZero, boolean right, boolean top) {
+ // see description of setAxesZero
+ autoRange = true;
+ double BorneMin = (dataset.getDomainBounds())[0];
+ double BorneMax = (dataset.getDomainBounds())[1];
+ double del;
+ if (BorneMax - BorneMin < 1)
+ del = (BorneMax - BorneMin) * chartMargin;
+ else
+ del = chartMargin;
+ if (BorneMin < 0.0) BorneMin *= 1.0 + del;
+ else BorneMin *= 1.0 - del;
+ if (BorneMax < 0.0) BorneMax *= 1.0 - del;
+ else BorneMax *= 1.0 + del;
+ double [] newRange = new double[2];
+ newRange = adjustRangeBounds (BorneMin, BorneMax);
+ if (probFlag && (BorneMin == 0.0))
+ newRange[0] = 0.0;
+ XAxis.getAxis().setLowerBound(newRange[0]);
+ XAxis.getAxis().setUpperBound(newRange[1]);
+
+ BorneMin = (dataset.getRangeBounds())[0];
+ BorneMax = (dataset.getRangeBounds())[1];
+ if (BorneMax - BorneMin < 1)
+ del = (BorneMax - BorneMin) * chartMargin;
+ else
+ del = chartMargin;
+ if (BorneMin < 0.0) BorneMin *= 1.0 + del;
+ else BorneMin *= 1.0 - del;
+ if (BorneMax < 0.0) BorneMax *= 1.0 - del;
+ else BorneMax *= 1.0 + del;
+ newRange = adjustRangeBounds (BorneMin, BorneMax);
+ if (probFlag && (newRange[0] <= 0.0)) // probabilities are always >= 0
+ newRange[0] = 0.0;
+ YAxis.getAxis().setLowerBound(newRange[0]);
+ YAxis.getAxis().setUpperBound(newRange[1]);
+
+ if (xZero)
+ XAxis.setTwinAxisPosition(0);
+ else {
+ if (right)
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getLowerBound());
+ else
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getUpperBound());
+ }
+
+ if (yZero)
+ YAxis.setTwinAxisPosition(0);
+ else {
+ if (top)
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getLowerBound());
+ else
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getUpperBound());
+ }
+ }
+
+ /**
+ * The <SPAN CLASS="MATH"><I>x</I></SPAN> and the <SPAN CLASS="MATH"><I>y</I></SPAN> ranges of the chart are set automatically.
+ * If <TT>xZero</TT> is <TT>true</TT>, the vertical axis will pass through the
+ * point <SPAN CLASS="MATH">(0, <I>y</I>)</SPAN>. If <TT>yZero</TT> is <TT>true</TT>, the horizontal axis
+ * will pass through the point <SPAN CLASS="MATH">(<I>x</I>, 0)</SPAN>.
+ *
+ * @param xZero true if vertical axis passes through point 0
+ *
+ * @param yZero true if horizontal axis passes through point 0
+ *
+ *
+ */
+ public void setAutoRange00 (boolean xZero, boolean yZero) {
+ setAutoRange (xZero, yZero, true, true);
+ }
+
+
+ /**
+ * Sets the <SPAN CLASS="MATH"><I>x</I></SPAN> and <SPAN CLASS="MATH"><I>y</I></SPAN> ranges of the chart using the format: <TT>range =
+ * [xmin, xmax, ymin, ymax]</TT>.
+ * @param range new axis ranges.
+ *
+ *
+ */
+ public void setManualRange (double[] range) {
+ setManualRange (range, false, false, true, true);
+ }
+
+
+ /**
+ * Sets the <SPAN CLASS="MATH"><I>x</I></SPAN> and <SPAN CLASS="MATH"><I>y</I></SPAN> ranges of the chart using the format: <TT>range =
+ * [xmin, xmax, ymin, ymax]</TT>.
+ * If <TT>right</TT> is <TT>true</TT>, the vertical axis will be on the left of
+ * the points, otherwise on the right. If <TT>top</TT> is <TT>true</TT>,
+ * the horizontal axis will be under the points, otherwise above the points.
+ *
+ * @param range new axis ranges.
+ *
+ * @param right true if the x-values on the right.
+ *
+ * @param top true if the y-values on the top.
+ *
+ *
+ */
+ public void setManualRange (double[] range, boolean right, boolean top) {
+ setManualRange (range, false, false, right, top);
+ }
+
+
+ private void setManualRange (double[] range, boolean xZero, boolean yZero,
+ boolean right, boolean top) {
+ if (range.length != 4)
+ throw new IllegalArgumentException (
+ "range must have the format: [xmin, xmax, ymin, ymax]");
+ autoRange = false;
+ XAxis.getAxis().setLowerBound(Math.min(range[0],range[1]));
+ XAxis.getAxis().setUpperBound(Math.max(range[0],range[1]));
+ YAxis.getAxis().setLowerBound(Math.min(range[2],range[3]));
+ YAxis.getAxis().setUpperBound(Math.max(range[2],range[3]));
+
+ if (xZero)
+ XAxis.setTwinAxisPosition(0);
+ else {
+ if (right)
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getLowerBound());
+ else
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getUpperBound());
+ }
+
+ if (yZero)
+ YAxis.setTwinAxisPosition(0);
+ else {
+ if (top)
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getLowerBound());
+ else
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getUpperBound());
+ }
+ }
+
+ /**
+ * Sets the <SPAN CLASS="MATH"><I>x</I></SPAN> and <SPAN CLASS="MATH"><I>y</I></SPAN> ranges of the chart using the format: <TT>range =
+ * [xmin, xmax, ymin, ymax]</TT>.
+ * If <TT>xZero</TT> is <TT>true</TT>, the vertical axis will pass through the
+ * point <SPAN CLASS="MATH">(0, <I>y</I>)</SPAN>. If <TT>yZero</TT> is <TT>true</TT>, the horizontal axis
+ * will pass through the point <SPAN CLASS="MATH">(<I>x</I>, 0)</SPAN>.
+ *
+ * @param xZero true if vertical axis passes through point 0
+ *
+ * @param yZero true if horizontal axis passes through point 0
+ *
+ *
+ */
+ public void setManualRange00 (double[] range, boolean xZero, boolean yZero) {
+ setManualRange (range, xZero, yZero, true, true);
+ }
+
+
+ /**
+ * Returns the chart margin, which is the fraction by which the chart
+ * is enlarged on its borders. The default value is <SPAN CLASS="MATH">0.02</SPAN>.
+ *
+ */
+ public double getChartMargin() {
+ return chartMargin;
+ }
+
+
+ /**
+ * Sets the chart margin to <TT>margin</TT>. It is the fraction by
+ * which the chart is enlarged on its borders.
+ * Restriction:
+ * <SPAN CLASS="MATH"><texttt>margin</texttt> >= 0</SPAN>.
+ *
+ * @param margin margin percentage amount.
+ *
+ *
+ */
+ public void setChartMargin (double margin) {
+ if (margin < 0.0)
+ throw new IllegalArgumentException ("margin < 0");
+ chartMargin = margin;
+ }
+
+
+ /**
+ * Synchronizes <SPAN CLASS="MATH"><I>x</I></SPAN>-axis ticks to the <SPAN CLASS="MATH"><I>s</I></SPAN>-th series <SPAN CLASS="MATH"><I>x</I></SPAN>-values.
+ *
+ * @param s series.
+ *
+ *
+ */
+ public abstract void setTicksSynchro (int s);
+
+
+ /**
+ * Draws a vertical line on the chart at <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate <TT>x</TT>.
+ * <TT>name</TT> is written near the line at <SPAN CLASS="MATH"><I>y</I></SPAN> position <TT>yfrac</TT>
+ * (a fraction of the <SPAN CLASS="MATH"><I>y</I></SPAN>-size of the chart, 0 is the bottom, 1 is the top);
+ * if <TT>right</TT> is <TT>true</TT>, <TT>name</TT> is written on the right
+ * of the line, else on the left.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate of the line
+ *
+ * @param name description of the line
+ *
+ * @param yfrac <SPAN CLASS="MATH"><I>y</I></SPAN>-position of name
+ *
+ * @param right <SPAN CLASS="MATH"><I>x</I></SPAN>-position of name
+ *
+ */
+ public void drawVerticalLine (double x, String name, double yfrac,
+ boolean right) {
+ double ybottom = YAxis.getAxis().getLowerBound();
+ final Object o = this;
+ if (this instanceof HistogramChart)
+ ybottom = 0;
+ double ytop = YAxis.getAxis().getUpperBound();
+ XYLineAnnotation line = new XYLineAnnotation(x, ybottom, x, ytop);
+ XYTextAnnotation text = new XYTextAnnotation(name, x, ytop*yfrac);
+ if (!right)
+ text.setTextAnchor(TextAnchor.HALF_ASCENT_RIGHT);
+ else
+ text.setTextAnchor(TextAnchor.HALF_ASCENT_LEFT);
+ XYPlot plot = getJFreeChart().getXYPlot();
+ plot.addAnnotation(line);
+ plot.addAnnotation(text);
+ }
+
+
+ /**
+ * Puts a grid on the background. It is important to note that the grid is
+ * always shifted in such a way that it contains the axes. Thus, the grid does
+ * not always have an intersection at the corner points; this occurs
+ * only if the corner points are multiples of the steps: <TT>xstep</TT>
+ * and <TT>ystep</TT> sets the step in each direction.
+ *
+ * @param xstep sets the step in the x-direction.
+ *
+ * @param ystep sets the step in the y-direction.
+ *
+ *
+ */
+ public void enableGrid (double xstep, double ystep) {
+ this.grid = true;
+ this.xstepGrid = xstep;
+ this.ystepGrid = ystep;
+ }
+
+
+ /**
+ * Disables the background grid.
+ *
+ */
+ public void disableGrid() {
+ this.grid = false;
+ }
+
+
+ /**
+ * Exports the chart to a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> source code using PGF/TikZ.
+ * This method constructs and returns a string that can be written to
+ * a <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> document to render the plot. <TT>width</TT> and <TT>height</TT>
+ * represents the width and the height of the produced chart. These dimensions
+ * do not take into account the axes and labels extra space. The <TT>width</TT>
+ * and the <TT>height</TT> of the chart are measured in centimeters.
+ *
+ * @param width Chart's width in centimeters.
+ *
+ * @param height Chart's height in centimeters.
+ *
+ * @return LaTeX source code.
+ *
+ */
+ public abstract String toLatex (double width, double height);
+
+
+ /**
+ * Transforms the chart to <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> form and writes it in file <TT>fileName</TT>.
+ * The chart's width and height (in centimeters) are <TT>width</TT> and <TT>height</TT>.
+ *
+ */
+ public void toLatexFile (String fileName, double width, double height) {
+ String output = toLatex(width, height);
+ Writer file = null;
+ try {
+ file = new FileWriter(fileName);
+ file.write(output);
+ file.close();
+ } catch (IOException e) {
+ System.err.println (" toLatexFile: cannot write to " + fileName);
+ e.printStackTrace();
+ try {
+ if (file != null)
+ file.close();
+ } catch (IOException ioe) {}
+ }
+ }
+
+
+ /**
+ * Flag to remove the <code>\documentclass</code> (and other) commands in the
+ * created <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> files.
+ * If <TT>flag</TT> is <TT>true</TT>, then when charts are translated into
+ * <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> form, it will be as a self-contained file that can be directly
+ * compiled with <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN>. However, in this form, the file cannot be included in
+ * another <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> file without causing compilation errors because of the multiple
+ * instructions <code>\documentclass</code> and <code>\begin{document}</code>.
+ * By setting <TT>flag</TT> to <TT>false</TT>, these instructions will be
+ * removed from the <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> chart files, which can then be included in a master
+ * <SPAN CLASS="logo,LaTeX">L<SUP><SMALL>A</SMALL></SUP>T<SMALL>E</SMALL>X</SPAN> file. By default, the flag is <TT>true</TT>.
+ *
+ */
+ public void setLatexDocFlag (boolean flag) {
+ latexDocFlag = flag;
+ }
+
+
+
+ protected void setTick0Flags() {
+ // Set flag true if first or last label is on perpendicular axis.
+ // The label will be moved a little to the right (x-label), or above
+ // (y-label) to prevent it from being on the perpendicular axis.
+ // But it is unnecessary when graph begins or ends where label is;
+ // in this case, flag is false.
+ // We cannot put this method in Axis because it depends on the
+ // other axis.
+ double minAxis = Math.min (XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition());
+ double maxAxis = Math.max (XAxis.getAxis().getRange().getUpperBound(),
+ XAxis.getTwinAxisPosition());
+ if (XAxis.getTwinAxisPosition() == minAxis ||
+ XAxis.getTwinAxisPosition() == maxAxis)
+ YAxis.setTick0Flag(false);
+ else
+ YAxis.setTick0Flag(true);
+
+ minAxis = Math.min (YAxis.getAxis().getRange().getLowerBound(),
+ YAxis.getTwinAxisPosition());
+ maxAxis = Math.max (YAxis.getAxis().getRange().getUpperBound(),
+ YAxis.getTwinAxisPosition());
+ if (YAxis.getTwinAxisPosition() == minAxis ||
+ YAxis.getTwinAxisPosition() == maxAxis)
+ XAxis.setTick0Flag(false);
+ else
+ XAxis.setTick0Flag(true);
+ }
+
+
+ protected double computeXScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = XAxis.getAxis().getLowerBound();
+ bounds[1] = XAxis.getAxis().getUpperBound();
+
+ if (position < bounds[0])
+ bounds[0] = position;
+ if (position > bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale (bounds);
+ }
+
+
+ protected double computeYScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = YAxis.getAxis().getLowerBound();
+ bounds[1] = YAxis.getAxis().getUpperBound();
+
+ if (position < bounds[0])
+ bounds[0] = position;
+ if (position > bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale (bounds);
+ }
+
+
+ protected double computeScale (double[] bounds) {
+ int tenPowerRatio = 0;
+ // echelle < 1 si les valeurs sont grandes
+ while (bounds[1] > 1000 || bounds[0] < -1000) {
+ bounds[1] /= 10;
+ bounds[0] /= 10;
+ tenPowerRatio++;
+ }
+ // echelle > 1 si les valeurs sont petites
+ while (bounds[1] < 100 && bounds[0] > -100) {
+ bounds[1] *= 10;
+ bounds[0] *= 10;
+ tenPowerRatio--;
+ }
+ return 1/Math.pow(10, tenPowerRatio);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/XYChart.tex b/source/umontreal/iro/lecuyer/charts/XYChart.tex
new file mode 100644
index 0000000..1c98e2c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/XYChart.tex
@@ -0,0 +1,599 @@
+\defmodule {XYChart}
+
+This class provides tools to create charts from data in a simple way. Its main
+feature is to produce
+ TikZ/PGF (see WWW link \url{http://sourceforge.net/projects/pgf/})
+ compatible source code which can be included
+in \LaTeX\ documents, but it can also produce charts in other formats.
+One can easily create a new chart, and customize its appearance using methods
+of this class, with the encapsulated
+\externalclass{umontreal.iro.lecuyer.charts}{SSJXYSeriesCollection} object
+representing the data, and the two
+\externalclass{umontreal.iro.lecuyer.charts}{Axis} objects representing the axes.
+All these classes depend on the \texttt{JFreeChart} API (see WWW link
+\url{http://www.jfree.org/jfreechart/}) which provides tools to build charts with
+Java, to draw them, and export them to files. However, only basic features are
+used here.
+
+Moreover, \texttt{XYChart} provides methods to plot data using a MATLAB friendly
+syntax. None of these methods provides new features; they just propose a
+different syntax to create charts. Therefore some features are unavailable
+when using these methods only.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: XYChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+import java.io.*;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.annotations.*;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.ui.TextAnchor;
+import javax.swing.JFrame;\end{hide}
+
+public abstract class XYChart \begin{hide} {
+ protected Axis XAxis;
+ protected Axis YAxis;
+
+ protected SSJXYSeriesCollection dataset;
+ protected JFreeChart chart;
+ protected boolean latexDocFlag = true;
+
+ protected boolean autoRange;
+ protected double[] manualRange;
+
+ protected boolean grid = false;
+ protected double xstepGrid;
+ protected double ystepGrid;
+
+ // this flag is set true when plotting probabilities. In that case,
+ // y is always >= 0.
+ protected boolean probFlag = false;
+
+ protected double chartMargin = 0.02; // margin around the chart
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public JFreeChart getJFreeChart() \begin{hide} {
+ return chart;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{JFreeChart} object associated with this chart.
+\end{tabb}
+\begin{htmlonly}
+ \return{the associated JFreeChart object.}
+\end{htmlonly}
+\begin{code}
+
+ public Axis getXAxis() \begin{hide} {
+ return XAxis;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's domain axis ($x$-axis) object.
+\end{tabb}
+\begin{htmlonly}
+ \return{chart's domain axis ($x$-axis) object.}
+\end{htmlonly}
+\begin{code}
+
+ public Axis getYAxis() \begin{hide} {
+ return YAxis;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's range axis ($y$-axis) object.
+\end{tabb}
+\begin{htmlonly}
+ \return{chart's range axis ($y$-axis) object.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract JFrame view (int width, int height);
+\end{code}
+\begin{code}
+
+ public String getTitle() \begin{hide} {
+ return chart.getTitle().getText();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current chart title.
+\end{tabb}
+\begin{htmlonly}
+ \return{Chart title.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTitle (String title) \begin{hide} {
+ chart.setTitle(title);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets a title to this chart. This title will appear on the chart displayed
+ by method \method{view}{}.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+\end{htmlonly}
+\begin{code}
+
+ public void setprobFlag (boolean flag) \begin{hide} {
+ probFlag = flag;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Must be set \texttt{true} when plotting probabilities,
+ \texttt{false} otherwise.
+\end{tabb}
+\begin{htmlonly}
+ \param{flag}{\texttt{true} for plotting probabilities}
+\end{htmlonly}
+\begin{code}
+
+ public void setAutoRange() \begin{hide} {
+ setAutoRange (false, false, true, true);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ The $x$ and the $y$ ranges of the chart are set automatically.
+% The axes cross at point $(0,0)$.
+\end{tabb}
+\begin{code}
+
+ public void setAutoRange (boolean right, boolean top) \begin{hide} {
+ setAutoRange (false, false, right, top);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ The $x$ and the $y$ ranges of the chart are set automatically.
+ If \texttt{right} is \texttt{true}, the vertical axis will be on the left of
+ the points, otherwise on the right. If \texttt{top} is \texttt{true},
+ the horizontal axis will be under the points, otherwise above the points.
+\end{tabb}
+\begin{htmlonly}
+ \param{right}{true if the x-values on the right of axis.}
+ \param{top}{true if the y-values on the top of axis.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+ private double[] adjustRangeBounds (double bmin, double bmax) {
+ // resets chart lower and upper bounds to round values.
+ // Returns corrected [lowerBound, upperBound]
+
+ double del = (bmax - bmin)/20.0; // Choose 20 intervals to round
+ int a = (int) Math.floor(0.5 + Math.log10(del));
+ double d = Math.pow(10.0, (double) a); // power of 10
+ double lower = d*Math.ceil((bmin - del)/d);
+ if (lower > bmin)
+ lower -= d;
+ if (0 == Math.abs(bmin))
+ lower = 0;
+ double upper = d*Math.floor((bmax + del)/d);
+ if (upper < bmax)
+ upper += d;
+ double [] range = new double[2];
+ range[0] = lower;
+ range[1] = upper;
+ return range;
+ }
+
+ protected void setAutoRange (boolean xZero, boolean yZero, boolean right, boolean top) {
+ // see description of setAxesZero
+ autoRange = true;
+ double BorneMin = (dataset.getDomainBounds())[0];
+ double BorneMax = (dataset.getDomainBounds())[1];
+ double del;
+ if (BorneMax - BorneMin < 1)
+ del = (BorneMax - BorneMin) * chartMargin;
+ else
+ del = chartMargin;
+ if (BorneMin < 0.0) BorneMin *= 1.0 + del;
+ else BorneMin *= 1.0 - del;
+ if (BorneMax < 0.0) BorneMax *= 1.0 - del;
+ else BorneMax *= 1.0 + del;
+ double [] newRange = new double[2];
+ newRange = adjustRangeBounds (BorneMin, BorneMax);
+ if (probFlag && (BorneMin == 0.0))
+ newRange[0] = 0.0;
+ XAxis.getAxis().setLowerBound(newRange[0]);
+ XAxis.getAxis().setUpperBound(newRange[1]);
+
+ BorneMin = (dataset.getRangeBounds())[0];
+ BorneMax = (dataset.getRangeBounds())[1];
+ if (BorneMax - BorneMin < 1)
+ del = (BorneMax - BorneMin) * chartMargin;
+ else
+ del = chartMargin;
+ if (BorneMin < 0.0) BorneMin *= 1.0 + del;
+ else BorneMin *= 1.0 - del;
+ if (BorneMax < 0.0) BorneMax *= 1.0 - del;
+ else BorneMax *= 1.0 + del;
+ newRange = adjustRangeBounds (BorneMin, BorneMax);
+ if (probFlag && (newRange[0] <= 0.0)) // probabilities are always >= 0
+ newRange[0] = 0.0;
+ YAxis.getAxis().setLowerBound(newRange[0]);
+ YAxis.getAxis().setUpperBound(newRange[1]);
+
+ if (xZero)
+ XAxis.setTwinAxisPosition(0);
+ else {
+ if (right)
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getLowerBound());
+ else
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getUpperBound());
+ }
+
+ if (yZero)
+ YAxis.setTwinAxisPosition(0);
+ else {
+ if (top)
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getLowerBound());
+ else
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getUpperBound());
+ }
+ }\end{hide}
+
+ public void setAutoRange00 (boolean xZero, boolean yZero) \begin{hide} {
+ setAutoRange (xZero, yZero, true, true);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ The $x$ and the $y$ ranges of the chart are set automatically.
+If \texttt{xZero} is \texttt{true}, the vertical axis will pass through the
+point $(0, y)$. If \texttt{yZero} is \texttt{true}, the horizontal axis
+will pass through the point $(x, 0)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{xZero}{true if vertical axis passes through point 0}
+ \param{yZero}{true if horizontal axis passes through point 0}
+\end{htmlonly}
+\begin{code}
+
+ public void setManualRange (double[] range) \begin{hide} {
+ setManualRange (range, false, false, true, true);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the $x$ and $y$ ranges of the chart using the format: \texttt{range =
+ [xmin, xmax, ymin, ymax]}. %The axes cross at point $(0,0)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{range}{new axis ranges.}
+\end{htmlonly}
+\begin{code}
+
+ public void setManualRange (double[] range, boolean right, boolean top) \begin{hide} {
+ setManualRange (range, false, false, right, top);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the $x$ and $y$ ranges of the chart using the format: \texttt{range =
+ [xmin, xmax, ymin, ymax]}.
+ If \texttt{right} is \texttt{true}, the vertical axis will be on the left of
+ the points, otherwise on the right. If \texttt{top} is \texttt{true},
+ the horizontal axis will be under the points, otherwise above the points.
+\end{tabb}
+\begin{htmlonly}
+ \param{range}{new axis ranges.}
+ \param{right}{true if the x-values on the right.}
+ \param{top}{true if the y-values on the top.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private void setManualRange (double[] range, boolean xZero, boolean yZero,
+ boolean right, boolean top) {
+ if (range.length != 4)
+ throw new IllegalArgumentException (
+ "range must have the format: [xmin, xmax, ymin, ymax]");
+ autoRange = false;
+ XAxis.getAxis().setLowerBound(Math.min(range[0],range[1]));
+ XAxis.getAxis().setUpperBound(Math.max(range[0],range[1]));
+ YAxis.getAxis().setLowerBound(Math.min(range[2],range[3]));
+ YAxis.getAxis().setUpperBound(Math.max(range[2],range[3]));
+
+ if (xZero)
+ XAxis.setTwinAxisPosition(0);
+ else {
+ if (right)
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getLowerBound());
+ else
+ XAxis.setTwinAxisPosition(XAxis.getAxis().getUpperBound());
+ }
+
+ if (yZero)
+ YAxis.setTwinAxisPosition(0);
+ else {
+ if (top)
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getLowerBound());
+ else
+ YAxis.setTwinAxisPosition(YAxis.getAxis().getUpperBound());
+ }
+ }\end{hide}
+
+ public void setManualRange00 (double[] range, boolean xZero, boolean yZero) \begin{hide} {
+ setManualRange (range, xZero, yZero, true, true);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the $x$ and $y$ ranges of the chart using the format: \texttt{range =
+ [xmin, xmax, ymin, ymax]}.
+ If \texttt{xZero} is \texttt{true}, the vertical axis will pass through the
+point $(0, y)$. If \texttt{yZero} is \texttt{true}, the horizontal axis
+will pass through the point $(x, 0)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{xZero}{true if vertical axis passes through point 0}
+ \param{yZero}{true if horizontal axis passes through point 0}
+\end{htmlonly}
+\begin{code}
+
+ public double getChartMargin() \begin{hide} {
+ return chartMargin;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart margin, which is the fraction by which the chart
+ is enlarged on its borders. The default value is $0.02$.
+\end{tabb}
+\begin{code}
+
+ public void setChartMargin (double margin) \begin{hide} {
+ if (margin < 0.0)
+ throw new IllegalArgumentException ("margin < 0");
+ chartMargin = margin;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the chart margin to \texttt{margin}. It is the fraction by
+ which the chart is enlarged on its borders.
+ Restriction: $\texttt{margin} \ge 0$.
+\end{tabb}
+\begin{htmlonly}
+ \param{margin}{margin percentage amount.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract void setTicksSynchro (int s);
+\end{code}
+\begin{tabb}
+ Synchronizes $x$-axis ticks to the $s$-th series $x$-values.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{series.}
+\end{htmlonly}
+\begin{code}
+
+ public void drawVerticalLine (double x, String name, double yfrac,
+ boolean right) \begin{hide} {
+ double ybottom = YAxis.getAxis().getLowerBound();
+ final Object o = this;
+ if (this instanceof HistogramChart)
+ ybottom = 0;
+ double ytop = YAxis.getAxis().getUpperBound();
+ XYLineAnnotation line = new XYLineAnnotation(x, ybottom, x, ytop);
+ XYTextAnnotation text = new XYTextAnnotation(name, x, ytop*yfrac);
+ if (!right)
+ text.setTextAnchor(TextAnchor.HALF_ASCENT_RIGHT);
+ else
+ text.setTextAnchor(TextAnchor.HALF_ASCENT_LEFT);
+ XYPlot plot = getJFreeChart().getXYPlot();
+ plot.addAnnotation(line);
+ plot.addAnnotation(text);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Draws a vertical line on the chart at $x$-coordinate \texttt{x}.
+\texttt{name} is written near the line at $y$ position \texttt{yfrac}
+(a fraction of the $y$-size of the chart, 0 is the bottom, 1 is the top);
+if \texttt{right} is \texttt{true}, \texttt{name} is written on the right
+of the line, else on the left.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x$-coordinate of the line}
+ \param{name}{description of the line}
+ \param{yfrac}{$y$-position of name}
+ \param{right}{$x$-position of name}
+\end{htmlonly}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Latex-specific methods}
+
+\begin{code}
+
+ public void enableGrid (double xstep, double ystep) \begin{hide} {
+ this.grid = true;
+ this.xstepGrid = xstep;
+ this.ystepGrid = ystep;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Puts a grid on the background. It is important to note that the grid is
+ always shifted in such a way that it contains the axes. Thus, the grid does
+ not always have an intersection at the corner points; this occurs
+ only if the corner points are multiples of the steps: \texttt{xstep}
+ and \texttt{ystep} sets the step in each direction.
+\end{tabb}
+\begin{htmlonly}
+ \param{xstep}{sets the step in the x-direction.}
+ \param{ystep}{sets the step in the y-direction.}
+\end{htmlonly}
+\begin{code}
+
+ public void disableGrid() \begin{hide} {
+ this.grid = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Disables the background grid.
+\end{tabb}
+\begin{code}
+
+ public abstract String toLatex (double width, double height);
+\end{code}
+\begin{tabb}
+ Exports the chart to a \LaTeX\ source code using PGF/TikZ.
+ This method constructs and returns a string that can be written to
+ a \LaTeX\ document to render the plot. \texttt{width} and \texttt{height}
+ represents the width and the height of the produced chart. These dimensions
+ do not take into account the axes and labels extra space. The \texttt{width}
+ and the \texttt{height} of the chart are measured in centimeters.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{Chart's width in centimeters.}
+ \param{height}{Chart's height in centimeters.}
+ \return{LaTeX source code.}
+\end{htmlonly}
+\begin{code}
+
+ public void toLatexFile (String fileName, double width, double height) \begin{hide} {
+ String output = toLatex(width, height);
+ Writer file = null;
+ try {
+ file = new FileWriter(fileName);
+ file.write(output);
+ file.close();
+ } catch (IOException e) {
+ System.err.println (" toLatexFile: cannot write to " + fileName);
+ e.printStackTrace();
+ try {
+ if (file != null)
+ file.close();
+ } catch (IOException ioe) {}
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Transforms the chart to \LaTeX{} form and writes it in file \texttt{fileName}.
+ The chart's width and height (in centimeters) are \texttt{width} and \texttt{height}.
+\end{tabb}
+\begin{code}
+
+ public void setLatexDocFlag (boolean flag) \begin{hide} {
+ latexDocFlag = flag;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Flag to remove the \verb#\documentclass# (and other) commands in the
+created \LaTeX{} files.
+If \texttt{flag} is \texttt{true}, then when charts are translated into
+\LaTeX{} form, it will be as a self-contained file that can be directly
+compiled with \LaTeX{}. However, in this form, the file cannot be included in
+another \LaTeX{} file without causing compilation errors because of the multiple
+instructions \verb#\documentclass# and \verb#\begin{document}#.
+By setting \texttt{flag} to \texttt{false}, these instructions will be
+removed from the \LaTeX{} chart files, which can then be included in a master
+\LaTeX{} file. By default, the flag is \texttt{true}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ protected void setTick0Flags() {
+ // Set flag true if first or last label is on perpendicular axis.
+ // The label will be moved a little to the right (x-label), or above
+ // (y-label) to prevent it from being on the perpendicular axis.
+ // But it is unnecessary when graph begins or ends where label is;
+ // in this case, flag is false.
+ // We cannot put this method in Axis because it depends on the
+ // other axis.
+ double minAxis = Math.min (XAxis.getAxis().getRange().getLowerBound(),
+ XAxis.getTwinAxisPosition());
+ double maxAxis = Math.max (XAxis.getAxis().getRange().getUpperBound(),
+ XAxis.getTwinAxisPosition());
+ if (XAxis.getTwinAxisPosition() == minAxis ||
+ XAxis.getTwinAxisPosition() == maxAxis)
+ YAxis.setTick0Flag(false);
+ else
+ YAxis.setTick0Flag(true);
+
+ minAxis = Math.min (YAxis.getAxis().getRange().getLowerBound(),
+ YAxis.getTwinAxisPosition());
+ maxAxis = Math.max (YAxis.getAxis().getRange().getUpperBound(),
+ YAxis.getTwinAxisPosition());
+ if (YAxis.getTwinAxisPosition() == minAxis ||
+ YAxis.getTwinAxisPosition() == maxAxis)
+ XAxis.setTick0Flag(false);
+ else
+ XAxis.setTick0Flag(true);
+ }
+
+
+ protected double computeXScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = XAxis.getAxis().getLowerBound();
+ bounds[1] = XAxis.getAxis().getUpperBound();
+
+ if (position < bounds[0])
+ bounds[0] = position;
+ if (position > bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale (bounds);
+ }
+
+
+ protected double computeYScale (double position) {
+ double[] bounds = new double[2];
+ bounds[0] = YAxis.getAxis().getLowerBound();
+ bounds[1] = YAxis.getAxis().getUpperBound();
+
+ if (position < bounds[0])
+ bounds[0] = position;
+ if (position > bounds[1])
+ bounds[1] = position;
+ bounds[0] -= position;
+ bounds[1] -= position;
+ return computeScale (bounds);
+ }
+
+
+ protected double computeScale (double[] bounds) {
+ int tenPowerRatio = 0;
+ // echelle < 1 si les valeurs sont grandes
+ while (bounds[1] > 1000 || bounds[0] < -1000) {
+ bounds[1] /= 10;
+ bounds[0] /= 10;
+ tenPowerRatio++;
+ }
+ // echelle > 1 si les valeurs sont petites
+ while (bounds[1] < 100 && bounds[0] > -100) {
+ bounds[1] *= 10;
+ bounds[0] *= 10;
+ tenPowerRatio--;
+ }
+ return 1/Math.pow(10, tenPowerRatio);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/XYLineChart.java b/source/umontreal/iro/lecuyer/charts/XYLineChart.java
new file mode 100644
index 0000000..3fbc8ed
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/XYLineChart.java
@@ -0,0 +1,555 @@
+
+
+/*
+ * Class: XYLineChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.chart.*;
+import org.jfree.chart.axis.*;
+import org.jfree.chart.plot.*;
+import org.jfree.chart.renderer.xy.*;
+import org.jfree.data.xy.*;
+import org.jfree.data.category.*;
+import org.jfree.chart.renderer.category.*;
+import java.util.Locale;
+import java.util.Formatter;
+import java.lang.Math;
+import java.awt.*;
+import java.awt.geom.*;
+import cern.colt.list.DoubleArrayList;
+import javax.swing.JFrame;
+
+/**
+ * This class provides tools to create and manage curve plots. Using the
+ * {@link XYLineChart} class is the simplest way to produce curve plots only.
+ * Each {@link XYLineChart} object is linked with a
+ * {@link umontreal.iro.lecuyer.charts.XYListSeriesCollection XYListSeriesCollection} data set.
+ *
+ */
+public class XYLineChart extends XYChart {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart (
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ false, // include legend
+ true, // tooltips
+ false // urls
+ );
+
+ if (null != title) {
+ if (title.startsWith("cdf") || title.startsWith("prob") || title.startsWith("density"))
+ setprobFlag (true);
+ }
+
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ initAxis();
+ }
+
+ protected void initAxis(){
+ XAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ setAutoRange(true, true);
+ }
+
+
+
+ /**
+ * Initializes a new <TT>XYLineChart</TT> instance with an empty data set.
+ *
+ */
+ public XYLineChart() {
+ super();
+ dataset = new XYListSeriesCollection();
+ init (null, null, null);
+ }
+
+
+ /**
+ * Initializes a new <TT>XYLineChart</TT> instance with sets of points <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input parameter <TT>data</TT> represents a set of plotting data.
+ *
+ * <P>
+ * For example, if one <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix <TT>data1</TT> is given as argument
+ * <TT>data</TT>, then the first row <TT>data1</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data1</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set for a curve.
+ * Therefore matrix <TT>data1</TT><SPAN CLASS="MATH">[<I>i</I>][<I>j</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ *
+ * <P>
+ * However, one may want to plot several curves with different <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * In that case, one should give the curves as matrices with two rows.
+ * For examples, if the argument <TT>data</TT> is made of three 2-row matrices
+ * <TT>data1</TT>, <TT>data2</TT> and <TT>data3</TT>, then they represents
+ * three different curves, <TT>data*</TT><SPAN CLASS="MATH">[0]</SPAN> being the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates,
+ * and <TT>data*</TT><SPAN CLASS="MATH">[1]</SPAN> the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of the curves.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public XYLineChart (String title, String XLabel, String YLabel,
+ double[][]... data) {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>XYLineChart</TT> instance with sets of points <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * If <TT>data</TT> is a <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix,
+ * then the first row <TT>data</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set of points.
+ * Therefore matrix <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][ ]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * However, only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT> will
+ * be considered to plot each curve.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public XYLineChart (String title, String XLabel, String YLabel,
+ double[][] data, int numPoints) {
+ super();
+ dataset = new XYListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>XYLineChart</TT> instance using subsets of <TT>data</TT>.
+ * <TT>data[x][.]</TT> will form the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and
+ * <TT>data[y][.]</TT> will form the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of the chart.
+ * <TT>title</TT> sets a title, <TT>XLabel</TT> is a short description of the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> is a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * Warning: if the new <SPAN CLASS="MATH"><I>x</I></SPAN>-axis coordinates are not monotone increasing, then
+ * they will automatically be sorted in increasing order so the points will
+ * be reordered, but the original <TT>data</TT> is not changed.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ * @param x Index of data forming the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ *
+ * @param y Index of data forming the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates
+ *
+ *
+ */
+ public XYLineChart (String title, String XLabel, String YLabel,
+ double[][] data, int x, int y) {
+ super();
+ int len = data[0].length;
+ double[][] proj = new double[2][len];
+ for (int i = 0; i < len; i++) {
+ proj[0][i] = data[x][i];
+ proj[1][i] = data[y][i];
+ }
+ dataset = new XYListSeriesCollection(proj);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>XYLineChart</TT> instance with data <TT>data</TT>.
+ * The input parameter <TT>data</TT> represents a set of plotting data. A
+ * {@link cern.colt.list.DoubleArrayList DoubleArrayList} from the Colt library is
+ * used to store the data. The description is similar to the
+ * constructor {@link YListChart} with <TT>double[]... data</TT>.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public XYLineChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>XYLineChart</TT> instance with data <TT>data</TT>.
+ * The input parameter <TT>data</TT> represents a set of plotting data.
+ * {@link org.jfree.data.xy.XYSeriesCollection XYSeriesCollection} is a
+ * <TT>JFreeChart</TT> container class to store <SPAN CLASS="MATH"><I>XY</I></SPAN> plots.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series collection.
+ *
+ */
+ public XYLineChart (String title, String XLabel, String YLabel,
+ XYSeriesCollection data) {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series. <TT>name</TT> and <TT>plotStyle</TT> are the name and the plot
+ * style associated to the series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param name Name of the series.
+ *
+ * @param plotStyle Plot style of the series.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y, String name, String plotStyle) {
+ int seriesIndex = add(x,y);
+ getSeriesCollection().setName(seriesIndex, name);
+ getSeriesCollection().setPlotStyle (seriesIndex, plotStyle);
+ return seriesIndex;
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y) {
+ int seriesIndex = getSeriesCollection().add(x,y);
+ initAxis();
+ return seriesIndex;
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series. Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>x</TT> and
+ * <TT>y</TT> will be taken into account for the new series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param numPoints Number of points to add
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y, int numPoints) {
+ int seriesIndex = getSeriesCollection().add(x, y, numPoints);
+ initAxis();
+ return seriesIndex;
+ }
+
+
+ /**
+ * Adds the new collection of data series <TT>data</TT> into the series collection.
+ * If <TT>data</TT> is a <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix,
+ * then the first row <TT>data</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set of points.
+ * Therefore matrix <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][ ]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public int add (double[][] data) {
+ int seriesIndex = getSeriesCollection().add(data);
+ initAxis();
+ return seriesIndex;
+ }
+
+
+ /**
+ * Adds the new collection of data series <TT>data</TT> into the series collection.
+ * If <TT>data</TT> is a <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix,
+ * then the first row <TT>data</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set of points.
+ * Therefore matrix <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][ ]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * However, only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT> will
+ * be taken into account for the new series.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public int add (double[][] data, int numPoints) {
+ int seriesIndex = getSeriesCollection().add(data, numPoints);
+ initAxis();
+ return seriesIndex;
+ }
+
+
+ /**
+ * Returns the chart's dataset.
+ *
+ * @return the chart's dataset.
+ *
+ */
+ public XYListSeriesCollection getSeriesCollection() {
+ return (XYListSeriesCollection)dataset;
+ }
+
+
+ /**
+ * Links a new dataset to the current chart.
+ *
+ * @param dataset new dataset.
+ *
+ *
+ */
+ public void setSeriesCollection (XYListSeriesCollection dataset) {
+ this.dataset = dataset;
+ }
+
+
+ /**
+ * Synchronizes <SPAN CLASS="MATH"><I>X</I></SPAN>-axis ticks to the <SPAN CLASS="MATH"><I>s</I></SPAN>-th series <SPAN CLASS="MATH"><I>x</I></SPAN>-values.
+ *
+ * @param s series used to define ticks.
+ *
+ *
+ */
+ public void setTicksSynchro (int s) {
+ XYSeriesCollection seriesCollection =
+ (XYSeriesCollection)this.dataset.getSeriesCollection();
+ double[] values = new double[seriesCollection.getItemCount(s)];
+
+ for(int i = 0; i < seriesCollection.getItemCount(s); i++)
+ values[i] = seriesCollection.getXValue(s, i);
+
+ XAxis.setLabels(values);
+ }
+
+
+ /**
+ * Displays chart on the screen using Swing.
+ * This method creates an application containing a chart panel displaying
+ * the chart. The created frame is positioned on-screen, and displayed before
+ * it is returned. The <TT>width</TT> and the <TT>height</TT>
+ * of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @return frame containing the chart.;
+ *
+ */
+ public JFrame view (int width, int height) {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame("XYLineChart from SSJ: " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("XYLineChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }
+
+ /**
+ * Displays bar chart on the screen using Swing.
+ * This method creates an application containing a bar chart panel displaying
+ * the chart. The created frame is positioned on-screen, and displayed before
+ * it is returned. The <TT>width</TT> and the <TT>height</TT>
+ * of the chart are measured in pixels.
+ *
+ * @param width frame width in pixels.
+ *
+ * @param height frame height in pixels.
+ *
+ * @return frame containing the bar chart.;
+ *
+ */
+ public JFrame viewBar (int width, int height) {
+ JFrame myFrame;
+ if (chart.getTitle() != null)
+ myFrame = new JFrame("XYLineChart from SSJ: " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("XYLineChart from SSJ");
+
+ XYPlot plot = (XYPlot) chart.getPlot();
+
+ //Create the bar
+ plot.setDataset(0, dataset.getSeriesCollection());
+ final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true);
+ renderer.setSeriesPaint(0,Color.ORANGE);
+ renderer.setSeriesShape(0,new Line2D.Double(0, 0, 0 , 1000));
+ plot.setRenderer(0, renderer);
+
+ //Create the points
+ plot.setDataset(1, dataset.getSeriesCollection());
+ final XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(false, true);
+ renderer2.setSeriesPaint(0,Color.ORANGE);
+ renderer2.setSeriesShape(0,new Ellipse2D.Double(-2.0,-2.0,4.0,4.0));
+ plot.setRenderer(1, renderer2);
+
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }
+
+
+ public String toLatex (double width, double height) {
+ double xunit=0, yunit=0;
+ double[] save = new double[4];
+
+ if (dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ // taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ xunit = width / ((Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale));
+ // taille d'une unite en y et en cm dans l'objet "tikzpicture"
+ yunit = height / ((Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale));
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if (chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ: %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if (grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(
+ XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/charts/XYLineChart.tex b/source/umontreal/iro/lecuyer/charts/XYLineChart.tex
new file mode 100644
index 0000000..062defb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/XYLineChart.tex
@@ -0,0 +1,545 @@
+\defmodule {XYLineChart}
+
+% Extends \externalclass{umontreal.iro.lecuyer.charts}{XYChart}.
+This class provides tools to create and manage curve plots. Using the
+\class{XYLineChart} class is the simplest way to produce curve plots only.
+Each \class{XYLineChart} object is linked with a
+\externalclass{umontreal.iro.lecuyer.charts}{XYListSeriesCollection} data set.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: XYLineChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.chart.*;
+import org.jfree.chart.axis.*;
+import org.jfree.chart.plot.*;
+import org.jfree.chart.renderer.xy.*;
+import org.jfree.data.xy.*;
+import org.jfree.data.category.*;
+import org.jfree.chart.renderer.category.*;
+import java.util.Locale;
+import java.util.Formatter;
+import java.lang.Math;
+import java.awt.*;
+import java.awt.geom.*;
+import cern.colt.list.DoubleArrayList;
+import javax.swing.JFrame;\end{hide}
+
+public class XYLineChart extends XYChart \begin{hide} {
+
+ protected void init (String title, String XLabel, String YLabel) {
+ // create the chart...
+ chart = ChartFactory.createXYLineChart (
+ title, // chart title
+ XLabel, // x axis label
+ YLabel, // y axis label
+ dataset.getSeriesCollection(), // data
+ PlotOrientation.VERTICAL,
+ false, // include legend
+ true, // tooltips
+ false // urls
+ );
+
+ if (null != title) {
+ if (title.startsWith("cdf") || title.startsWith("prob") || title.startsWith("density"))
+ setprobFlag (true);
+ }
+
+ ((XYPlot)chart.getPlot()).setRenderer(dataset.getRenderer());
+ // Initialize axis variables
+ initAxis();
+ }
+
+ protected void initAxis(){
+ XAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getDomainAxis(),
+ Axis.ORIENTATION_HORIZONTAL);
+ YAxis = new Axis((NumberAxis)((XYPlot)chart.getPlot()).getRangeAxis(),
+ Axis.ORIENTATION_VERTICAL);
+ setAutoRange(true, true);
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public XYLineChart() \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection();
+ init (null, null, null);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{XYLineChart} instance with an empty data set.
+\end{tabb}
+\begin{code}
+
+ public XYLineChart (String title, String XLabel, String YLabel,
+ double[][]... data) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{XYLineChart} instance with sets of points \texttt{data}.
+\texttt{title} is a title, \texttt{XLabel} is a short description of the
+$x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+The input parameter \texttt{data} represents a set of plotting data.
+
+ For example, if one $n$-row matrix \texttt{data1} is given as argument
+ \texttt{data}, then the first row \texttt{data1}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data1}$[i],
+ i=1,\ldots, n-1$, represents a $y$-coordinate set for a curve.
+ Therefore matrix \texttt{data1}$[i][j]$, $i=0,\ldots, n-1$, corresponds
+ to $n-1$ curves, all with the same $x$-coordinates.
+
+ However, one may want to plot several curves with different $x$-coordinates.
+ In that case, one should give the curves as matrices with two rows.
+For examples, if the argument \texttt{data} is made of three 2-row matrices
+\texttt{data1}, \texttt{data2} and \texttt{data3}, then they represents
+ three different curves, \texttt{data*}$[0]$ being the $x$-coordinates,
+ and \texttt{data*}$[1]$ the $y$-coordinates of the curves.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public XYLineChart (String title, String XLabel, String YLabel,
+ double[][] data, int numPoints) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{XYLineChart} instance with sets of points \texttt{data}.
+\texttt{title} is a title, \texttt{XLabel} is a short description of the
+$x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ If \texttt{data} is a $n$-row matrix,
+ then the first row \texttt{data}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data}$[i],
+ i=1,\ldots, n-1$, represents a $y$-coordinate set of points.
+ Therefore matrix \texttt{data}$[i][\ ]$, $i=0,\ldots, n-1$, corresponds
+ to $n-1$ curves, all with the same $x$-coordinates.
+ However, only \emph{the first} \texttt{numPoints} of \texttt{data} will
+ be considered to plot each curve.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public XYLineChart (String title, String XLabel, String YLabel,
+ double[][] data, int x, int y) \begin{hide} {
+ super();
+ int len = data[0].length;
+ double[][] proj = new double[2][len];
+ for (int i = 0; i < len; i++) {
+ proj[0][i] = data[x][i];
+ proj[1][i] = data[y][i];
+ }
+ dataset = new XYListSeriesCollection(proj);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Initializes a new \texttt{XYLineChart} instance using subsets of \texttt{data}.
+\texttt{data[x][.]} will form the $x$-coordinates and
+\texttt{data[y][.]} will form the $y$-coordinates of the chart.
+\texttt{title} sets a title, \texttt{XLabel} is a short description of the
+$x$-axis, and \texttt{YLabel} is a short description of the $y$-axis.
+Warning: if the new $x$-axis coordinates are not monotone increasing, then
+they will automatically be sorted in increasing order so the points will
+be reordered, but the original \texttt{data} is not changed.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+ \param{x}{Index of data forming the $x$-coordinates}
+ \param{y}{Index of data forming the $y$-coordinates}
+\end{htmlonly}
+\begin{code}
+
+ public XYLineChart (String title, String XLabel, String YLabel,
+ DoubleArrayList... data) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{XYLineChart} instance with data \texttt{data}.
+ The input parameter \texttt{data} represents a set of plotting data. A
+ \externalclass{cern.colt.list}{DoubleArrayList} from the Colt library is
+ used to store the data. The description is similar to the
+ constructor \class{YListChart} with \texttt{double[]... data}.
+% Therefore \texttt{data}$[0]$ will form the first
+ % curve, \texttt{data}$[1]$ forms the second and in general,
+% \texttt{data}$[i], i = 0, \ldots,n,$ forms a point set.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public XYLineChart (String title, String XLabel, String YLabel,
+ XYSeriesCollection data) \begin{hide} {
+ super();
+ dataset = new XYListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{XYLineChart} instance with data \texttt{data}.
+ The input parameter \texttt{data} represents a set of plotting data.
+ \externalclass{org.jfree.data.xy}{XYSeriesCollection} is a
+ \texttt{JFreeChart} container class to store $XY$ plots.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series collection.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public int add (double[] x, double[] y, String name, String plotStyle) \begin{hide} {
+ int seriesIndex = add(x,y);
+ getSeriesCollection().setName(seriesIndex, name);
+ getSeriesCollection().setPlotStyle (seriesIndex, plotStyle);
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series. \texttt{name} and \texttt{plotStyle} are the name and the plot
+ style associated to the series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \param{name}{Name of the series.}
+ \param{plotStyle}{Plot style of the series.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] x, double[] y) \begin{hide} {
+ int seriesIndex = getSeriesCollection().add(x,y);
+ initAxis();
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] x, double[] y, int numPoints) \begin{hide} {
+ int seriesIndex = getSeriesCollection().add(x, y, numPoints);
+ initAxis();
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series. Only \emph{the first} \texttt{numPoints} of \texttt{x} and
+ \texttt{y} will be taken into account for the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \param{numPoints}{Number of points to add}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[][] data) \begin{hide} {
+ int seriesIndex = getSeriesCollection().add(data);
+ initAxis();
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds the new collection of data series \texttt{data} into the series collection.
+ If \texttt{data} is a $n$-row matrix,
+ then the first row \texttt{data}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data}$[i],
+ i=1,\ldots, n-1$, represents a $y$-coordinate set of points.
+ Therefore matrix \texttt{data}$[i][\ ]$, $i=0,\ldots, n-1$, corresponds
+ to $n-1$ curves, all with the same $x$-coordinates.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[][] data, int numPoints) \begin{hide} {
+ int seriesIndex = getSeriesCollection().add(data, numPoints);
+ initAxis();
+ return seriesIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds the new collection of data series \texttt{data} into the series collection.
+ If \texttt{data} is a $n$-row matrix,
+ then the first row \texttt{data}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data}$[i],
+ i=1,\ldots, n-1$, represents a $y$-coordinate set of points.
+ Therefore matrix \texttt{data}$[i][\ ]$, $i=0,\ldots, n-1$, corresponds
+ to $n-1$ curves, all with the same $x$-coordinates.
+ However, only \emph{the first} \texttt{numPoints} of \texttt{data} will
+ be taken into account for the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public XYListSeriesCollection getSeriesCollection() \begin{hide} {
+ return (XYListSeriesCollection)dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the chart's dataset.
+\end{tabb}
+\begin{htmlonly}
+ \return{the chart's dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeriesCollection (XYListSeriesCollection dataset) \begin{hide} {
+ this.dataset = dataset;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Links a new dataset to the current chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{dataset}{new dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public void setTicksSynchro (int s) \begin{hide} {
+ XYSeriesCollection seriesCollection =
+ (XYSeriesCollection)this.dataset.getSeriesCollection();
+ double[] values = new double[seriesCollection.getItemCount(s)];
+
+ for(int i = 0; i < seriesCollection.getItemCount(s); i++)
+ values[i] = seriesCollection.getXValue(s, i);
+
+ XAxis.setLabels(values);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Synchronizes $X$-axis ticks to the $s$-th series $x$-values.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{series used to define ticks.}
+\end{htmlonly}
+\begin{code}
+
+ public JFrame view (int width, int height) \begin{hide} {
+ JFrame myFrame;
+ if(chart.getTitle() != null)
+ myFrame = new JFrame("XYLineChart from SSJ: " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("XYLineChart from SSJ");
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays chart on the screen using Swing.
+ This method creates an application containing a chart panel displaying
+ the chart. The created frame is positioned on-screen, and displayed before
+ it is returned. The \texttt{width} and the \texttt{height}
+ of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \return{frame containing the chart.};
+\end{htmlonly}
+\begin{code}
+ public JFrame viewBar (int width, int height) \begin{hide} {
+ JFrame myFrame;
+ if (chart.getTitle() != null)
+ myFrame = new JFrame("XYLineChart from SSJ: " + chart.getTitle().getText());
+ else
+ myFrame = new JFrame("XYLineChart from SSJ");
+
+ XYPlot plot = (XYPlot) chart.getPlot();
+
+ //Create the bar
+ plot.setDataset(0, dataset.getSeriesCollection());
+ final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true);
+ renderer.setSeriesPaint(0,Color.ORANGE);
+ renderer.setSeriesShape(0,new Line2D.Double(0, 0, 0 , 1000));
+ plot.setRenderer(0, renderer);
+
+ //Create the points
+ plot.setDataset(1, dataset.getSeriesCollection());
+ final XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(false, true);
+ renderer2.setSeriesPaint(0,Color.ORANGE);
+ renderer2.setSeriesShape(0,new Ellipse2D.Double(-2.0,-2.0,4.0,4.0));
+ plot.setRenderer(1, renderer2);
+
+ ChartPanel chartPanel = new ChartPanel(chart);
+ chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
+ myFrame.setContentPane(chartPanel);
+ myFrame.pack();
+ myFrame.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
+ myFrame.setLocationRelativeTo (null);
+ myFrame.setVisible(true);
+ return myFrame;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Displays bar chart on the screen using Swing.
+ This method creates an application containing a bar chart panel displaying
+ the chart. The created frame is positioned on-screen, and displayed before
+ it is returned. The \texttt{width} and the \texttt{height}
+ of the chart are measured in pixels.
+\end{tabb}
+\begin{htmlonly}
+ \param{width}{frame width in pixels.}
+ \param{height}{frame height in pixels.}
+ \return{frame containing the bar chart.};
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Latex-specific method}
+
+\begin{code}
+
+ public String toLatex (double width, double height) \begin{hide} {
+ double xunit=0, yunit=0;
+ double[] save = new double[4];
+
+ if (dataset.getSeriesCollection().getSeriesCount() == 0)
+ throw new IllegalArgumentException("Empty chart");
+
+ //Calcul des parametres d'echelle et de decalage
+ double XScale = computeXScale(XAxis.getTwinAxisPosition());
+ double YScale = computeYScale(YAxis.getTwinAxisPosition());
+
+ // taille d'une unite en x et en cm dans l'objet "tikzpicture"
+ xunit = width / ((Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition()) * XScale) - (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition()) * XScale));
+ // taille d'une unite en y et en cm dans l'objet "tikzpicture"
+ yunit = height / ((Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition()) * YScale) - (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition()) * YScale));
+
+ Formatter formatter = new Formatter(Locale.US);
+
+ /*Entete du document*/
+ if (latexDocFlag) {
+ formatter.format("\\documentclass[12pt]{article}%n%n");
+ formatter.format("\\usepackage{tikz}%n\\usetikzlibrary{plotmarks}%n\\begin{document}%n%n");
+ }
+ if (chart.getTitle() != null)
+ formatter.format("%% PGF/TikZ picture from SSJ: %s%n", chart.getTitle().getText());
+ else
+ formatter.format("%% PGF/TikZ picture from SSJ %n");
+ formatter.format("%% XScale = %s, YScale = %s, XShift = %s, YShift = %s%n", XScale, YScale, XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition());
+ formatter.format("%% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale%n");
+ formatter.format("%% and thisFileYValue = (originalSeriesYValue+YShift)*YScale%n%n");
+ if (chart.getTitle() != null)
+ formatter.format("\\begin{figure}%n");
+ formatter.format("\\begin{center}%n");
+ formatter.format("\\begin{tikzpicture}[x=%scm, y=%scm]%n", xunit, yunit);
+ formatter.format("\\footnotesize%n");
+ if (grid)
+ formatter.format("\\draw[color=lightgray] (%s, %s) grid[xstep = %s, ystep=%s] (%s, %s);%n",
+ (Math.min(XAxis.getAxis().getRange().getLowerBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.min(YAxis.getAxis().getRange().getLowerBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale,
+ xstepGrid*XScale, ystepGrid*YScale,
+ (Math.max(XAxis.getAxis().getRange().getUpperBound(), XAxis.getTwinAxisPosition())-XAxis.getTwinAxisPosition()) * XScale,
+ (Math.max(YAxis.getAxis().getRange().getUpperBound(), YAxis.getTwinAxisPosition())-YAxis.getTwinAxisPosition()) * YScale );
+ setTick0Flags();
+ formatter.format("%s", XAxis.toLatex(XScale) );
+ formatter.format("%s", YAxis.toLatex(YScale) );
+
+ formatter.format("%s", dataset.toLatex(
+ XScale, YScale,
+ XAxis.getTwinAxisPosition(), YAxis.getTwinAxisPosition(),
+ XAxis.getAxis().getLowerBound(), XAxis.getAxis().getUpperBound(),
+ YAxis.getAxis().getLowerBound(), YAxis.getAxis().getUpperBound()));
+
+ formatter.format("\\end{tikzpicture}%n");
+ formatter.format("\\end{center}%n");
+ if (chart.getTitle() != null) {
+ formatter.format("\\caption{");
+ formatter.format(chart.getTitle().getText());
+ formatter.format("}%n\\end{figure}%n");
+ }
+ if (latexDocFlag)
+ formatter.format("\\end{document}%n");
+ return formatter.toString();
+ }\end{hide}
+\end{code}
+
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/XYListSeriesCollection.java b/source/umontreal/iro/lecuyer/charts/XYListSeriesCollection.java
new file mode 100644
index 0000000..54d139f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/XYListSeriesCollection.java
@@ -0,0 +1,891 @@
+
+
+/*
+ * Class: XYListSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import umontreal.iro.lecuyer.functions.MathFunction;
+import umontreal.iro.lecuyer.functionfit.SmoothingCubicSpline;
+import umontreal.iro.lecuyer.util.RootFinder;
+
+import org.jfree.data.xy.*;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.awt.Color;
+
+/**
+ * This class extends
+ * {@link umontreal.iro.lecuyer.charts.SSJXYSeriesCollection SSJXYSeriesCollection}.
+ * It stores data used in a <TT>XYLineChart</TT> or in other related charts.
+ * <TT>XYListSeriesCollection</TT> provides complementary tools to draw
+ * simple curves; for example, one may
+ * add or remove plots series and modify plot style.
+ * This class is linked with the JFreeChart <TT>XYSeriesCollection</TT> class to
+ * store data plots,
+ * and linked with the JFreeChart <TT>XYLineAndShapeRenderer</TT> to render the plot.
+ * Each series must contain enough points to plot a nice curve.
+ * It is recommended to use about 30 points. However, some rapidly
+ * varying functions may require many more points. This class can be used to draw scatter plots.
+ *
+ */
+public class XYListSeriesCollection extends SSJXYSeriesCollection {
+ protected String[] marksType; // marks on points (+, x, *...)
+ protected String[] dashPattern; // line dashing (solid, dotted, densely dotted, loosely dotted,
+ // dashed, densely dashed, loosely dashed, only marks)
+ protected String[] plotStyle; // plot style (lines, curves...)
+ private boolean autoCompletion = false;
+
+
+ /**
+ * Creates a new <TT>XYListSeriesCollection</TT> instance with an empty dataset.
+ *
+ */
+ public XYListSeriesCollection() {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection();
+ }
+
+
+ /**
+ * Creates a new <TT>XYListSeriesCollection</TT> instance with default
+ * parameters and given data series. The input parameter <TT>data</TT>
+ * represents a set of plotting data.
+ *
+ * <P>
+ * For example, if one <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix <TT>data1</TT> is given as argument,
+ * then the first row <TT>data1</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data1</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set for the points.
+ * Therefore matrix <TT>data1</TT><SPAN CLASS="MATH">[<I>i</I>][<I>j</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ *
+ * <P>
+ * However, one may want to plot several curves with different <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * In that case, one should give the curves as matrices with two rows.
+ * For examples, if the argument <TT>data</TT> is made of three 2-row matrices
+ * <TT>data1</TT>, <TT>data2</TT> and <TT>data3</TT>, then they represents
+ * three different curves, <TT>data*</TT><SPAN CLASS="MATH">[0]</SPAN> being the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates,
+ * and <TT>data*</TT><SPAN CLASS="MATH">[1]</SPAN> the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of the curves.
+ *
+ * <P>
+ * However, we may also consider the sets of points above not as part of curves,
+ * but rather as several list of points.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public XYListSeriesCollection (double[][]... data) {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+
+ if (data[i].length < 2)
+ throw new IllegalArgumentException (
+ "Unable to render the plot. data["+ i +"] contains less than two rows");
+
+ for (int j = 0; j < data[i].length-1; j++)
+ if (data[i][j].length != data[i][j+1].length)
+ throw new IllegalArgumentException(
+ "data["+ i +"][" + j + "] and data["+ i +"]["+ (j+1) +"] must share the same length");
+
+ for (int j = 1; j < data[i].length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ for (int k = 0; k < data[i][0].length; k++)
+ serie.add(data[i][0][k], data[i][j][k]);
+ tempSeriesCollection.addSeries(serie);
+ }
+ }
+
+ // set default colors
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+
+ // set default plot style
+ plotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>XYListSeriesCollection</TT> instance with default
+ * parameters and given points <TT>data</TT>.
+ * If <TT>data</TT> is a <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix,
+ * then the first row <TT>data</TT><SPAN CLASS="MATH">[0]</SPAN> represents the
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate vector, and every other row <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 1,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set of points.
+ * Therefore, if the points represents curves to be plotted,
+ * <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][ ]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, corresponds
+ * to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> curves, all with the same <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * Only the first <TT>numPoints</TT> of <TT>data</TT> will be considered
+ * for each of the set of points.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public XYListSeriesCollection (double[][] data, int numPoints) {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ if (data.length < 2)
+ throw new IllegalArgumentException (
+ "Unable to render the plot. data contains less than two rows");
+
+ // n-1 curves: data[0] is x; data[i] is y for each curve
+ for (int j = 1; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ for (int k = 0; k < numPoints; k++)
+ serie.add(data[0][k], data[j][k]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+
+ // set default plot style
+ plotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>XYListSeriesCollection</TT> instance with default parameters and given data.
+ * The input parameter represents a set of data plots, the constructor will count the occurrence number
+ * <SPAN CLASS="MATH"><I>Y</I></SPAN> of each value <SPAN CLASS="MATH"><I>X</I></SPAN> in the <TT>DoubleArrayList</TT>, and plot the point <SPAN CLASS="MATH">(<I>X</I>, <I>Y</I>)</SPAN>.
+ * Each {@link cern.colt.list.DoubleArrayList DoubleArrayList} variable corresponds to a curve on the chart.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public XYListSeriesCollection (DoubleArrayList... data) {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection ();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;
+
+ XYSeries serie;
+ double[] elements;
+ int count = 0;
+ DoubleArrayList temp;
+ for(int i = 0; i < data.length; i++) {
+ serie = new XYSeries(" ");
+
+ temp = data[i].copy(); // deep copy
+ temp.trimToSize(); // set capacity to the current size
+ temp.quickSortFromTo(0, temp.size()-1); // sort list in increasing order, simplify the next processings
+ elements = temp.elements();
+
+ int j = 0;
+ int l = 0;
+ while(j < elements.length) {
+ while(j < elements.length && elements[j] == elements[l]) {
+ j++;
+ count++;
+ }
+ serie.add(elements[l], count);
+ count = 0;
+ l = j;
+ }
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ // set default colors
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Creates a new <TT>XYListSeriesCollection</TT> instance with default parameters and given data series.
+ * The input parameter represents a set of plotting data.
+ * Each series of the given collection corresponds to a curve on the plot.
+ *
+ * @param data series of point sets.
+ *
+ */
+ public XYListSeriesCollection (XYSeriesCollection data) {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = data;
+ for(int i = 0; i < data.getSeriesCount(); i++) {
+ XYSeries serie = data.getSeries(i);
+ }
+
+ // set default colors
+ for(int i = 0; i < data.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[data.getSeriesCount()];
+ marksType = new String[data.getSeriesCount()];
+ dashPattern = new String[data.getSeriesCount()];
+ for(int i = 0; i < data.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("x and y must have the same length");
+ return add (x, y, x.length);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. Vector <TT>x</TT> represents
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates and vector <TT>y</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of
+ * the series. Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>x</TT>
+ * and <TT>y</TT> will be added to the new series.
+ *
+ * @param x <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param numPoints Number of points to add
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (double[] x, double[] y, int numPoints) {
+ XYSeries serie = new XYSeries(" ");
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;
+ serie.setNotify(true);
+ if ((x.length < numPoints) ||(y.length < numPoints))
+ throw new IllegalArgumentException("numPoints > length of x or y");
+ for (int i = 0; i < numPoints; i++)
+ serie.add(x[i], y[i]);
+ tempSeriesCollection.addSeries(serie);
+
+ // color
+ int j = tempSeriesCollection.getSeriesCount()-1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ int co = tempSeriesCollection.getSeriesCount();
+ String[] newPlotStyle = new String[co];
+ String[] newMarksType = new String[co];
+ String[] newDashPattern = new String[co];
+ for(j = 0; j < co - 1; j++) {
+ newPlotStyle[j] = plotStyle[j];
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ newPlotStyle[j] = "smooth";
+ newMarksType[j] = " ";
+ newDashPattern[j] = "solid";
+ plotStyle = newPlotStyle;
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return tempSeriesCollection.getSeriesCount()-1;
+ }
+
+
+ /**
+ * Adds a data series into the series collection. The input format of
+ * <TT>data</TT> is described in constructor
+ * <TT>XYListSeriesCollection(double[][] data)</TT>.
+ *
+ * @param data input data.
+ *
+ * @return Integer that represent the number of point sets added to the current dataset.
+ *
+ */
+ public int add (double[][] data) {
+ return add (data, data[0].length);
+ }
+
+
+ /**
+ * Adds data series into the series collection. The input format of
+ * <TT>data</TT> is described in constructor
+ * <TT>XYListSeriesCollection(double[][] data)</TT>.
+ * Only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT>
+ * (the first <TT>numPoints</TT> columns of the matrix)
+ * will be added to each new series.
+ *
+ * @param data input data.
+ *
+ * @param numPoints Number of points to add for each new series
+ *
+ * @return Integer that represent the number of point sets added to the current dataset.
+ *
+ */
+ public int add (double[][] data, int numPoints) {
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection) seriesCollection;
+ int n = tempSeriesCollection.getSeriesCount();
+
+ if (data.length < 2)
+ throw new IllegalArgumentException(
+ "Unable to render the plot. data contains less than two rows");
+
+ for (int j = 0; j < data.length; j++)
+ if (data[j].length < numPoints)
+ throw new IllegalArgumentException(
+ "data[" + j + "] has not enough points");
+
+ for (int j = 1; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ serie.setNotify(true);
+ for (int k = 0; k < numPoints; k++)
+ serie.add(data[0][k], data[j][k]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ // color
+ for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++)
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int j = 0; j < n; j++) {
+ newPlotStyle[j] = plotStyle[j];
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++) {
+ newPlotStyle[j] = "smooth";
+ newMarksType[j] = " ";
+ newDashPattern[j] = "solid";
+ }
+ plotStyle = newPlotStyle;
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return (tempSeriesCollection.getSeriesCount()-n);
+ }
+
+
+ /**
+ * Adds a data series into the series collection. The input format of
+ * <TT>data</TT> is described in constructor
+ * <TT>XYListSeriesCollection (DoubleArrayList... data)</TT>.
+ *
+ * @param data data series.
+ *
+ * @return Integer that represent the new point set's position in the JFreeChart <TT>XYSeriesCollection</TT> object.
+ *
+ */
+ public int add (DoubleArrayList data) {
+ XYSeries serie = new XYSeries(" ");
+ DoubleArrayList temp = data.copy(); // deep copy
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
+
+ temp.trimToSize(); // set capacity to the current size
+ temp.quickSortFromTo(0, temp.size()-1); // sort list in increasing order, simplify the next processings
+ double[] elements = temp.elements();
+
+ int count = 0;
+ int j = 0;
+ int l = 0;
+ while(j < elements.length) {
+ while(j < elements.length && elements[j] == elements[l]) {
+ j++;
+ count++;
+ }
+ serie.add(elements[l], count);
+ count = 0;
+ l = j;
+ }
+ tempSeriesCollection.addSeries(serie);
+
+ // color
+ j = tempSeriesCollection.getSeriesCount()-1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for(j = 0; j < tempSeriesCollection.getSeriesCount()-1; j++) {
+ newPlotStyle[j] = plotStyle[j];
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ newPlotStyle[j] = "smooth";
+ newMarksType[j] = " ";
+ newDashPattern[j] = "solid";
+ plotStyle = newPlotStyle;
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return tempSeriesCollection.getSeriesCount()-1;
+ }
+
+
+ /**
+ * Gets the current name of the selected series.
+ *
+ * @param series series index.
+ *
+ * @return current name of the series.
+ *
+ */
+ public String getName (int series) {
+ return (String)((XYSeriesCollection )seriesCollection).getSeries(series).getKey();
+ }
+
+
+ /**
+ * Sets the name of the selected series.
+ *
+ * @param series series index.
+ *
+ * @param name point set new name.
+ *
+ */
+ public void setName (int series, String name) {
+ if(name == null)
+ name = " ";
+ ((XYSeriesCollection)seriesCollection).getSeries(series).setKey(name);
+ }
+
+
+ /**
+ * Enables the auto completion option. When this parameter
+ * is enabled, straight lines are used to approximate points on the
+ * chart bounds if the method isn't able to display all points,
+ * because the user defined bounds are smaller than the
+ * most significant data point coordinate, for instance.
+ * It does not extrapolate the point sets, but simply estimates
+ * point coordinates on the curve at bound positions for a better visual rendering.
+ *
+ */
+ public void enableAutoCompletion() {
+ this.autoCompletion = true;
+ }
+
+
+ /**
+ * Disables auto completion option. Default status is <TT>disabled</TT>.
+ *
+ */
+ public void disableAutoCompletion() {
+ this.autoCompletion = false;
+ }
+
+
+ /**
+ * Returns the mark type associated with the <TT>series</TT>th data series.
+ *
+ * @param series series index.
+ *
+ * @return mark type.
+ *
+ */
+ public String getMarksType (int series) {
+ return marksType[series];
+ }
+
+
+ /**
+ * Adds marks on the points of a data series.
+ * It is possible to use any of the marks provided by the TikZ package,
+ * some of which are ``<TT>*</TT>'', ``<TT>+</TT>'' and ``<TT>x</TT>''.
+ * A blank character, used by default, disables marks.
+ * The PGF/TikZ documentation provides more information about placing marks on plots.
+ *
+ * @param series series index.
+ *
+ * @param marksType mark type.
+ *
+ *
+ */
+ public void setMarksType (int series, String marksType) {
+ this.marksType[series] = marksType;
+ }
+
+
+ /**
+ * Returns the dash pattern associated with the <TT>series</TT>th data series.
+ *
+ * @param series series index.
+ *
+ * @return mark type.
+ *
+ */
+ public String getDashPattern (int series) {
+ return dashPattern[series];
+ }
+
+
+ /**
+ * Selects dash pattern for a data series. It is possible to use all the dash
+ * options provided by the TikZ package: ``<TT>solid</TT>'', ``<TT>dotted</TT>'',
+ * ``<TT>densely dotted</TT>'', ``<TT>loosely dotted</TT>'', ``<TT>dashed</TT>'',
+ * ``<TT>densely dashed</TT>'', ``<TT>loosely dashed</TT>'' and
+ * ``<TT>only marks</TT>''. If ``<TT>only marks</TT>" is chosen, then method
+ * {@link #setMarksType setMarksType} must be called to choose the marks
+ * (which are blank by default).
+ *
+ * @param series series index.
+ *
+ * @param dashPattern dash style.
+ *
+ *
+ */
+ public void setDashPattern (int series, String dashPattern) {
+ this.dashPattern[series] = dashPattern;
+ if (dashPattern.equals("only marks")) {
+ ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, false);
+ ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, true);
+ } else {
+ ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, true);
+ ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, false);
+ }
+ }
+
+
+ /**
+ * Gets the current plot style for the selected series.
+ *
+ * @param series series index.
+ *
+ * @return current plot style.
+ *
+ */
+ public String getPlotStyle (int series) {
+ return plotStyle[series];
+ }
+
+
+ /**
+ * Selects the plot style for a given series. It is possible to use all the
+ * plot options provided by the TikZ package. Some of which are:
+ * ``<TT>sharp plot</TT>'', which joins points with straight lines,
+ * ``<TT>smooth</TT>'', which joins points with a smoothing curve,
+ * ``<TT>only marks</TT>'', which does not join points, etc.
+ * The PGF/TikZ documentation provides more information about smooth plots,
+ * sharp plots and comb plots.
+ *
+ * @param series series index.
+ *
+ * @param plotStyle plot style.
+ *
+ *
+ */
+ public void setPlotStyle (int series, String plotStyle) {
+ this.plotStyle[series] = plotStyle;
+ }
+
+
+ public String toLatex (double XScale, double YScale,
+ double XShift, double YShift,
+ double xmin, double xmax,
+ double ymin, double ymax) {
+
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+ xmin = Math.min(XShift, xmin);
+ xmax = Math.max(XShift, xmax);
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ Formatter formatter = new Formatter(Locale.US);
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
+ double XEPSILON = (1.0E-4/XScale)+XShift;
+ double YEPSILON = (1.0E-4/YScale)+YShift;
+ boolean outOfBounds = false;
+ MathFunction[] spline = null;
+ double[] xBounds = getRangeBounds();
+ double[] yBounds = getDomainBounds();
+ double x, y;
+// Smoothing splines, consulter ref: QA278.2 G74, QA278.2 T35, QA278.2 E87
+
+// if(xBounds[0] < xmin || xBounds[1] > xmax || yBounds[0] < ymin || yBounds[1] > ymax) {
+// // on sait qu'il y a des points qui vont sortir du chart
+// // initialisation d'une spline pour chaque courbe
+// spline = new SmoothingCubicSpline[seriesCollection.getSeriesCount()];
+// for(int i = 0; i<seriesCollection.getSeriesCount(); i++)
+// spline[i] = new SmoothingCubicSpline( (seriesCollection.getSeries(i).toArray())[0],
+// (seriesCollection.getSeries(i).toArray())[1], 0.5);
+// }
+
+ // on sait qu'il y a des points qui vont sortir du chart
+ // initialisation d'une spline pour chaque courbe
+ if (true) {
+ spline = new SmoothingCubicSpline[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ spline[i] = new SmoothingCubicSpline((tempSeriesCollection.getSeries(i).toArray())[0],
+ (tempSeriesCollection.getSeries(i).toArray())[1], 1);
+ } else {
+ spline = new AffineFit[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ spline[i] = new AffineFit((tempSeriesCollection.getSeries(i).toArray())[0],
+ (tempSeriesCollection.getSeries(i).toArray())[1]);
+ }
+
+ for(int i = tempSeriesCollection.getSeriesCount()-1; i >= 0; i--) {
+ XYSeries temp = tempSeriesCollection.getSeries(i);
+
+ if (temp.getItemCount() < 2)
+ throw new IllegalArgumentException("Unable to plot series " + i +
+ ": this series must have two points at least");
+
+ Color color = (Color)renderer.getSeriesPaint(i);
+ String colorString = detectXColorClassic(color);
+ if( colorString == null) {
+ colorString = "color"+i;
+ formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+ colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
+ }
+
+ // Cas particulier pour le premier point, on doit savoir si il est dans le chart ou pas
+ if ( temp.getX(0).doubleValue() >= xmin && temp.getX(0).doubleValue() <= xmax &&
+ temp.getY(0).doubleValue() >= ymin && temp.getY(0).doubleValue() <= ymax) {
+ outOfBounds = false;
+ formatter.format( "\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
+ plotStyle[i], colorString, marksType[i], dashPattern[i]);
+ }
+ else {
+ outOfBounds = true;
+ formatter.format("%% ");
+ }
+ formatter.format("(%.2f,%.4f)", (temp.getX(0).doubleValue()-XShift)*XScale,
+ (temp.getY(0).doubleValue()-YShift)*YScale);
+ formatter.format(" %% (%f, %f)%n", temp.getX(0).doubleValue(), temp.getY(0).doubleValue());
+
+ // Cas general
+ for(int j = 1; j < temp.getItemCount(); j++) {
+ double[] result;
+ if (!outOfBounds) { //on est dans le chart
+ result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, false);
+ // on regarde si on ne sort pas du chart, si c'est le cas on evalue le point en limite
+ if (result != null) { // le point courant n'est pas dans le chart, on sort donc du chart
+ outOfBounds = true;
+ if (autoCompletion)
+ formatter.format("(%.2f,%.4f) %%%n", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
+ formatter.format("}%%%n%% ");
+ }
+ }
+ else { // le point precedent etait hors du chart
+ if ( temp.getX(j).doubleValue() >= xmin && temp.getX(j).doubleValue() <= xmax &&
+ temp.getY(j).doubleValue() >= ymin && temp.getY(j).doubleValue() <= ymax) {
+ // on rentre dans le chart, il faut evaluer le point en limite
+ j = j-1;
+ result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, true);
+ // ici result ne peut pas etre null
+ formatter.format( ";%%%n\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
+ plotStyle[i], colorString, marksType[i], dashPattern[i]);
+ if (autoCompletion)
+ formatter.format("(%.2f,%.4f) %%%n ", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
+ formatter.format("%% ");
+ outOfBounds = false;
+ }
+ else {
+ formatter.format("%% ");
+ // on les donnees sont toujours hors du chart
+ }
+ }
+ /* on affiche les coordonnees du point quoiqu'il arrive,
+ si celui ci est hors du chart alors la balise de commentaire a ete deja place */
+ formatter.format("(%.2f,%.4f)", (temp.getX(j).doubleValue()-XShift)*XScale,
+ (temp.getY(j).doubleValue()-YShift)*YScale);
+ if(j == temp.getItemCount()-1)
+ formatter.format("}");
+ formatter.format(" %% (%f, %f)%n", temp.getX(j).doubleValue(), temp.getY(j).doubleValue());
+// formatter.format(" %%%n");
+ }
+ formatter.format(" node[right] {%s};%n", (String)temp.getKey());
+ }
+ return formatter.toString();
+ }
+
+
+ /* *
+ * Compute x and y to chart limit bounds for extra-bounded points
+ *
+ * @param xmin lower bound for x coordinates
+ * @param xmax upper bound for x coordinates
+ * @param ymin lower bound for y coordinates
+ * @param ymax upper bound for y coordinates
+ * @param XEPSILON increment step size for x coordinates
+ * @param YEPSILON increment step size for y coordinates
+ * @param spline sline used to approximate points
+ * @param temp current series
+ * @param numPoint point index in the current series
+ * @param sens direction of the in chart last point
+ * true : point numPoint+1 is in the chart
+ * false : point numPoint-1 is in the chart
+ *
+ * @return x and y coordinates on the chart bounds.
+ */
+ private static double[] evalLimitValues(double xmin, double xmax, double ymin, double ymax, double XEPSILON, double YEPSILON, MathFunction spline, XYSeries temp, int numPoint, boolean sens) {
+ int j = numPoint;
+ int k = 0;
+ double x, y;
+ if(sens)
+ k = j+1;
+ else
+ k = j-1;
+ if(temp.getX(j).doubleValue() < xmin) {// Hors du chart mais on etait dans le chart au point precedent
+ x = xmin;
+ y = spline.evaluate(xmin); // spline puis evaluer en xmin
+ while(y<ymin) {
+ x += XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x>xmin tantque y<ymin, y peut etre superieur a ymin car le point precedent est dans le chart
+ while(y > ymax) {
+ x += XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x en ymax avec x > xmin
+ }
+ else if(temp.getX(j).doubleValue() > xmax) {
+ x = xmax;
+ y = spline.evaluate(xmax);
+ while(y<ymin) {
+ x -= XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x<xmax tantque y<ymin
+ while(y > ymax) {
+ x -= XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x<xmax tantque y>ymax
+ }
+ else if(temp.getY(j).doubleValue() < ymin) {
+ y = ymin;
+ x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymin avec x ente xValue(ptCourant) et xValue(ptCourant-1)
+ while(x < xmin) {
+ y += YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ while(x > xmax) {
+ y += YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ }
+ else if(temp.getY(j).doubleValue() > ymax) {
+ y = ymax;
+ x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymax avec x ente xValue(ptCourant) et xValue(ptCourant-1)
+ while(x < xmin) {
+ y -= YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ while(x > xmax) {
+ y -= YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ }
+ else
+ return null;
+ double[] retour = new double[2];
+ retour[0] = x;
+ retour[1] = y;
+ return retour;
+ }
+
+
+ private static double evaluateX (final MathFunction spline, final double y, double xPrincipal, double xAnnexe) {
+ final MathFunction xFunction = new MathFunction () {
+ public double evaluate (double t) {
+ return spline.evaluate(t) - y;
+ }
+ };
+ return RootFinder.brentDekker (xPrincipal, xAnnexe-1.0E-6, xFunction, 1e-6);
+ }
+
+
+ private class AffineFit implements MathFunction{
+
+ double[] x;
+ double[] y;
+
+ public AffineFit(double[] x, double[] y) {
+ this.x =x;
+ this.y =y;
+ }
+
+ public double evaluate(double t) {
+ int i = 0;
+ if (t <= x[0])
+ return y[0];
+ while (i < x.length && t > x[i])
+ i++;
+ i--;
+ if (i == x.length)
+ return x[x.length-1];
+
+ return y[i] + ((t - x[i]) / (x[i+1] - x[i])) * (y[i+1] - y[i]);
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/XYListSeriesCollection.tex b/source/umontreal/iro/lecuyer/charts/XYListSeriesCollection.tex
new file mode 100644
index 0000000..f8897a9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/XYListSeriesCollection.tex
@@ -0,0 +1,898 @@
+\defmodule {XYListSeriesCollection}
+
+This class extends
+\externalclass{umontreal.iro.lecuyer.charts}{SSJXYSeriesCollection}.
+It stores data used in a \texttt{XYLineChart} or in other related charts.
+%
+\texttt{XYListSeriesCollection} provides complementary tools to draw
+ simple curves; for example, one may
+add or remove plots series and modify plot style.
+This class is linked with the JFreeChart \texttt{XYSeriesCollection} class to
+store data plots,
+and linked with the JFreeChart \texttt{XYLineAndShapeRenderer} to render the plot.
+Each series must contain enough points to plot a nice curve.
+It is recommended to use about 30 points. However, some rapidly
+varying functions may require many more points. This class can be used to draw scatter plots.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: XYListSeriesCollection
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import umontreal.iro.lecuyer.functions.MathFunction;
+import umontreal.iro.lecuyer.functionfit.SmoothingCubicSpline;
+import umontreal.iro.lecuyer.util.RootFinder;
+
+import org.jfree.data.xy.*;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+
+import cern.colt.list.DoubleArrayList;
+
+import java.util.Locale;
+import java.util.Formatter;
+import java.awt.Color;\end{hide}
+
+public class XYListSeriesCollection extends SSJXYSeriesCollection \begin{hide} {
+ protected String[] marksType; // marks on points (+, x, *...)
+ protected String[] dashPattern; // line dashing (solid, dotted, densely dotted, loosely dotted,
+ // dashed, densely dashed, loosely dashed, only marks)
+ protected String[] plotStyle; // plot style (lines, curves...)
+ private boolean autoCompletion = false;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+ public XYListSeriesCollection() \begin{hide} {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{XYListSeriesCollection} instance with an empty dataset.
+\end{tabb}
+\begin{code}
+
+ public XYListSeriesCollection (double[][]... data) \begin{hide} {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+
+ if (data[i].length < 2)
+ throw new IllegalArgumentException (
+ "Unable to render the plot. data["+ i +"] contains less than two rows");
+
+ for (int j = 0; j < data[i].length-1; j++)
+ if (data[i][j].length != data[i][j+1].length)
+ throw new IllegalArgumentException(
+ "data["+ i +"][" + j + "] and data["+ i +"]["+ (j+1) +"] must share the same length");
+
+ for (int j = 1; j < data[i].length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ for (int k = 0; k < data[i][0].length; k++)
+ serie.add(data[i][0][k], data[i][j][k]);
+ tempSeriesCollection.addSeries(serie);
+ }
+ }
+
+ // set default colors
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+
+ // set default plot style
+ plotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{XYListSeriesCollection} instance with default
+ parameters and given data series. The input parameter \texttt{data}
+ represents a set of plotting data.
+
+ For example, if one $n$-row matrix \texttt{data1} is given as argument,
+ then the first row \texttt{data1}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data1}$[i],
+ i=1,\ldots, n-1$, represents a $y$-coordinate set for the points.
+ Therefore matrix \texttt{data1}$[i][j]$, $i=0,\ldots, n-1$, corresponds
+ to $n-1$ curves, all with the same $x$-coordinates.
+
+ However, one may want to plot several curves with different $x$-coordinates.
+ In that case, one should give the curves as matrices with two rows.
+For examples, if the argument \texttt{data} is made of three 2-row matrices
+\texttt{data1}, \texttt{data2} and \texttt{data3}, then they represents
+ three different curves, \texttt{data*}$[0]$ being the $x$-coordinates,
+ and \texttt{data*}$[1]$ the $y$-coordinates of the curves.
+
+ However, we may also consider the sets of points above not as part of curves,
+but rather as several list of points.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public XYListSeriesCollection (double[][] data, int numPoints) \begin{hide} {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection)seriesCollection;
+ if (data.length < 2)
+ throw new IllegalArgumentException (
+ "Unable to render the plot. data contains less than two rows");
+
+ // n-1 curves: data[0] is x; data[i] is y for each curve
+ for (int j = 1; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ for (int k = 0; k < numPoints; k++)
+ serie.add(data[0][k], data[j][k]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ // set default colors
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+
+ // set default plot style
+ plotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{XYListSeriesCollection} instance with default
+ parameters and given points \texttt{data}.
+ If \texttt{data} is a $n$-row matrix,
+ then the first row \texttt{data}$[0]$ represents the
+ $x$-coordinate vector, and every other row \texttt{data}$[i],
+ i=1,\ldots, n-1$, represents a $y$-coordinate set of points.
+ Therefore, if the points represents curves to be plotted,
+ \texttt{data}$[i][\ ]$, $i=0,\ldots, n-1$, corresponds
+ to $n-1$ curves, all with the same $x$-coordinates.
+ Only the first \texttt{numPoints} of \texttt{data} will be considered
+for each of the set of points.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public XYListSeriesCollection (DoubleArrayList... data) \begin{hide} {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = new XYSeriesCollection ();
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;
+
+ XYSeries serie;
+ double[] elements;
+ int count = 0;
+ DoubleArrayList temp;
+ for(int i = 0; i < data.length; i++) {
+ serie = new XYSeries(" ");
+
+ temp = data[i].copy(); // deep copy
+ temp.trimToSize(); // set capacity to the current size
+ temp.quickSortFromTo(0, temp.size()-1); // sort list in increasing order, simplify the next processings
+ elements = temp.elements();
+
+ int j = 0;
+ int l = 0;
+ while(j < elements.length) {
+ while(j < elements.length && elements[j] == elements[l]) {
+ j++;
+ count++;
+ }
+ serie.add(elements[l], count);
+ count = 0;
+ l = j;
+ }
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ // set default colors
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ marksType = new String[tempSeriesCollection.getSeriesCount()];
+ dashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for(int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{XYListSeriesCollection} instance with default parameters and given data.
+ The input parameter represents a set of data plots, the constructor will count the occurrence number
+ $Y$ of each value $X$ in the \texttt{DoubleArrayList}, and plot the point $(X, Y)$.
+ Each \externalclass{cern.colt.list}{DoubleArrayList} variable corresponds to a curve on the chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public XYListSeriesCollection (XYSeriesCollection data) \begin{hide} {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ // ((XYLineAndShapeRenderer)renderer).setShapesVisible(false);
+ seriesCollection = data;
+ for(int i = 0; i < data.getSeriesCount(); i++) {
+ XYSeries serie = data.getSeries(i);
+ }
+
+ // set default colors
+ for(int i = 0; i < data.getSeriesCount(); i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[data.getSeriesCount()];
+ marksType = new String[data.getSeriesCount()];
+ dashPattern = new String[data.getSeriesCount()];
+ for(int i = 0; i < data.getSeriesCount(); i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{XYListSeriesCollection} instance with default parameters and given data series.
+ The input parameter represents a set of plotting data.
+ Each series of the given collection corresponds to a curve on the plot.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Data control methods}
+
+\begin{code}
+
+ public int add (double[] x, double[] y) \begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("x and y must have the same length");
+ return add (x, y, x.length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[] x, double[] y, int numPoints) \begin{hide} {
+ XYSeries serie = new XYSeries(" ");
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection )seriesCollection;
+ serie.setNotify(true);
+ if ((x.length < numPoints) ||(y.length < numPoints))
+ throw new IllegalArgumentException("numPoints > length of x or y");
+ for (int i = 0; i < numPoints; i++)
+ serie.add(x[i], y[i]);
+ tempSeriesCollection.addSeries(serie);
+
+ // color
+ int j = tempSeriesCollection.getSeriesCount()-1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ int co = tempSeriesCollection.getSeriesCount();
+ String[] newPlotStyle = new String[co];
+ String[] newMarksType = new String[co];
+ String[] newDashPattern = new String[co];
+ for(j = 0; j < co - 1; j++) {
+ newPlotStyle[j] = plotStyle[j];
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ newPlotStyle[j] = "smooth";
+ newMarksType[j] = " ";
+ newDashPattern[j] = "solid";
+ plotStyle = newPlotStyle;
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return tempSeriesCollection.getSeriesCount()-1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. Vector \texttt{x} represents
+ the $x$-coordinates and vector \texttt{y} represents the $y$-coordinates of
+ the series. Only \emph{the first} \texttt{numPoints} of \texttt{x}
+ and \texttt{y} will be added to the new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{$x_i$ coordinates.}
+ \param{y}{$y_i$ coordinates.}
+ \param{numPoints}{Number of points to add}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[][] data) \begin{hide} {
+ return add (data, data[0].length);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. The input format of
+ \texttt{data} is described in constructor
+\texttt{XYListSeriesCollection(double[][] data)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{input data.}
+ \return{Integer that represent the number of point sets added to the current dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (double[][] data, int numPoints) \begin{hide} {
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection) seriesCollection;
+ int n = tempSeriesCollection.getSeriesCount();
+
+ if (data.length < 2)
+ throw new IllegalArgumentException(
+ "Unable to render the plot. data contains less than two rows");
+
+ for (int j = 0; j < data.length; j++)
+ if (data[j].length < numPoints)
+ throw new IllegalArgumentException(
+ "data[" + j + "] has not enough points");
+
+ for (int j = 1; j < data.length; j++) {
+ XYSeries serie = new XYSeries(" ");
+ serie.setNotify(true);
+ for (int k = 0; k < numPoints; k++)
+ serie.add(data[0][k], data[j][k]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ // color
+ for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++)
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for (int j = 0; j < n; j++) {
+ newPlotStyle[j] = plotStyle[j];
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ for(int j = n; j < tempSeriesCollection.getSeriesCount(); j++) {
+ newPlotStyle[j] = "smooth";
+ newMarksType[j] = " ";
+ newDashPattern[j] = "solid";
+ }
+ plotStyle = newPlotStyle;
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return (tempSeriesCollection.getSeriesCount()-n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds data series into the series collection. The input format of
+ \texttt{data} is described in constructor
+\texttt{XYListSeriesCollection(double[][] data)}.
+ Only \emph{the first} \texttt{numPoints} of \texttt{data}
+ (the first \texttt{numPoints} columns of the matrix)
+ will be added to each new series.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{input data.}
+ \param{numPoints}{Number of points to add for each new series}
+ \return{Integer that represent the number of point sets added to the current dataset.}
+\end{htmlonly}
+\begin{code}
+
+ public int add (DoubleArrayList data) \begin{hide} {
+ XYSeries serie = new XYSeries(" ");
+ DoubleArrayList temp = data.copy(); // deep copy
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
+
+ temp.trimToSize(); // set capacity to the current size
+ temp.quickSortFromTo(0, temp.size()-1); // sort list in increasing order, simplify the next processings
+ double[] elements = temp.elements();
+
+ int count = 0;
+ int j = 0;
+ int l = 0;
+ while(j < elements.length) {
+ while(j < elements.length && elements[j] == elements[l]) {
+ j++;
+ count++;
+ }
+ serie.add(elements[l], count);
+ count = 0;
+ l = j;
+ }
+ tempSeriesCollection.addSeries(serie);
+
+ // color
+ j = tempSeriesCollection.getSeriesCount()-1;
+ renderer.setSeriesPaint(j, getDefaultColor(j));
+
+ String[] newPlotStyle = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newMarksType = new String[tempSeriesCollection.getSeriesCount()];
+ String[] newDashPattern = new String[tempSeriesCollection.getSeriesCount()];
+ for(j = 0; j < tempSeriesCollection.getSeriesCount()-1; j++) {
+ newPlotStyle[j] = plotStyle[j];
+ newMarksType[j] = marksType[j];
+ newDashPattern[j] = dashPattern[j];
+ }
+
+ newPlotStyle[j] = "smooth";
+ newMarksType[j] = " ";
+ newDashPattern[j] = "solid";
+ plotStyle = newPlotStyle;
+ marksType = newMarksType;
+ dashPattern = newDashPattern;
+
+ return tempSeriesCollection.getSeriesCount()-1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds a data series into the series collection. The input format of
+ \texttt{data} is described in constructor
+\texttt{XYListSeriesCollection (DoubleArrayList... data)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{data series.}
+ \return{Integer that represent the new point set's position in the JFreeChart \texttt{XYSeriesCollection} object.}
+\end{htmlonly}
+\begin{code}
+
+ public String getName (int series) \begin{hide} {
+ return (String)((XYSeriesCollection )seriesCollection).getSeries(series).getKey();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current name of the selected series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{current name of the series.}
+\end{htmlonly}
+\begin{code}
+
+ public void setName (int series, String name) \begin{hide} {
+ if(name == null)
+ name = " ";
+ ((XYSeriesCollection)seriesCollection).getSeries(series).setKey(name);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the name of the selected series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{name}{point set new name.}
+\end{htmlonly}
+
+
+\newpage
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Rendering methods}
+
+\begin{code}
+
+ public void enableAutoCompletion() \begin{hide} {
+ this.autoCompletion = true;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Enables the auto completion option. When this parameter
+ is enabled, straight lines are used to approximate points on the
+ chart bounds if the method isn't able to display all points,
+ because the user defined bounds are smaller than the
+ most significant data point coordinate, for instance.
+ It does not extrapolate the point sets, but simply estimates
+ point coordinates on the curve at bound positions for a better visual rendering.
+\end{tabb}
+\begin{code}
+
+ public void disableAutoCompletion() \begin{hide} {
+ this.autoCompletion = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Disables auto completion option. Default status is \texttt{disabled}.
+\end{tabb}
+\begin{code}
+
+ public String getMarksType (int series) \begin{hide} {
+ return marksType[series];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the mark type associated with the \texttt{series}th data series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{mark type.}
+\end{htmlonly}
+\begin{code}
+
+ public void setMarksType (int series, String marksType) \begin{hide} {
+ this.marksType[series] = marksType;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds marks on the points of a data series.
+ It is possible to use any of the marks provided by the TikZ package,
+ some of which are ``\texttt{*}'', ``\texttt{+}'' and ``\texttt{x}''.
+ A blank character, used by default, disables marks.
+ The PGF/TikZ documentation provides more information about placing marks on plots.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{marksType}{mark type.}
+\end{htmlonly}
+\begin{code}
+
+ public String getDashPattern (int series) \begin{hide} {
+ return dashPattern[series];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dash pattern associated with the \texttt{series}th data series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{mark type.}
+\end{htmlonly}
+\begin{code}
+
+ public void setDashPattern (int series, String dashPattern) \begin{hide} {
+ this.dashPattern[series] = dashPattern;
+ if (dashPattern.equals("only marks")) {
+ ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, false);
+ ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, true);
+ } else {
+ ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series, true);
+ ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series, false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Selects dash pattern for a data series. It is possible to use all the dash
+options provided by the TikZ package: ``\texttt{solid}'', ``\texttt{dotted}'',
+``\texttt{densely dotted}'', ``\texttt{loosely dotted}'', ``\texttt{dashed}'',
+``\texttt{densely dashed}'', ``\texttt{loosely dashed}'' and
+``\texttt{only marks}''. If ``\texttt{only marks}" is chosen, then method
+\method{setMarksType}{} must be called to choose the marks
+(which are blank by default).
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{dashPattern}{dash style.}
+\end{htmlonly}
+\begin{code}
+
+ public String getPlotStyle (int series) \begin{hide} {
+ return plotStyle[series];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Gets the current plot style for the selected series.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \return{current plot style.}
+\end{htmlonly}
+\begin{code}
+
+ public void setPlotStyle (int series, String plotStyle) \begin{hide} {
+ this.plotStyle[series] = plotStyle;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Selects the plot style for a given series. It is possible to use all the
+ plot options provided by the TikZ package. Some of which are:
+ ``\texttt{sharp plot}'', which joins points with straight lines,
+ ``\texttt{smooth}'', which joins points with a smoothing curve,
+ ``\texttt{only marks}'', which does not join points, etc.
+ The PGF/TikZ documentation provides more information about smooth plots,
+ sharp plots and comb plots.
+\end{tabb}
+\begin{htmlonly}
+ \param{series}{series index.}
+ \param{plotStyle}{plot style.}
+\end{htmlonly}
+\begin{code}
+
+ public String toLatex (double XScale, double YScale,
+ double XShift, double YShift,
+ double xmin, double xmax,
+ double ymin, double ymax)\begin{hide} {
+
+ // Calcule les bornes reelles du graphique, en prenant en compte la position des axes
+ xmin = Math.min(XShift, xmin);
+ xmax = Math.max(XShift, xmax);
+ ymin = Math.min(YShift, ymin);
+ ymax = Math.max(YShift, ymax);
+
+ Formatter formatter = new Formatter(Locale.US);
+ XYSeriesCollection tempSeriesCollection = (XYSeriesCollection) seriesCollection;
+ double XEPSILON = (1.0E-4/XScale)+XShift;
+ double YEPSILON = (1.0E-4/YScale)+YShift;
+ boolean outOfBounds = false;
+ MathFunction[] spline = null;
+ double[] xBounds = getRangeBounds();
+ double[] yBounds = getDomainBounds();
+ double x, y;
+// Smoothing splines, consulter ref: QA278.2 G74, QA278.2 T35, QA278.2 E87
+
+// if(xBounds[0] < xmin || xBounds[1] > xmax || yBounds[0] < ymin || yBounds[1] > ymax) {
+// // on sait qu'il y a des points qui vont sortir du chart
+// // initialisation d'une spline pour chaque courbe
+// spline = new SmoothingCubicSpline[seriesCollection.getSeriesCount()];
+// for(int i = 0; i<seriesCollection.getSeriesCount(); i++)
+// spline[i] = new SmoothingCubicSpline( (seriesCollection.getSeries(i).toArray())[0],
+// (seriesCollection.getSeries(i).toArray())[1], 0.5);
+// }
+
+ // on sait qu'il y a des points qui vont sortir du chart
+ // initialisation d'une spline pour chaque courbe
+ if (true) {
+ spline = new SmoothingCubicSpline[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ spline[i] = new SmoothingCubicSpline((tempSeriesCollection.getSeries(i).toArray())[0],
+ (tempSeriesCollection.getSeries(i).toArray())[1], 1);
+ } else {
+ spline = new AffineFit[tempSeriesCollection.getSeriesCount()];
+ for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++)
+ spline[i] = new AffineFit((tempSeriesCollection.getSeries(i).toArray())[0],
+ (tempSeriesCollection.getSeries(i).toArray())[1]);
+ }
+
+ for(int i = tempSeriesCollection.getSeriesCount()-1; i >= 0; i--) {
+ XYSeries temp = tempSeriesCollection.getSeries(i);
+
+ if (temp.getItemCount() < 2)
+ throw new IllegalArgumentException("Unable to plot series " + i +
+ ": this series must have two points at least");
+
+ Color color = (Color)renderer.getSeriesPaint(i);
+ String colorString = detectXColorClassic(color);
+ if( colorString == null) {
+ colorString = "color"+i;
+ formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
+ colorString, color.getRed()/255.0, color.getGreen()/255.0, color.getBlue()/255.0);
+ }
+
+ // Cas particulier pour le premier point, on doit savoir si il est dans le chart ou pas
+ if ( temp.getX(0).doubleValue() >= xmin && temp.getX(0).doubleValue() <= xmax &&
+ temp.getY(0).doubleValue() >= ymin && temp.getY(0).doubleValue() <= ymax) {
+ outOfBounds = false;
+ formatter.format( "\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
+ plotStyle[i], colorString, marksType[i], dashPattern[i]);
+ }
+ else {
+ outOfBounds = true;
+ formatter.format("%% ");
+ }
+ formatter.format("(%.2f,%.4f)", (temp.getX(0).doubleValue()-XShift)*XScale,
+ (temp.getY(0).doubleValue()-YShift)*YScale);
+ formatter.format(" %% (%f, %f)%n", temp.getX(0).doubleValue(), temp.getY(0).doubleValue());
+
+ // Cas general
+ for(int j = 1; j < temp.getItemCount(); j++) {
+ double[] result;
+ if (!outOfBounds) { //on est dans le chart
+ result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, false);
+ // on regarde si on ne sort pas du chart, si c'est le cas on evalue le point en limite
+ if (result != null) { // le point courant n'est pas dans le chart, on sort donc du chart
+ outOfBounds = true;
+ if (autoCompletion)
+ formatter.format("(%.2f,%.4f) %%%n", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
+ formatter.format("}%%%n%% ");
+ }
+ }
+ else { // le point precedent etait hors du chart
+ if ( temp.getX(j).doubleValue() >= xmin && temp.getX(j).doubleValue() <= xmax &&
+ temp.getY(j).doubleValue() >= ymin && temp.getY(j).doubleValue() <= ymax) {
+ // on rentre dans le chart, il faut evaluer le point en limite
+ j = j-1;
+ result = evalLimitValues(xmin, xmax, ymin, ymax, XEPSILON, YEPSILON, spline[i], temp, j, true);
+ // ici result ne peut pas etre null
+ formatter.format( ";%%%n\\draw [%s, color=%s, mark=%s, style=%s] plot coordinates {%%%n",
+ plotStyle[i], colorString, marksType[i], dashPattern[i]);
+ if (autoCompletion)
+ formatter.format("(%.2f,%.4f) %%%n ", (result[0]-XShift)*XScale, (result[1]-YShift)*YScale);
+ formatter.format("%% ");
+ outOfBounds = false;
+ }
+ else {
+ formatter.format("%% ");
+ // on les donnees sont toujours hors du chart
+ }
+ }
+ /* on affiche les coordonnees du point quoiqu'il arrive,
+ si celui ci est hors du chart alors la balise de commentaire a ete deja place */
+ formatter.format("(%.2f,%.4f)", (temp.getX(j).doubleValue()-XShift)*XScale,
+ (temp.getY(j).doubleValue()-YShift)*YScale);
+ if(j == temp.getItemCount()-1)
+ formatter.format("}");
+ formatter.format(" %% (%f, %f)%n", temp.getX(j).doubleValue(), temp.getY(j).doubleValue());
+// formatter.format(" %%%n");
+ }
+ formatter.format(" node[right] {%s};%n", (String)temp.getKey());
+ }
+ return formatter.toString();
+ }
+
+
+ /**
+ * Compute x and y to chart limit bounds for extra-bounded points
+ *
+ * @param xmin lower bound for x coordinates
+ * @param xmax upper bound for x coordinates
+ * @param ymin lower bound for y coordinates
+ * @param ymax upper bound for y coordinates
+ * @param XEPSILON increment step size for x coordinates
+ * @param YEPSILON increment step size for y coordinates
+ * @param spline sline used to approximate points
+ * @param temp current series
+ * @param numPoint point index in the current series
+ * @param sens direction of the in chart last point
+ * true : point numPoint+1 is in the chart
+ * false : point numPoint-1 is in the chart
+ *
+ * @return x and y coordinates on the chart bounds.
+ */
+ private static double[] evalLimitValues(double xmin, double xmax, double ymin, double ymax, double XEPSILON, double YEPSILON, MathFunction spline, XYSeries temp, int numPoint, boolean sens) {
+ int j = numPoint;
+ int k = 0;
+ double x, y;
+ if(sens)
+ k = j+1;
+ else
+ k = j-1;
+ if(temp.getX(j).doubleValue() < xmin) {// Hors du chart mais on etait dans le chart au point precedent
+ x = xmin;
+ y = spline.evaluate(xmin); // spline puis evaluer en xmin
+ while(y<ymin) {
+ x += XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x>xmin tantque y<ymin, y peut etre superieur a ymin car le point precedent est dans le chart
+ while(y > ymax) {
+ x += XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x en ymax avec x > xmin
+ }
+ else if(temp.getX(j).doubleValue() > xmax) {
+ x = xmax;
+ y = spline.evaluate(xmax);
+ while(y<ymin) {
+ x -= XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x<xmax tantque y<ymin
+ while(y > ymax) {
+ x -= XEPSILON;
+ y = spline.evaluate(x);
+ } // evaluer un x<xmax tantque y>ymax
+ }
+ else if(temp.getY(j).doubleValue() < ymin) {
+ y = ymin;
+ x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymin avec x ente xValue(ptCourant) et xValue(ptCourant-1)
+ while(x < xmin) {
+ y += YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ while(x > xmax) {
+ y += YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ }
+ else if(temp.getY(j).doubleValue() > ymax) {
+ y = ymax;
+ x = evaluateX(spline, y, temp.getX(j).doubleValue(), temp.getX(k).doubleValue());// spline puis evaluer en ymax avec x ente xValue(ptCourant) et xValue(ptCourant-1)
+ while(x < xmin) {
+ y -= YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ while(x > xmax) {
+ y -= YEPSILON;
+ x = evaluateX(spline, y, x, temp.getX(k).doubleValue());
+ }
+ }
+ else
+ return null;
+ double[] retour = new double[2];
+ retour[0] = x;
+ retour[1] = y;
+ return retour;
+ }
+
+
+ private static double evaluateX (final MathFunction spline, final double y, double xPrincipal, double xAnnexe) {
+ final MathFunction xFunction = new MathFunction () {
+ public double evaluate (double t) {
+ return spline.evaluate(t) - y;
+ }
+ };
+ return RootFinder.brentDekker (xPrincipal, xAnnexe-1.0E-6, xFunction, 1e-6);
+ }
+
+
+ private class AffineFit implements MathFunction{
+
+ double[] x;
+ double[] y;
+
+ public AffineFit(double[] x, double[] y) {
+ this.x =x;
+ this.y =y;
+ }
+
+ public double evaluate(double t) {
+ int i = 0;
+ if (t <= x[0])
+ return y[0];
+ while (i < x.length && t > x[i])
+ i++;
+ i--;
+ if (i == x.length)
+ return x[x.length-1];
+
+ return y[i] + ((t - x[i]) / (x[i+1] - x[i])) * (y[i+1] - y[i]);
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/charts/YListChart.java b/source/umontreal/iro/lecuyer/charts/YListChart.java
new file mode 100644
index 0000000..237b6a0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/YListChart.java
@@ -0,0 +1,201 @@
+
+
+/*
+ * Class: YListChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+/**
+ * This class extends the class
+ * {@link umontreal.iro.lecuyer.charts.XYLineChart XYLineChart}.
+ * Each {@link YListChart} object is associated with a
+ * {@link umontreal.iro.lecuyer.charts.YListSeriesCollection YListSeriesCollection} data set.
+ * The data is given as one or more lists of <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates.
+ * The <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates are
+ * regularly-spaced multiples of the indices of the data points.
+ *
+ */
+public class YListChart extends XYLineChart {
+
+
+
+
+ /**
+ * Empty constructor.
+ *
+ */
+ public YListChart() {
+ super();
+ // dataset = new XYListSeriesCollection();
+ // init (null, null, null);
+ }
+
+
+ /**
+ * Initializes a new <TT>YListChart</TT> instance with set of points <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input vectors represents a set of plotting data. More specifically,
+ * each vector <TT>data</TT> represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates set.
+ * Position in the vector will form the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates.
+ * Indeed, the value <TT>data</TT><SPAN CLASS="MATH">[<I>j</I>]</SPAN> corresponds to the point
+ *
+ * <SPAN CLASS="MATH">(<I>j</I> + 1,<texttt>data</texttt>[<I>j</I>])</SPAN> (but rescaled) on the chart.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public YListChart (String title, String XLabel, String YLabel,
+ double[]... data) {
+ super();
+ dataset = new YListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Similar to the constructor above. Except that if <TT>flag</TT> is <TT>true</TT>, the points are
+ *
+ * <SPAN CLASS="MATH">(<I>j</I> + 1,<TT>data</TT>[<I>j</I>])</SPAN> for each series;
+ * but if <TT>flag</TT> is <TT>false</TT>,
+ * the points are
+ * <SPAN CLASS="MATH">((<I>j</I> + 1)/<I>n</I>,<TT>data</TT>[<I>j</I>])</SPAN>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is
+ * the number of points of each series in <TT>data</TT>.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param flag to choose the step between <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public YListChart (String title, String XLabel, String YLabel,
+ boolean flag, double[]... data) {
+ super();
+ dataset = new YListSeriesCollection(flag, data);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>YListChart</TT> instance with a set of points
+ * <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input vector represents a set of plotting data.
+ * Position in the vector gives the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates of the curve.
+ * The value <TT>data</TT><SPAN CLASS="MATH">[<I>j</I>]</SPAN> corresponds to the point
+ * <SPAN CLASS="MATH">(<I>j</I> + 1</SPAN>, <TT>data</TT><SPAN CLASS="MATH">[<I>j</I>]</SPAN>) (but rescaled on the chart) for the curve.
+ * However, only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT>
+ * will be considered to plot the curve.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data point set.
+ *
+ * @param numPoints number of points to plot
+ *
+ *
+ */
+ public YListChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) {
+ super();
+ dataset = new YListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Similar to the constructor above, but the points are
+ * <SPAN CLASS="MATH">(<I>h</I>(<I>j</I> + 1), <TT>data</TT>[<I>j</I>])</SPAN>.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param h step between <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ *
+ * @param data point set.
+ *
+ * @param numPoints number of points to plot
+ *
+ *
+ */
+ public YListChart (String title, String XLabel, String YLabel,
+ double h, double[] data, int numPoints) {
+ super();
+ dataset = new YListSeriesCollection(h, data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+
+
+ /**
+ * Initializes a new <TT>YListChart</TT> instance with set of points <TT>data</TT>.
+ * <TT>title</TT> is a title, <TT>XLabel</TT> is a short description of
+ * the <SPAN CLASS="MATH"><I>x</I></SPAN>-axis, and <TT>YLabel</TT> a short description of the <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ * The input vectors represents a set of plotting data. More specifically,
+ * for a <SPAN CLASS="MATH"><I>n</I></SPAN>-row matrix <TT>data</TT>, each row <TT>data</TT>
+ * <SPAN CLASS="MATH">[<I>i</I>], <I>i</I> = 0,…, <I>n</I> - 1</SPAN>, represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate set for a curve.
+ * Position in the vector gives the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates of the curves.
+ * Indeed, the value <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][<I>j</I>]</SPAN> corresponds to the point
+ * <SPAN CLASS="MATH">(<I>j</I> + 1</SPAN>, <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][<I>j</I>]</SPAN>) (but rescaled on the chart) for curve <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ * However, only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of each <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>]</SPAN>
+ * will be considered to plot each curve.
+ *
+ * @param title chart title.
+ *
+ * @param XLabel Label on <SPAN CLASS="MATH"><I>x</I></SPAN>-axis.
+ *
+ * @param YLabel Label on <SPAN CLASS="MATH"><I>y</I></SPAN>-axis.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints number of points to plot
+ *
+ */
+ public YListChart (String title, String XLabel, String YLabel,
+ double[][] data, int numPoints) {
+ super();
+ dataset = new YListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/YListChart.tex b/source/umontreal/iro/lecuyer/charts/YListChart.tex
new file mode 100644
index 0000000..fbe86ce
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/YListChart.tex
@@ -0,0 +1,199 @@
+\defmodule {YListChart}
+
+% This class in necessary because of the ambiguity in the constructors with
+% a variable number of arguments in the mother class. Without this class,
+% when one gives only 1 array[][] as argument for one curve, the wrong
+% constructor is called and we get instead 2 curves with arguments array[],
+% array[]. That is why I removed the constructor
+% XYLineChart (String, String, String, double[]... data)
+% from the mother class and created this class.
+
+This class extends the class
+\externalclass{umontreal.iro.lecuyer.charts}{XYLineChart}.
+Each \class{YListChart} object is associated with a
+\externalclass{umontreal.iro.lecuyer.charts}{YListSeriesCollection} data set.
+The data is given as one or more lists of $y$-coordinates.
+The $x$-coordinates are
+regularly-spaced multiples of the indices of the data points.
+
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: YListChart
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;
+
+public class YListChart extends XYLineChart \begin{hide} {
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public YListChart() \begin{hide} {
+ super();
+ // dataset = new XYListSeriesCollection();
+ // init (null, null, null);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Empty constructor.
+\end{tabb}
+\begin{code}
+
+ public YListChart (String title, String XLabel, String YLabel,
+ double[]... data) \begin{hide} {
+ super();
+ dataset = new YListSeriesCollection(data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{YListChart} instance with set of points \texttt{data}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The input vectors represents a set of plotting data. More specifically,
+ each vector \texttt{data} represents a $y$-coordinates set.
+ Position in the vector will form the $x$-coordinates.
+ Indeed, the value \texttt{data}$[j]$ corresponds to the point
+ $(j+1, \texttt{data}[j])$ (but rescaled) on the chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public YListChart (String title, String XLabel, String YLabel,
+ boolean flag, double[]... data) \begin{hide} {
+ super();
+ dataset = new YListSeriesCollection(flag, data);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to the constructor %
+ \latex{\method{YListChart}{String,String,String,double[]}
+ \texttt{(title, XLabel, YLabel, data)}} above. %
+ Except that if \texttt{flag} is \texttt{true}, the points are
+ $(j+1, \mbox{\texttt{data}}[j])$ for each series;
+ but if \texttt{flag} is \texttt{false},
+ the points are $((j+1)/n, \mbox{\texttt{data}}[j])$, where $n$ is
+ the number of points of each series in \texttt{data}.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{flag}{to choose the step between $x$-coordinates}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public YListChart (String title, String XLabel, String YLabel,
+ double[] data, int numPoints) \begin{hide} {
+ super();
+ dataset = new YListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{YListChart} instance with a set of points
+ \texttt{data}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The input vector represents a set of plotting data.
+ Position in the vector gives the $x$-coordinates of the curve.
+ The value \texttt{data}$[j]$ corresponds to the point
+ $(j+1$, \texttt{data}$[j]$) (but rescaled on the chart) for the curve.
+ However, only \emph{the first} \texttt{numPoints} of \texttt{data}
+ will be considered to plot the curve.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{point set.}
+ \param{numPoints}{number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public YListChart (String title, String XLabel, String YLabel,
+ double h, double[] data, int numPoints) \begin{hide} {
+ super();
+ dataset = new YListSeriesCollection(h, data, numPoints);
+ init (title, XLabel, YLabel);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to the constructor %
+ \latex{\method{YListChart}{String,String,String,double[],int}
+ \texttt{(title, XLabel, YLabel, data, numPoints)}} %
+ above, but the points are $(h(j+1),\ \mbox{\texttt{data}}[j])$.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{h}{step between $x$-coordinates}
+ \param{data}{point set.}
+ \param{numPoints}{number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public YListChart (String title, String XLabel, String YLabel,
+ double[][] data, int numPoints) \begin{hide} {
+ super();
+ dataset = new YListSeriesCollection(data, numPoints);
+ init (title, XLabel, YLabel);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Initializes a new \texttt{YListChart} instance with set of points \texttt{data}.
+ \texttt{title} is a title, \texttt{XLabel} is a short description of
+ the $x$-axis, and \texttt{YLabel} a short description of the $y$-axis.
+ The input vectors represents a set of plotting data. More specifically,
+ for a $n$-row matrix \texttt{data}, each row \texttt{data}$[i],
+ i=0,\ldots, n-1$, represents a $y$-coordinate set for a curve.
+ Position in the vector gives the $x$-coordinates of the curves.
+ Indeed, the value \texttt{data}$[i][j]$ corresponds to the point
+ $(j+1$, \texttt{data}$[i][j]$) (but rescaled on the chart) for curve $i$.
+ However, only \emph{the first} \texttt{numPoints} of each \texttt{data}$[i]$
+ will be considered to plot each curve.
+\end{tabb}
+\begin{htmlonly}
+ \param{title}{chart title.}
+ \param{XLabel}{Label on $x$-axis.}
+ \param{YLabel}{Label on $y$-axis.}
+ \param{data}{series of point sets.}
+ \param{numPoints}{number of points to plot}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/charts/YListSeriesCollection.java b/source/umontreal/iro/lecuyer/charts/YListSeriesCollection.java
new file mode 100644
index 0000000..1a5e4b3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/YListSeriesCollection.java
@@ -0,0 +1,237 @@
+
+
+/*
+ * Class: YListSeriesCollection
+ * Description: Lists of y-coordinates of charts
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.charts;
+
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+
+
+/**
+ * This class extends
+ * {@link umontreal.iro.lecuyer.charts.XYListSeriesCollection XYListSeriesCollection}.
+ * The data is given as lists of <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates. The <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates are
+ * regularly spaced multiples of the indices of the data points.
+ *
+ */
+public class YListSeriesCollection extends XYListSeriesCollection {
+
+ private void initYListSeries (double h, double[] data, int numPoints)
+ {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection)seriesCollection;
+ XYSeries serie = new XYSeries(" ");
+ for (int j = 0; j < numPoints; j++)
+ serie.add(h*(j+1), data[j]);
+ tempSeriesCollection.addSeries(serie);
+
+ // set default colors
+ renderer.setSeriesPaint(0, getDefaultColor(0));
+
+ // set default plot style
+ plotStyle = new String[1];
+ marksType = new String[1];
+ dashPattern = new String[1];
+ marksType[0] = " ";
+ plotStyle[0] = "smooth";
+ dashPattern[0] = "solid";
+ }
+
+
+ private void initYListSeries (boolean flag, double[]... data)
+ {
+ // if flag = true, h = 1; else h = 1/numPoints
+ double h;
+ renderer = new XYLineAndShapeRenderer(true, false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+ XYSeries serie = new XYSeries(" ");
+ if (flag)
+ h = 1;
+ else
+ h = 1.0 / data[i].length;
+ for (int j = 0; j < data[i].length; j++)
+ serie.add(h*(j+1), data[i][j]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ final int s = tempSeriesCollection.getSeriesCount();
+
+ // set default colors
+ for(int i = 0; i < s; i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[s];
+ marksType = new String[s];
+ dashPattern = new String[s];
+ for (int i = 0; i < s; i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ // dashPattern[s-1] = "dashed"; // for the line y = x
+ }
+
+
+ /**
+ * Creates a new <TT>YListSeriesCollection</TT> instance with default
+ * parameters and given data series. The input vectors represent sets of
+ * plotting data. More specifically, each vector <TT>data</TT> represents
+ * a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates set.
+ * Position in the vector will form the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates. Indeed the value
+ * <TT>data</TT><SPAN CLASS="MATH">[<I>j</I>]</SPAN> corresponds to the point
+ *
+ * <SPAN CLASS="MATH">(<I>j</I> + 1,<TT>data</TT>[<I>j</I>])</SPAN> on the chart.
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public YListSeriesCollection (double[]... data) {
+ initYListSeries (true, data);
+ }
+
+
+ /**
+ * Similar to the constructor above, except that if <TT>flag</TT> is <TT>true</TT>, the points are
+ *
+ * <SPAN CLASS="MATH">(<I>j</I> + 1,<TT>data</TT>[<I>j</I>])</SPAN> for each series;
+ * but if <TT>flag</TT> is <TT>false</TT>,
+ * the points are
+ * <SPAN CLASS="MATH">((<I>j</I> + 1)/<I>n</I>,<TT>data</TT>[<I>j</I>])</SPAN>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is
+ * the number of points of each series in <TT>data</TT>.
+ *
+ * @param flag to choose the step between <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ *
+ * @param data series of point sets.
+ *
+ *
+ */
+ public YListSeriesCollection (boolean flag, double[]... data) {
+ initYListSeries (flag, data);
+ }
+
+
+ /**
+ * Creates a new <TT>YListSeriesCollection</TT> instance with default
+ * parameters and one data series.
+ * The vector <TT>data</TT> represents the <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinate of the points,
+ * and position in the vector represents the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinate.
+ * However, only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT> will
+ * be considered in the series.
+ * Thus the coordinates of the points are given by
+ *
+ * <SPAN CLASS="MATH">(<I>j</I>, <TT>data</TT>[<I>j</I> - 1])</SPAN>,
+ * for
+ * <SPAN CLASS="MATH"><I>j</I> = 1, 2,…,<texttt>numPoints</texttt></SPAN>.
+ *
+ * @param data point set.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public YListSeriesCollection (double[] data, int numPoints) {
+ initYListSeries (1, data, numPoints);
+ }
+
+
+ /**
+ * Similar to the constructor {@link #YListSeriesCollection(double[],int) YListSeriesCollection}<TT>(data, numPoints)</TT> above, but the points are
+ * <SPAN CLASS="MATH">(<I>hj</I>, <TT>data</TT>[<I>j</I> - 1])</SPAN>,
+ * for
+ * <SPAN CLASS="MATH"><I>j</I> = 1, 2,…,<texttt>numPoints</texttt></SPAN>.
+ *
+ * @param h step between <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates
+ *
+ * @param data point set.
+ *
+ * @param numPoints Number of points to plot
+ *
+ *
+ */
+ public YListSeriesCollection (double h, double[] data, int numPoints) {
+ initYListSeries (h, data, numPoints);
+ }
+
+
+ /**
+ * Creates a new <TT>YListSeriesCollection</TT> instance with default
+ * parameters and given data series. The matrix <TT>data</TT> represents a
+ * set of plotting data. More specifically, each row of <TT>data</TT>
+ * represents a <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates set.
+ * Position in the vector will form the <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates. Indeed, for each serie
+ * <SPAN CLASS="MATH"><I>i</I></SPAN>, the value <TT>data</TT><SPAN CLASS="MATH">[<I>i</I>][<I>j</I>]</SPAN> corresponds to the point
+ *
+ * <SPAN CLASS="MATH">(<I>j</I> + 1,<TT>data</TT>[<I>j</I>])</SPAN> on the chart.
+ * However, only <SPAN CLASS="textit">the first</SPAN> <TT>numPoints</TT> of <TT>data</TT> will
+ * be considered for each series of points.
+ *
+ * @param data series of point sets.
+ *
+ * @param numPoints Number of points to plot
+ *
+ */
+ public YListSeriesCollection (double[][] data, int numPoints) {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+ XYSeries serie = new XYSeries(" ");
+ for (int j = 0; j < numPoints; j++)
+ serie.add(j + 1, data[i][j]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ final int s = tempSeriesCollection.getSeriesCount();
+
+ // set default colors
+ for (int i = 0; i < s; i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[s];
+ marksType = new String[s];
+ dashPattern = new String[s];
+ for (int i = 0; i < s; i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/YListSeriesCollection.tex b/source/umontreal/iro/lecuyer/charts/YListSeriesCollection.tex
new file mode 100644
index 0000000..c32086f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/YListSeriesCollection.tex
@@ -0,0 +1,235 @@
+\defmodule {YListSeriesCollection}
+
+This class extends
+\externalclass{umontreal.iro.lecuyer.charts}{XYListSeriesCollection}.
+The data is given as lists of $y$-coordinates. The $x$-coordinates are
+regularly spaced multiples of the indices of the data points.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: YListSeriesCollection
+ * Description: Lists of y-coordinates of charts
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.charts;\begin{hide}
+
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+\end{hide}
+
+public class YListSeriesCollection extends XYListSeriesCollection \begin{hide} {
+
+ private void initYListSeries (double h, double[] data, int numPoints)
+ {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection)seriesCollection;
+ XYSeries serie = new XYSeries(" ");
+ for (int j = 0; j < numPoints; j++)
+ serie.add(h*(j+1), data[j]);
+ tempSeriesCollection.addSeries(serie);
+
+ // set default colors
+ renderer.setSeriesPaint(0, getDefaultColor(0));
+
+ // set default plot style
+ plotStyle = new String[1];
+ marksType = new String[1];
+ dashPattern = new String[1];
+ marksType[0] = " ";
+ plotStyle[0] = "smooth";
+ dashPattern[0] = "solid";
+ }
+
+
+ private void initYListSeries (boolean flag, double[]... data)
+ {
+ // if flag = true, h = 1; else h = 1/numPoints
+ double h;
+ renderer = new XYLineAndShapeRenderer(true, false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+ XYSeries serie = new XYSeries(" ");
+ if (flag)
+ h = 1;
+ else
+ h = 1.0 / data[i].length;
+ for (int j = 0; j < data[i].length; j++)
+ serie.add(h*(j+1), data[i][j]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ final int s = tempSeriesCollection.getSeriesCount();
+
+ // set default colors
+ for(int i = 0; i < s; i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[s];
+ marksType = new String[s];
+ dashPattern = new String[s];
+ for (int i = 0; i < s; i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ // dashPattern[s-1] = "dashed"; // for the line y = x
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public YListSeriesCollection (double[]... data) \begin{hide} {
+ initYListSeries (true, data);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{YListSeriesCollection} instance with default
+ parameters and given data series. The input vectors represent sets of
+ plotting data. More specifically, each vector \texttt{data} represents
+ a $y$-coordinates set.
+ Position in the vector will form the $x$-coordinates. Indeed the value
+ \texttt{data}$[j]$ corresponds to the point
+ $(j+1, \mbox{\texttt{data}}[j])$ on the chart.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public YListSeriesCollection (boolean flag, double[]... data) \begin{hide} {
+ initYListSeries (flag, data);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to the constructor %
+ \latex{\method{YListSeriesCollection}{double[]}\texttt{(data)}} %
+ above, except that if \texttt{flag} is \texttt{true}, the points are
+ $(j+1, \mbox{\texttt{data}}[j])$ for each series;
+ but if \texttt{flag} is \texttt{false},
+ the points are $((j+1)/n, \mbox{\texttt{data}}[j])$, where $n$ is
+ the number of points of each series in \texttt{data}.
+\end{tabb}
+\begin{htmlonly}
+ \param{flag}{to choose the step between $x$-coordinates}
+ \param{data}{series of point sets.}
+\end{htmlonly}
+\begin{code}
+
+ public YListSeriesCollection (double[] data, int numPoints) \begin{hide} {
+ initYListSeries (1, data, numPoints);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{YListSeriesCollection} instance with default
+ parameters and one data series.
+ The vector \texttt{data} represents the $y$-coordinate of the points,
+ and position in the vector represents the $x$-coordinate.
+ However, only \emph{the first} \texttt{numPoints} of \texttt{data} will
+ be considered in the series.
+ Thus the coordinates of the points are given by
+ $(j,\ \mbox{\texttt{data}}[j-1])$,
+ for $j=1,2,\ldots, \texttt{numPoints}$.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{point set.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public YListSeriesCollection (double h, double[] data, int numPoints) \begin{hide} {
+ initYListSeries (h, data, numPoints);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to the constructor %
+ \method{YListSeriesCollection}{double[],int}\texttt{(data, numPoints)} %
+ above, but the points are $(hj,\ \mbox{\texttt{data}}[j-1])$,
+ for $j=1,2,\ldots, \texttt{numPoints}$.
+\end{tabb}
+\begin{htmlonly}
+ \param{h}{step between $x$-coordinates}
+ \param{data}{point set.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
+\begin{code}
+
+ public YListSeriesCollection (double[][] data, int numPoints) \begin{hide} {
+ renderer = new XYLineAndShapeRenderer(true, false);
+ seriesCollection = new XYSeriesCollection();
+
+ XYSeriesCollection tempSeriesCollection =
+ (XYSeriesCollection)seriesCollection;
+ for (int i = 0; i < data.length; i ++) {
+ XYSeries serie = new XYSeries(" ");
+ for (int j = 0; j < numPoints; j++)
+ serie.add(j + 1, data[i][j]);
+ tempSeriesCollection.addSeries(serie);
+ }
+
+ final int s = tempSeriesCollection.getSeriesCount();
+
+ // set default colors
+ for (int i = 0; i < s; i++) {
+ renderer.setSeriesPaint(i, getDefaultColor(i));
+ }
+
+ // set default plot style
+ plotStyle = new String[s];
+ marksType = new String[s];
+ dashPattern = new String[s];
+ for (int i = 0; i < s; i++) {
+ marksType[i] = " ";
+ plotStyle[i] = "smooth";
+ dashPattern[i] = "solid";
+ }
+ }
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new \texttt{YListSeriesCollection} instance with default
+ parameters and given data series. The matrix \texttt{data} represents a
+ set of plotting data. More specifically, each row of \texttt{data}
+ represents a $y$-coordinates set.
+ Position in the vector will form the $x$-coordinates. Indeed, for each serie
+ $i$, the value \texttt{data}$[i][j]$ corresponds to the point
+ $(j+1, \mbox{\texttt{data}}[j])$ on the chart.
+ However, only \emph{the first} \texttt{numPoints} of \texttt{data} will
+ be considered for each series of points.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{series of point sets.}
+ \param{numPoints}{Number of points to plot}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/BoxTest.java b/source/umontreal/iro/lecuyer/charts/exam/BoxTest.java
new file mode 100644
index 0000000..5c43e32
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/BoxTest.java
@@ -0,0 +1,25 @@
+import umontreal.iro.lecuyer.charts.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.rng.*;
+import java.io.*;
+
+public class BoxTest
+{
+ public static void main (String[] args) throws IOException {
+ int count = 1000;
+ double[] data1 = new double[count];
+ double[] data2 = new double[count];
+
+ RandomStream stream = new LFSR113();
+ RandomVariateGen log = new LognormalGen(stream);
+ RandomVariateGen poi = new PoissonGen(stream, 5.0);
+
+ for (int i = 0; i < count; i++) {
+ data1[i] = log.nextDouble();
+ data2[i] = poi.nextDouble();
+ }
+
+ BoxChart bc = new BoxChart("Boxplot1", "Series", "Y", data1, data2);
+ bc.view(600, 400);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/BoxTest.png b/source/umontreal/iro/lecuyer/charts/exam/BoxTest.png
new file mode 100644
index 0000000..2a0ec16
Binary files /dev/null and b/source/umontreal/iro/lecuyer/charts/exam/BoxTest.png differ
diff --git a/source/umontreal/iro/lecuyer/charts/exam/ChartTest1.java b/source/umontreal/iro/lecuyer/charts/exam/ChartTest1.java
new file mode 100644
index 0000000..f095921
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/ChartTest1.java
@@ -0,0 +1,45 @@
+import umontreal.iro.lecuyer.charts.XYLineChart;
+
+public class ChartTest1
+{
+ private static double[][] getPoints1() {
+ double[][] points = new double[2][200];
+ for (int i = 0; i < points[0].length; i++) {
+ double x = i / 25.0;
+ points[0][i] = x;
+ points[1][i] = Math.sqrt (x);
+ }
+ return points;
+ }
+
+ private static double[][] getPoints2() {
+ double[][] points = new double[2][21];
+ for (int i = 0; i < points[0].length; i++) {
+ double x = -Math.PI + 2 * i * Math.PI / (points[0].length - 1);
+ points[0][i] = x;
+ points[1][i] = Math.cos (x);
+ }
+ return points;
+ }
+
+ private static double[][] getPoints3() {
+ double[][] points = new double[2][11];
+ for (int i = 0; i < points[0].length; i++) {
+ points[0][i] = -5 + i;
+ points[1][i] = -3 + i;
+ }
+ return points;
+ }
+
+ public static void main(String[] args) {
+ // Get data; data1 has length 2 and contains one array for
+ // X-axis values, and one array for Y-axis values.
+ double[][] data1 = getPoints1();
+ double[][] data2 = getPoints2();
+ double[][] data3 = getPoints3();
+
+ // Create a new chart with the previous data series.
+ XYLineChart chart = new XYLineChart(null, "X", "Y", data1, data2, data3);
+ chart.toLatexFile("ChartTest1.tex", 12, 8);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/ChartTest1.tex b/source/umontreal/iro/lecuyer/charts/exam/ChartTest1.tex
new file mode 100644
index 0000000..0fb4d43
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/ChartTest1.tex
@@ -0,0 +1,262 @@
+% PGF/TikZ picture from SSJ
+% XScale = 10.0, YScale = 10.0, XShift = -6.0, YShift = -4.0
+% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale
+% and thisFileYValue = (originalSeriesYValue+YShift)*YScale
+
+\begin{center}
+\begin{tikzpicture}[x=0.08cm, y=0.06666666666666667cm]
+\footnotesize
+\draw [-latex] ([xshift=-0mm] 0.0,0) -- ([xshift=3mm] 150.0,0) node[right] {X};
+\draw (0.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-6};
+\draw (25.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-3.5};
+\draw (50.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-1};
+\draw (75.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {1.5};
+\draw (100.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {4};
+\draw (125.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {6.5};
+\draw (150.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {9};
+\draw [-latex] ([yshift=-0mm] 0,0.0) -- ([yshift=3mm] 0, 120.0) node[above] {Y};
+\draw (0,0.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {-4};
+\draw (0,25.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {-1.5};
+\draw (0,50.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {1};
+\draw (0,75.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {3.5};
+\draw (0,100.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {6};
+\draw [smooth, color=green, mark= , style=solid] plot coordinates {%
+(10.00,10.0000) % (-5.000000, -3.000000)
+(20.00,20.0000) % (-4.000000, -2.000000)
+(30.00,30.0000) % (-3.000000, -1.000000)
+(40.00,40.0000) % (-2.000000, 0.000000)
+(50.00,50.0000) % (-1.000000, 1.000000)
+(60.00,60.0000) % (0.000000, 2.000000)
+(70.00,70.0000) % (1.000000, 3.000000)
+(80.00,80.0000) % (2.000000, 4.000000)
+(90.00,90.0000) % (3.000000, 5.000000)
+(100.00,100.0000) % (4.000000, 6.000000)
+(110.00,110.0000)} % (5.000000, 7.000000)
+ node[right] { };
+\draw [smooth, color=blue, mark= , style=solid] plot coordinates {%
+(28.58,30.0000) % (-3.141593, -1.000000)
+(31.73,30.4894) % (-2.827433, -0.951057)
+(34.87,31.9098) % (-2.513274, -0.809017)
+(38.01,34.1221) % (-2.199115, -0.587785)
+(41.15,36.9098) % (-1.884956, -0.309017)
+(44.29,40.0000) % (-1.570796, 0.000000)
+(47.43,43.0902) % (-1.256637, 0.309017)
+(50.58,45.8779) % (-0.942478, 0.587785)
+(53.72,48.0902) % (-0.628319, 0.809017)
+(56.86,49.5106) % (-0.314159, 0.951057)
+(60.00,50.0000) % (0.000000, 1.000000)
+(63.14,49.5106) % (0.314159, 0.951057)
+(66.28,48.0902) % (0.628319, 0.809017)
+(69.42,45.8779) % (0.942478, 0.587785)
+(72.57,43.0902) % (1.256637, 0.309017)
+(75.71,40.0000) % (1.570796, 0.000000)
+(78.85,36.9098) % (1.884956, -0.309017)
+(81.99,34.1221) % (2.199115, -0.587785)
+(85.13,31.9098) % (2.513274, -0.809017)
+(88.27,30.4894) % (2.827433, -0.951057)
+(91.42,30.0000)} % (3.141593, -1.000000)
+ node[right] { };
+\draw [smooth, color=red, mark= , style=solid] plot coordinates {%
+(60.00,40.0000) % (0.000000, 0.000000)
+(60.40,42.0000) % (0.040000, 0.200000)
+(60.80,42.8284) % (0.080000, 0.282843)
+(61.20,43.4641) % (0.120000, 0.346410)
+(61.60,44.0000) % (0.160000, 0.400000)
+(62.00,44.4721) % (0.200000, 0.447214)
+(62.40,44.8990) % (0.240000, 0.489898)
+(62.80,45.2915) % (0.280000, 0.529150)
+(63.20,45.6569) % (0.320000, 0.565685)
+(63.60,46.0000) % (0.360000, 0.600000)
+(64.00,46.3246) % (0.400000, 0.632456)
+(64.40,46.6332) % (0.440000, 0.663325)
+(64.80,46.9282) % (0.480000, 0.692820)
+(65.20,47.2111) % (0.520000, 0.721110)
+(65.60,47.4833) % (0.560000, 0.748331)
+(66.00,47.7460) % (0.600000, 0.774597)
+(66.40,48.0000) % (0.640000, 0.800000)
+(66.80,48.2462) % (0.680000, 0.824621)
+(67.20,48.4853) % (0.720000, 0.848528)
+(67.60,48.7178) % (0.760000, 0.871780)
+(68.00,48.9443) % (0.800000, 0.894427)
+(68.40,49.1652) % (0.840000, 0.916515)
+(68.80,49.3808) % (0.880000, 0.938083)
+(69.20,49.5917) % (0.920000, 0.959166)
+(69.60,49.7980) % (0.960000, 0.979796)
+(70.00,50.0000) % (1.000000, 1.000000)
+(70.40,50.1980) % (1.040000, 1.019804)
+(70.80,50.3923) % (1.080000, 1.039230)
+(71.20,50.5830) % (1.120000, 1.058301)
+(71.60,50.7703) % (1.160000, 1.077033)
+(72.00,50.9545) % (1.200000, 1.095445)
+(72.40,51.1355) % (1.240000, 1.113553)
+(72.80,51.3137) % (1.280000, 1.131371)
+(73.20,51.4891) % (1.320000, 1.148913)
+(73.60,51.6619) % (1.360000, 1.166190)
+(74.00,51.8322) % (1.400000, 1.183216)
+(74.40,52.0000) % (1.440000, 1.200000)
+(74.80,52.1655) % (1.480000, 1.216553)
+(75.20,52.3288) % (1.520000, 1.232883)
+(75.60,52.4900) % (1.560000, 1.249000)
+(76.00,52.6491) % (1.600000, 1.264911)
+(76.40,52.8062) % (1.640000, 1.280625)
+(76.80,52.9615) % (1.680000, 1.296148)
+(77.20,53.1149) % (1.720000, 1.311488)
+(77.60,53.2665) % (1.760000, 1.326650)
+(78.00,53.4164) % (1.800000, 1.341641)
+(78.40,53.5647) % (1.840000, 1.356466)
+(78.80,53.7113) % (1.880000, 1.371131)
+(79.20,53.8564) % (1.920000, 1.385641)
+(79.60,54.0000) % (1.960000, 1.400000)
+(80.00,54.1421) % (2.000000, 1.414214)
+(80.40,54.2829) % (2.040000, 1.428286)
+(80.80,54.4222) % (2.080000, 1.442221)
+(81.20,54.5602) % (2.120000, 1.456022)
+(81.60,54.6969) % (2.160000, 1.469694)
+(82.00,54.8324) % (2.200000, 1.483240)
+(82.40,54.9666) % (2.240000, 1.496663)
+(82.80,55.0997) % (2.280000, 1.509967)
+(83.20,55.2315) % (2.320000, 1.523155)
+(83.60,55.3623) % (2.360000, 1.536229)
+(84.00,55.4919) % (2.400000, 1.549193)
+(84.40,55.6205) % (2.440000, 1.562050)
+(84.80,55.7480) % (2.480000, 1.574802)
+(85.20,55.8745) % (2.520000, 1.587451)
+(85.60,56.0000) % (2.560000, 1.600000)
+(86.00,56.1245) % (2.600000, 1.612452)
+(86.40,56.2481) % (2.640000, 1.624808)
+(86.80,56.3707) % (2.680000, 1.637071)
+(87.20,56.4924) % (2.720000, 1.649242)
+(87.60,56.6132) % (2.760000, 1.661325)
+(88.00,56.7332) % (2.800000, 1.673320)
+(88.40,56.8523) % (2.840000, 1.685230)
+(88.80,56.9706) % (2.880000, 1.697056)
+(89.20,57.0880) % (2.920000, 1.708801)
+(89.60,57.2047) % (2.960000, 1.720465)
+(90.00,57.3205) % (3.000000, 1.732051)
+(90.40,57.4356) % (3.040000, 1.743560)
+(90.80,57.5499) % (3.080000, 1.754993)
+(91.20,57.6635) % (3.120000, 1.766352)
+(91.60,57.7764) % (3.160000, 1.777639)
+(92.00,57.8885) % (3.200000, 1.788854)
+(92.40,58.0000) % (3.240000, 1.800000)
+(92.80,58.1108) % (3.280000, 1.811077)
+(93.20,58.2209) % (3.320000, 1.822087)
+(93.60,58.3303) % (3.360000, 1.833030)
+(94.00,58.4391) % (3.400000, 1.843909)
+(94.40,58.5472) % (3.440000, 1.854724)
+(94.80,58.6548) % (3.480000, 1.865476)
+(95.20,58.7617) % (3.520000, 1.876166)
+(95.60,58.8680) % (3.560000, 1.886796)
+(96.00,58.9737) % (3.600000, 1.897367)
+(96.40,59.0788) % (3.640000, 1.907878)
+(96.80,59.1833) % (3.680000, 1.918333)
+(97.20,59.2873) % (3.720000, 1.928730)
+(97.60,59.3907) % (3.760000, 1.939072)
+(98.00,59.4936) % (3.800000, 1.949359)
+(98.40,59.5959) % (3.840000, 1.959592)
+(98.80,59.6977) % (3.880000, 1.969772)
+(99.20,59.7990) % (3.920000, 1.979899)
+(99.60,59.8997) % (3.960000, 1.989975)
+(100.00,60.0000) % (4.000000, 2.000000)
+(100.40,60.0998) % (4.040000, 2.009975)
+(100.80,60.1990) % (4.080000, 2.019901)
+(101.20,60.2978) % (4.120000, 2.029778)
+(101.60,60.3961) % (4.160000, 2.039608)
+(102.00,60.4939) % (4.200000, 2.049390)
+(102.40,60.5913) % (4.240000, 2.059126)
+(102.80,60.6882) % (4.280000, 2.068816)
+(103.20,60.7846) % (4.320000, 2.078461)
+(103.60,60.8806) % (4.360000, 2.088061)
+(104.00,60.9762) % (4.400000, 2.097618)
+(104.40,61.0713) % (4.440000, 2.107131)
+(104.80,61.1660) % (4.480000, 2.116601)
+(105.20,61.2603) % (4.520000, 2.126029)
+(105.60,61.3542) % (4.560000, 2.135416)
+(106.00,61.4476) % (4.600000, 2.144761)
+(106.40,61.5407) % (4.640000, 2.154066)
+(106.80,61.6333) % (4.680000, 2.163331)
+(107.20,61.7256) % (4.720000, 2.172556)
+(107.60,61.8174) % (4.760000, 2.181742)
+(108.00,61.9089) % (4.800000, 2.190890)
+(108.40,62.0000) % (4.840000, 2.200000)
+(108.80,62.0907) % (4.880000, 2.209072)
+(109.20,62.1811) % (4.920000, 2.218107)
+(109.60,62.2711) % (4.960000, 2.227106)
+(110.00,62.3607) % (5.000000, 2.236068)
+(110.40,62.4499) % (5.040000, 2.244994)
+(110.80,62.5389) % (5.080000, 2.253886)
+(111.20,62.6274) % (5.120000, 2.262742)
+(111.60,62.7156) % (5.160000, 2.271563)
+(112.00,62.8035) % (5.200000, 2.280351)
+(112.40,62.8910) % (5.240000, 2.289105)
+(112.80,62.9783) % (5.280000, 2.297825)
+(113.20,63.0651) % (5.320000, 2.306513)
+(113.60,63.1517) % (5.360000, 2.315167)
+(114.00,63.2379) % (5.400000, 2.323790)
+(114.40,63.3238) % (5.440000, 2.332381)
+(114.80,63.4094) % (5.480000, 2.340940)
+(115.20,63.4947) % (5.520000, 2.349468)
+(115.60,63.5797) % (5.560000, 2.357965)
+(116.00,63.6643) % (5.600000, 2.366432)
+(116.40,63.7487) % (5.640000, 2.374868)
+(116.80,63.8328) % (5.680000, 2.383275)
+(117.20,63.9165) % (5.720000, 2.391652)
+(117.60,64.0000) % (5.760000, 2.400000)
+(118.00,64.0832) % (5.800000, 2.408319)
+(118.40,64.1661) % (5.840000, 2.416609)
+(118.80,64.2487) % (5.880000, 2.424871)
+(119.20,64.3311) % (5.920000, 2.433105)
+(119.60,64.4131) % (5.960000, 2.441311)
+(120.00,64.4949) % (6.000000, 2.449490)
+(120.40,64.5764) % (6.040000, 2.457641)
+(120.80,64.6577) % (6.080000, 2.465766)
+(121.20,64.7386) % (6.120000, 2.473863)
+(121.60,64.8193) % (6.160000, 2.481935)
+(122.00,64.8998) % (6.200000, 2.489980)
+(122.40,64.9800) % (6.240000, 2.497999)
+(122.80,65.0599) % (6.280000, 2.505993)
+(123.20,65.1396) % (6.320000, 2.513961)
+(123.60,65.2190) % (6.360000, 2.521904)
+(124.00,65.2982) % (6.400000, 2.529822)
+(124.40,65.3772) % (6.440000, 2.537716)
+(124.80,65.4558) % (6.480000, 2.545584)
+(125.20,65.5343) % (6.520000, 2.553429)
+(125.60,65.6125) % (6.560000, 2.561250)
+(126.00,65.6905) % (6.600000, 2.569047)
+(126.40,65.7682) % (6.640000, 2.576820)
+(126.80,65.8457) % (6.680000, 2.584570)
+(127.20,65.9230) % (6.720000, 2.592296)
+(127.60,66.0000) % (6.760000, 2.600000)
+(128.00,66.0768) % (6.800000, 2.607681)
+(128.40,66.1534) % (6.840000, 2.615339)
+(128.80,66.2298) % (6.880000, 2.622975)
+(129.20,66.3059) % (6.920000, 2.630589)
+(129.60,66.3818) % (6.960000, 2.638181)
+(130.00,66.4575) % (7.000000, 2.645751)
+(130.40,66.5330) % (7.040000, 2.653300)
+(130.80,66.6083) % (7.080000, 2.660827)
+(131.20,66.6833) % (7.120000, 2.668333)
+(131.60,66.7582) % (7.160000, 2.675818)
+(132.00,66.8328) % (7.200000, 2.683282)
+(132.40,66.9072) % (7.240000, 2.690725)
+(132.80,66.9815) % (7.280000, 2.698148)
+(133.20,67.0555) % (7.320000, 2.705550)
+(133.60,67.1293) % (7.360000, 2.712932)
+(134.00,67.2029) % (7.400000, 2.720294)
+(134.40,67.2764) % (7.440000, 2.727636)
+(134.80,67.3496) % (7.480000, 2.734959)
+(135.20,67.4226) % (7.520000, 2.742262)
+(135.60,67.4955) % (7.560000, 2.749545)
+(136.00,67.5681) % (7.600000, 2.756810)
+(136.40,67.6405) % (7.640000, 2.764055)
+(136.80,67.7128) % (7.680000, 2.771281)
+(137.20,67.7849) % (7.720000, 2.778489)
+(137.60,67.8568) % (7.760000, 2.785678)
+(138.00,67.9285) % (7.800000, 2.792848)
+(138.40,68.0000) % (7.840000, 2.800000)
+(138.80,68.0713) % (7.880000, 2.807134)
+(139.20,68.1425) % (7.920000, 2.814249)
+(139.60,68.2135)} % (7.960000, 2.821347)
+ node[right] { };
+\end{tikzpicture}
+\end{center}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/ChartTest2.java b/source/umontreal/iro/lecuyer/charts/exam/ChartTest2.java
new file mode 100644
index 0000000..48a272b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/ChartTest2.java
@@ -0,0 +1,67 @@
+import umontreal.iro.lecuyer.charts.*;
+import java.awt.Color;
+
+public class ChartTest2
+{
+ private static double[][] getPoints1() {
+ double[][] points = new double[2][40];
+ for (int i = 0; i < points[0].length; i++) {
+ double x = i / 4.0;
+ points[0][i] = x;
+ points[1][i] = Math.sqrt(x);
+ }
+ return points;
+ }
+
+ private static double[][] getPoints2() {
+ double[][] points = new double[2][21];
+ for (int i = 0; i < points[0].length; i++) {
+ double x = -Math.PI + 2 * i * Math.PI / (points[0].length - 1);
+ points[0][i] = x;
+ points[1][i] = Math.cos(x);
+ }
+ return points;
+ }
+
+ private static double[][] getPoints3() {
+ double[][] points = new double[2][11];
+ for (int i = 0; i < points[0].length; i++) {
+ points[0][i] = -5 + i;
+ points[1][i] = -3 + i;
+ }
+ return points;
+ }
+
+ public static void main(String[] args) {
+ double[][] data1 = getPoints1();
+ double[][] data2 = getPoints2();
+ double[][] data3 = getPoints3();
+
+ // Create a new chart with the previous data series.
+ XYLineChart chart = new XYLineChart(null, "X", "Y", data1, data2, data3);
+
+ // Customizing axes
+ Axis xaxis = chart.getXAxis();
+ Axis yaxis = chart.getYAxis();
+ String[] labels = { "-9", "$-\\lambda$", "$-\\sqrt{2}$",
+ "0", "$\\frac{14}{\\pi}$", "\\LaTeX" };
+ double[] values = { -9, -5, -Math.sqrt(2), 0, 14.0 / Math.PI, 9 };
+ xaxis.setLabels(values, labels);
+ yaxis.setLabels(1);
+
+ // Data plots customizing
+ XYListSeriesCollection collec = chart.getSeriesCollection();
+ collec.setColor(0, new Color(0, 64, 128));
+ collec.setName(0, "$f(x) = \\sqrt(x)$");
+ collec.setMarksType(0, "");
+ collec.setDashPattern(0, "dotted");
+ collec.setName(1, "$f(x) = \\cos(x)$");
+ collec.setMarksType(1, "");
+ collec.setColor(2, Color.ORANGE);
+ collec.setPlotStyle(2, "ycomb,very thick");
+ collec.setMarksType(2, "*");
+
+ // Export to LaTex format
+ chart.toLatexFile("ChartTest2.tex", 12, 8); // 12cm width, 8cm height
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/ChartTest2.tex b/source/umontreal/iro/lecuyer/charts/exam/ChartTest2.tex
new file mode 100644
index 0000000..176cfd0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/ChartTest2.tex
@@ -0,0 +1,101 @@
+% PGF/TikZ picture from SSJ
+% XScale = 10.0, YScale = 10.0, XShift = -6.0, YShift = -4.0
+% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale
+% and thisFileYValue = (originalSeriesYValue+YShift)*YScale
+
+\begin{center}
+\begin{tikzpicture}[x=0.06315789473684211cm, y=0.06666666666666667cm]
+\footnotesize
+\draw [latex-latex] ([xshift=-3mm] -30.0,0) -- ([xshift=3mm] 160.0,0) node[right] {X};
+\draw (-30.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-9};
+\draw (10.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {$-\lambda$};
+\draw (45.85786437626905,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {$-\sqrt{2}$};
+\draw (60.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0};
+\draw (104.5633840657307,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {$\frac{14}{\pi}$};
+\draw (150.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {\LaTeX};
+\draw [-latex] ([yshift=-0mm] 0,0.0) -- ([yshift=3mm] 0, 120.0) node[above] {Y};
+\draw (0,0.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {-4};
+\draw (0,50.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {1};
+\draw (0,100.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {6};
+\draw [ycomb,very thick, color=orange, mark=*, style=solid] plot coordinates {%
+(10.00,10.0000) % (-5.000000, -3.000000)
+(20.00,20.0000) % (-4.000000, -2.000000)
+(30.00,30.0000) % (-3.000000, -1.000000)
+(40.00,40.0000) % (-2.000000, 0.000000)
+(50.00,50.0000) % (-1.000000, 1.000000)
+(60.00,60.0000) % (0.000000, 2.000000)
+(70.00,70.0000) % (1.000000, 3.000000)
+(80.00,80.0000) % (2.000000, 4.000000)
+(90.00,90.0000) % (3.000000, 5.000000)
+(100.00,100.0000) % (4.000000, 6.000000)
+(110.00,110.0000)} % (5.000000, 7.000000)
+ node[right] { };
+\draw [smooth, color=blue, mark=, style=solid] plot coordinates {%
+(28.58,30.0000) % (-3.141593, -1.000000)
+(31.73,30.4894) % (-2.827433, -0.951057)
+(34.87,31.9098) % (-2.513274, -0.809017)
+(38.01,34.1221) % (-2.199115, -0.587785)
+(41.15,36.9098) % (-1.884956, -0.309017)
+(44.29,40.0000) % (-1.570796, 0.000000)
+(47.43,43.0902) % (-1.256637, 0.309017)
+(50.58,45.8779) % (-0.942478, 0.587785)
+(53.72,48.0902) % (-0.628319, 0.809017)
+(56.86,49.5106) % (-0.314159, 0.951057)
+(60.00,50.0000) % (0.000000, 1.000000)
+(63.14,49.5106) % (0.314159, 0.951057)
+(66.28,48.0902) % (0.628319, 0.809017)
+(69.42,45.8779) % (0.942478, 0.587785)
+(72.57,43.0902) % (1.256637, 0.309017)
+(75.71,40.0000) % (1.570796, 0.000000)
+(78.85,36.9098) % (1.884956, -0.309017)
+(81.99,34.1221) % (2.199115, -0.587785)
+(85.13,31.9098) % (2.513274, -0.809017)
+(88.27,30.4894) % (2.827433, -0.951057)
+(91.42,30.0000)} % (3.141593, -1.000000)
+ node[right] {$f(x) = \cos(x)$};
+\definecolor{color0}{rgb}{0.00, 0.25, 0.50}
+\draw [smooth, color=color0, mark=, style=dotted] plot coordinates {%
+(60.00,40.0000) % (0.000000, 0.000000)
+(62.50,45.0000) % (0.250000, 0.500000)
+(65.00,47.0711) % (0.500000, 0.707107)
+(67.50,48.6603) % (0.750000, 0.866025)
+(70.00,50.0000) % (1.000000, 1.000000)
+(72.50,51.1803) % (1.250000, 1.118034)
+(75.00,52.2474) % (1.500000, 1.224745)
+(77.50,53.2288) % (1.750000, 1.322876)
+(80.00,54.1421) % (2.000000, 1.414214)
+(82.50,55.0000) % (2.250000, 1.500000)
+(85.00,55.8114) % (2.500000, 1.581139)
+(87.50,56.5831) % (2.750000, 1.658312)
+(90.00,57.3205) % (3.000000, 1.732051)
+(92.50,58.0278) % (3.250000, 1.802776)
+(95.00,58.7083) % (3.500000, 1.870829)
+(97.50,59.3649) % (3.750000, 1.936492)
+(100.00,60.0000) % (4.000000, 2.000000)
+(102.50,60.6155) % (4.250000, 2.061553)
+(105.00,61.2132) % (4.500000, 2.121320)
+(107.50,61.7945) % (4.750000, 2.179449)
+(110.00,62.3607) % (5.000000, 2.236068)
+(112.50,62.9129) % (5.250000, 2.291288)
+(115.00,63.4521) % (5.500000, 2.345208)
+(117.50,63.9792) % (5.750000, 2.397916)
+(120.00,64.4949) % (6.000000, 2.449490)
+(122.50,65.0000) % (6.250000, 2.500000)
+(125.00,65.4951) % (6.500000, 2.549510)
+(127.50,65.9808) % (6.750000, 2.598076)
+(130.00,66.4575) % (7.000000, 2.645751)
+(132.50,66.9258) % (7.250000, 2.692582)
+(135.00,67.3861) % (7.500000, 2.738613)
+(137.50,67.8388) % (7.750000, 2.783882)
+(140.00,68.2843) % (8.000000, 2.828427)
+(142.50,68.7228) % (8.250000, 2.872281)
+(145.00,69.1548) % (8.500000, 2.915476)
+(147.50,69.5804) % (8.750000, 2.958040)
+(150.00,70.0000) % (9.000000, 3.000000)
+(152.50,70.4138) % (9.250000, 3.041381)
+(155.00,70.8221) % (9.500000, 3.082207)
+(157.50,71.2250)} % (9.750000, 3.122499)
+ node[right] {$f(x) = \sqrt(x)$};
+\end{tikzpicture}
+\end{center}
+
diff --git a/source/umontreal/iro/lecuyer/charts/exam/ContDistPlot.java b/source/umontreal/iro/lecuyer/charts/exam/ContDistPlot.java
new file mode 100644
index 0000000..3609496
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/ContDistPlot.java
@@ -0,0 +1,11 @@
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.charts.*;
+
+public class ContDistPlot
+{
+ public static void main (String[] args) {
+ ContinuousDistribution dist = new NormalDist();
+ ContinuousDistChart plot = new ContinuousDistChart(dist, -3.5, 3.5, 1000);
+ plot.viewDensity(600, 400);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/DistIntTest.java b/source/umontreal/iro/lecuyer/charts/exam/DistIntTest.java
new file mode 100644
index 0000000..12535b4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/DistIntTest.java
@@ -0,0 +1,17 @@
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.charts.*;
+import java.io.*;
+
+public class DistIntTest
+{
+ public static void main(String[] args) throws IOException {
+ PoissonDist dist = new PoissonDist(50);
+ DiscreteDistIntChart dic = new DiscreteDistIntChart(dist);
+
+ // Export to Latex format
+ String output = dic.toLatexProb(12, 8); // 12cm width, 8cm height
+ Writer file = new FileWriter("DistIntTest.tex");
+ file.write(output);
+ file.close();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/DistIntTest.tex b/source/umontreal/iro/lecuyer/charts/exam/DistIntTest.tex
new file mode 100644
index 0000000..535e3b0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/DistIntTest.tex
@@ -0,0 +1,69 @@
+
+% PGF/TikZ picture from SSJ: probability: PoissonDist: lambda = 50.0
+% XScale = 10.0, YScale = 10000.0, XShift = 27.0, YShift = 0.0
+% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale
+% and thisFileYValue = (originalSeriesYValue+YShift)*YScale
+
+\begin{center}
+\begin{tikzpicture}[x=0.02553191489361702cm, y=0.013559322033898305cm]
+\footnotesize
+\draw [-latex] ([xshift=-0mm] 0.0,0) -- ([xshift=3mm] 470.0,0) node[right] {};
+\draw (0.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {27};
+\draw (100.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {37};
+\draw (200.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {47};
+\draw (300.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {57};
+\draw (400.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {67};
+\draw [-latex] ([yshift=-0mm] 0,0.0) -- ([yshift=3mm] 0, 590.0) node[above] { $(10^{-3})$};
+\draw (0,0.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0};
+\draw (0,100.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {10};
+\draw (0,200.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {20};
+\draw (0,300.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {30};
+\draw (0,400.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {40};
+\draw (0,500.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {50};
+\draw [ycomb, color=orange, mark=*, style=solid] plot coordinates {%
+(20.00,4.0632) % (29.000000, 0.000406)
+(30.00,6.7720) % (30.000000, 0.000677)
+(40.00,10.9226) % (31.000000, 0.001092)
+(50.00,17.0665) % (32.000000, 0.001707)
+(60.00,25.8583) % (33.000000, 0.002586)
+(70.00,38.0269) % (34.000000, 0.003803)
+(80.00,54.3242) % (35.000000, 0.005432)
+(90.00,75.4503) % (36.000000, 0.007545)
+(100.00,101.9599) % (37.000000, 0.010196)
+(110.00,134.1577) % (38.000000, 0.013416)
+(120.00,171.9970) % (39.000000, 0.017200)
+(130.00,214.9963) % (40.000000, 0.021500)
+(140.00,262.1906) % (41.000000, 0.026219)
+(150.00,312.1317) % (42.000000, 0.031213)
+(160.00,362.9438) % (43.000000, 0.036294)
+(170.00,412.4362) % (44.000000, 0.041244)
+(180.00,458.2624) % (45.000000, 0.045826)
+(190.00,498.1113) % (46.000000, 0.049811)
+(200.00,529.9057) % (47.000000, 0.052991)
+(210.00,551.9851) % (48.000000, 0.055199)
+(220.00,563.2501) % (49.000000, 0.056325)
+(230.00,563.2501) % (50.000000, 0.056325)
+(240.00,552.2059) % (51.000000, 0.055221)
+(250.00,530.9673) % (52.000000, 0.053097)
+(260.00,500.9125) % (53.000000, 0.050091)
+(270.00,463.8079) % (54.000000, 0.046381)
+(280.00,421.6435) % (55.000000, 0.042164)
+(290.00,376.4674) % (56.000000, 0.037647)
+(300.00,330.2346) % (57.000000, 0.033023)
+(310.00,284.6850) % (58.000000, 0.028468)
+(320.00,241.2585) % (59.000000, 0.024126)
+(330.00,201.0487) % (60.000000, 0.020105)
+(340.00,164.7940) % (61.000000, 0.016479)
+(350.00,132.8984) % (62.000000, 0.013290)
+(360.00,105.4749) % (63.000000, 0.010547)
+(370.00,82.4023) % (64.000000, 0.008240)
+(380.00,63.3864) % (65.000000, 0.006339)
+(390.00,48.0200) % (66.000000, 0.004802)
+(400.00,35.8358) % (67.000000, 0.003584)
+(410.00,26.3499) % (68.000000, 0.002635)
+(420.00,19.0941) % (69.000000, 0.001909)
+(430.00,13.6386) % (70.000000, 0.001364)
+(440.00,9.6047)} % (71.000000, 0.000960)
+ node[right] { };
+\end{tikzpicture}
+\end{center}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/EmpiricalChartTest.java b/source/umontreal/iro/lecuyer/charts/exam/EmpiricalChartTest.java
new file mode 100644
index 0000000..5865609
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/EmpiricalChartTest.java
@@ -0,0 +1,44 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.charts.*;
+import java.util.Arrays;
+import java.awt.Color;
+
+public class EmpiricalChartTest
+{
+ private static double[] getPoints1() {
+ RandomVariateGen gen = new UniformGen(new LFSR113());
+ final int N = 10;
+ double[] data = new double[N];
+ for (int i = 0; i < N; i++)
+ data[i] = gen.nextDouble();
+ Arrays.sort(data);
+ return data;
+ }
+
+ private static double[] getPoints2() {
+ RandomVariateGen gen = new BetaGen(new LFSR113(), 3, 1);
+ final int N = 20;
+ double[] data = new double[N];
+ for (int i = 0; i < N; i++)
+ data[i] = gen.nextDouble();
+ Arrays.sort(data);
+ return data;
+ }
+
+ public static void main(String[] args) {
+ double[] data1 = getPoints1();
+ double[] data2 = getPoints2();
+
+ // Create a new chart with the previous data series.
+ EmpiricalChart chart = new EmpiricalChart(null, null, null, data1, data2);
+
+ // Data plots customizing
+ EmpiricalSeriesCollection collec = chart.getSeriesCollection();
+ collec.setMarksType(0, "square*");
+ collec.setColor(0, Color.MAGENTA);
+
+ chart.enableGrid(0.1, 0.1); // Enables grid
+ chart.toLatexFile("EmpiricalChartTest.tex", 12, 8);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/EmpiricalChartTest.tex b/source/umontreal/iro/lecuyer/charts/exam/EmpiricalChartTest.tex
new file mode 100644
index 0000000..6971dd8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/EmpiricalChartTest.tex
@@ -0,0 +1,62 @@
+
+% PGF/TikZ picture from SSJ
+% XScale = 1000.0, YScale = 100.0, XShift = 0.0, YShift = 0.0
+% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale
+% and thisFileYValue = (originalSeriesYValue+YShift)*YScale
+
+\begin{center}
+\begin{tikzpicture}[x=0.012380800583714944cm, y=0.08cm]
+\footnotesize
+\draw[color=lightgray] (0.0, 0.0) grid[xstep = 100.0, ystep=10.0] (969.2426526749952, 100.0);
+\draw [-latex] ([xshift=-0mm] 0.0,0) -- ([xshift=3mm] 969.2426526749952,0) node[right] { };
+\draw (0.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0};
+\draw (100.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.1};
+\draw (200.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.2};
+\draw (300.00000000000006,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.3};
+\draw (400.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.4};
+\draw (500.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.5};
+\draw (600.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.6};
+\draw (700.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.7};
+\draw (799.9999999999999,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.8};
+\draw (899.9999999999999,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0.9};
+\draw [-latex] ([yshift=-0mm] 0,0.0) -- ([yshift=3mm] 0, 100.0) node[above] { };
+\draw (0,0.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0};
+\draw (0,25.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.25};
+\draw (0,50.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.5};
+\draw (0,75.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.75};
+\draw (0,100.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {1};
+\draw [color=blue] plot[mark=*] (0.0000, 0.0000) --plot[style=solid] (301.5304, 0.0000); %
+\draw [color=blue] plot[mark=*] (301.5304, 5.0000) --plot[style=solid] (453.3903, 5.0000); %
+\draw [color=blue] plot[mark=*] (453.3903, 10.0000) --plot[style=solid] (453.4337, 10.0000); %
+\draw [color=blue] plot[mark=*] (453.4337, 15.0000) --plot[style=solid] (467.7741, 15.0000); %
+\draw [color=blue] plot[mark=*] (467.7741, 20.0000) --plot[style=solid] (479.6471, 20.0000); %
+\draw [color=blue] plot[mark=*] (479.6471, 25.0000) --plot[style=solid] (617.1170, 25.0000); %
+\draw [color=blue] plot[mark=*] (617.1170, 30.0000) --plot[style=solid] (680.7208, 30.0000); %
+\draw [color=blue] plot[mark=*] (680.7208, 35.0000) --plot[style=solid] (681.9469, 35.0000); %
+\draw [color=blue] plot[mark=*] (681.9469, 40.0000) --plot[style=solid] (713.6196, 40.0000); %
+\draw [color=blue] plot[mark=*] (713.6196, 45.0000) --plot[style=solid] (724.2162, 45.0000); %
+\draw [color=blue] plot[mark=*] (724.2162, 50.0000) --plot[style=solid] (731.0242, 50.0000); %
+\draw [color=blue] plot[mark=*] (731.0242, 55.0000) --plot[style=solid] (761.2374, 55.0000); %
+\draw [color=blue] plot[mark=*] (761.2374, 60.0000) --plot[style=solid] (800.9418, 60.0000); %
+\draw [color=blue] plot[mark=*] (800.9418, 65.0000) --plot[style=solid] (804.9549, 65.0000); %
+\draw [color=blue] plot[mark=*] (804.9549, 70.0000) --plot[style=solid] (894.2900, 70.0000); %
+\draw [color=blue] plot[mark=*] (894.2900, 75.0000) --plot[style=solid] (908.3347, 75.0000); %
+\draw [color=blue] plot[mark=*] (908.3347, 80.0000) --plot[style=solid] (918.6914, 80.0000); %
+\draw [color=blue] plot[mark=*] (918.6914, 85.0000) --plot[style=solid] (926.3442, 85.0000); %
+\draw [color=blue] plot[mark=*] (926.3442, 90.0000) --plot[style=solid] (955.9302, 90.0000); %
+\draw [color=blue] plot[mark=*] (955.9302, 95.0000) --plot[style=solid] (969.2427, 95.0000); %
+\draw [color=blue] plot[mark=*] (969.2427, 100.0000) --plot[style=solid] (969.2427, 100.0000); %
+\draw [color=magenta] plot[mark=square*] (0.0000, 0.0000) --plot[style=solid] (34.2733, 0.0000); %
+\draw [color=magenta] plot[mark=square*] (34.2733, 10.0000) --plot[style=solid] (52.9135, 10.0000); %
+\draw [color=magenta] plot[mark=square*] (52.9135, 20.0000) --plot[style=solid] (273.4031, 20.0000); %
+\draw [color=magenta] plot[mark=square*] (273.4031, 30.0000) --plot[style=solid] (313.6786, 30.0000); %
+\draw [color=magenta] plot[mark=square*] (313.6786, 40.0000) --plot[style=solid] (395.3887, 40.0000); %
+\draw [color=magenta] plot[mark=square*] (395.3887, 50.0000) --plot[style=solid] (460.9833, 50.0000); %
+\draw [color=magenta] plot[mark=square*] (460.9833, 60.0000) --plot[style=solid] (514.2070, 60.0000); %
+\draw [color=magenta] plot[mark=square*] (514.2070, 70.0000) --plot[style=solid] (736.6297, 70.0000); %
+\draw [color=magenta] plot[mark=square*] (736.6297, 80.0000) --plot[style=solid] (777.2346, 80.0000); %
+\draw [color=magenta] plot[mark=square*] (777.2346, 90.0000) --plot[style=solid] (857.6070, 90.0000); %
+\draw [color=magenta] plot[mark=square*] (857.6070, 100.0000) --plot[style=solid] (969.2427, 100.0000); %
+\end{tikzpicture}
+\end{center}
+
diff --git a/source/umontreal/iro/lecuyer/charts/exam/HistogramChartTest.java b/source/umontreal/iro/lecuyer/charts/exam/HistogramChartTest.java
new file mode 100644
index 0000000..cd28cd0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/HistogramChartTest.java
@@ -0,0 +1,45 @@
+import umontreal.iro.lecuyer.charts.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import java.awt.Color;
+
+public class HistogramChartTest
+{
+ private static double[] getPoints1() {
+ NormalGen gen = new NormalGen(new MRG32k3a(), 0, 2);
+ final int N = 100000;
+ double[] ad = new double[N];
+ for (int i = 0; i < N; i++)
+ ad[i] = gen.nextDouble();
+ return ad;
+ }
+
+ private static double[] getPoints2() {
+ ExponentialGen gen = new ExponentialGen(new MRG32k3a(), 1);
+ final int N = 100000;
+ double[] ad = new double[N];
+ for (int i = 0; i < N; i++)
+ ad[i] = gen.nextDouble();
+ return ad;
+ }
+
+ public static void main(String[] args) {
+ double[] data1 = getPoints1();
+ double[] data2 = getPoints2();
+
+ // Create a new chart with the previous data series.
+ HistogramChart chart = new HistogramChart(null, null, null, data1, data2);
+
+ // Customizes the data plots
+ HistogramSeriesCollection collec = chart.getSeriesCollection();
+ collec.setColor(0, new Color(255, 0, 0, 128));
+ collec.setColor(1, new Color(0, 255, 0, 128));
+ collec.setBins(0, 40, -6, 6);
+
+ // Define range bounds.
+ double[] bounds = { -6, 6, 0, 30000 };
+ chart.setManualRange00(bounds, true, true);
+
+ chart.toLatexFile("HistogramChartTest.tex", 12, 8);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/HistogramChartTest.tex b/source/umontreal/iro/lecuyer/charts/exam/HistogramChartTest.tex
new file mode 100644
index 0000000..8d8217c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/HistogramChartTest.tex
@@ -0,0 +1,79 @@
+
+% PGF/TikZ picture from SSJ
+% XScale = 100.0, YScale = 0.01, XShift = 0.0, YShift = 0.0
+% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale
+% and thisFileYValue = (originalSeriesYValue+YShift)*YScale
+
+\begin{center}
+\begin{tikzpicture}[x=0.01cm, y=0.02666666666666667cm]
+\footnotesize
+\draw [latex-latex] ([xshift=-3mm] -600.0,0) -- ([xshift=3mm] 600.0,0) node[right] { };
+\draw (0.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0};
+\draw (250.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {2.5};
+\draw (500.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {5};
+\draw (-250.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-2.5};
+\draw (-500.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-5};
+\draw [-latex] ([yshift=-0mm] 0,0.0) -- ([yshift=3mm] 0, 300.0) node[above] { $(10^{4})$};
+\draw (0,0.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {0};
+\draw (0,125.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {1.25};
+\draw (0,250.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {2.5};
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 0.0025, 0.0000) rectangle ([xshift=-0.0000] 34.9737, 294.9100); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 34.9737, 0.0000) rectangle ([xshift=-0.0000] 69.9450, 207.8600); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 69.9450, 0.0000) rectangle ([xshift=-0.0000] 104.9162, 147.4900); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 104.9162, 0.0000) rectangle ([xshift=-0.0000] 139.8875, 103.3800); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 139.8875, 0.0000) rectangle ([xshift=-0.0000] 174.8587, 73.0000); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 174.8587, 0.0000) rectangle ([xshift=-0.0000] 209.8300, 50.3900); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 209.8300, 0.0000) rectangle ([xshift=-0.0000] 244.8012, 37.1400); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 244.8012, 0.0000) rectangle ([xshift=-0.0000] 279.7725, 25.8700); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 279.7725, 0.0000) rectangle ([xshift=-0.0000] 314.7437, 17.5400); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 314.7437, 0.0000) rectangle ([xshift=-0.0000] 349.7150, 12.5400); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 349.7150, 0.0000) rectangle ([xshift=-0.0000] 384.6862, 8.9700); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 384.6862, 0.0000) rectangle ([xshift=-0.0000] 419.6575, 6.3400); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 419.6575, 0.0000) rectangle ([xshift=-0.0000] 454.6287, 4.1600); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 454.6287, 0.0000) rectangle ([xshift=-0.0000] 489.6000, 3.0000); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 489.6000, 0.0000) rectangle ([xshift=-0.0000] 524.5712, 2.3400); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 524.5712, 0.0000) rectangle ([xshift=-0.0000] 559.5425, 1.3100); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 559.5425, 0.0000) rectangle ([xshift=-0.0000] 594.5137, 1.2300); %
+\draw [line width=0.50pt, color=green] ([xshift=0.0000] 594.5137, 0.0000) rectangle ([xshift=-0.0000] 600.0000, 0.7400); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -600.0000, 0.0000) rectangle ([xshift=-0.0000] -570.0000, 0.8900); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -570.0000, 0.0000) rectangle ([xshift=-0.0000] -540.0000, 1.1600); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -540.0000, 0.0000) rectangle ([xshift=-0.0000] -510.0000, 2.0500); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -510.0000, 0.0000) rectangle ([xshift=-0.0000] -480.0000, 2.7500); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -480.0000, 0.0000) rectangle ([xshift=-0.0000] -450.0000, 4.1400); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -450.0000, 0.0000) rectangle ([xshift=-0.0000] -420.0000, 5.5800); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -420.0000, 0.0000) rectangle ([xshift=-0.0000] -390.0000, 7.5300); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -390.0000, 0.0000) rectangle ([xshift=-0.0000] -360.0000, 10.2600); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -360.0000, 0.0000) rectangle ([xshift=-0.0000] -330.0000, 13.0900); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -330.0000, 0.0000) rectangle ([xshift=-0.0000] -300.0000, 17.1400); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -300.0000, 0.0000) rectangle ([xshift=-0.0000] -270.0000, 22.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -270.0000, 0.0000) rectangle ([xshift=-0.0000] -240.0000, 26.2900); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -240.0000, 0.0000) rectangle ([xshift=-0.0000] -210.0000, 32.1400); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -210.0000, 0.0000) rectangle ([xshift=-0.0000] -180.0000, 37.4900); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -180.0000, 0.0000) rectangle ([xshift=-0.0000] -150.0000, 42.8600); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -150.0000, 0.0000) rectangle ([xshift=-0.0000] -120.0000, 46.8800); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -120.0000, 0.0000) rectangle ([xshift=-0.0000] -90.0000, 51.6000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -90.0000, 0.0000) rectangle ([xshift=-0.0000] -60.0000, 55.1300); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -60.0000, 0.0000) rectangle ([xshift=-0.0000] -30.0000, 57.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] -30.0000, 0.0000) rectangle ([xshift=-0.0000] 0.0000, 60.5300); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 0.0000, 0.0000) rectangle ([xshift=-0.0000] 30.0000, 59.7400); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 30.0000, 0.0000) rectangle ([xshift=-0.0000] 60.0000, 59.1500); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 60.0000, 0.0000) rectangle ([xshift=-0.0000] 90.0000, 56.5200); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 90.0000, 0.0000) rectangle ([xshift=-0.0000] 120.0000, 52.6300); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 120.0000, 0.0000) rectangle ([xshift=-0.0000] 150.0000, 47.8100); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 150.0000, 0.0000) rectangle ([xshift=-0.0000] 180.0000, 43.1000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 180.0000, 0.0000) rectangle ([xshift=-0.0000] 210.0000, 36.9200); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 210.0000, 0.0000) rectangle ([xshift=-0.0000] 240.0000, 30.9900); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 240.0000, 0.0000) rectangle ([xshift=-0.0000] 270.0000, 26.6600); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 270.0000, 0.0000) rectangle ([xshift=-0.0000] 300.0000, 21.0800); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 300.0000, 0.0000) rectangle ([xshift=-0.0000] 330.0000, 16.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 330.0000, 0.0000) rectangle ([xshift=-0.0000] 360.0000, 13.8300); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 360.0000, 0.0000) rectangle ([xshift=-0.0000] 390.0000, 9.9100); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 390.0000, 0.0000) rectangle ([xshift=-0.0000] 420.0000, 7.3800); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 420.0000, 0.0000) rectangle ([xshift=-0.0000] 450.0000, 5.4600); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 450.0000, 0.0000) rectangle ([xshift=-0.0000] 480.0000, 4.1800); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 480.0000, 0.0000) rectangle ([xshift=-0.0000] 510.0000, 2.6900); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 510.0000, 0.0000) rectangle ([xshift=-0.0000] 540.0000, 2.0300); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 540.0000, 0.0000) rectangle ([xshift=-0.0000] 570.0000, 1.3500); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 570.0000, 0.0000) rectangle ([xshift=-0.0000] 600.0000, 0.9400); %
+\end{tikzpicture}
+\end{center}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/HistogramTest1.java b/source/umontreal/iro/lecuyer/charts/exam/HistogramTest1.java
new file mode 100644
index 0000000..707386b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/HistogramTest1.java
@@ -0,0 +1,32 @@
+import umontreal.iro.lecuyer.charts.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import java.awt.Color;
+
+public class HistogramTest1
+{
+ private static double[] getData() {
+ NormalGen gen = new NormalGen(new LFSR113());
+ final int N = 100000;
+ double[] ad = new double[N];
+ for (int i = 0; i < N; i++)
+ ad[i] = gen.nextDouble();
+ return ad;
+ }
+
+ public static void main(String[] args) {
+ double[] data = getData();
+
+ HistogramChart chart;
+ chart = new HistogramChart("Standard Normal", null, null, data);
+
+ // Customizes the data plot
+ HistogramSeriesCollection collec = chart.getSeriesCollection();
+ collec.setBins(0, 80);
+ double[] bounds = { -4, 4, 0, 5000 };
+ chart.setManualRange(bounds);
+
+ chart.view(800, 500);
+ chart.toLatexFile("HistogramTest1.tex", 12, 8);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/HistogramTest1.tex b/source/umontreal/iro/lecuyer/charts/exam/HistogramTest1.tex
new file mode 100644
index 0000000..deb3d5a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/HistogramTest1.tex
@@ -0,0 +1,100 @@
+% PGF/TikZ picture from SSJ: Standard Normal
+% XScale = 100.0, YScale = 0.1, XShift = -4.0, YShift = 0.0
+% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale
+% and thisFileYValue = (originalSeriesYValue+YShift)*YScale
+
+\begin{center}
+\begin{tikzpicture}[x=0.015cm, y=0.016cm]
+\footnotesize
+\draw [-latex] ([xshift=-0mm] 0.0,0) -- ([xshift=3mm] 800.0,0) node[right] { };
+\draw (0.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-4};
+\draw (100.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-3};
+\draw (200.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-2};
+\draw (300.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-1};
+\draw (400.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {0};
+\draw (500.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {1};
+\draw (600.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {2};
+\draw (700.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {3};
+\draw (800.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {4};
+\draw [-latex] ([yshift=-0mm] 0,0.0) -- ([yshift=3mm] 0, 500.0) node[above] { $(10^{3})$};
+\draw (0,0.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0};
+\draw (0,100.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {1};
+\draw (0,200.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {2};
+\draw (0,300.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {3};
+\draw (0,400.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {4};
+\draw (0,500.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {5};
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 9.0228, 0.0000) rectangle ([xshift=-0.0000] 19.9923, 0.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 19.9923, 0.0000) rectangle ([xshift=-0.0000] 30.9618, 0.5000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 30.9618, 0.0000) rectangle ([xshift=-0.0000] 41.9313, 0.6000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 41.9313, 0.0000) rectangle ([xshift=-0.0000] 52.9008, 1.0000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 52.9008, 0.0000) rectangle ([xshift=-0.0000] 63.8703, 1.0000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 63.8703, 0.0000) rectangle ([xshift=-0.0000] 74.8398, 1.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 74.8398, 0.0000) rectangle ([xshift=-0.0000] 85.8093, 1.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 85.8093, 0.0000) rectangle ([xshift=-0.0000] 96.7788, 3.5000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 96.7788, 0.0000) rectangle ([xshift=-0.0000] 107.7482, 4.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 107.7482, 0.0000) rectangle ([xshift=-0.0000] 118.7177, 7.1000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 118.7177, 0.0000) rectangle ([xshift=-0.0000] 129.6872, 12.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 129.6872, 0.0000) rectangle ([xshift=-0.0000] 140.6567, 14.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 140.6567, 0.0000) rectangle ([xshift=-0.0000] 151.6262, 16.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 151.6262, 0.0000) rectangle ([xshift=-0.0000] 162.5957, 25.1000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 162.5957, 0.0000) rectangle ([xshift=-0.0000] 173.5652, 28.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 173.5652, 0.0000) rectangle ([xshift=-0.0000] 184.5347, 40.0000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 184.5347, 0.0000) rectangle ([xshift=-0.0000] 195.5042, 48.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 195.5042, 0.0000) rectangle ([xshift=-0.0000] 206.4737, 56.2000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 206.4737, 0.0000) rectangle ([xshift=-0.0000] 217.4432, 75.1000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 217.4432, 0.0000) rectangle ([xshift=-0.0000] 228.4127, 95.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 228.4127, 0.0000) rectangle ([xshift=-0.0000] 239.3822, 108.2000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 239.3822, 0.0000) rectangle ([xshift=-0.0000] 250.3517, 128.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 250.3517, 0.0000) rectangle ([xshift=-0.0000] 261.3212, 159.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 261.3212, 0.0000) rectangle ([xshift=-0.0000] 272.2907, 177.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 272.2907, 0.0000) rectangle ([xshift=-0.0000] 283.2602, 206.1000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 283.2602, 0.0000) rectangle ([xshift=-0.0000] 294.2297, 237.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 294.2297, 0.0000) rectangle ([xshift=-0.0000] 305.1992, 264.5000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 305.1992, 0.0000) rectangle ([xshift=-0.0000] 316.1686, 285.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 316.1686, 0.0000) rectangle ([xshift=-0.0000] 327.1381, 321.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 327.1381, 0.0000) rectangle ([xshift=-0.0000] 338.1076, 356.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 338.1076, 0.0000) rectangle ([xshift=-0.0000] 349.0771, 380.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 349.0771, 0.0000) rectangle ([xshift=-0.0000] 360.0466, 392.6000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 360.0466, 0.0000) rectangle ([xshift=-0.0000] 371.0161, 411.6000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 371.0161, 0.0000) rectangle ([xshift=-0.0000] 381.9856, 427.0000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 381.9856, 0.0000) rectangle ([xshift=-0.0000] 392.9551, 440.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 392.9551, 0.0000) rectangle ([xshift=-0.0000] 403.9246, 434.2000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 403.9246, 0.0000) rectangle ([xshift=-0.0000] 414.8941, 436.1000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 414.8941, 0.0000) rectangle ([xshift=-0.0000] 425.8636, 430.2000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 425.8636, 0.0000) rectangle ([xshift=-0.0000] 436.8331, 397.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 436.8331, 0.0000) rectangle ([xshift=-0.0000] 447.8026, 398.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 447.8026, 0.0000) rectangle ([xshift=-0.0000] 458.7721, 383.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 458.7721, 0.0000) rectangle ([xshift=-0.0000] 469.7416, 350.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 469.7416, 0.0000) rectangle ([xshift=-0.0000] 480.7111, 332.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 480.7111, 0.0000) rectangle ([xshift=-0.0000] 491.6806, 306.1000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 491.6806, 0.0000) rectangle ([xshift=-0.0000] 502.6501, 274.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 502.6501, 0.0000) rectangle ([xshift=-0.0000] 513.6196, 248.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 513.6196, 0.0000) rectangle ([xshift=-0.0000] 524.5891, 218.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 524.5891, 0.0000) rectangle ([xshift=-0.0000] 535.5585, 183.2000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 535.5585, 0.0000) rectangle ([xshift=-0.0000] 546.5280, 158.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 546.5280, 0.0000) rectangle ([xshift=-0.0000] 557.4975, 132.5000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 557.4975, 0.0000) rectangle ([xshift=-0.0000] 568.4670, 117.9000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 568.4670, 0.0000) rectangle ([xshift=-0.0000] 579.4365, 98.6000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 579.4365, 0.0000) rectangle ([xshift=-0.0000] 590.4060, 73.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 590.4060, 0.0000) rectangle ([xshift=-0.0000] 601.3755, 67.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 601.3755, 0.0000) rectangle ([xshift=-0.0000] 612.3450, 52.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 612.3450, 0.0000) rectangle ([xshift=-0.0000] 623.3145, 42.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 623.3145, 0.0000) rectangle ([xshift=-0.0000] 634.2840, 32.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 634.2840, 0.0000) rectangle ([xshift=-0.0000] 645.2535, 22.5000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 645.2535, 0.0000) rectangle ([xshift=-0.0000] 656.2230, 17.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 656.2230, 0.0000) rectangle ([xshift=-0.0000] 667.1925, 15.2000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 667.1925, 0.0000) rectangle ([xshift=-0.0000] 678.1620, 10.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 678.1620, 0.0000) rectangle ([xshift=-0.0000] 689.1315, 8.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 689.1315, 0.0000) rectangle ([xshift=-0.0000] 700.1010, 6.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 700.1010, 0.0000) rectangle ([xshift=-0.0000] 711.0705, 3.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 711.0705, 0.0000) rectangle ([xshift=-0.0000] 722.0400, 3.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 722.0400, 0.0000) rectangle ([xshift=-0.0000] 733.0095, 1.8000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 733.0095, 0.0000) rectangle ([xshift=-0.0000] 743.9789, 1.7000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 743.9789, 0.0000) rectangle ([xshift=-0.0000] 754.9484, 1.0000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 754.9484, 0.0000) rectangle ([xshift=-0.0000] 765.9179, 0.3000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 765.9179, 0.0000) rectangle ([xshift=-0.0000] 776.8874, 0.2000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 776.8874, 0.0000) rectangle ([xshift=-0.0000] 787.8569, 0.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 787.8569, 0.0000) rectangle ([xshift=-0.0000] 798.8264, 0.4000); %
+\draw [line width=0.50pt, color=red] ([xshift=0.0000] 798.8264, 0.0000) rectangle ([xshift=-0.0000] 800.0000, 0.3000); %
+\end{tikzpicture}
+\end{center}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/NormalChart.java b/source/umontreal/iro/lecuyer/charts/exam/NormalChart.java
new file mode 100644
index 0000000..d96d554
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/NormalChart.java
@@ -0,0 +1,26 @@
+import umontreal.iro.lecuyer.charts.XYLineChart;
+
+public class NormalChart
+{
+ private static double[][] getPoints() {
+ // The density of the standard normal probability distribution
+ // points contains one array for X values and one array for Y values
+ final int N = 400;
+ double[][] points = new double[2][N + 1];
+ final double CPI = Math.sqrt (2*Math.PI);
+ for (int i = 0; i <= N; ++i) {
+ double x = -3.5 + i * 7.0 / N;
+ points[0][i] = x;
+ points[1][i] = Math.exp (-x*x/2.0) / CPI;
+ }
+ return points;
+ }
+
+ public static void main(String[] args) {
+ double[][] points = getPoints();
+ XYLineChart chart = new XYLineChart(null, "X", null, points);
+ chart.setAutoRange00(true, true); // Axes pass through (0,0)
+ chart.toLatexFile("NormalChart.tex", 12, 8);
+ chart.view(800,500);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/charts/exam/NormalChart.tex b/source/umontreal/iro/lecuyer/charts/exam/NormalChart.tex
new file mode 100644
index 0000000..66cddb4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/exam/NormalChart.tex
@@ -0,0 +1,433 @@
+% PGF/TikZ picture from SSJ
+% XScale = 100.0, YScale = 1000.0, XShift = 0.0, YShift = 0.0
+% Therefore, thisFileXValue = (originalSeriesXValue+XShift)*XScale
+% and thisFileYValue = (originalSeriesYValue+YShift)*YScale
+
+\begin{center}
+\begin{tikzpicture}[x=0.015cm, y=0.018604651162790697cm]
+\footnotesize
+\draw [latex-latex] ([xshift=-3mm] -400.0,0) -- ([xshift=3mm] 400.0,0) node[right] {X};
+\draw (0.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below right] {0};
+\draw (100.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {1};
+\draw (200.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {2};
+\draw (300.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {3};
+\draw (400.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {4};
+\draw (-100.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-1};
+\draw (-200.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-2};
+\draw (-300.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-3};
+\draw (-400.0,0) -- +(0mm,1mm) -- +(0mm,-1mm) node[below] {-4};
+\draw [latex-latex] ([yshift=-3mm] 0,-10.0) -- ([yshift=3mm] 0, 420.0) node[above] { };
+\draw (0,0.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[above left] {0};
+\draw (0,50.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.05};
+\draw (0,100.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.1};
+\draw (0,150.00000000000003) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.15};
+\draw (0,200.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.2};
+\draw (0,250.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.25};
+\draw (0,300.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.3};
+\draw (0,350.0) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.35};
+\draw (0,399.99999999999994) -- +(1mm,0mm) -- +(-1mm,0mm) node[left] {0.4};
+\draw [smooth, color=red, mark= , style=solid] plot coordinates {%
+(-350.00,0.8727) % (-3.500000, 0.000873)
+(-348.25,0.9277) % (-3.482500, 0.000928)
+(-346.50,0.9858) % (-3.465000, 0.000986)
+(-344.75,1.0473) % (-3.447500, 0.001047)
+(-343.00,1.1122) % (-3.430000, 0.001112)
+(-341.25,1.1809) % (-3.412500, 0.001181)
+(-339.50,1.2533) % (-3.395000, 0.001253)
+(-337.75,1.3298) % (-3.377500, 0.001330)
+(-336.00,1.4106) % (-3.360000, 0.001411)
+(-334.25,1.4958) % (-3.342500, 0.001496)
+(-332.50,1.5857) % (-3.325000, 0.001586)
+(-330.75,1.6804) % (-3.307500, 0.001680)
+(-329.00,1.7803) % (-3.290000, 0.001780)
+(-327.25,1.8855) % (-3.272500, 0.001885)
+(-325.50,1.9963) % (-3.255000, 0.001996)
+(-323.75,2.1130) % (-3.237500, 0.002113)
+(-322.00,2.2358) % (-3.220000, 0.002236)
+(-320.25,2.3651) % (-3.202500, 0.002365)
+(-318.50,2.5010) % (-3.185000, 0.002501)
+(-316.75,2.6440) % (-3.167500, 0.002644)
+(-315.00,2.7943) % (-3.150000, 0.002794)
+(-313.25,2.9522) % (-3.132500, 0.002952)
+(-311.50,3.1180) % (-3.115000, 0.003118)
+(-309.75,3.2922) % (-3.097500, 0.003292)
+(-308.00,3.4751) % (-3.080000, 0.003475)
+(-306.25,3.6670) % (-3.062500, 0.003667)
+(-304.50,3.8683) % (-3.045000, 0.003868)
+(-302.75,4.0794) % (-3.027500, 0.004079)
+(-301.00,4.3007) % (-3.010000, 0.004301)
+(-299.25,4.5326) % (-2.992500, 0.004533)
+(-297.50,4.7755) % (-2.975000, 0.004776)
+(-295.75,5.0300) % (-2.957500, 0.005030)
+(-294.00,5.2963) % (-2.940000, 0.005296)
+(-292.25,5.5751) % (-2.922500, 0.005575)
+(-290.50,5.8668) % (-2.905000, 0.005867)
+(-288.75,6.1718) % (-2.887500, 0.006172)
+(-287.00,6.4907) % (-2.870000, 0.006491)
+(-285.25,6.8240) % (-2.852500, 0.006824)
+(-283.50,7.1721) % (-2.835000, 0.007172)
+(-281.75,7.5358) % (-2.817500, 0.007536)
+(-280.00,7.9155) % (-2.800000, 0.007915)
+(-278.25,8.3117) % (-2.782500, 0.008312)
+(-276.50,8.7251) % (-2.765000, 0.008725)
+(-274.75,9.1563) % (-2.747500, 0.009156)
+(-273.00,9.6058) % (-2.730000, 0.009606)
+(-271.25,10.0743) % (-2.712500, 0.010074)
+(-269.50,10.5624) % (-2.695000, 0.010562)
+(-267.75,11.0708) % (-2.677500, 0.011071)
+(-266.00,11.6001) % (-2.660000, 0.011600)
+(-264.25,12.1510) % (-2.642500, 0.012151)
+(-262.50,12.7242) % (-2.625000, 0.012724)
+(-260.75,13.3203) % (-2.607500, 0.013320)
+(-259.00,13.9401) % (-2.590000, 0.013940)
+(-257.25,14.5842) % (-2.572500, 0.014584)
+(-255.50,15.2534) % (-2.555000, 0.015253)
+(-253.75,15.9485) % (-2.537500, 0.015948)
+(-252.00,16.6701) % (-2.520000, 0.016670)
+(-250.25,17.4190) % (-2.502500, 0.017419)
+(-248.50,18.1960) % (-2.485000, 0.018196)
+(-246.75,19.0019) % (-2.467500, 0.019002)
+(-245.00,19.8374) % (-2.450000, 0.019837)
+(-243.25,20.7032) % (-2.432500, 0.020703)
+(-241.50,21.6002) % (-2.415000, 0.021600)
+(-239.75,22.5292) % (-2.397500, 0.022529)
+(-238.00,23.4910) % (-2.380000, 0.023491)
+(-236.25,24.4863) % (-2.362500, 0.024486)
+(-234.50,25.5160) % (-2.345000, 0.025516)
+(-232.75,26.5808) % (-2.327500, 0.026581)
+(-231.00,27.6816) % (-2.310000, 0.027682)
+(-229.25,28.8191) % (-2.292500, 0.028819)
+(-227.50,29.9942) % (-2.275000, 0.029994)
+(-225.75,31.2077) % (-2.257500, 0.031208)
+(-224.00,32.4603) % (-2.240000, 0.032460)
+(-222.25,33.7528) % (-2.222500, 0.033753)
+(-220.50,35.0861) % (-2.205000, 0.035086)
+(-218.75,36.4608) % (-2.187500, 0.036461)
+(-217.00,37.8779) % (-2.170000, 0.037878)
+(-215.25,39.3379) % (-2.152500, 0.039338)
+(-213.50,40.8417) % (-2.135000, 0.040842)
+(-211.75,42.3900) % (-2.117500, 0.042390)
+(-210.00,43.9836) % (-2.100000, 0.043984)
+(-208.25,45.6231) % (-2.082500, 0.045623)
+(-206.50,47.3092) % (-2.065000, 0.047309)
+(-204.75,49.0426) % (-2.047500, 0.049043)
+(-203.00,50.8239) % (-2.030000, 0.050824)
+(-201.25,52.6538) % (-2.012500, 0.052654)
+(-199.50,54.5329) % (-1.995000, 0.054533)
+(-197.75,56.4618) % (-1.977500, 0.056462)
+(-196.00,58.4409) % (-1.960000, 0.058441)
+(-194.25,60.4710) % (-1.942500, 0.060471)
+(-192.50,62.5524) % (-1.925000, 0.062552)
+(-190.75,64.6856) % (-1.907500, 0.064686)
+(-189.00,66.8711) % (-1.890000, 0.066871)
+(-187.25,69.1093) % (-1.872500, 0.069109)
+(-185.50,71.4005) % (-1.855000, 0.071400)
+(-183.75,73.7450) % (-1.837500, 0.073745)
+(-182.00,76.1433) % (-1.820000, 0.076143)
+(-180.25,78.5954) % (-1.802500, 0.078595)
+(-178.50,81.1017) % (-1.785000, 0.081102)
+(-176.75,83.6623) % (-1.767500, 0.083662)
+(-175.00,86.2773) % (-1.750000, 0.086277)
+(-173.25,88.9468) % (-1.732500, 0.088947)
+(-171.50,91.6708) % (-1.715000, 0.091671)
+(-169.75,94.4493) % (-1.697500, 0.094449)
+(-168.00,97.2823) % (-1.680000, 0.097282)
+(-166.25,100.1695) % (-1.662500, 0.100169)
+(-164.50,103.1108) % (-1.645000, 0.103111)
+(-162.75,106.1060) % (-1.627500, 0.106106)
+(-161.00,109.1548) % (-1.610000, 0.109155)
+(-159.25,112.2567) % (-1.592500, 0.112257)
+(-157.50,115.4115) % (-1.575000, 0.115412)
+(-155.75,118.6186) % (-1.557500, 0.118619)
+(-154.00,121.8775) % (-1.540000, 0.121878)
+(-152.25,125.1876) % (-1.522500, 0.125188)
+(-150.50,128.5482) % (-1.505000, 0.128548)
+(-148.75,131.9587) % (-1.487500, 0.131959)
+(-147.00,135.4181) % (-1.470000, 0.135418)
+(-145.25,138.9256) % (-1.452500, 0.138926)
+(-143.50,142.4804) % (-1.435000, 0.142480)
+(-141.75,146.0813) % (-1.417500, 0.146081)
+(-140.00,149.7275) % (-1.400000, 0.149727)
+(-138.25,153.4176) % (-1.382500, 0.153418)
+(-136.50,157.1505) % (-1.365000, 0.157151)
+(-134.75,160.9250) % (-1.347500, 0.160925)
+(-133.00,164.7397) % (-1.330000, 0.164740)
+(-131.25,168.5932) % (-1.312500, 0.168593)
+(-129.50,172.4840) % (-1.295000, 0.172484)
+(-127.75,176.4105) % (-1.277500, 0.176410)
+(-126.00,180.3712) % (-1.260000, 0.180371)
+(-124.25,184.3643) % (-1.242500, 0.184364)
+(-122.50,188.3881) % (-1.225000, 0.188388)
+(-120.75,192.4408) % (-1.207500, 0.192441)
+(-119.00,196.5205) % (-1.190000, 0.196520)
+(-117.25,200.6252) % (-1.172500, 0.200625)
+(-115.50,204.7530) % (-1.155000, 0.204753)
+(-113.75,208.9017) % (-1.137500, 0.208902)
+(-112.00,213.0691) % (-1.120000, 0.213069)
+(-110.25,217.2532) % (-1.102500, 0.217253)
+(-108.50,221.4516) % (-1.085000, 0.221452)
+(-106.75,225.6621) % (-1.067500, 0.225662)
+(-105.00,229.8821) % (-1.050000, 0.229882)
+(-103.25,234.1094) % (-1.032500, 0.234109)
+(-101.50,238.3414) % (-1.015000, 0.238341)
+(-99.75,242.5757) % (-0.997500, 0.242576)
+(-98.00,246.8095) % (-0.980000, 0.246809)
+(-96.25,251.0403) % (-0.962500, 0.251040)
+(-94.50,255.2655) % (-0.945000, 0.255266)
+(-92.75,259.4823) % (-0.927500, 0.259482)
+(-91.00,263.6880) % (-0.910000, 0.263688)
+(-89.25,267.8799) % (-0.892500, 0.267880)
+(-87.50,272.0550) % (-0.875000, 0.272055)
+(-85.75,276.2106) % (-0.857500, 0.276211)
+(-84.00,280.3438) % (-0.840000, 0.280344)
+(-82.25,284.4517) % (-0.822500, 0.284452)
+(-80.50,288.5315) % (-0.805000, 0.288531)
+(-78.75,292.5801) % (-0.787500, 0.292580)
+(-77.00,296.5948) % (-0.770000, 0.296595)
+(-75.25,300.5724) % (-0.752500, 0.300572)
+(-73.50,304.5101) % (-0.735000, 0.304510)
+(-71.75,308.4049) % (-0.717500, 0.308405)
+(-70.00,312.2539) % (-0.700000, 0.312254)
+(-68.25,316.0542) % (-0.682500, 0.316054)
+(-66.50,319.8027) % (-0.665000, 0.319803)
+(-64.75,323.4966) % (-0.647500, 0.323497)
+(-63.00,327.1330) % (-0.630000, 0.327133)
+(-61.25,330.7089) % (-0.612500, 0.330709)
+(-59.50,334.2216) % (-0.595000, 0.334222)
+(-57.75,337.6682) % (-0.577500, 0.337668)
+(-56.00,341.0458) % (-0.560000, 0.341046)
+(-54.25,344.3517) % (-0.542500, 0.344352)
+(-52.50,347.5833) % (-0.525000, 0.347583)
+(-50.75,350.7377) % (-0.507500, 0.350738)
+(-49.00,353.8124) % (-0.490000, 0.353812)
+(-47.25,356.8047) % (-0.472500, 0.356805)
+(-45.50,359.7122) % (-0.455000, 0.359712)
+(-43.75,362.5323) % (-0.437500, 0.362532)
+(-42.00,365.2627) % (-0.420000, 0.365263)
+(-40.25,367.9009) % (-0.402500, 0.367901)
+(-38.50,370.4447) % (-0.385000, 0.370445)
+(-36.75,372.8919) % (-0.367500, 0.372892)
+(-35.00,375.2403) % (-0.350000, 0.375240)
+(-33.25,377.4879) % (-0.332500, 0.377488)
+(-31.50,379.6327) % (-0.315000, 0.379633)
+(-29.75,381.6728) % (-0.297500, 0.381673)
+(-28.00,383.6063) % (-0.280000, 0.383606)
+(-26.25,385.4316) % (-0.262500, 0.385432)
+(-24.50,387.1469) % (-0.245000, 0.387147)
+(-22.75,388.7508) % (-0.227500, 0.388751)
+(-21.00,390.2419) % (-0.210000, 0.390242)
+(-19.25,391.6187) % (-0.192500, 0.391619)
+(-17.50,392.8800) % (-0.175000, 0.392880)
+(-15.75,394.0247) % (-0.157500, 0.394025)
+(-14.00,395.0517) % (-0.140000, 0.395052)
+(-12.25,395.9602) % (-0.122500, 0.395960)
+(-10.50,396.7492) % (-0.105000, 0.396749)
+(-8.75,397.4180) % (-0.087500, 0.397418)
+(-7.00,397.9661) % (-0.070000, 0.397966)
+(-5.25,398.3929) % (-0.052500, 0.398393)
+(-3.50,398.6980) % (-0.035000, 0.398698)
+(-1.75,398.8812) % (-0.017500, 0.398881)
+(0.00,398.9423) % (0.000000, 0.398942)
+(1.75,398.8812) % (0.017500, 0.398881)
+(3.50,398.6980) % (0.035000, 0.398698)
+(5.25,398.3929) % (0.052500, 0.398393)
+(7.00,397.9661) % (0.070000, 0.397966)
+(8.75,397.4180) % (0.087500, 0.397418)
+(10.50,396.7492) % (0.105000, 0.396749)
+(12.25,395.9602) % (0.122500, 0.395960)
+(14.00,395.0517) % (0.140000, 0.395052)
+(15.75,394.0247) % (0.157500, 0.394025)
+(17.50,392.8800) % (0.175000, 0.392880)
+(19.25,391.6187) % (0.192500, 0.391619)
+(21.00,390.2419) % (0.210000, 0.390242)
+(22.75,388.7508) % (0.227500, 0.388751)
+(24.50,387.1469) % (0.245000, 0.387147)
+(26.25,385.4316) % (0.262500, 0.385432)
+(28.00,383.6063) % (0.280000, 0.383606)
+(29.75,381.6728) % (0.297500, 0.381673)
+(31.50,379.6327) % (0.315000, 0.379633)
+(33.25,377.4879) % (0.332500, 0.377488)
+(35.00,375.2403) % (0.350000, 0.375240)
+(36.75,372.8919) % (0.367500, 0.372892)
+(38.50,370.4447) % (0.385000, 0.370445)
+(40.25,367.9009) % (0.402500, 0.367901)
+(42.00,365.2627) % (0.420000, 0.365263)
+(43.75,362.5323) % (0.437500, 0.362532)
+(45.50,359.7122) % (0.455000, 0.359712)
+(47.25,356.8047) % (0.472500, 0.356805)
+(49.00,353.8124) % (0.490000, 0.353812)
+(50.75,350.7377) % (0.507500, 0.350738)
+(52.50,347.5833) % (0.525000, 0.347583)
+(54.25,344.3517) % (0.542500, 0.344352)
+(56.00,341.0458) % (0.560000, 0.341046)
+(57.75,337.6682) % (0.577500, 0.337668)
+(59.50,334.2216) % (0.595000, 0.334222)
+(61.25,330.7089) % (0.612500, 0.330709)
+(63.00,327.1330) % (0.630000, 0.327133)
+(64.75,323.4966) % (0.647500, 0.323497)
+(66.50,319.8027) % (0.665000, 0.319803)
+(68.25,316.0542) % (0.682500, 0.316054)
+(70.00,312.2539) % (0.700000, 0.312254)
+(71.75,308.4049) % (0.717500, 0.308405)
+(73.50,304.5101) % (0.735000, 0.304510)
+(75.25,300.5724) % (0.752500, 0.300572)
+(77.00,296.5948) % (0.770000, 0.296595)
+(78.75,292.5801) % (0.787500, 0.292580)
+(80.50,288.5315) % (0.805000, 0.288531)
+(82.25,284.4517) % (0.822500, 0.284452)
+(84.00,280.3438) % (0.840000, 0.280344)
+(85.75,276.2106) % (0.857500, 0.276211)
+(87.50,272.0550) % (0.875000, 0.272055)
+(89.25,267.8799) % (0.892500, 0.267880)
+(91.00,263.6880) % (0.910000, 0.263688)
+(92.75,259.4823) % (0.927500, 0.259482)
+(94.50,255.2655) % (0.945000, 0.255266)
+(96.25,251.0403) % (0.962500, 0.251040)
+(98.00,246.8095) % (0.980000, 0.246809)
+(99.75,242.5757) % (0.997500, 0.242576)
+(101.50,238.3414) % (1.015000, 0.238341)
+(103.25,234.1094) % (1.032500, 0.234109)
+(105.00,229.8821) % (1.050000, 0.229882)
+(106.75,225.6621) % (1.067500, 0.225662)
+(108.50,221.4516) % (1.085000, 0.221452)
+(110.25,217.2532) % (1.102500, 0.217253)
+(112.00,213.0691) % (1.120000, 0.213069)
+(113.75,208.9017) % (1.137500, 0.208902)
+(115.50,204.7530) % (1.155000, 0.204753)
+(117.25,200.6252) % (1.172500, 0.200625)
+(119.00,196.5205) % (1.190000, 0.196520)
+(120.75,192.4408) % (1.207500, 0.192441)
+(122.50,188.3881) % (1.225000, 0.188388)
+(124.25,184.3643) % (1.242500, 0.184364)
+(126.00,180.3712) % (1.260000, 0.180371)
+(127.75,176.4105) % (1.277500, 0.176410)
+(129.50,172.4840) % (1.295000, 0.172484)
+(131.25,168.5932) % (1.312500, 0.168593)
+(133.00,164.7397) % (1.330000, 0.164740)
+(134.75,160.9250) % (1.347500, 0.160925)
+(136.50,157.1505) % (1.365000, 0.157151)
+(138.25,153.4176) % (1.382500, 0.153418)
+(140.00,149.7275) % (1.400000, 0.149727)
+(141.75,146.0813) % (1.417500, 0.146081)
+(143.50,142.4804) % (1.435000, 0.142480)
+(145.25,138.9256) % (1.452500, 0.138926)
+(147.00,135.4181) % (1.470000, 0.135418)
+(148.75,131.9587) % (1.487500, 0.131959)
+(150.50,128.5482) % (1.505000, 0.128548)
+(152.25,125.1876) % (1.522500, 0.125188)
+(154.00,121.8775) % (1.540000, 0.121878)
+(155.75,118.6186) % (1.557500, 0.118619)
+(157.50,115.4115) % (1.575000, 0.115412)
+(159.25,112.2567) % (1.592500, 0.112257)
+(161.00,109.1548) % (1.610000, 0.109155)
+(162.75,106.1060) % (1.627500, 0.106106)
+(164.50,103.1108) % (1.645000, 0.103111)
+(166.25,100.1695) % (1.662500, 0.100169)
+(168.00,97.2823) % (1.680000, 0.097282)
+(169.75,94.4493) % (1.697500, 0.094449)
+(171.50,91.6708) % (1.715000, 0.091671)
+(173.25,88.9468) % (1.732500, 0.088947)
+(175.00,86.2773) % (1.750000, 0.086277)
+(176.75,83.6623) % (1.767500, 0.083662)
+(178.50,81.1017) % (1.785000, 0.081102)
+(180.25,78.5954) % (1.802500, 0.078595)
+(182.00,76.1433) % (1.820000, 0.076143)
+(183.75,73.7450) % (1.837500, 0.073745)
+(185.50,71.4005) % (1.855000, 0.071400)
+(187.25,69.1093) % (1.872500, 0.069109)
+(189.00,66.8711) % (1.890000, 0.066871)
+(190.75,64.6856) % (1.907500, 0.064686)
+(192.50,62.5524) % (1.925000, 0.062552)
+(194.25,60.4710) % (1.942500, 0.060471)
+(196.00,58.4409) % (1.960000, 0.058441)
+(197.75,56.4618) % (1.977500, 0.056462)
+(199.50,54.5329) % (1.995000, 0.054533)
+(201.25,52.6538) % (2.012500, 0.052654)
+(203.00,50.8239) % (2.030000, 0.050824)
+(204.75,49.0426) % (2.047500, 0.049043)
+(206.50,47.3092) % (2.065000, 0.047309)
+(208.25,45.6231) % (2.082500, 0.045623)
+(210.00,43.9836) % (2.100000, 0.043984)
+(211.75,42.3900) % (2.117500, 0.042390)
+(213.50,40.8417) % (2.135000, 0.040842)
+(215.25,39.3379) % (2.152500, 0.039338)
+(217.00,37.8779) % (2.170000, 0.037878)
+(218.75,36.4608) % (2.187500, 0.036461)
+(220.50,35.0861) % (2.205000, 0.035086)
+(222.25,33.7528) % (2.222500, 0.033753)
+(224.00,32.4603) % (2.240000, 0.032460)
+(225.75,31.2077) % (2.257500, 0.031208)
+(227.50,29.9942) % (2.275000, 0.029994)
+(229.25,28.8191) % (2.292500, 0.028819)
+(231.00,27.6816) % (2.310000, 0.027682)
+(232.75,26.5808) % (2.327500, 0.026581)
+(234.50,25.5160) % (2.345000, 0.025516)
+(236.25,24.4863) % (2.362500, 0.024486)
+(238.00,23.4910) % (2.380000, 0.023491)
+(239.75,22.5292) % (2.397500, 0.022529)
+(241.50,21.6002) % (2.415000, 0.021600)
+(243.25,20.7032) % (2.432500, 0.020703)
+(245.00,19.8374) % (2.450000, 0.019837)
+(246.75,19.0019) % (2.467500, 0.019002)
+(248.50,18.1960) % (2.485000, 0.018196)
+(250.25,17.4190) % (2.502500, 0.017419)
+(252.00,16.6701) % (2.520000, 0.016670)
+(253.75,15.9485) % (2.537500, 0.015948)
+(255.50,15.2534) % (2.555000, 0.015253)
+(257.25,14.5842) % (2.572500, 0.014584)
+(259.00,13.9401) % (2.590000, 0.013940)
+(260.75,13.3203) % (2.607500, 0.013320)
+(262.50,12.7242) % (2.625000, 0.012724)
+(264.25,12.1510) % (2.642500, 0.012151)
+(266.00,11.6001) % (2.660000, 0.011600)
+(267.75,11.0708) % (2.677500, 0.011071)
+(269.50,10.5624) % (2.695000, 0.010562)
+(271.25,10.0743) % (2.712500, 0.010074)
+(273.00,9.6058) % (2.730000, 0.009606)
+(274.75,9.1563) % (2.747500, 0.009156)
+(276.50,8.7251) % (2.765000, 0.008725)
+(278.25,8.3117) % (2.782500, 0.008312)
+(280.00,7.9155) % (2.800000, 0.007915)
+(281.75,7.5358) % (2.817500, 0.007536)
+(283.50,7.1721) % (2.835000, 0.007172)
+(285.25,6.8240) % (2.852500, 0.006824)
+(287.00,6.4907) % (2.870000, 0.006491)
+(288.75,6.1718) % (2.887500, 0.006172)
+(290.50,5.8668) % (2.905000, 0.005867)
+(292.25,5.5751) % (2.922500, 0.005575)
+(294.00,5.2963) % (2.940000, 0.005296)
+(295.75,5.0300) % (2.957500, 0.005030)
+(297.50,4.7755) % (2.975000, 0.004776)
+(299.25,4.5326) % (2.992500, 0.004533)
+(301.00,4.3007) % (3.010000, 0.004301)
+(302.75,4.0794) % (3.027500, 0.004079)
+(304.50,3.8683) % (3.045000, 0.003868)
+(306.25,3.6670) % (3.062500, 0.003667)
+(308.00,3.4751) % (3.080000, 0.003475)
+(309.75,3.2922) % (3.097500, 0.003292)
+(311.50,3.1180) % (3.115000, 0.003118)
+(313.25,2.9522) % (3.132500, 0.002952)
+(315.00,2.7943) % (3.150000, 0.002794)
+(316.75,2.6440) % (3.167500, 0.002644)
+(318.50,2.5010) % (3.185000, 0.002501)
+(320.25,2.3651) % (3.202500, 0.002365)
+(322.00,2.2358) % (3.220000, 0.002236)
+(323.75,2.1130) % (3.237500, 0.002113)
+(325.50,1.9963) % (3.255000, 0.001996)
+(327.25,1.8855) % (3.272500, 0.001885)
+(329.00,1.7803) % (3.290000, 0.001780)
+(330.75,1.6804) % (3.307500, 0.001680)
+(332.50,1.5857) % (3.325000, 0.001586)
+(334.25,1.4958) % (3.342500, 0.001496)
+(336.00,1.4106) % (3.360000, 0.001411)
+(337.75,1.3298) % (3.377500, 0.001330)
+(339.50,1.2533) % (3.395000, 0.001253)
+(341.25,1.1809) % (3.412500, 0.001181)
+(343.00,1.1122) % (3.430000, 0.001112)
+(344.75,1.0473) % (3.447500, 0.001047)
+(346.50,0.9858) % (3.465000, 0.000986)
+(348.25,0.9277) % (3.482500, 0.000928)
+(350.00,0.8727)} % (3.500000, 0.000873)
+ node[right] { };
+\end{tikzpicture}
+\end{center}
diff --git a/source/umontreal/iro/lecuyer/charts/guidecharts.bbl b/source/umontreal/iro/lecuyer/charts/guidecharts.bbl
new file mode 100644
index 0000000..9b431fa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/guidecharts.bbl
@@ -0,0 +1,3 @@
+\begin{thebibliography}{}
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/charts/guidecharts.tex b/source/umontreal/iro/lecuyer/charts/guidecharts.tex
new file mode 100644
index 0000000..e50b5c1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/guidecharts.tex
@@ -0,0 +1,71 @@
+\documentclass[12pt]{article}
+\usepackage{ssj}
+\mytwoheads
+\dateheadtrue
+\usepackage{tikz}
+\usetikzlibrary{plotmarks}
+\usepackage{color}
+\usepackage{crayola}
+\usepackage[procnames]{listings}
+\usepackage{lstpatch}
+
+\lstloadlanguages{Java}
+\lstset{language=Java,
+float=tbhp,
+captionpos=t,
+frame=trbl,
+abovecaptionskip=1.5em,
+belowskip=2em,
+basicstyle=\small\ttfamily,
+stringstyle=\color{OliveGreen},
+commentstyle=\color{red},
+identifierstyle=\color{Bittersweet},
+basewidth={0.5em},
+showstringspaces=false,
+framerule=0.8pt,
+procnamestyle=\bfseries\color{blue},
+emphstyle=\bfseries\color{Cerulean},
+procnamekeys={class,extends,interface,implements}
+}
+
+
+%\includeonly{XYLineChart}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\include{title}
+\pagenumbering{roman}
+\tableofcontents
+
+\pagenumbering{arabic}
+\include{overview}
+
+\include{XYChart}
+\include{XYLineChart}
+\include{YListChart}
+\include{PPPlot}
+\include{QQPlot}
+\include{EmpiricalChart}
+\include{HistogramChart}
+\include{ScatterChart}
+\include{CategoryChart}
+\include{BoxChart}
+\include{ContinuousDistChart}
+\include{DiscreteDistIntChart}
+\include{MultipleDatasetChart}
+\include{SSJXYSeriesCollection}
+\include{SSJCategorySeriesCollection}
+\include{XYListSeriesCollection}
+\include{YListSeriesCollection}
+\include{EmpiricalSeriesCollection}
+\include{HistogramSeriesCollection}
+\include{BoxSeriesCollection}
+\include{Axis}
+\include{PlotFormat}
+
+\bibliographystyle{plain}
+% \bibliography{simul,random,ift,stat,prob,math} % Dans texmac.
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/charts/overview.tex b/source/umontreal/iro/lecuyer/charts/overview.tex
new file mode 100644
index 0000000..74db2e8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/overview.tex
@@ -0,0 +1,371 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+\latex{\label {sec:overview}}
+
+\begin {htmlonly}
+This package contains classes to produce charts used in the Java software developed in the {\em simulation laboratory\/}
+of the DIRO, at the Universite de Montreal.
+\end {htmlonly}
+
+This package provides tools for easy
+construction, visualization, and customization
+of XY plots, histograms, and empirical styled charts
+from a Java program.
+It uses and extends the free and ``open source''
+JFreeChart tool to manage the
+charts. JFreeChart is
+distributed under the terms of the GNU Lesser General Public License
+(LGPL), and can be found at \url{http://www.jfree.org/jfreechart/}.
+
+This package also provides facilities
+to export charts to PGF/TikZ source code, which can be included into
+\LaTeX{} documents. TikZ is a free \LaTeX{}
+package to produce pictures such as plots, and can be downloaded from
+\url{http://sourceforge.net/projects/pgf}.
+
+The user does not need to be familiar with the JFreeChart package
+or the TikZ syntax to
+use these tools, except if customization is required.
+For this, one may see the API specification of JFreeChart, and
+the reference manual of TikZ.
+
+The two basic abstract classes of package \texttt{charts} are
+\externalclass{umontreal.iro.lecuyer.charts}{XYChart} and
+\externalclass{umontreal.iro.lecuyer.charts}{CategoryChart}. All other
+charts inherit from one of these two. Charts are managed by
+the mother class which contains the data tables in a
+\texttt{*SeriesCollection} object, and the
+information about $x$-axis and $y$-axis in
+\externalclass{umontreal.iro.lecuyer.charts}{Axis} instances.
+All these objects encapsulate JFreeChart instances along with some
+additional TikZ-specific attributes.
+The method
+\externalmethod{umontreal.iro.lecuyer.charts}{XYChart}{view}{}
+displays charts on screen while the method
+\texttt{toLatex} formats and returns a
+\externalclass{java.lang}{String}{} which contains the TikZ source
+code that can be written to a \LaTeX{} file.
+
+Several chart styles are available and each one is represented by a
+ subclass of \externalclass{umontreal.iro.lecuyer.charts}{XYChart} or of
+\externalclass{umontreal.iro.lecuyer.charts}{CategoryChart}.
+The \externalclass{umontreal.iro.lecuyer.charts}{XYLineChart} uses
+\externalclass{umontreal.iro.lecuyer.charts}{XYListSeriesCollection}
+to plot curves and lines, the
+\externalclass{umontreal.iro.lecuyer.charts}{HistogramSeries\-Collection}
+class is used by
+\externalclass{umontreal.iro.lecuyer.charts}{HistogramChart} to plot
+histograms, and the
+\externalclass{umontreal.iro.lecuyer.charts}{EmpiricalSeries\-Collection} is used by
+\externalclass{umontreal.iro.lecuyer.charts}{EmpiricalChart} to plot empirical style charts. It is possible to draw a scatter plot or a box plot using
+\externalclass{umontreal.iro.lecuyer.charts}{ScatterChart} or
+\externalclass{umontreal.iro.lecuyer.charts}{BoxChart} respectively.
+These concrete subclasses have similar APIs, but they are specialized for
+different kinds of charts.
+
+These charts can be customized using
+\texttt{*SeriesCollection} subclasses and
+\externalclass{umontreal.iro.lecuyer.charts}{Axis}.
+First, one can use methods in
+the \externalclass{umontreal.iro.lecuyer.charts}{XYChart} class for
+setting the range values and a background grid.
+One can also use the method
+\texttt{getSeriesCollection()} for subclasses of charts to obtain
+the dataset of the chart, which is represented by a
+\texttt{*SeriesCollection} object.
+This dataset can be customized in the following ways by calling
+methods
+on the series collection:
+selecting color, changing
+plot style (straight lines, curves, marks only, \ldots), putting marks
+on points, setting a label and selecting the dash pattern (solid,
+dotted, dashed, \ldots). The available properties depend on the type
+of chart.
+Moreover, objects representing the axes can be retrieved with
+\externalmethod{umontreal.iro.lecuyer.charts}{XYChart}{getXAxis}{}
+for the
+$x$-axis, and
+\externalmethod{umontreal.iro.lecuyer.charts}{XYChart}{getYAxis}{}
+for the $y$-axis.
+By using methods in \externalclass{umontreal.iro.lecuyer.charts}{Axis},
+many customizations are possible: setting a label to the axis,
+setting ticks labels and values (auto ticks, periodical ticks or
+manually defined ticks) and changing the twin axis position on the
+current axis to select where axes must appear.
+These settings are independent for each axis.
+
+Each chart object from SSJ encapsulates a JFreeChart object: a
+\externalclass{umontreal.iro.lecuyer.charts}{XYChart} instance contains a
+\texttt{JFreeChart} instance from JFreeChart API, a
+\texttt{*SeriesCollection} contains a
+\texttt{XYDataset} and a \texttt{XYItem\-Renderer}, and finally an
+\externalclass{umontreal.iro.lecuyer.charts}{Axis} contains a
+\texttt{NumberAxis}. So any parameter proposed by
+JFreeChart is reachable through
+getter methods. However,
+changing the JFreeChart parameters directly may have no impact
+on the produced TikZ source code.
+
+The two special classes
+\externalclass{umontreal.iro.lecuyer.charts}{ContinuousDistChart} and
+\externalclass{umontreal.iro.lecuyer.charts}{DiscreteDistIntChart}
+can be used to plot probability densities, mass functions,
+and cumulative probabilities for continuous or discrete distributions,
+which are implemented in package
+\externalclass{umontreal.iro.lecuyer}{probdist} of SSJ.
+
+
+The package \texttt{charts} provides additional tools for formatting
+ data plot, and creating charts with multiple datasets. The
+\externalclass{umontreal.iro.lecuyer.charts}{PlotFormat} class
+offers basic tools to import and export data from files.
+Supported file formats are GNUPlot and standard CSV, which is widely
+used and accepted by most mathematical softwares such as
+MATLAB and Mathematica. Customizing file input and output format is also possible.
+Finally \externalclass{umontreal.iro.lecuyer.charts}{MultipleDatasetChart}
+ provides tools to plot with different styles on the same chart.
+
+\latex{\section*{Examples}\addcontentsline{toc}{subsection}{Examples}}
+\latex{\label {sec:examples}}
+
+\begin{htmlonly}
+For a series of examples, see the \textbf{pdf} documentation.
+\end{htmlonly}
+
+\begin{latexonly}
+
+The following examples demonstrate how to build charts with this package.
+
+\lstinputlisting[label=lst:normalchart,%
+caption={A simple example of chart creation},%
+lineskip=-2pt,%
+emph={getPoints,main}
+]{exam/NormalChart.java}
+
+\begin{figure}
+\input{exam/NormalChart.tex}
+\caption{The density of the standard normal.\label{fig:normalchart-res}}
+\end{figure}
+
+
+The first program, displayed in Listing~\ref{lst:normalchart},
+shows the simplest way to export charts from data tables.
+First, a curve is defined with regularly spaced $x$-coordinates
+in method \texttt{getPoints}; it represents the curve
+ $y = e^{-x^2/2}/\sqrt{2\pi}$, the density of the standard normal probability
+ distribution. Arrays \texttt{points[0]} contain
+the $x$-coordinates and \texttt{points[1]} the $y$-coordinates
+of the points.
+Figure~\ref{fig:normalchart-res} presents the resulting chart if the produced
+TikZ code is added to a \LaTeX{} document using the \texttt{tikz}
+package, and compiled using \LaTeX{} or Pdf\LaTeX.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+A simpler way to plot a
+ probability density or a distribution function is to use the class
+\externalclass{umontreal.iro.lecuyer.charts}{ContinuousDistChart}, as shown
+in Listing~\ref{lst:normaldist} where however, the normal density will be plotted
+directly on the screen.
+
+\lstinputlisting[label=lst:normaldist,%
+caption={The normal density},%
+lineskip=-1pt,%
+emph={main}
+]{exam/ContDistPlot.java}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The next example, displayed in Listing~\ref{lst:poissonchart}, plots the
+probability mass function for a Poisson distribution with $\lambda= 50$
+by simply creating an instance of
+\externalclass{umontreal.iro.lecuyer.charts}{DiscreteDistIntChart}.
+Figure~\ref{fig:poissonchart-res} presents the resulting chart obtained
+from Pdf\LaTeX.
+
+\begin{figure}
+\input{exam/DistIntTest.tex}
+\caption{The probabilities of the Poisson distribution with $\lambda= 50$.\label{fig:poissonchart-res}}
+\end{figure}
+
+
+\lstinputlisting[label=lst:poissonchart,%
+caption={Probabilities of the Poisson distribution},%
+lineskip=-1pt,%
+emph={main}
+]{exam/DistIntTest.java}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The next program, displayed in Listing~\ref{lst:simplechart},
+shows how to export several charts from data tables.
+First, three curves are defined with regularly spaced $x$-coordinates
+in methods \texttt{getPoints*}; they represent the curves $y = \sqrt x$,
+$y = \cos(x)$ and $y = x+2$, respectively. Array \texttt{points[0]} contains
+the $x$-coordinates and \texttt{points[1]} the $y$-coordinates
+of the points.
+Figure~\ref{fig:simplechart-res} presents the resulting chart if the produced
+TikZ code is added to a \LaTeX{} document using the \texttt{tikz}
+package, and compiled using \LaTeX{} or Pdf\LaTeX.
+
+
+\lstinputlisting[label=lst:simplechart,%
+caption={Three curves on the same chart},%
+lineskip=-1pt,%
+emph={getPoints1,getPoints2,getPoints3,main}
+]{exam/ChartTest1.java}
+
+
+\begin{figure}
+\input{exam/ChartTest1.tex}
+\caption{Results for three curves on the same chart.\label{fig:simplechart-res}}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+The next example, given in Listing~\ref{lst:customchart},
+ shows how to customize a chart. First, three curves are defined as
+in example~\ref{lst:simplechart} above and the chart is created. Then the
+axes are customized by adding labels at chosen values on the $x$-axis.
+On the $y$-axis, successive labels are set regularly at points 1 unit apart.
+Then the data plot itself is customized. A new color is created in the RGB
+model for the first curve which also receives a label name and a dash plot style.
+Similarly, the other two curves receive their label name, plot style
+ and color. Note that the third curve is drawn in the ORANGE color predefined
+ in the AWT package of the standard Java toolkit.
+Finally, the charts are exported to a file in \LaTeX{}
+format. If the file is compiled with \LaTeX{} or Pdf\LaTeX, the
+resulting chart will appear as displayed in Figure~\ref{fig:customchart-res}.
+
+
+\lstinputlisting[label=lst:customchart,%
+caption={Code for creating and customizing a chart},%
+lineskip=-1pt,%
+emph={getPoints1,getPoints2,getPoints3,main}
+]{exam/ChartTest2.java}
+
+
+\begin{figure}
+\input{exam/ChartTest2.tex}
+\caption{A customized chart.\label{fig:customchart-res}}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+The next example, given in Listing~\ref{lst:empiricalchart}, shows how to
+plot and customize empirical distributions. First, two empirical distributions
+are defined from a sample of points obtained from a uniform
+generator and a Beta$(3, 1)$ generator, both on the interval
+$[0, 1]$. Then an empirical chart is
+created to plot these two distributions. The first distribution is plotted
+in MAGENTA color with filled square marks from TikZ. The second distribution
+uses default color and plot marks. A background grid is also added with cells
+of size $0.1\times 0.1$. Finally, the charts are exported to a file in \LaTeX{} format. Figure~\ref{fig:empiricalchart-res} shows the
+resulting chart.
+
+
+\lstinputlisting[label=lst:empiricalchart,%
+caption={Creating and customizing empirical distribution charts},%
+lineskip=-1pt,%
+emph={getPoints1,getPoints2,main}
+]{exam/EmpiricalChartTest.java}
+
+
+\begin{figure}
+\input{exam/EmpiricalChartTest.tex}
+\caption{A customized empirical distribution chart\label {fig:empiricalchart-res}}
+\end{figure}
+
+% \clearpage
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The next example, given in Listing~\ref{lst:histo1}, shows how to plot
+a simple histogram. First, data is generated from a standard normal.
+ Then the plot is customized: 80 bins are selected, and the range of the
+ plot is set manually with \texttt{bounds}; the interval is $[-4, 4]$ for
+ the $x$-coordinates, and $[0, 5000]$ for the $y$-coordinates.
+ Finally, the histogram chart is viewed on the screen, then exported to
+ file \texttt{HistogramTest1.tex} in
+\LaTeX{} format. Figure~\ref{fig:histo1-res} displays the resulting chart.
+
+
+\lstinputlisting[label=lst:histo1,%
+caption={Source code creating a simple histogram},%
+lineskip=-1pt,%
+emph={getData,main}
+]{exam/HistogramTest1.java}
+
+
+\begin{figure}
+\input{exam/HistogramTest1.tex}
+\caption{A simple histogram for the standard normal density\label{fig:histo1-res}}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The next example, given in Listing~\ref{lst:histochart}, shows how to plot
+and customize histograms. First, the two histogram charts \texttt{data1} and
+\texttt{data2} are created. Then the data plots are customized: two colors
+are selected in the sRGB model from the \texttt{java.awt.Color} package.
+40 bins are selected on the interval $[-6, 6]$ for the first histogram.
+The number of bins for the second histogram is set automatically.
+The range of the plot is set manually with \texttt{bounds}.
+ Finally, the two histograms
+charts are exported to file \texttt{HistogramChartTest.tex} in
+\LaTeX{} format. Figure~\ref{fig:histochart-res} displays the resulting chart.
+
+
+\lstinputlisting[label=lst:histochart,%
+caption={Source code creating and customizing histograms.},%
+lineskip=-1pt,%
+emph={getPoints1,getPoints2,main}
+]{exam/HistogramChartTest.java}
+
+
+\begin{figure}
+\input{exam/HistogramChartTest.tex}
+\caption{A customized histogram chart\label{fig:histochart-res}}
+\end{figure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+The next example, given in Listing~\ref{lst:boxplot}, shows how to create
+a box-and-whisker plot. First, two series of 1000 points
+are obtained from a lognormal distribution and from a Poisson distribution
+with $\lambda=5$. These are passed to a \texttt{BoxChart} object, which
+creates the boxplot, which can be viewed on screen by executing
+the program. We find that the boxplot for the Poisson data (the box on the right
+of the chart) has median = 5
+(the line inside the box), a mean = 5.009 (the center of the black circle)
+the first and the third quartiles at 3 and 6, while the lower and upper
+whiskers are at 0 and 10. Finally, there are outliers at 11 and 12 (the hollow
+circles) and extreme outliers (the triangle) at 13, outside the chart.
+We see that the Poisson data is skewed towards higher values.
+A similar description applies to the lognormal data (the box on the left
+of the chart), which is strongly skewed towards smaller values.
+
+\lstinputlisting[label=lst:boxplot,%
+caption={Source code creating a box-and-whisker plot.},%
+lineskip=-1pt,%
+emph={main}
+]{exam/BoxTest.java}
+
+\vspace{1cm}
+
+\begin{figure}
+\begin{center}
+\includegraphics[scale=0.5]{exam/BoxTest.png}
+\caption{A box-plot\label{fig:boxchart-res}}
+\end{center}
+\end{figure}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\end{latexonly}
diff --git a/source/umontreal/iro/lecuyer/charts/title.tex b/source/umontreal/iro/lecuyer/charts/title.tex
new file mode 100644
index 0000000..dce83d2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/charts/title.tex
@@ -0,0 +1,42 @@
+\begin{titlepage}
+
+\iffalse %%%%%%
+
+Other potential names:
+
+ Simja, Simjava, Javasim, Javarand, Javaran, Javas
+ flexim, simflex, SIM-J
+
+ {\sc Pierre L'Ecuyer} %% and Lakhdar Meliani
+\footnote {\normalsize
+ Most of the implementation of SSJ was done by Lakhdar Meliani
+ for his master's thesis.} \\
+\medskip
+ D\'epartement d'Informatique et de Recherche Op\'erationnelle \\
+ Universit\'e de Montr\'eal \
+\vfill\vfill
+\end {center}
+
+\fi %%%%%%%%%%%%%%
+
+\title{charts}{Charts generation}
+
+\begin {quote}
+This package contains utility classes to produce charts
+used in the Java software developed in the {\em simulation laboratory\/}
+of the DIRO, at the Universit\'e de Montr\'eal.
+\end {quote}
+
+\begin {comment}
+SSJ (an acronym for {\em Stochastic Simulation in Java\/}) is
+a library of classes, implemented in the Java programming language,
+offering general-purpose facilities for simulation programming.
+It supports the event view, process view, continuous simulation,
+and arbitrary mixtures of these.
+% It provides random number generators, clock and event list management,
+% general list management facilities, etc.
+\end {comment}
+
+\vfill\vfill
+
+\end{titlepage}
diff --git a/source/umontreal/iro/lecuyer/examples/Asian.java b/source/umontreal/iro/lecuyer/examples/Asian.java
new file mode 100644
index 0000000..a23dcea
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Asian.java
@@ -0,0 +1,74 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.*;
+
+public class Asian {
+ double strike; // Strike price.
+ int s; // Number of observation times.
+ double discount; // Discount factor exp(-r * zeta[t]).
+ double[] muDelta; // Differences * (r - sigma^2/2).
+ double[] sigmaSqrtDelta; // Square roots of differences * sigma.
+ double[] logS; // Log of the GBM process: logS[t] = log (S[t]).
+
+ // Array zeta[0..s] must contain zeta[0]=0.0, plus the s observation times.
+ public Asian (double r, double sigma, double strike,
+ double s0, int s, double[] zeta) {
+ this.strike = strike;
+ this.s = s;
+ discount = Math.exp (-r * zeta[s]);
+ double mu = r - 0.5 * sigma * sigma;
+ muDelta = new double[s];
+ sigmaSqrtDelta = new double[s];
+ logS = new double[s+1];
+ double delta;
+ for (int j = 0; j < s; j++) {
+ delta = zeta[j+1] - zeta[j];
+ muDelta[j] = mu * delta;
+ sigmaSqrtDelta[j] = sigma * Math.sqrt (delta);
+ }
+ logS[0] = Math.log (s0);
+ }
+
+ // Generates the process S.
+ public void generatePath (RandomStream stream) {
+ for (int j = 0; j < s; j++)
+ logS[j+1] = logS[j] + muDelta[j] + sigmaSqrtDelta[j]
+ * NormalDist.inverseF01 (stream.nextDouble());
+ }
+
+ // Computes and returns the discounted option payoff.
+ public double getPayoff () {
+ double average = 0.0; // Average of the GBM process.
+ for (int j = 1; j <= s; j++) average += Math.exp (logS[j]);
+ average /= s;
+ if (average > strike) return discount * (average - strike);
+ else return 0.0;
+ }
+
+ // Performs n indep. runs using stream and collects statistics in statValue.
+ public void simulateRuns (int n, RandomStream stream, Tally statValue) {
+ statValue.init();
+ for (int i=0; i<n; i++) {
+ generatePath (stream);
+ statValue.add (getPayoff ());
+ stream.resetNextSubstream();
+ }
+ }
+
+ public static void main (String[] args) {
+ int s = 12;
+ double[] zeta = new double[s+1]; zeta[0] = 0.0;
+ for (int j=1; j<=s; j++)
+ zeta[j] = (double)j / (double)s;
+ Asian process = new Asian (0.05, 0.5, 100.0, 100.0, s, zeta);
+ Tally statValue = new Tally ("Stats on value of Asian option");
+
+ Chrono timer = new Chrono();
+ int n = 100000;
+ process.simulateRuns (n, new MRG32k3a(), statValue);
+ statValue.setConfidenceIntervalStudent();
+ System.out.println (statValue.report (0.95, 3));
+ System.out.println ("Total CPU time: " + timer.format() + "\n");
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Asian.res b/source/umontreal/iro/lecuyer/examples/Asian.res
new file mode 100644
index 0000000..b56497e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Asian.res
@@ -0,0 +1,7 @@
+REPORT on Tally stat. collector ==> Stats on value of Asian option
+ num. obs. min max average standard dev.
+ 100000 0.000 386.378 13.119 22.696
+ 95.0% confidence interval for mean (student): ( 12.978, 13.260 )
+
+Total CPU time: 0:0:0.77
+
diff --git a/source/umontreal/iro/lecuyer/examples/AsianQMC.java b/source/umontreal/iro/lecuyer/examples/AsianQMC.java
new file mode 100644
index 0000000..47097e2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/AsianQMC.java
@@ -0,0 +1,66 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.hups.*;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.Chrono;
+
+public class AsianQMC extends Asian {
+
+ public AsianQMC (double r, double sigma, double strike,
+ double s0, int s, double[] zeta) {
+ super (r, sigma, strike, s0, s, zeta);
+ }
+
+ // Makes m independent randomizations of the digital net p using stream
+ // noise. For each of them, performs one simulation run for each point
+ // of p, and adds the average over these points to the collector statQMC.
+ public void simulateQMC (int m, DigitalNet p,
+ RandomStream noise, Tally statQMC) {
+ Tally statValue = new Tally ("stat on value of Asian option");
+ PointSetIterator stream = p.iterator ();
+ for (int j=0; j<m; j++) {
+ p.leftMatrixScramble (noise);
+ p.addRandomShift (0, p.getDimension(), noise);
+ stream.resetStartStream();
+ simulateRuns (p.getNumPoints(), stream, statValue);
+ statQMC.add (statValue.average());
+ }
+ }
+
+
+ public static void main (String[] args) {
+ int s = 12;
+ double[] zeta = new double[s+1];
+ for (int j=0; j<=s; j++)
+ zeta[j] = (double)j / (double)s;
+ AsianQMC process = new AsianQMC (0.05, 0.5, 100.0, 100.0, s, zeta);
+ Tally statValue = new Tally ("value of Asian option");
+ Tally statQMC = new Tally ("QMC averages for Asian option");
+
+ Chrono timer = new Chrono();
+ int n = 100000;
+ System.out.println ("Ordinary MC:\n");
+ process.simulateRuns (n, new MRG32k3a(), statValue);
+ statValue.setConfidenceIntervalStudent();
+ System.out.println (statValue.report (0.95, 3));
+ System.out.println ("Total CPU time: " + timer.format());
+ double varMC = statValue.variance();
+ double cpuMC = timer.getSeconds() / n; // CPU seconds per run.
+ System.out.println ("------------------------\n");
+
+ timer.init();
+ DigitalNet p = new SobolSequence (16, 31, s); // 2^{16} points.
+ n = p.getNumPoints();
+ int m = 20; // Number of QMC randomizations.
+ process.simulateQMC (m, p, new MRG32k3a(), statQMC);
+ System.out.println ("QMC with Sobol point set with " + n +
+ " points and affine matrix scramble:\n");
+ statQMC.setConfidenceIntervalStudent();
+ System.out.println (statQMC.report (0.95, 3));
+ System.out.println ("Total CPU time: " + timer.format() + "\n");
+ double varQMC = p.getNumPoints() * statQMC.variance();
+ double cpuQMC = timer.getSeconds() / (m * n);
+ System.out.printf ("Variance ratio: %9.4g%n", varMC/varQMC);
+ System.out.printf ("Efficiency ratio: %9.4g%n",
+ (varMC * cpuMC) / (varQMC * cpuQMC));
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/AsianQMC.res b/source/umontreal/iro/lecuyer/examples/AsianQMC.res
new file mode 100644
index 0000000..e365005
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/AsianQMC.res
@@ -0,0 +1,21 @@
+Ordinary MC:
+
+REPORT on Tally stat. collector ==> value of Asian option
+ num. obs. min max average standard dev.
+ 100000 0.000 386.378 13.119 22.696
+ 95.0% confidence interval for mean (student): ( 12.978, 13.260 )
+
+Total CPU time: 0:0:0.71
+------------------------
+
+QMC with Sobol point set with 65536 points and affine matrix scramble:
+
+REPORT on Tally stat. collector ==> QMC averages for Asian option
+ num. obs. min max average standard dev.
+ 20 13.108 13.133 13.120 5.6E-3
+ 95.0% confidence interval for mean (student): ( 13.118, 13.123 )
+
+Total CPU time: 0:0:3.43
+
+Variance ratio: 250.2
+Efficiency ratio: 678.8
diff --git a/source/umontreal/iro/lecuyer/examples/AsianQMCk.java b/source/umontreal/iro/lecuyer/examples/AsianQMCk.java
new file mode 100644
index 0000000..62d59d9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/AsianQMCk.java
@@ -0,0 +1,67 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.hups.*;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.Chrono;
+
+public class AsianQMCk extends AsianQMC {
+
+ public AsianQMCk (double r, double sigma, double strike,
+ double s0, int s, double[] zeta) {
+ super (r, sigma, strike, s0, s, zeta);
+ }
+
+ // Makes m independent randomizations of the digital net p using stream
+ // noise. For each of them, performs one simulation run for each point
+ // of p, and adds the average over these points to the collector statQMC.
+ public void simulateQMC (int m, PointSet p,
+ RandomStream noise, Tally statQMC) {
+ Tally statValue = new Tally ("stat on value of Asian option");
+ PointSetIterator stream = p.iterator ();
+ for (int j=0; j<m; j++) {
+ p.randomize (noise);
+ stream.resetStartStream();
+ simulateRuns (p.getNumPoints(), stream, statValue);
+ statQMC.add (statValue.average());
+ }
+ }
+
+
+ public static void main (String[] args) {
+ int s = 12;
+ double[] zeta = new double[s+1];
+ for (int j=0; j<=s; j++)
+ zeta[j] = (double)j / (double)s;
+ AsianQMCk process = new AsianQMCk (0.05, 0.5, 100.0, 100.0, s, zeta);
+ Tally statValue = new Tally ("value of Asian option");
+ Tally statQMC = new Tally ("QMC averages for Asian option");
+
+ Chrono timer = new Chrono();
+ int n = 100000;
+ System.out.println ("Ordinary MC:\n");
+ process.simulateRuns (n, new MRG32k3a(), statValue);
+ statValue.setConfidenceIntervalStudent();
+ System.out.println (statValue.report (0.95, 3));
+ System.out.println ("Total CPU time: " + timer.format());
+ double varMC = statValue.variance();
+ double cpuMC = timer.getSeconds() / n; // CPU seconds per run.
+ System.out.println ("------------------------\n");
+
+ timer.init();
+ KorobovLattice p = new KorobovLattice (65521, 944, s); // approx. 2^{16} points.
+ BakerTransformedPointSet pb = new BakerTransformedPointSet (p);
+
+ n = p.getNumPoints();
+ int m = 20; // Number of QMC randomizations.
+ process.simulateQMC (m, pb, new MRG32k3a(), statQMC);
+ System.out.println ("QMC with Korobov point set with " + n +
+ " points and random shift + baker:\n");
+ statQMC.setConfidenceIntervalStudent();
+ System.out.println (statQMC.report (0.95, 3));
+ System.out.println ("Total CPU time: " + timer.format() + "\n");
+ double varQMC = p.getNumPoints() * statQMC.variance();
+ double cpuQMC = timer.getSeconds() / (m * n);
+ System.out.printf ("Variance ratio: %9.4g%n", varMC/varQMC);
+ System.out.printf ("Efficiency ratio: %9.4g%n",
+ (varMC * cpuMC) / (varQMC * cpuQMC));
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Bank.res b/source/umontreal/iro/lecuyer/examples/Bank.res
new file mode 100644
index 0000000..80736a8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Bank.res
@@ -0,0 +1,8 @@
+REPORT on Tally stat. collector ==> Nb. served per day
+ num. obs. min max average standard dev.
+ 100 152.000 285.000 240.590 19.210
+
+REPORT on Tally stat. collector ==> Average wait per day (hours)
+ num. obs. min max average standard dev.
+ 100 0.816 35.613 4.793 5.186
+
diff --git a/source/umontreal/iro/lecuyer/examples/Bank.tex b/source/umontreal/iro/lecuyer/examples/Bank.tex
new file mode 100644
index 0000000..ffcc7d7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Bank.tex
@@ -0,0 +1,167 @@
+\section {A simplified bank}
+\label {sec:bank}
+
+This is Example~1.4.1 of \cite{sBRA87a}, page~14.
+% Bratley, Fox, and Schrage (1987).
+A bank has a random number of tellers every morning.
+On any given day, the bank has $t$ tellers with probability $q_t$,
+where $q_3 = 0.80$, $q_2 = 0.15$, and $q_1 = 0.05$.
+All the tellers are assumed to be identical from the modeling viewpoint.
+
+
+%%%%%%%%%%%%%
+\setbox0=\vbox{\hsize=6.0in
+%% {Arrival rate of customers to the bank.}
+\beginpicture
+\setcoordinatesystem units <1.8cm,2cm>
+%% 72.27pt = 1in
+\setplotarea x from 0 to 6.5, y from 0 to 1
+\axis left
+ label {\lines {arrival \ \cr rate }}
+ ticks length <2pt> withvalues 0.5 1 / at 0.5 1 / /
+\axis bottom
+ label {\hbox to 5.4in {\hfill time}}
+ ticks length <2pt> withvalues 9:00 9:45 11:00 14:00 15:00 /
+ at 0.0 0.75 2.0 5.0 6.0 / /
+\shaderectangleson
+\putrectangle corners at 0.75 0.0 and 2.0 0.5
+\putrectangle corners at 2.0 0.0 and 5.0 1.0
+\putrectangle corners at 5.0 0.0 and 6.0 0.5
+\endpicture
+}
+
+\begin{figure}[htb]
+\box0
+\caption {Arrival rate of customers to the bank.}
+\label {fig:blambda}
+\end{figure}
+
+\bigskip
+\lstinputlisting[label=lst:BankEv,caption={Event-oriented simulation of the bank model}]{BankEv.java}
+%\lstlabel{lst:BankEv}
+
+\clearpage
+\lstinputlisting[label=st:BankProc,caption={Process-oriented simulation of the bank model}]{BankProc.java}
+%\lstlabel{lst:BankProc}
+
+The bank opens at 10:00 and closes at 15:00 (i.e., {\sc 3 p.m.}).
+The customers arrive randomly according to a Poisson process
+with piecewise constant rate $\lambda(t)$, $t\ge 0$.
+The arrival rate $\lambda(t)$ (see Fig.{}~\ref{fig:blambda})
+is 0.5 customer per minute from
+9:45 until 11:00 and from 14:00 until 15:00, and
+one customer per minute from 11:00 until 14:00.
+The customers who arrive before 9:45 join a FIFO queue
+and wait for the bank to open.
+At 15:00, the door is closed, but all the customers already in will be served.
+Service starts at 10:00.
+
+Customers form a FIFO queue for the tellers, with balking.
+An arriving customer will balk (walk out) with probability $p_k$ if there
+are $k$ customers ahead of him in the queue (not counting the people
+receiving service), where
+ $$ p_k = \cases { 0 & if $k\le 5$;\cr
+ (n-5)/5 & if $5 < k < 10$;\cr
+ 1 & if $k\ge 10$.\cr }$$
+The customer service times are independent Erlang random
+variables: Each service time is the sum of
+two independent exponential random variables with mean one.
+
+We want to estimate the expected number of customers served in a
+day, and the expected average wait for the customers
+served on a day.
+% We could also be interested in the effect of changing the number of tellers,
+% changing their speed, and so on.
+
+Listings~\ref{lst:BankEv} and~\ref{lst:BankProc} give simulation
+programs for this bank model.
+The first program uses only events and the second one
+views the customers as processes.
+Both programs have events at the fixed times 9:45, 10:00, etc.;
+these events are implicit in the process-oriented implementation.
+At 9:45, the counters are initialized and the arrival process
+is started. The time until the first arrival,
+or the time between one arrival and the next one, is (tentatively)
+an exponential with a mean of 2 minutes.
+However, as soon as an arrival turns out to be past 11:00,
+its time must be readjusted to take into account the increase of the
+arrival rate at 11:00.
+The event 11:00 takes care of this readjustment,
+and the event at 14:00 makes a similar readjustment
+when the arrival rate decreases.
+We give the specific name \texttt{nextArriv} to the next planned
+arrival event in the event-oriented case, and \texttt{nextCust} to the
+next customer scheduled to arrive in the process-oriented case,
+in order to be able to reschedule
+that particular event (or process) to a different time.
+Note that in the event-oriented case, a {\em single\/} arrival event is
+created at the beginning and this same event is scheduled over and
+over again. This can be done because there is never more than one
+arrival event in the event list.
+We cannot do this for the customer processes in the
+process-oriented case, however, because several processes can be alive
+simultaneously.
+
+At the bank opening at 10:00, an event generates the number
+of tellers and starts the service for the corresponding customers.
+The event at 15:00 cancels the next arrival.
+
+Upon arrival, a customer checks if a teller is free.
+If so, one teller becomes busy and the customer generates its
+service time and schedules his departure, otherwise the
+customer either balks or joins the queue.
+The balking decision is computed by the method \texttt{balk},
+using the random number stream \texttt{streamBalk}.
+The arrival event also generates the next scheduled arrival.
+Upon departure, the customer frees the teller, and the first
+customer in the queue, if any, can start its service.
+
+The constructor (\texttt{BankEv} or \texttt{BankProc}) simulates the bank
+for 100 days and prints a statistical report.
+It chooses as \texttt{genServ} the \texttt{ErlangConvolutionGen} generator so that
+the Erlang variates are generated by adding two exponentials instead
+of using inversion.
+If $X_i$ is the number of customers served on day $i$ and
+$Q_i$ the total waiting time on day $i$, the program estimates
+$E[X_i]$ and $E[Q_i]$ by their sample averages $\bar X_n$ and
+$\bar Q_n$ with $n=100$.
+For each simulation run (each day), \texttt{simulOneDay} initializes
+the clock, event list, and statistical probe for the waiting times,
+schedules the deterministic events, and runs the simulation.
+After 15:00, no more arrival occurs and the event list becomes
+empty when the last customer departs.
+At that point, the program returns to right after the \texttt{Sim.start()}
+statement and updates the statistical counters for the number of
+customers served during the day and their total waiting time.
+
+The process-oriented version of the program is shorter,
+because certain aspects (such as the details of an arrival
+or departure event) are taken care of automatically by the
+process/resource construct, and the events 9:45, 10:00, etc.,
+are replaced by a single process.
+At 10 o'clock, the \texttt{setCapacity} statement that fixes the
+number of tellers also takes
+care of starting service for the appropriate number of customers.
+
+These two programs give the same results, shown in
+Figure~\ref{fig:bank-res}.
+However, the process-oriented program take approximately 4 to 5 times
+longer to run than its event-oriented counterpart.
+
+\setbox5=\vbox {\hsize = 6.0in
+\begin{verbatim}
+REPORT on Tally stat. collector ==> Nb. served per day
+ min max average standard dev. nb. obs.
+ 152 285 240.59 19.21 100
+
+REPORT on Tally stat. collector ==> Average wait per day (hours)
+ min max average standard dev. nb. obs.
+ 0.816 35.613 4.793 5.186 100
+\end{verbatim}
+}
+
+\begin{figure}[h]
+\centerline{\boxit{\box5}}
+\caption {Results of the \texttt{BankEv} and \texttt{BankProc} programs.}
+\label {fig:bank-res}
+\end{figure}
diff --git a/source/umontreal/iro/lecuyer/examples/BankEv.java b/source/umontreal/iro/lecuyer/examples/BankEv.java
new file mode 100644
index 0000000..9df8b47
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/BankEv.java
@@ -0,0 +1,114 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+
+public class BankEv {
+ static final double minute = 1.0 / 60.0;
+ int nbTellers; // Number of tellers.
+ int nbBusy; // Number of tellers busy.
+ int nbWait; // Queue length.
+ int nbServed; // Number of customers served so far
+ double meanDelay; // Mean time between arrivals.
+ Event nextArriv = new Arrival(); // The next arrival.
+ RandomStream streamArr = new MRG32k3a(); // Customer's arrivals
+ ErlangGen genServ = new ErlangConvolutionGen (new MRG32k3a(), 2, 1.0/minute);
+ RandomStream streamTeller = new MRG32k3a(); // Number of tellers
+ RandomStream streamBalk = new MRG32k3a(); // Balking decisions
+ Tally statServed = new Tally ("Nb. served per day");
+ Tally avWait = new Tally ("Average wait per day (hours)");
+ Accumulate wait = new Accumulate ("cumulated wait for this day");
+
+ Event e9h45 = new Event() {
+ public void actions() {
+ meanDelay = 2.0*minute;
+ nextArriv.schedule
+ (ExponentialGen.nextDouble (streamArr, 1.0/meanDelay));
+ }
+ };
+
+ Event e10h = new Event() {
+ public void actions() {
+ double u = streamTeller.nextDouble();
+ if (u >= 0.2) nbTellers = 3;
+ else if (u < 0.05) nbTellers = 1;
+ else nbTellers = 2;
+ while (nbWait > 0 && nbBusy < nbTellers) {
+ nbBusy++; nbWait--;
+ new Departure().schedule (genServ.nextDouble());
+ }
+ wait.update (nbWait);
+ }
+ };
+
+ Event e11h = new Event() {
+ public void actions() {
+ nextArriv.reschedule ((nextArriv.time() - Sim.time())/2.0);
+ meanDelay = minute;
+ }
+ };
+
+ Event e14h = new Event() {
+ public void actions() {
+ nextArriv.reschedule ((nextArriv.time() - Sim.time())*2.0);
+ meanDelay = 2.0*minute;
+ }
+ };
+
+ Event e15h = new Event() {
+ public void actions() { nextArriv.cancel(); }
+ };
+
+ private boolean balk() {
+ return (nbWait > 9) ||
+ (nbWait > 5 && (5.0*streamBalk.nextDouble() < nbWait-5));
+ }
+
+ class Arrival extends Event {
+ public void actions() {
+ nextArriv.schedule
+ (ExponentialGen.nextDouble (streamArr, 1.0/meanDelay));
+ if (nbBusy < nbTellers) {
+ nbBusy++;
+ new Departure().schedule (genServ.nextDouble());
+ } else if (!balk())
+ { nbWait++; wait.update (nbWait); }
+ }
+ }
+
+ class Departure extends Event {
+ public void actions() {
+ nbServed++;
+ if (nbWait > 0) {
+ new Departure().schedule (genServ.nextDouble());
+ nbWait--; wait.update (nbWait);
+ }
+ else nbBusy--;
+ }
+ };
+
+ public void simulOneDay() {
+ Sim.init(); wait.init();
+ nbTellers = 0; nbBusy = 0;
+ nbWait = 0; nbServed = 0;
+ e9h45.schedule (9.75);
+ e10h.schedule (10.0);
+ e11h.schedule (11.0);
+ e14h.schedule (14.0);
+ e15h.schedule (15.0);
+ Sim.start();
+ statServed.add (nbServed);
+ wait.update();
+ avWait.add (wait.sum());
+ }
+
+ public void simulateDays (int numDays) {
+ for (int i=1; i<=numDays; i++) simulOneDay();
+ System.out.println (statServed.report());
+ System.out.println (avWait.report());
+ }
+
+ public static void main (String[] args) {
+ new BankEv().simulateDays (100);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/BankProc.java b/source/umontreal/iro/lecuyer/examples/BankProc.java
new file mode 100644
index 0000000..34a51d8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/BankProc.java
@@ -0,0 +1,88 @@
+import umontreal.iro.lecuyer.simprocs.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+
+public class BankProc {
+
+ ProcessSimulator sim = new ThreadProcessSimulator();
+ double minute = 1.0 / 60.0;
+ int nbServed; // Number of customers served so far
+ double meanDelay; // Mean time between arrivals
+ SimProcess nextCust; // Next customer to arrive
+ Resource tellers = new Resource (sim, 0, "The tellers");
+ RandomStream streamArr = new MRG32k3a(); // Customer's arrivals
+ ErlangGen genServ = new ErlangConvolutionGen
+ (new MRG32k3a(), 2, 1.0/minute);
+ RandomStream streamTeller = new MRG32k3a(); // Number of tellers
+ RandomStream streamBalk = new MRG32k3a(); // Balking decisions
+ Tally statServed = new Tally ("Nb. served per day");
+ Tally avWait = new Tally ("Average wait per day (hours)");
+
+ class OneDay extends SimProcess {
+ public OneDay(ProcessSimulator sim) { super(sim); }
+ public void actions() {
+ int nbTellers; // Number of tellers today.
+ nbServed = 0;
+ tellers.setCapacity (0);
+ tellers.waitList().initStat();
+ meanDelay = 2.0 * minute;
+ // It is 9:45, start arrival process.
+ (nextCust = new Customer(sim)).schedule
+ (ExponentialGen.nextDouble (streamArr, 1.0/meanDelay));
+ delay (15.0 * minute);
+ // Bank opens at 10:00, generate number of tellers.
+ double u = streamTeller.nextDouble();
+ if (u >= 0.2) nbTellers = 3;
+ else if (u < 0.05) nbTellers = 1;
+ else nbTellers = 2;
+ tellers.setCapacity (nbTellers);
+ delay (1.0); // It is 11:00, double arrival rate.
+ nextCust.reschedule (nextCust.getDelay() / 2.0);
+ meanDelay = minute;
+ delay (3.0); // It is 14:00, halve arrival rate.
+ nextCust.reschedule (nextCust.getDelay() * 2.0);
+ meanDelay = 2.0 * minute;
+ delay (1.0); // It is 15:00, bank closes.
+ nextCust.cancel();
+ }
+ }
+
+ class Customer extends SimProcess {
+ public Customer(ProcessSimulator sim) { super(sim); }
+ public void actions() {
+ (nextCust = new Customer(sim)).schedule
+ (ExponentialGen.nextDouble (streamArr, 1.0/meanDelay));
+ if (!balk()) {
+ tellers.request (1);
+ delay (genServ.nextDouble());
+ tellers.release (1);
+ nbServed++;
+ }
+ }
+
+ private boolean balk() {
+ int n = tellers.waitList().size();
+ return n > 9 || (n > 5 && 5.0*streamBalk.nextDouble() < n - 5);
+ }
+ }
+
+ public void simulOneDay() {
+ sim.init();
+ new OneDay(sim).schedule (9.75);
+ sim.start();
+ statServed.add (nbServed);
+ avWait.add (tellers.waitList().statSize().sum());
+ }
+
+ public void simulateDays (int numDays) {
+ tellers.waitList().setStatCollecting (true);
+ for (int i=1; i<=numDays; i++) simulOneDay();
+ System.out.println (statServed.report());
+ System.out.println (avWait.report());
+ }
+
+ public static void main (String[] args) {
+ new BankProc().simulateDays (100);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Cafeteria.java b/source/umontreal/iro/lecuyer/examples/Cafeteria.java
new file mode 100644
index 0000000..07b4eda
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Cafeteria.java
@@ -0,0 +1,160 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.simprocs.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.Tally;
+
+/**
+ * This example simulates the Cafeteria from
+ * A. M. Law and W. D. Kelton, Simulation Modeling and Analysis,
+ * Second Edition, McGraw-Hill, New York, 1991,
+ * exercise 2.30, p. 224.
+ */
+public class Cafeteria {
+ static final int Hotfood = 0;
+ static final int Sandwich = 1;
+ static final int Drinks = 2;
+ static final double minHotST = 50.0; static final double maxHotST = 120.0;
+ static final double minSandST = 60.0; static final double maxSandST = 180.0;
+ static final double minDrinkST = 5.0; static final double maxDrinkST = 20.0;
+ static final double minHotACT = 20.0; static final double maxHotACT = 40.0;
+ static final double minSandACT = 5.0; static final double maxSandACT = 15.0;
+ static final double minDrinkACT= 5.0; static final double maxDrinkACT= 10.0;
+ static final double meanArr = 30.0;
+
+ int NbCaisses; // Nb de caisses.
+ double Nb; // Nb de clients dans le systeme.
+ int NbAttente = 0; // Nb. en attente pour caisses.
+ double NbHot, NbSand; // Nb. serveurs au HotFood et aux Sandwichs.
+
+ RandomStream streamArr = new MRG32k3a(), streamGrSize = new MRG32k3a(),
+ streamRoute = new MRG32k3a(), streamHotServ = new MRG32k3a(),
+ streamSandServ = new MRG32k3a(), streamDrinksServ = new MRG32k3a(),
+ streamHotACT = new MRG32k3a(), streamSandACT = new MRG32k3a(),
+ streamDrinksACT = new MRG32k3a();
+ RandomVariateGen genArr = new ExponentialGen (streamArr, 1.0/meanArr);
+
+ Resource HotServ = new Resource(1,"Service repas chauds");
+ Resource SandServ = new Resource(1,"Service sandwich");
+ Resource Caisse[] = new Resource[3];
+
+ Accumulate NbClients = new Accumulate ("Nb. de clients dans le systeme");
+ Accumulate CaissesNb = new Accumulate ("Nb. en attente pour les caisses");
+ Tally CaissesAttente = new Tally ("Temps d'attente pour caisses");
+ Tally AttClient[] = new Tally[3]; // Temps d'attente, par type de client.
+
+ class ProcGenArr extends SimProcess {
+ // Processus de generation des arrivees des clients.
+ public void actions() {
+ double u;
+ while (true){
+ delay (genArr.nextDouble());
+ u = streamGrSize.nextDouble();
+ new ProcClient().schedule (0.0);
+ if (u > 0.5) new ProcClient().schedule (0.0);
+ if (u > 0.8) new ProcClient().schedule (0.0);
+ if (u > 0.9) new ProcClient().schedule (0.0);
+ }
+ }
+ }
+
+ class ProcClient extends SimProcess {
+ // Comportement d'un client.
+ double u, ACT, TArr, Attente;
+ int T; // Type de client
+
+ public void actions() {
+ Nb += 1.0; NbClients.update(Nb); TArr = Sim.time();
+ u = streamRoute.nextDouble();
+ if (u < 0.8) {
+ T = Hotfood;
+ HotServ.request(1); Attente = Sim.time() - TArr;
+ delay (UniformGen.nextDouble (streamHotServ, minHotST/NbHot,
+ maxHotST/NbHot));
+ HotServ.release (1);
+ ACT = UniformGen.nextDouble (streamHotACT, minHotACT, maxHotACT);
+ }
+ else if ( u < 0.95) {
+ T = Sandwich;
+ SandServ.request (1); Attente = Sim.time() - TArr;
+ delay (UniformGen.nextDouble (streamSandServ, minSandST/NbSand,
+ maxSandST/NbSand));
+ SandServ.release (1);
+ ACT = UniformGen.nextDouble (streamSandACT, minSandACT, maxSandACT);
+ }
+ else T = Drinks;
+ delay (UniformGen.nextDouble (streamDrinksServ, minDrinkST, maxDrinkST));
+ ACT += UniformGen.nextDouble (streamDrinksACT, minDrinkACT, maxDrinkACT);
+ int c = 0; // c sera la caisse choisie.
+ for (int i=1; i<NbCaisses; i++) {
+ if (Caisse [i].waitList().size() <
+ Caisse [c].waitList().size()) c = i;
+ }
+ TArr = Sim.time();
+ CaissesNb.update (++NbAttente);
+ Caisse[c].request (1);
+ CaissesNb.update (--NbAttente);
+ AttClient [T].add (Attente + Sim.time() - TArr);
+ CaissesAttente.add (Sim.time() - TArr);
+ delay (ACT);
+ Caisse[c].release (1);
+ Nb -= 1.0; NbClients.update (Nb);
+ }
+ }
+
+ class ProcFinSim extends Event {
+ public void actions(){
+ System.out.println(HotServ.report());
+ System.out.println(SandServ.report());
+ System.out.println(CaissesNb.report());
+ System.out.println(CaissesAttente.report());
+ System.out.println(NbClients.report());
+ System.out.println(AttClient[Hotfood].report());
+ System.out.println(AttClient [Sandwich].report());
+ System.out.println(AttClient [Drinks].report());
+ System.out.print ("\nAttente moyenne globale: "+
+ (0.80 * AttClient [Hotfood].average()
+ + 0.15 * AttClient [Sandwich].average()
+ + 0.05 * AttClient [Drinks].average() )+"\n\n");
+ Sim.stop();
+ System.out.print ("==========================================\n");
+ }
+ }
+
+ public void SimCafeteria (int Ncais, double Nhot, double Nsand ){
+ System.out.print (Ncais+" caisses, "+(int)Nhot+" aux repas chauds et "+
+ (int)Nsand+" aux sandwichs\n");
+ NbCaisses = Ncais; NbHot=Nhot; NbSand=Nsand;
+ Nb = 0.0; NbAttente=0;
+ SimProcess.init();
+ HotServ.init(); SandServ.init(); NbClients.init();
+ CaissesNb.init(); CaissesAttente.init();
+ for (int i=0; i<3; i++) { Caisse[i].init(); AttClient[i].init(); }
+ new ProcFinSim().schedule (4500.0); // On va simuler 4500 secondes.
+ new ProcGenArr().schedule (0.0);
+ Sim.start();
+ }
+
+ public Cafeteria(){
+ AttClient[Hotfood] = new Tally("Temps d'attente repas chauds");
+ AttClient[Sandwich] = new Tally("Temps d'attente sandwich");
+ AttClient[Drinks] = new Tally("Temps d'attente breuvages");
+ for (int i=0; i<3; i++) {
+ Caisse[i] = new Resource (1, "Une caisse");
+ Caisse[i].setStatCollecting (true);
+ }
+ HotServ.setStatCollecting (true);
+ SandServ.setStatCollecting (true);
+ SimCafeteria (2, 1.0, 1.0);
+ SimCafeteria (3, 1.0, 1.0);
+ SimCafeteria (2, 2.0, 1.0);
+ SimCafeteria (2, 1.0, 2.0);
+ SimCafeteria (2, 2.0, 2.0);
+ SimCafeteria (3, 2.0, 1.0);
+ SimCafeteria (3, 1.0, 2.0);
+ SimCafeteria (3, 2.0, 2.0);
+ }
+
+ public static void main (String[] args) {new Cafeteria();}
+
+}
diff --git a/source/umontreal/iro/lecuyer/examples/CallCenter.dat b/source/umontreal/iro/lecuyer/examples/CallCenter.dat
new file mode 100644
index 0000000..0b8193f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/CallCenter.dat
@@ -0,0 +1,46 @@
+8.0 opening
+13 numPeriods
+4 100.0 numAgents and base arrival rate
+6 150.0
+8 150.0
+8 180.0
+8 200.0
+7 150.0
+8 150.0
+8 150.0
+6 120.0
+6 100.0
+4 80.0
+4 70.0
+4 60.0
+10.0 alpha_0
+0.1 p
+0.001 nu
+1.0 alpha
+0.01 beta (mean service time = 100 sec)
+20.0 s
+
+
+==========================================================
+Commentaire:
+Vous pourriez avoir une erreur
+
+ "java.util.InputMismatchException"
+
+avec CallCenter.java si votre système Java est configuré pour lire
+des nombres réels en virgule-flottante (par exemple un nombre réel
+s'écrit: 8,5 pour presque tous les pays dans le monde) au lieu
+de la convention anglo-saxonne point-flottant
+(où un nombre réel s'écrit: 8.5). Dans ce cas, soit changez tous les
+point-flottants ci-dessus en virgule-flottantes, soit ajoutez
+l'instruction
+
+ Locale.setDefault(Locale.US);
+
+ou
+
+ Locale.setDefault(Locale.ROOT);
+
+avant la lecture du fichier CallCenter.dat.
+Locale se trouve dans java.util.
+
diff --git a/source/umontreal/iro/lecuyer/examples/CallCenter.java b/source/umontreal/iro/lecuyer/examples/CallCenter.java
new file mode 100644
index 0000000..5de5914
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/CallCenter.java
@@ -0,0 +1,197 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.stat.*;
+import java.io.*;
+import java.util.*;
+
+public class CallCenter {
+ static final double HOUR = 3600.0; // Time is in seconds.
+
+ // Data
+ // Arrival rates are per hour, service and patience times are in seconds.
+ double openingTime; // Opening time of the center (in hours).
+ int numPeriods; // Number of working periods (hours) in the day.
+ int[] numAgents; // Number of agents for each period.
+ double[] lambda; // Base arrival rate lambda_j for each j.
+ double alpha0; // Parameter of gamma distribution for B.
+ double p; // Probability that patience time is 0.
+ double nu; // Parameter of exponential for patience time.
+ double alpha, beta; // Parameters of gamma service time distribution.
+ double s; // Want stats on waiting times smaller than s.
+
+ // Variables
+ double busyness; // Current value of B.
+ double arrRate = 0.0; // Current arrival rate.
+ int nAgents; // Number of agents in current period.
+ int nBusy; // Number of agents occupied;
+ int nArrivals; // Number of arrivals today;
+ int nAbandon; // Number of abandonments during the day.
+ int nGoodQoS; // Number of waiting times less than s today.
+ double nCallsExpected; // Expected number of calls per day.
+
+ Event nextArrival = new Arrival(); // The next Arrival event.
+ LinkedList<Call> waitList = new LinkedList<Call>();
+
+ RandomStream streamB = new MRG32k3a(); // For B.
+ RandomStream streamArr = new MRG32k3a(); // For arrivals.
+ RandomStream streamPatience = new MRG32k3a(); // For patience times.
+ GammaGen genServ; // For service times; created in readData().
+
+ Tally[] allTal = new Tally [4];
+ Tally statArrivals = allTal[0] = new Tally ("Number of arrivals per day");
+ Tally statWaits = allTal[1] = new Tally ("Average waiting time per customer");
+ Tally statGoodQoS = allTal[2] = new Tally ("Proportion of waiting times < s");
+ Tally statAbandon = allTal[3] = new Tally ("Proportion of calls lost");
+ Tally statWaitsDay = new Tally ("Waiting times within a day");
+
+ public CallCenter (String fileName) throws IOException {
+ readData (fileName);
+ // genServ can be created only after its parameters are read.
+ // The acceptance/rejection method is much faster than inversion.
+ genServ = new GammaAcceptanceRejectionGen (new MRG32k3a(), alpha, beta);
+ }
+
+ // Reads data and construct arrays.
+ public void readData (String fileName) throws IOException {
+ Locale loc = Locale.getDefault();
+ Locale.setDefault(Locale.US); // to read reals as 8.3 instead of 8,3
+ BufferedReader input = new BufferedReader (new FileReader (fileName));
+ Scanner scan = new Scanner(input);
+ openingTime = scan.nextDouble(); scan.nextLine();
+ numPeriods = scan.nextInt(); scan.nextLine();
+ numAgents = new int[numPeriods];
+ lambda = new double[numPeriods];
+ nCallsExpected = 0.0;
+ for (int j = 0; j < numPeriods; j++) {
+ numAgents[j] = scan.nextInt();
+ lambda[j] = scan.nextDouble();
+ nCallsExpected += lambda[j]; scan.nextLine();
+ }
+ alpha0 = scan.nextDouble(); scan.nextLine();
+ p = scan.nextDouble(); scan.nextLine();
+ nu = scan.nextDouble(); scan.nextLine();
+ alpha = scan.nextDouble(); scan.nextLine();
+ beta = scan.nextDouble(); scan.nextLine();
+ s = scan.nextDouble();
+ scan.close();
+ Locale.setDefault(loc);
+ }
+
+ // A phone call.
+ class Call {
+ double arrivalTime, serviceTime, patienceTime;
+
+ public Call() {
+ serviceTime = genServ.nextDouble(); // Generate service time.
+ if (nBusy < nAgents) { // Start service immediately.
+ nBusy++;
+ nGoodQoS++;
+ statWaitsDay.add (0.0);
+ new CallCompletion().schedule (serviceTime);
+ } else { // Join the queue.
+ patienceTime = generPatience();
+ arrivalTime = Sim.time();
+ waitList.addLast (this);
+ }
+ }
+
+ public void endWait() {
+ double wait = Sim.time() - arrivalTime;
+ if (patienceTime < wait) { // Caller has abandoned.
+ nAbandon++;
+ wait = patienceTime; // Effective waiting time.
+ } else {
+ nBusy++;
+ new CallCompletion().schedule (serviceTime);
+ }
+ if (wait < s) nGoodQoS++;
+ statWaitsDay.add (wait);
+ }
+ }
+
+ // Event: A new period begins.
+ class NextPeriod extends Event {
+ int j; // Number of the new period.
+ public NextPeriod (int period) { j = period; }
+ public void actions() {
+ if (j < numPeriods) {
+ nAgents = numAgents[j];
+ arrRate = busyness * lambda[j] / HOUR;
+ if (j == 0) {
+ nextArrival.schedule
+ (ExponentialDist.inverseF (arrRate, streamArr.nextDouble()));
+ } else {
+ checkQueue();
+ nextArrival.reschedule ((nextArrival.time() - Sim.time())
+ * lambda[j-1] / lambda[j]);
+ }
+ new NextPeriod(j+1).schedule (1.0 * HOUR);
+ } else
+ nextArrival.cancel(); // End of the day.
+ }
+ }
+
+ // Event: A call arrives.
+ class Arrival extends Event {
+ public void actions() {
+ nextArrival.schedule
+ (ExponentialDist.inverseF (arrRate, streamArr.nextDouble()));
+ nArrivals++;
+ new Call(); // Call just arrived.
+ }
+ }
+
+ // Event: A call is completed.
+ class CallCompletion extends Event {
+ public void actions() { nBusy--; checkQueue(); }
+ }
+
+ // Start answering new calls if agents are free and queue not empty.
+ public void checkQueue() {
+ while ((waitList.size() > 0) && (nBusy < nAgents))
+ (waitList.removeFirst()).endWait();
+ }
+
+ // Generates the patience time for a call.
+ public double generPatience() {
+ double u = streamPatience.nextDouble();
+ if (u <= p)
+ return 0.0;
+ else
+ return ExponentialDist.inverseF (nu, (1.0-u) / (1.0-p));
+ }
+
+ public void simulateOneDay (double busyness) {
+ Sim.init(); statWaitsDay.init();
+ nArrivals = 0; nAbandon = 0;
+ nGoodQoS = 0; nBusy = 0;
+ this.busyness = busyness;
+
+ new NextPeriod(0).schedule (openingTime * HOUR);
+ Sim.start();
+ // Here the simulation is running...
+
+ statArrivals.add ((double)nArrivals);
+ statAbandon.add ((double)nAbandon / nCallsExpected);
+ statGoodQoS.add ((double)nGoodQoS / nCallsExpected);
+ statWaits.add (statWaitsDay.sum() / nCallsExpected);
+ }
+
+ public void simulateOneDay () {
+ simulateOneDay (GammaDist.inverseF (alpha0, alpha0, 8,
+ streamB.nextDouble()));
+ }
+
+ static public void main (String[] args) throws IOException {
+ CallCenter cc = new CallCenter ("CallCenter.dat");
+ for (int i = 0; i < 1000; i++) cc.simulateOneDay();
+ System.out.println ("\nNum. calls expected = " + cc.nCallsExpected +"\n");
+ for (int i = 0; i < cc.allTal.length; i++) {
+ cc.allTal[i].setConfidenceIntervalStudent();
+ cc.allTal[i].setConfidenceLevel (0.90);
+ }
+ System.out.println (Tally.report ("CallCenter:", cc.allTal));
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/CallCenter.res b/source/umontreal/iro/lecuyer/examples/CallCenter.res
new file mode 100644
index 0000000..2fd99c0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/CallCenter.res
@@ -0,0 +1,14 @@
+
+Num. calls expected = 1660.0
+
+Report for CallCenter:
+ num obs. min max average std. dev. conf. int. 90.0%
+
+Num. arrivals per day:
+ 1000 460.000 4206.000 1639.507 513.189 ( 1612.8, 1666.2)
+Average wait time per customer:
+ 1000 0.000 570.757 11.834 34.078 ( 10.060, 13.608)
+Proportion of waiting times < s:
+ 1000 0.277 1.155 0.853 0.169 ( 0.844, 0.862)
+Proportion of calls lost:
+ 1000 0.000 0.844 0.034 0.061 ( 0.031, 0.037)
diff --git a/source/umontreal/iro/lecuyer/examples/CallEv.dat b/source/umontreal/iro/lecuyer/examples/CallEv.dat
new file mode 100644
index 0000000..9a7d1b0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/CallEv.dat
@@ -0,0 +1,22 @@
+1000 numDays
+8.0 opening
+13 numPeriods
+4 100.0 numAgents and base arrival rate
+6 150.0
+8 150.0
+8 180.0
+8 200.0
+7 150.0
+8 150.0
+8 150.0
+6 120.0
+6 100.0
+4 80.0
+4 70.0
+4 60.0
+10.0 alpha_0
+0.1 p
+0.001 nu
+1.0 alpha
+0.01 beta (mean service time = 100 sec)
+20.0 s
diff --git a/source/umontreal/iro/lecuyer/examples/CallEv.java b/source/umontreal/iro/lecuyer/examples/CallEv.java
new file mode 100644
index 0000000..09b88d2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/CallEv.java
@@ -0,0 +1,193 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.stat.Tally;
+import java.io.*;
+import java.util.*;
+
+public class CallEv {
+
+ static final double HOUR = 3600.0; // Time is in seconds.
+
+ // Data
+ // Arrival rates are per hour, service and patience times are in seconds.
+ int numDays; // Number of days to simulate.
+ double openingTime; // Opening time of the center (in hours).
+ int numPeriods; // Number of working periods (hours) in the day.
+ int[] numAgents; // Number of agents for each period.
+ double[] lambda; // Base arrival rate lambda_j for each j.
+ double alpha0; // Parameter of gamma distribution for W.
+ double p; // Probability that patience time is 0.
+ double nu; // Parameter of exponential for patience time.
+ double alpha, beta; // Parameters of gamma service time distribution.
+ double s; // Want stats on waiting times smaller than s.
+
+ // Variables
+ double busyness; // Current value of W.
+ double arrRate = 0.0; // Current arrival rate.
+ int nAgents; // Number of agents in current period.
+ int nBusy; // Number of agents occupied;
+ int nArrivals; // Number of arrivals today;
+ int nAbandon; // Number of abandonments during the day.
+ int nGoodQoS; // Number of waiting times less than s today.
+ double nCallsExpected; // Expected number of calls per day.
+
+ Event nextArrival = new Arrival(); // The next Arrival event.
+
+ RandomStream streamW = new MRG32k3a(); // For W.
+ RandomStream streamArr = new MRG32k3a(); // For arrivals.
+ RandomStream streamPatience = new MRG32k3a(); // For patience times.
+ GammaGen genServ; // For service times; created in readData().
+
+ LinkedListStat<Call> waitList = new LinkedListStat<Call> ("Waiting calls");
+
+ Tally statArrivals = new Tally ("Number of arrivals per day");
+ Tally statWaits = new Tally ("Average waiting time per customer");
+ Tally statWaitsDay = new Tally ("Waiting times within a day");
+ Tally statGoodQoS = new Tally ("Proportion of waiting times < s");
+ Tally statAbandon = new Tally ("Proportion of calls lost");
+
+ class Call { double arrivTime, servTime, patienceTime; }
+
+ static public void main (String[] args) throws IOException { new CallEv(); }
+
+ public CallEv() throws IOException {
+ readData();
+ for (int i=1; i <= numDays; i++) simulOneDay();
+ System.out.println ("\n Num. calls expected = " + nCallsExpected + "\n");
+ statArrivals.setConfidenceIntervalStudent();
+ statWaits.setConfidenceIntervalStudent();
+ statGoodQoS.setConfidenceIntervalStudent();
+ statAbandon.setConfidenceIntervalStudent();
+ System.out.println (statArrivals.report (0.9, 3));
+ System.out.println (statWaits.report (0.9, 3));
+ System.out.println (statGoodQoS.report (0.9, 3));
+ System.out.println (statAbandon.report (0.9, 3));
+ }
+
+ class NextPeriod extends Event {
+ int j; // Number of the new period.
+ public NextPeriod (int period) { j = period; }
+ public void actions() {
+ if (j < numPeriods) {
+ nAgents = numAgents[j];
+ arrRate = busyness * lambda[j] / HOUR;
+ if (j == 0)
+ nextArrival.schedule
+ (ExponentialDist.inverseF (arrRate, streamArr.nextDouble()));
+ else {
+ checkQueue();
+ nextArrival.reschedule ((nextArrival.time() - Sim.time())
+ * lambda[j-1] / lambda[j]);
+ }
+ new NextPeriod(j + 1).schedule (1.0 * HOUR);
+ } else
+ nextArrival.cancel(); // End of the day.
+ }
+ }
+
+ class Arrival extends Event {
+ public void actions() {
+ nextArrival.schedule
+ (ExponentialDist.inverseF (arrRate, streamArr.nextDouble()));
+ nArrivals++;
+ Call call = new Call(); // Call just arrived.
+ call.servTime = genServ.nextDouble(); // Generate service time.
+ if (nBusy < nAgents) { // Start service immediately.
+ nBusy++;
+ nGoodQoS++;
+ statWaitsDay.add (0.0);
+ new CallCompletion().schedule (call.servTime);
+ } else { // Join the queue.
+ call.patienceTime = generPatience();
+ call.arrivTime = Sim.time();
+ waitList.addLast (call);
+ }
+ }
+ }
+
+ class CallCompletion extends Event {
+ public void actions() { nBusy--; checkQueue(); }
+ }
+
+ public void checkQueue() {
+ // Start answering new calls if agents are free and queue not empty.
+ while ((waitList.size() > 0) && (nBusy < nAgents)) {
+ Call call = waitList.removeFirst();
+ double wait = Sim.time() - call.arrivTime;
+ if (call.patienceTime < wait) { // Caller has abandoned.
+ nAbandon++;
+ wait = call.patienceTime; // Effective waiting time.
+ } else {
+ nBusy++;
+ new CallCompletion().schedule (call.servTime);
+ }
+ if (wait < s) nGoodQoS++;
+ statWaitsDay.add (wait);
+ }
+ }
+
+ public double generPatience() {
+ // Generates the patience time for a call.
+ double u = streamPatience.nextDouble();
+ if (u <= p)
+ return 0.0;
+ else
+ return ExponentialDist.inverseF (nu, (1.0 - u) / (1.0 - p));
+ }
+
+ public void readData() throws IOException {
+ // Reads data and construct arrays.
+ Locale loc = Locale.getDefault();
+ Locale.setDefault(Locale.US); // to read reals as 8.3 instead of 8,3
+ BufferedReader input = new BufferedReader (new FileReader ("CallEv.dat"));
+ Scanner scan = new Scanner(input);
+ numDays = scan.nextInt();
+ scan.nextLine();
+ openingTime = scan.nextDouble();
+ scan.nextLine();
+ numPeriods = scan.nextInt();
+ scan.nextLine();
+ numAgents = new int[numPeriods];
+ lambda = new double[numPeriods];
+ nCallsExpected = 0.0;
+ for (int j = 0; j < numPeriods; j++) {
+ numAgents[j] = scan.nextInt();
+ lambda[j] = scan.nextDouble();
+ nCallsExpected += lambda[j];
+ scan.nextLine();
+ }
+ alpha0 = scan.nextDouble();
+ scan.nextLine();
+ p = scan.nextDouble();
+ scan.nextLine();
+ nu = scan.nextDouble();
+ scan.nextLine();
+ alpha = scan.nextDouble();
+ scan.nextLine();
+ beta = scan.nextDouble();
+ scan.nextLine();
+ s = scan.nextDouble();
+ scan.close();
+ Locale.setDefault(loc);
+
+ // genServ can be created only after its parameters are known.
+ genServ = new GammaAcceptanceRejectionGen ( // Faster than inversion
+ new MRG32k3a(), alpha, beta);
+ }
+
+ public void simulOneDay() {
+ Sim.init(); statWaitsDay.init();
+ nArrivals = 0; nAbandon = 0;
+ nGoodQoS = 0; nBusy = 0;
+ busyness = GammaDist.inverseF (alpha0, alpha0, 8, streamW.nextDouble());
+ new NextPeriod(0).schedule (openingTime * HOUR);
+ Sim.start();
+ // Here the simulation is running...
+ statArrivals.add ((double)nArrivals);
+ statAbandon.add ((double)nAbandon / nCallsExpected);
+ statGoodQoS.add ((double)nGoodQoS / nCallsExpected);
+ statWaits.add (statWaitsDay.sum() / nCallsExpected);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/CallEv.res b/source/umontreal/iro/lecuyer/examples/CallEv.res
new file mode 100644
index 0000000..d8768ad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/CallEv.res
@@ -0,0 +1,23 @@
+
+ Num. calls expected = 1660.0
+
+REPORT on Tally stat. collector ==> Number of arrivals per day
+ num. obs. min max average standard dev.
+ 1000 460.000 4206.000 1639.507 513.189
+ 90.0% confidence interval for mean (student): ( 1612.789, 1666.225 )
+
+REPORT on Tally stat. collector ==> Average waiting time per customer
+ num. obs. min max average standard dev.
+ 1000 0.000 570.757 11.834 34.078
+ 90.0% confidence interval for mean (student): ( 10.060, 13.608 )
+
+REPORT on Tally stat. collector ==> Proportion of waiting times < s
+ num. obs. min max average standard dev.
+ 1000 0.277 1.155 0.853 0.169
+ 90.0% confidence interval for mean (student): ( 0.844, 0.862 )
+
+REPORT on Tally stat. collector ==> Proportion of calls lost
+ num. obs. min max average standard dev.
+ 1000 0.000 0.844 0.034 0.061
+ 90.0% confidence interval for mean (student): ( 0.031, 0.037 )
+
diff --git a/source/umontreal/iro/lecuyer/examples/Collision.java b/source/umontreal/iro/lecuyer/examples/Collision.java
new file mode 100644
index 0000000..8ea9faf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Collision.java
@@ -0,0 +1,43 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.stat.*;
+
+public class Collision {
+ int k; // Number of locations.
+ int m; // Number of items.
+ double lambda; // Theoretical expectation of C (asymptotic).
+ boolean[] used; // Locations already used.
+
+ public Collision (int k, int m) {
+ this.k = k;
+ this.m = m;
+ lambda = (double) m * m / (2.0 * k);
+ used = new boolean[k];
+ }
+
+ // Generates and returns the number of collisions.
+ public int generateC (RandomStream stream) {
+ int C = 0;
+ for (int i = 0; i < k; i++) used[i] = false;
+ for (int j = 0; j < m; j++) {
+ int loc = stream.nextInt (0, k-1);
+ if (used[loc]) C++;
+ else used[loc] = true;
+ }
+ return C;
+ }
+
+ // Performs n indep. runs using stream and collects statistics in statC.
+ public void simulateRuns (int n, RandomStream stream, Tally statC) {
+ statC.init();
+ for (int i=0; i<n; i++) statC.add (generateC (stream));
+ statC.setConfidenceIntervalStudent();
+ System.out.println (statC.report (0.95, 3));
+ System.out.println (" Theoretical mean: " + lambda);
+ }
+
+ public static void main (String[] args) {
+ Tally statC = new Tally ("Statistics on collisions");
+ Collision col = new Collision (10000, 500);
+ col.simulateRuns (100000, new MRG32k3a(), statC);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Collision.res b/source/umontreal/iro/lecuyer/examples/Collision.res
new file mode 100644
index 0000000..6d13d65
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Collision.res
@@ -0,0 +1,6 @@
+REPORT on Tally stat. collector ==> Statistics on collisions
+ num. obs. min max average standard dev.
+ 100000 1.000 29.000 12.271 3.380
+ 95.0% confidence interval for mean (student): ( 12.250, 12.292 )
+
+ Theoretical mean: 12.5
diff --git a/source/umontreal/iro/lecuyer/examples/Inventory.java b/source/umontreal/iro/lecuyer/examples/Inventory.java
new file mode 100644
index 0000000..0802cc9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Inventory.java
@@ -0,0 +1,63 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.probdist.PoissonDist;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.*;
+
+public class Inventory {
+
+ double lambda; // Mean demand size.
+ double c; // Sale price.
+ double h; // Inventory cost per item per day.
+ double K; // Fixed ordering cost.
+ double k; // Marginal ordering cost per item.
+ double p; // Probability that an order arrives.
+
+ RandomVariateGenInt genDemand;
+ RandomStream streamDemand = new MRG32k3a();
+ RandomStream streamOrder = new MRG32k3a();
+ Tally statProfit = new Tally ("stats on profit");
+
+ public Inventory (double lambda, double c, double h,
+ double K, double k, double p) {
+ this.lambda = lambda;
+ this.c = c; this.h = h; this.K = K; this.k = k; this.p = p;
+ genDemand = new PoissonGen (streamDemand, new PoissonDist (lambda));
+ }
+
+ // Simulates the system for m days, with the (s,S) policy,
+ // and returns the average profit per day.
+ public double simulateOneRun (int m, int s, int S) {
+ int Xj = S, Yj; // Stock in the morning and in the evening.
+ double profit = 0.0; // Cumulated profit.
+ for (int j = 0; j < m; j++) {
+ Yj = Xj - genDemand.nextInt(); // Subtract demand for the day.
+ if (Yj < 0) Yj = 0; // Lost demand.
+ profit += c * (Xj - Yj) - h * Yj;
+ if ((Yj < s) && (streamOrder.nextDouble() < p)) {
+ // We have a successful order.
+ profit -= K + k * (S - Yj);
+ Xj = S;
+ } else
+ Xj = Yj;
+ }
+ return profit / m;
+ }
+
+ // Performs n independent simulation runs of the system for m days,
+ // with the (s,S) policy, and returns a report with a 90% confidence
+ // interval on the expected average profit per day.
+ public void simulateRuns (int n, int m, int s, int S) {
+ for (int i = 0; i < n; i++)
+ statProfit.add (simulateOneRun (m, s, S));
+ }
+
+ public static void main (String[] args) {
+ Chrono timer = new Chrono();
+ Inventory system = new Inventory (100.0, 2.0, 0.1, 10.0, 1.0, 0.95);
+ system.simulateRuns (500, 2000, 80, 200);
+ system.statProfit.setConfidenceIntervalStudent();
+ System.out.println (system.statProfit.report (0.9, 3));
+ System.out.println ("Total CPU time: " + timer.format());
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Inventory.res b/source/umontreal/iro/lecuyer/examples/Inventory.res
new file mode 100644
index 0000000..e8e2d4e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Inventory.res
@@ -0,0 +1,6 @@
+REPORT on Tally stat. collector ==> stats on profit
+ num. obs. min max average standard dev.
+ 500 83.969 85.753 84.961 0.324
+ 90.0% confidence interval for mean (student): ( 84.938, 84.985 )
+
+Total CPU time: 0:0:0.39
diff --git a/source/umontreal/iro/lecuyer/examples/InventoryCRN.java b/source/umontreal/iro/lecuyer/examples/InventoryCRN.java
new file mode 100644
index 0000000..c6c1cdf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/InventoryCRN.java
@@ -0,0 +1,54 @@
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.Chrono;
+
+public class InventoryCRN extends Inventory {
+
+ Tally statDiff = new Tally ("stats on difference");
+
+ public InventoryCRN (double lambda, double c, double h,
+ double K, double k, double p) {
+ super (lambda, c, h, K, k, p);
+ }
+
+ public void simulateDiff (int n, int m, int s1, int S1, int s2, int S2) {
+ statDiff.init();
+ for (int i = 0; i < n; i++) {
+ double value1 = simulateOneRun (m, s1, S1);
+ double value2 = simulateOneRun (m, s2, S2);
+ statDiff.add (value2 - value1);
+ }
+ }
+
+ public void simulateDiffCRN (int n, int m, int s1, int S1, int s2, int S2) {
+ statDiff.init();
+ streamDemand.resetStartStream();
+ streamOrder.resetStartStream();
+ for (int i = 0; i < n; i++) {
+ double value1 = simulateOneRun (m, s1, S1);
+ streamDemand.resetStartSubstream();
+ streamOrder.resetStartSubstream();
+ double value2 = simulateOneRun (m, s2, S2);
+ statDiff.add (value2 - value1);
+ streamDemand.resetNextSubstream();
+ streamOrder.resetNextSubstream();
+ }
+ }
+
+ public static void main (String[] args) {
+ InventoryCRN system = new InventoryCRN (100.0, 2.0, 0.1, 10.0, 1.0, 0.95);
+ Chrono timer = new Chrono();
+
+ system.simulateDiff (5000, 200, 80, 198, 80, 200);
+ system.statDiff.setConfidenceIntervalStudent();
+ System.out.println (system.statDiff.report (0.9, 3));
+ double varianceIndep = system.statDiff.variance();
+ System.out.println ("Total CPU time: " + timer.format() + "\n");
+
+ timer.init();
+ system.simulateDiffCRN (5000, 200, 80, 198, 80, 200);
+ System.out.println (system.statDiff.report (0.9, 3));
+ double varianceCRN = system.statDiff.variance();
+ System.out.println ("Total CPU time: " + timer.format());
+ System.out.printf ("Variance ratio: %8.4g%n", varianceIndep/varianceCRN);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/InventoryCRN.res b/source/umontreal/iro/lecuyer/examples/InventoryCRN.res
new file mode 100644
index 0000000..36eca5e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/InventoryCRN.res
@@ -0,0 +1,14 @@
+REPORT on Tally stat. collector ==> stats on difference
+ num. obs. min max average standard dev.
+ 5000 -4.961 5.737 0.266 1.530
+ 90.0% confidence interval for mean (student): ( 0.230, 0.302 )
+
+Total CPU time: 0:0:0.56
+
+REPORT on Tally stat. collector ==> stats on difference
+ num. obs. min max average standard dev.
+ 5000 -1.297 2.124 0.315 0.352
+ 90.0% confidence interval for mean (student): ( 0.307, 0.324 )
+
+Total CPU time: 0:0:0.44
+Variance ratio: 18.85
diff --git a/source/umontreal/iro/lecuyer/examples/Jobshop.dat b/source/umontreal/iro/lecuyer/examples/Jobshop.dat
new file mode 100644
index 0000000..cab0c98
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Jobshop.dat
@@ -0,0 +1,14 @@
+100.0
+1000.0
+4 5
+Machine1 3
+Machine2 5
+Machine3 1
+Machine4 2
+Task1 0.1 3 3 3.0 1 5.0 2 10.0
+Task2 0.25 2 4 2.0 2 3.0
+Task3 0.15 4 1 5.0 2 3.0 3 1.0 4 4.0
+Task4 0.04 1 2 10.0
+Task5 0.05 3 3 2.0 1 2.0 4 2.0
+
+
diff --git a/source/umontreal/iro/lecuyer/examples/Jobshop.java b/source/umontreal/iro/lecuyer/examples/Jobshop.java
new file mode 100644
index 0000000..0eaf4a6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Jobshop.java
@@ -0,0 +1,129 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.simprocs.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.ExponentialGen;
+import umontreal.iro.lecuyer.stat.Tally;
+import java.io.*;
+import java.util.*;
+
+public class Jobshop {
+ int nbMachTypes; // Number of machine types M.
+ int nbTaskTypes; // Number of task types N.
+ double warmupTime; // Warmup time T_0.
+ double horizonTime; // Horizon length T.
+ boolean warmupDone; // Becomes true when warmup time is over.
+ Resource[] machType; // The machines groups as resources.
+ TaskType[] taskType; // The task types.
+ RandomStream streamArr = new MRG32k3a(); // Stream for arrivals.
+ BufferedReader input;
+
+ public Jobshop() throws IOException { readData(); }
+
+ // Reads data file, and creates machine types and task types.
+ void readData() throws IOException {
+ input = new BufferedReader (new FileReader ("Jobshop.dat"));
+ StringTokenizer line = new StringTokenizer (input.readLine());
+ warmupTime = Double.parseDouble (line.nextToken());
+ line = new StringTokenizer (input.readLine());
+ horizonTime = Double.parseDouble (line.nextToken());
+ line = new StringTokenizer (input.readLine());
+ nbMachTypes = Integer.parseInt (line.nextToken());
+ nbTaskTypes = Integer.parseInt (line.nextToken());
+ machType = new Resource[nbMachTypes];
+ for (int m=0; m < nbMachTypes; m++) {
+ line = new StringTokenizer (input.readLine());
+ String name = line.nextToken();
+ int nb = Integer.parseInt (line.nextToken());
+ machType[m] = new Resource (nb, name);
+ }
+ taskType = new TaskType[nbTaskTypes];
+ for (int n=0; n < nbTaskTypes; n++)
+ taskType[n] = new TaskType();
+ input.close();
+ }
+
+
+ class TaskType {
+ public String name; // Task name.
+ public double arrivalRate; // Arrival rate.
+ public int nbOper; // Number of operations.
+ public Resource[] machOper; // Machines where operations occur.
+ public double[] lengthOper; // Durations of operations.
+ public Tally statSojourn; // Stats on sojourn times.
+
+ // Reads data for new task type and creates data structures.
+ TaskType() throws IOException {
+ StringTokenizer line = new StringTokenizer (input.readLine());
+ statSojourn = new Tally (name = line.nextToken());
+ arrivalRate = Double.parseDouble (line.nextToken());
+ nbOper = Integer.parseInt (line.nextToken());
+ machOper = new Resource[nbOper];
+ lengthOper = new double[nbOper];
+ for (int i = 0; i < nbOper; i++) {
+ int p = Integer.parseInt (line.nextToken());
+ machOper[i] = machType[p-1];
+ lengthOper[i] = Double.parseDouble (line.nextToken());
+ }
+ }
+
+ // Performs the operations of this task (to be called by a process).
+ public void performTask (SimProcess p) {
+ double arrivalTime = Sim.time();
+ for (int i=0; i < nbOper; i++) {
+ machOper[i].request (1); p.delay (lengthOper[i]);
+ machOper[i].release (1);
+ }
+ if (warmupDone) statSojourn.add (Sim.time() - arrivalTime);
+ }
+ }
+
+ public class Task extends SimProcess {
+ TaskType type;
+
+ Task (TaskType type) { this.type = type; }
+
+ public void actions() {
+ // First schedules next task of this type, then executes task.
+ new Task (type).schedule (ExponentialGen.nextDouble
+ (streamArr, type.arrivalRate));
+ type.performTask (this);
+ }
+ }
+
+ Event endWarmup = new Event() {
+ public void actions() {
+ for (int m=0; m < nbMachTypes; m++)
+ machType[m].setStatCollecting (true);
+ warmupDone = true;
+ }
+ };
+
+ Event endOfSim = new Event() {
+ public void actions() { Sim.stop(); }
+ };
+
+ public void simulateOneRun() {
+ SimProcess.init();
+ endOfSim.schedule (horizonTime);
+ endWarmup.schedule (warmupTime);
+ warmupDone = false;
+ for (int n = 0; n < nbTaskTypes; n++) {
+ new Task (taskType[n]).schedule (ExponentialGen.nextDouble
+ (streamArr, taskType[n].arrivalRate));
+ }
+ Sim.start();
+ }
+
+ public void printReportOneRun() {
+ for (int m=0; m < nbMachTypes; m++)
+ System.out.println (machType[m].report());
+ for (int n=0; n < nbTaskTypes; n++)
+ System.out.println (taskType[n].statSojourn.report());
+ }
+
+ static public void main (String[] args) throws IOException {
+ Jobshop shop = new Jobshop();
+ shop.simulateOneRun();
+ shop.printReportOneRun();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Jobshop.tex b/source/umontreal/iro/lecuyer/examples/Jobshop.tex
new file mode 100644
index 0000000..094cc33
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Jobshop.tex
@@ -0,0 +1,79 @@
+\section {A job shop model}
+\label {sec:jobshop}
+
+This example is adapted from \cite[Section 2.6]{sLAW00a},
+and from \cite{sLEC88a}.
+A job shop contains $M$ groups of machines, the $m$th group having
+$s_m$ identical machines, for $m=1,\dots,M$.
+It is modeled as a network of queues:
+each group has a single FIFO queue, with $s_m$ identical servers for the
+$m$th group. There are $N$ types of tasks arriving to the shop
+at random. Tasks of type $n$ arrive according to a Poisson process
+with rate $\lambda_n$ per hour, for $n=1,\dots,N$.
+Each type of task requires a fixed sequence of operations,
+where each operation must be performed on a specific type of machine
+and has a deterministic duration.
+A task of type $n$ requires $p_n$ operations, to be performed on
+machines $m_{n,1},m_{n,2},\dots,m_{n,p_n}$, in that order, and whose
+respective durations are $d_{n,1},d_{n,2},\dots,d_{n,p_n}$, in hours.
+A task can pass more than once on the same machine type, so $p_n$ may
+exceed $M$.
+
+We want to simulate the job shop for $T$ hours,
+assuming that it is initially empty, and start collecting statistics
+only after a warm-up period of $T_0$ hours.
+We want to compute: (a) the average sojourn time in the shop for
+each type of task and
+(b) the average utilization rate, average length of the waiting
+queue, and average waiting time, for each type of machine,
+over the time interval $[T_0,T]$.
+For the average sojourn times and waiting times, the counted
+observations are the sojourn times and waits that {\em end\/} during
+the time interval $[T_0,T]$.
+Note that the only randomness in this model is in the task
+arrival process.
+
+The program \texttt{Jobshop} in Listing~\ref{lst:Jobshop} performs this
+simulation. Each group of machine is viewed as a resource, with
+capacity $s_m$ for the group $m$.
+The different {\em types\/} of task are objects of the class \texttt{TaskType}.
+This class is used to store the parameters of the different types:
+their arrival rate, the number of operations, the machine type
+and duration for each operation, and a statistical collector for
+their sojourn times in the shop.
+(In the program, the machine types and task types are numbered
+from 0 to $M-1$ and from 0 to $N-1$, respectively,
+because array indices in Java start at 0.)
+
+The tasks that circulate in the shop are objects of the class \texttt{Task}.
+The \texttt{actions} method in class \texttt{Task} describes the behavior
+of a task from its arrival until it exits the shop.
+Each task, upon arrival, schedules the arrival of the next task of the
+same type. The task then runs through the list of its operations.
+For each operation, it requests the appropriate type of machine,
+keeps it for the duration of the operation, and releases it.
+When the task terminates, it sends its sojourn time as a new observation
+to the collector \texttt{statSojourn}.
+
+Before starting the simulation, the class \texttt{Jobshop} first
+schedules two events: One for the end of the simulation and one
+for the end of the warm-up period. The latter simply reinitializes
+the statistical collectors.
+
+With this implementation, the event list always
+contain $N$ ``task arrival'' events, one for each type of task.
+An alternative implementation would be that each task schedules
+another task arrival in a number of hours that is an exponential r.v.\
+with rate $\lambda$, where $\lambda = \lambda_0 + \cdots + \lambda_{N-1}$
+is the global arrival rate, and then the type of each arriving task is
+$n$ with probability $\lambda_n/\lambda$, independently of the others.
+Initially, a {\em single\/} arrival would be scheduled by the class
+\texttt{Jobshop}.
+This approach is stochastically equivalent to the current implementation
+(see, e.g., \cite{sBRA87a,pWOL89a}), but the event list contains only
+one ``task arrival'' event at a time.
+On the other hand, there is the additional work of generating the task
+type on each arrival.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\lstinputlisting[label=lst:Jobshop,caption={A job shop simulation}]{Jobshop.java}
diff --git a/source/umontreal/iro/lecuyer/examples/Nonuniform.java b/source/umontreal/iro/lecuyer/examples/Nonuniform.java
new file mode 100644
index 0000000..6d7803a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Nonuniform.java
@@ -0,0 +1,46 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+
+public class Nonuniform {
+ // The parameter values are hardwired here to simplify the program.
+ double lambda = 5.0; double p = 0.2;
+ double alpha = 2.0; double beta = 1.0;
+ double mu = 5.0; double sigma = 1.0;
+
+ RandomStream stream = new LFSR113();
+ RandomVariateGenInt genN = new RandomVariateGenInt
+ (stream, new PoissonDist (lambda)); // For N
+ RandomVariateGen genY = new GammaAcceptanceRejectionGen
+ (stream, new GammaDist (alpha, beta)); // For Y_j
+ RandomVariateGen genW = new RandomVariateGen
+ (stream, new LognormalDist (mu, sigma)); // For W_j
+
+ // Generates and returns X.
+ public double generateX () {
+ int N; int M; int j; double X = 0.0;
+ N = genN.nextInt();
+ M = GeometricDist.inverseF (p, stream.nextDouble()); // Uses static method
+ for (j = 0; j < N; j++) X += genY.nextDouble();
+ for (j = 0; j < M; j++) X += genW.nextDouble();
+ return X;
+ }
+
+ // Performs n indep. runs and collects statistics in statX.
+ public void simulateRuns (int n) {
+ TallyStore statX = new TallyStore (n);
+ for (int i=0; i<n; i++) statX.add (generateX ());
+ System.out.println (statX.report ());
+ statX.quickSort();
+ double[] data = statX.getArray();
+ System.out.printf ("0.10 quantile: %9.3f%n", data[(int)(0.10 * n)]);
+ System.out.printf ("0.50 quantile: %9.3f%n", data[(int)(0.50 * n)]);
+ System.out.printf ("0.90 quantile: %9.3f%n", data[(int)(0.90 * n)]);
+ System.out.printf ("0.99 quantile: %9.3f%n", data[(int)(0.99 * n)]);
+ }
+
+ public static void main (String[] args) {
+ (new Nonuniform ()).simulateRuns (10000);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Nonuniform.res b/source/umontreal/iro/lecuyer/examples/Nonuniform.res
new file mode 100644
index 0000000..94a99f8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Nonuniform.res
@@ -0,0 +1,8 @@
+REPORT on Tally stat. collector ==> null
+ num. obs. min max average standard dev.
+ 10000 0.000 13184.890 989.135 1261.431
+
+0.10 quantile: 9.469
+0.50 quantile: 553.019
+0.90 quantile: 2555.540
+0.99 quantile: 5836.938
diff --git a/source/umontreal/iro/lecuyer/examples/PreyPred.java b/source/umontreal/iro/lecuyer/examples/PreyPred.java
new file mode 100644
index 0000000..c935c9a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/PreyPred.java
@@ -0,0 +1,54 @@
+import umontreal.iro.lecuyer.simevents.*;
+
+public class PreyPred {
+ double r = 0.005, c = 0.00001,
+ s = 0.01, d = 0.000005, h = 5.0;
+ double x0 = 2000.0, z0 = 150.0;
+ double horizon = 501.0;
+ Simulator sim = new Simulator();
+ Continuous x;
+ Continuous z;
+
+ public static void main (String[] args) { new PreyPred(); }
+
+ public PreyPred() {
+ x = new Preys(sim);
+ z = new Preds(sim);
+ sim.init();
+ new EndOfSim(sim).schedule (horizon);
+ new PrintPoint(sim).schedule (h);
+ (sim.continuousState()).selectRungeKutta4 (h);
+ x.startInteg (x0);
+ z.startInteg (z0);
+ sim.start();
+ }
+
+ public class Preys extends Continuous {
+ public Preys(Simulator sim) { super(sim); }
+
+ public double derivative (double t) {
+ return (r * value() - c * value() * z.value());
+ }
+ }
+
+ public class Preds extends Continuous {
+ public Preds(Simulator sim) { super(sim); }
+
+ public double derivative (double t) {
+ return (-s * value() + d * x.value() * value());
+ }
+ }
+
+ class PrintPoint extends Event {
+ public PrintPoint(Simulator sim) { super(sim); }
+ public void actions() {
+ System.out.println (sim.time() + " " + x.value() + " " + z.value());
+ this.schedule (h);
+ }
+ }
+
+ class EndOfSim extends Event {
+ public EndOfSim(Simulator sim) { super(sim); }
+ public void actions() { sim.stop(); }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/PreyPred.tex b/source/umontreal/iro/lecuyer/examples/PreyPred.tex
new file mode 100644
index 0000000..329b1f6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/PreyPred.tex
@@ -0,0 +1,38 @@
+%%%%%%%%%%%%%
+\section {Continuous simulation: A prey-predator system}
+\label {sec:preypred}
+
+We consider a classical prey-predator system, where the preys
+are food for the predators (see, e.g., \cite{sLAW00a}, page 87).
+Let $x(t)$ and $z(t)$ be the numbers of preys and predators
+at time $t$, respectively.
+These numbers are integers, but as an approximation,
+we shall assume that they are real-valued variables evolving
+according to the differential equations
+\begin{eqnarray*}
+ x'(t) &= &\ r x(t) - c x(t) z(t)\\
+ z'(t) &= & -s z(t) + d x(t) z(t)
+\end{eqnarray*}
+with initial values $x(0)=x_0>0$ et $z(0)=z_0>0$.
+This system is actually a Lotka-Volterra system of differential
+equations, and has a known analytical solution.
+Here, in the program of Listing~\ref{lst:PreyPred},
+we simply simulate its evolution, to illustrate the continuous
+simulation facilities of SSJ.
+
+%\bigskip
+\lstinputlisting[label=lst:PreyPred,caption={Simulation of the prey-predator system},lineskip=-1pt,%
+emph={main}]{PreyPred.java}
+
+
+This program prints the triples $(t, x(t), z(t))$ at values of
+$t$ that are multiples of \texttt{h}, one triple per line.
+This is done by an event of class \texttt{PrintPoint}, which is
+rescheduled at every \texttt{h} units of time.
+This output can be redirected to a file for later use,
+for example to plot a graph of the trajectory.
+The continuous variables \texttt{x} and \texttt{z} are instances of the
+classes \texttt{Preys} and \texttt{Preds}, whose method \texttt{derivative}
+give their derivative $x'(t)$ and $z'(t)$, respectively.
+The differential equations are integrated by a Runge-Kutta method
+of order 4.
diff --git a/source/umontreal/iro/lecuyer/examples/Queue.tex b/source/umontreal/iro/lecuyer/examples/Queue.tex
new file mode 100644
index 0000000..b4a560d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Queue.tex
@@ -0,0 +1,464 @@
+\section {A single-server queue}
+\label {sec:queue}
+
+Our first example is a {\em single-server queue}, where customers
+arrive randomly and are served one by one in their order of arrival,
+i.e., {\em first in, first out\/} (FIFO).
+We suppose that the times between successive arrivals are exponential
+random variables with mean 10 minutes, that the service times are exponential
+random variables with mean 9 minutes, and that all these random variables are
+mutually independent.
+The customers arriving while the server is busy must join the queue.
+The system initially starts empty. We want to simulate the first 1000
+minutes of operation and compute statistics such as the mean waiting time
+per customer, the mean queue length, etc.
+
+This simple model is well-known in queuing theory: It is called
+an $M/M/1$ queue. Simple formulas are available for this model to
+compute the average waiting time per customer, average queue length,
+etc., over an {\em infinite\/} time horizon \cite{pKLE75a}.
+When the time horizon is finite, these expectations can also
+be computed by numerical methods. The fact that we use this model
+to give a first tasting of SSJ should not be interpreted to mean that
+simulation is necessarily the best tool for it.
+
+We give three examples of simulation programs for the $M/M/1$ queue:
+The first one is event-oriented, the second one is process-oriented,
+and the third one uses a simple recurrence.
+The first program is longer and more complicated than the other two;
+it shows how things work at a lower level.
+
+
+%%%%%%%%%%%%%
+\subsection {Event-oriented program}
+
+\lstinputlisting[label=lst:QueueEv,caption={Event-oriented simulation of an $M/M/1$ queue},lineskip=-1pt]{QueueEv.java}
+
+Listing~\ref{lst:QueueEv} gives an event-oriented simulation program,
+where a subclass of the class \texttt{Event} is defined for each type
+of event that can occur in the simulation:
+arrival of a customer (\texttt{Arrival}),
+departure of a customer (\texttt{Departure}),
+and end of the simulation (\texttt{EndOfSim}).
+Each event {\em instance\/} is inserted into the {\em event list\/}
+upon its creation, with a scheduled time of occurrence, and is
+{\em executed\/} when the simulation clock reaches this time.
+Executing an event means invoking its \texttt{actions} method.
+Each event subclass must implement this method.
+The simulation clock and the event list (i.e., the list of events
+scheduled to occur in the future) are maintained behind the
+scenes by the class \texttt{Sim} of package \texttt{simevents}.
+
+When \texttt{QueueEv} is instantiated by the \texttt{main} method,
+the program creates
+two streams of random numbers,
+two random variate generators, two
+lists, and two statistical probes (or collectors).
+The random number streams can be
+viewed as virtual random number generators that generate random
+numbers in the interval $[0,1)$ according to the uniform probability
+distribution. These streams are attached to random variate generators
+\texttt{genArr} and \texttt{genServ} which are used to generate the times
+between successive arrivals and the service times, respectively.
+We can use such an attached generator because the means (parameters)
+do not change during simulation.
+The lists \texttt{waitList} and \texttt{servList} contain the customers
+waiting in the queue and the customer in service (if any), respectively.
+Maintaining a list for the customer in service may seem exaggerated,
+because this list never contains more than one object, but the current
+design has the advantage of working with very little change if the
+queuing model has more than one server, and in other more general
+situations.
+Note that we could use the class \texttt{List} from package
+\texttt{simevents} instead of \texttt{java.util.LinkedList}.
+However, with our current implementation,
+the automatic statistical collection in that \texttt{List}
+class would not count the customers whose waiting time is zero, because
+they are never placed in the list.
+\begin{comment}
+Here we use the class \texttt{List} from package \texttt{simevents}.
+This class is equivalent to the standard class \texttt{java.util.LinkedList},
+except that its implementation is more efficient than the current one
+in JDK and it can also collect statistics automatically.
+However, the automatic statistical collection on \texttt{waitList}
+would not count the customers whose waiting time is zero, because
+they are never placed in this list, so we do not use this facility.
+\end{comment}
+
+The statistical probe \texttt{custWaits} collects statistics on the
+customer's waiting times. It is of the class \texttt{Tally}, which
+is appropriate when the statistical data of interest is a sequence
+of observations $X_1, X_2, \dots$ of which we might want to compute
+the sample mean, variance, and so on.
+A new observation is given to this probe by the \texttt{add} method
+each time a customer starts its service.
+Every \texttt{add} to a \texttt{Tally} probe brings a new observation $X_i$,
+which corresponds here to a customer's waiting time in the queue.
+The other statistical probe, \texttt{totWait}, is of the class
+\texttt{Accumulate}, which means that it computes the integral
+(and, eventually, the time-average) of a continuous-time
+stochastic process with piecewise-constant trajectory.
+Here, the stochastic process of interest is the length of the queue
+as a function of time. One must call \texttt{totWait.update} whenever
+there is a change in the queue size, to update the (hidden)
+{\em accumulator\/} that keeps the current value of the integral
+of the queue length. This integral is equal, after each update,
+to the total waiting time in the queue, for all the customers,
+since the beginning of the simulation.
+
+Each customer is an object with two fields: \texttt{arrivTime}
+memorizes this customer's arrival time to the system, and
+\texttt{servTime} memorizes its service time.
+This object is created, and its fields are initialized,
+when the customer arrives.
+
+The method \texttt{Sim.init}, invoked in the constructor \texttt{QueueEv},
+initializes the clock and the event list,
+whereas \texttt{Sim.start} actually starts the simulation
+by advancing the clock to the time of
+the first event in the event list, removing this event
+from the list, and executing it. This is repeated until either
+\texttt{Sim.stop} is called or the event list becomes empty.
+\texttt{Sim.time} returns the current time on the simulation clock.
+Here, two events are scheduled before starting the simulation:
+the end of the simulation at time 1000, and the
+arrival of the first customer at a random time that has the exponential
+distribution with mean 10 (or \emph{rate} $\lambda=1/10$),
+generated by \texttt{genArr} using inversion and its attached random stream.
+The method \texttt{genArr.nextDouble} returns this exponential random variate.
+
+The method \texttt{actions} of the class \texttt{Arrival} describes what happens
+when an arrival occurs.
+Arrivals are scheduled by a domino effect:
+the first action of each arrival event schedules the next event in
+a random number of time units, generated from the exponential distribution
+with mean 10.
+Then, the newly arrived customer is created,
+its arrival time is set to the current simulation time,
+and its service time is generated from the exponential
+distribution with mean 9, using the random variate generator \texttt{genServ}.
+If the server is busy, this customer is inserted at the end of the
+queue (the list \texttt{waitList}) and the statistical probe
+\texttt{totWait}, that keeps track of the size of the queue, is updated.
+Otherwise, the customer is inserted in the server's list \texttt{servList},
+its departure is scheduled to happen in a number of time units
+equal to its service time, and a new observation of 0.0 is given to the
+statistical probe \texttt{custWaits} that collects the waiting times.
+
+When a \texttt{Departure} event occurs, the customer in service is
+removed from the list (and disappears).
+If the queue is not empty, the first customer is removed from
+the queue (\texttt{waitList}) and inserted in the server's list,
+and its departure is scheduled.
+The waiting time of that customer (the current time minus its
+arrival time) is given as a new observation to the probe
+\texttt{custWaits}, and the probe \texttt{totWait} is also updated
+with the new (reduced) size of the queue.
+
+The event \texttt{EndOfSim} calculates and
+prints statistical reports for
+the two probes and stops the simulation.
+The program prints the results shown in Figure~\ref{fig:quresEv}.
+When calling \texttt{report} on an \texttt{Accumulate} object, an implicit
+update is done using the current simulation time and the last
+value given to \texttt{update}. In this example, this ensures
+that the end time of the \texttt{Accumulate} will always be 1000,
+otherwise it would be a random number.
+
+\setbox4=\vbox{\hsize=5.8in
+\begin{verbatim}
+REPORT on Tally stat. collector ==> Waiting times
+ min max average standard dev nb. obs.
+ 0 113.721 49.554 22.336 97
+
+REPORT on Accumulate stat. collector ==> Size of queue
+
+ From time To time Min Max Average
+ 0 1000 0 12 4.85
+\end{verbatim}
+}
+
+\begin{figure}[htb]
+\centerline {\boxit{\box4}}
+\caption {Results of the program \texttt{QueueEv}.}
+\label {fig:quresEv}
+\end{figure}
+
+%%%%%%%%%%%%%
+\clearpage
+\subsection {Process-oriented program}
+
+\lstinputlisting[label=lst:QueueProc,caption={Process-oriented simulation of an $M/M/1$ queue}]{QueueProc.java}
+
+\clearpage
+Typical simulation languages offer higher-level constructs than those used
+in the program of Listing~\ref{lst:QueueEv}, and so does SSJ.
+This is illustrated by our second implementation of the single-server
+queue model, in Listing~\ref{lst:QueueProc}, based on a
+paradigm called the {\em process-oriented\/} approach.
+% initiated in the early sixties by the GPSS language (\cite {sSCH90a}).
+
+In the event-oriented implementation, each customer was a {\em passive\/}
+object, storing two real numbers, and performing no action by itself.
+In the process-oriented implementation given in Listing~\ref{lst:QueueProc},
+each customer (instance of the class \texttt{Customer}) is a
+\emph{process} whose activities are described by its \texttt{actions} method.
+This is implemented by associating a Java \texttt{Thread} to each
+\texttt{SimProcess}.
+The server is an object of the class \texttt{Resource}, created when
+\texttt{QueueProc} is instantiated by \texttt{main}.
+It is a {\em passive\/} object, in the sense that it executes no code.
+Active resources, when needed, can be implemented as processes.
+
+When it starts executing its actions, a customer first schedules
+the arrival of the next customer, as in the event-oriented case.
+(Behind the scenes, this effectively schedules an event, in the
+event list, that will start a new customer instance.
+The class \texttt{SimProcess} contains all scheduling facilities of {\tt
+ Event}, which permits one
+to schedule processes just like events.)
+The customer then requests the server by invoking \texttt{server.request}.
+If the server is free, the customer gets it and can continue its
+execution immediately. Otherwise, the customer is automatically
+(behind the scenes) placed in the server's queue, is suspended,
+and resumes its execution only when it obtains the server.
+When its service can start, the customer invokes \texttt{delay} to
+freeze itself for a duration equal to its service time, which is
+again generated from the exponential distribution with mean 9
+using the random variate generator \texttt{genServ}.
+After this delay has elapsed, the customer
+releases the server and ends its life.
+Invoking \texttt{delay(d)} can be interpreted as scheduling an event that
+% (behind the scenes)
+will resume the execution of the process in \texttt{d} units of time.
+Note that several distinct customers can
+co-exist in the simulation at any given point in time, and
+be at different phases of their \texttt{actions} method.
+However, only one process is executing at a time.
+
+The constructor \texttt{QueueProc} initializes the simulation,
+invokes \texttt{setStatCollecting (true)} to specify that detailed statistical
+collection must be performed automatically for the resource \texttt{server},
+schedules an event \texttt{EndOfSim} at time 1000,
+schedules the first customer's arrival, and starts the simulation.
+The \texttt{EndOfSim} event prints a detailed statistical report on
+the resource \texttt{server}.
+
+It should be pointed out that in the \texttt{QueueProc} program,
+the service time of a customer is generated
+only when the customer starts its service, whereas for \texttt{QueueEv},
+it was generated at customer's arrival.
+For this particular model, it turns out that this makes no difference
+in the results, because the customers are served in a FIFO order
+and because one random number stream is dedicated to the generation
+of service times.
+However, this may have an impact in other situations.
+
+The process-oriented program here is more compact and
+more elegant than its event-oriented counterpart.
+This tends to be often true: Process-oriented programming
+frequently gives less cumbersome and better looking programs.
+On the other hand, the process-oriented implementations also tend to
+execute more slowly, because they involve more overhead.
+For example, the process-driven single-server queue simulation
+is two to three times slower than its event-driven counterpart.
+In fact, process management is done via the event list:
+processes are started, suspended, reactivated, and so on,
+by hidden events.
+If the execution speed of a simulation program is really important,
+it may be better to stick to an event-oriented implementation.
+
+%%%%%%%%%%%%%%%
+%\subsection {Simulation results for the single-server queue}
+
+\setbox5=\vbox{\hsize=5.8in
+\begin{verbatim}
+REPORT ON RESOURCE : Server
+ From time : 0.0 to time : 1000.0
+ min max average Std. Dev. num.obs.
+ Capacity 1 1 1
+ Utilisation 0 1 0.999
+ Queue Size 0 12 4.85
+ Wait 0 113.721 49.554 22.336 97
+ Service 0.065 41.021 10.378 10.377 96
+ Sojourn 12.828 124.884 60.251 21.352 96
+
+\end{verbatim}
+}
+
+\begin{figure}[htb]
+\centerline {\boxit{\box5}}
+\caption {Results of the program \texttt{QueueProc}.}
+\label {fig:quresProc}
+\end{figure}
+
+
+Figure~\ref{fig:quresProc} shows the output of the program \texttt{QueueProc}.
+It contains more information than the output of \texttt{QueueEv}.
+It gives statistics on the server utilization, queue size,
+waiting times, service times, and sojourn times in the system.
+(The sojourn time of a customer is its waiting time plus its service time.)
+
+We see that by time $T =1000$, 97 customers have completed their waiting
+and 96 have completed their service.
+The maximal length of the queue has been 12 and its average length
+between time 0 and time 1000 was 4.85.
+The waiting times were in the range from 0 to 113.721, with an average of 49.554,
+while the service times were from 0.065 to 41.021, with an average of 10.378
+(recall that the theoretical mean service time is 9.0).
+Clearly, the largest waiting time and largest service time
+belong to different customers.
+The average waiting and service times do not sum up to
+the average sojourn time because there is one more observation for the
+waiting times.
+
+The report also gives the empirical standard deviations of the waiting,
+service, and sojourn times. It is important to note that these
+standard deviations should {\em not\/} be used to compute confidence
+intervals for the expected average waiting times or sojourn times
+in the standard way, because the observations here (e.g., the successive
+waiting times) are strongly dependent, and also not identically distributed.
+Appropriate techniques for computing confidence intervals in this type of
+situation are described, e.g., in \cite{sFIS96a,sLAW00a}.
+
+
+%%%%%%%%%%%%%
+\subsection {A simpler program based on Lindley's recurrence}
+
+The aim of the previous programs was
+to illustrate the notions of events and processes by a simple example.
+However, for a single-server queue,
+if $W_i$ and $S_i$ are the waiting time and service time of the
+$i$th customer, and $A_i$ is the time between the arrivals of
+the $i$th and $(i+1)$th customers, we have $W_1=0$ and the $W_i$
+follow the recurrence
+\eq
+ W_{i+1} = \max(0,\; W_i + S_i - A_i), \label {eq:lindley}
+\endeq
+known as Lindley's equation \cite {pKLE75a}.
+
+The program of Listing~\ref{lst:QueueLindley} exploits (\ref{eq:lindley})
+to compute the average waiting time of the first $100$ customers.
+This is different than for the previous programs, because we now fix
+the total number of customers instead of fixing the time horizon.
+For a change, the random variates are generated by static methods
+instead of via a \texttt{RandomVariateGen} object.
+The instruction ``\texttt{Wi += \dots}'' could also be replaced by the
+one that is commented out, and which directly implements inversion
+of the exponential distribution.
+This illustrates various ways of doing the same thing.
+
+
+\bigskip
+\lstinputlisting[label=lst:QueueLindley,caption={A simulation based on Lindley's recurrence}]{QueueLindley.java}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Using the observer design pattern}
+
+Listing~\ref{lst:QueueObs} adds a few ingredients to the program
+\texttt{QueueLindley}, in order to illustrate the \emph{observer}
+design pattern implemented in package \texttt{stat}.
+This mechanism permits one to separate data generation from data
+processing. It can be very helpful in large simulation programs or
+libraries, where different objects may need to process the same data
+in different ways. These objects may have the task of storing observations
+or displaying statistics in different formats, for example, and they are
+not necessarily fixed in advance.
+
+The \emph{observer} pattern, supported by the \texttt{Observable}
+class and \texttt{Observer} interface in Java,
+offers the appropriate flexibility for that kind of situation.
+An \texttt{Observable} object acts in a sense like a \emph{broadcasting}
+\emph{distribution agency} that maintains a list of registered
+\texttt{Observer} objects, and send information to all its registered
+observers whenever appropriate.
+
+\texttt{StatProbe} in package \texttt{stat}, as well as its subclasses
+\texttt{Tally} and \texttt{Accumulate}, extend the \texttt{Observable} class.
+Whenever they receive a new statistical observation, e.g., via
+\texttt{Tally.add} or \texttt{Accumulate.update}, they send the new value
+to all registered observers.
+To register as an observer, an object must implement the interface
+\texttt{Observer}.
+This implies that it must provide an implementation of the method
+\texttt{update}, whose purpose is to recover the information
+that the object has registered for.
+
+In the example, the statistical collector \texttt{waitingTimes}
+is the \texttt{Observable} object that transmits to all its registered
+listeners each new statistical observation that it receives via
+its \texttt{add} method.
+More specifically, each call to \texttt{waitingTimes.add(x)} generates
+in the background a call to \texttt{o.update(waitingTimes, dx)},
+where \texttt{dx} is a \texttt{Double} object that contains the value of \texttt{x},
+for all registered observers \texttt{o}.
+
+\begin{comment}
+The method \texttt{notifyObs} is used to
+turn the tally into such an agency. In fact, the collector is both a
+tally and a distribution agency, but its tally functionality can be
+disabled using the \texttt{stopCollectStat} method. This can be useful when
+the registered observers already perform statistical collection.
+\end{comment}
+
+The single observer that registers to receive observations from
+\texttt{waitingTimes} in the example is an anonymous object of class
+\texttt{ObservationTrace}. The task of this observer is to
+print the waiting times $W_5$, $W_{10}$, $W_{15}$, \dots
+The statistical collector \texttt{waitingTimes} himself also stores
+appropriate information to be able to provide a statistical report
+when asked.
+The \texttt{ObservationTrace} object gets informed of any new observation
+$W_i$ via its \texttt{update} method.
+The \texttt{Observer} interfaces specifies that \texttt{update} must have two
+formal parameters, of classes \texttt{Observable} and \texttt{Object}, respectively.
+A \texttt{StatProbe} always passes \texttt{Double} wrapper object as the
+second parameter of \texttt{update}, so this second parameter is normally
+type-casted to a \texttt{Double} inside the \texttt{update} method,
+before the observation can be extracted.
+In the case where the observer registers to several \texttt{Observable}
+objects, the first parameter of \texttt{update} tells him which one
+is sending the information and can behave accordingly.
+
+\bigskip
+\lstinputlisting[label=lst:QueueObs,caption={A simulation of Lindley's
+ recurrence using observers}]{QueueObs.java}
+
+
+\begin{comment}
+%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+Classes implementing data generation facilities use
+the statistical probes as usual but turn on the observation
+notification. In more complex situations, the data generating class
+could itself implement \texttt{Observable} to broadcast some custom
+observation types, such as calls in a call center simulation program.
+It is a good practice to offer a public access to the statistical
+collectors to allow observer registration from other classes.
+Here, the only collector used is the \texttt{waitingTimes}.
+In the case of a program that also keeps track of the waiting queue
+(as in \texttt{QueueEv}), we could also use
+a second collector for the queue size.
+The \texttt{QueueObs} is the only data generator of the program.
+In more complex examples, we could have several data generators,
+each with their own properties.
+
+The data processing portion of the simulation program comprises classes
+implementing the \texttt{Observer} interface. These
+observers can put data in collectors, in files for later plotting, etc.
+In our example, we register one listener for the waiting times.
+One can also register observers with the \texttt{Accumulate} class.
+However, note that the \texttt{QueueObs} simulation
+logic does not know anything about the observation trace.
+The \texttt{ObservationTrace}, is a user-defined
+class that prints waiting times of some customers. The output
+is only for demonstration purposes, it is not suited for passing
+to a plotting program, but it could easily be modified to do so.
+Such a listener could also collect waiting times greater or equal
+to a certain threshold, use other statistical collection facilities, etc.
+The user can implement data processing tools that
+the simulation library developers did not think about.
+
+\end{comment}
+
diff --git a/source/umontreal/iro/lecuyer/examples/QueueEv.java b/source/umontreal/iro/lecuyer/examples/QueueEv.java
new file mode 100644
index 0000000..162c979
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/QueueEv.java
@@ -0,0 +1,73 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+import java.util.LinkedList;
+
+public class QueueEv {
+
+ RandomVariateGen genArr;
+ RandomVariateGen genServ;
+ LinkedList<Customer> waitList = new LinkedList<Customer> ();
+ LinkedList<Customer> servList = new LinkedList<Customer> ();
+ Tally custWaits = new Tally ("Waiting times");
+ Accumulate totWait = new Accumulate ("Size of queue");
+
+ class Customer { double arrivTime, servTime; }
+
+ public QueueEv (double lambda, double mu) {
+ genArr = new ExponentialGen (new MRG32k3a(), lambda);
+ genServ = new ExponentialGen (new MRG32k3a(), mu);
+ }
+
+ public void simulateOneRun (double timeHorizon) {
+ Sim.init();
+ new EndOfSim().schedule (timeHorizon);
+ new Arrival().schedule (genArr.nextDouble());
+ Sim.start();
+ }
+
+ class Arrival extends Event {
+ public void actions() {
+ new Arrival().schedule (genArr.nextDouble()); // Next arrival.
+ Customer cust = new Customer(); // Cust just arrived.
+ cust.arrivTime = Sim.time();
+ cust.servTime = genServ.nextDouble();
+ if (servList.size() > 0) { // Must join the queue.
+ waitList.addLast (cust);
+ totWait.update (waitList.size());
+ } else { // Starts service.
+ custWaits.add (0.0);
+ servList.addLast (cust);
+ new Departure().schedule (cust.servTime);
+ }
+ }
+ }
+
+ class Departure extends Event {
+ public void actions() {
+ servList.removeFirst();
+ if (waitList.size() > 0) {
+ // Starts service for next one in queue.
+ Customer cust = waitList.removeFirst();
+ totWait.update (waitList.size());
+ custWaits.add (Sim.time() - cust.arrivTime);
+ servList.addLast (cust);
+ new Departure().schedule (cust.servTime);
+ }
+ }
+ }
+
+ class EndOfSim extends Event {
+ public void actions() {
+ Sim.stop();
+ }
+ }
+
+ public static void main (String[] args) {
+ QueueEv queue = new QueueEv (1.0, 2.0);
+ queue.simulateOneRun (1000.0);
+ System.out.println (queue.custWaits.report());
+ System.out.println (queue.totWait.report());
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/QueueEv.res b/source/umontreal/iro/lecuyer/examples/QueueEv.res
new file mode 100644
index 0000000..04a61ad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/QueueEv.res
@@ -0,0 +1,8 @@
+REPORT on Tally stat. collector ==> Waiting times
+ num. obs. min max average standard dev.
+ 1037 0.000 6.262 0.495 0.835
+
+REPORT on Accumulate stat. collector ==> Size of queue
+ from time to time min max average
+ 0.00 1000.00 0.000 10.000 0.513
+
diff --git a/source/umontreal/iro/lecuyer/examples/QueueLindley.java b/source/umontreal/iro/lecuyer/examples/QueueLindley.java
new file mode 100644
index 0000000..6dfe9bf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/QueueLindley.java
@@ -0,0 +1,37 @@
+import umontreal.iro.lecuyer.stat.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.ExponentialDist;
+import umontreal.iro.lecuyer.util.Chrono;
+
+public class QueueLindley {
+
+ RandomStream streamArr = new MRG32k3a();
+ RandomStream streamServ = new MRG32k3a();
+ Tally averageWaits = new Tally ("Average waits");
+
+ public double simulateOneRun (int numCust, double lambda, double mu) {
+ double Wi = 0.0;
+ double sumWi = 0.0;
+ for (int i = 2; i <= numCust; i++) {
+ Wi += ExponentialDist.inverseF (mu, streamServ.nextDouble()) -
+ ExponentialDist.inverseF (lambda, streamArr.nextDouble());
+ if (Wi < 0.0) Wi = 0.0;
+ sumWi += Wi;
+ }
+ return sumWi / numCust;
+ }
+
+ public void simulateRuns (int n, int numCust, double lambda, double mu) {
+ averageWaits.init();
+ for (int i=0; i<n; i++)
+ averageWaits.add (simulateOneRun (numCust, lambda, mu));
+ }
+
+ public static void main (String[] args) {
+ Chrono timer = new Chrono();
+ QueueLindley queue = new QueueLindley();
+ queue.simulateRuns (100, 10000, 1.0, 2.0);
+ System.out.println (queue.averageWaits.report());
+ System.out.println ("Total CPU time: " + timer.format());
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/QueueObs.java b/source/umontreal/iro/lecuyer/examples/QueueObs.java
new file mode 100644
index 0000000..4566e82
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/QueueObs.java
@@ -0,0 +1,76 @@
+import java.util.*;
+import umontreal.iro.lecuyer.stat.*;
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+public class QueueObs {
+
+ Tally waitingTimes = new Tally ("Waiting times");
+ Tally averageWaits = new Tally ("Average wait");
+ RandomVariateGen genArr;
+ RandomVariateGen genServ;
+ int cust; // Number of the current customer.
+
+ public QueueObs (double lambda, double mu, int step) {
+ genArr = new ExponentialGen (new MRG32k3a(), lambda);
+ genServ = new ExponentialGen (new MRG32k3a(), mu);
+ waitingTimes.setBroadcasting (true);
+ waitingTimes.addObservationListener (new ObservationTrace (step));
+ waitingTimes.addObservationListener (new LargeWaitsCollector (2.0));
+ }
+
+ public double simulateOneRun (int numCust) {
+ waitingTimes.init();
+ double Wi = 0.0;
+ waitingTimes.add (Wi);
+ for (cust = 2; cust <= numCust; cust++) {
+ Wi += genServ.nextDouble() - genArr.nextDouble();
+ if (Wi < 0.0) Wi = 0.0;
+ waitingTimes.add (Wi);
+ }
+ return waitingTimes.average();
+ }
+
+ public void simulateRuns (int n, int numCust) {
+ averageWaits.init();
+ for (int i=0; i<n; i++)
+ averageWaits.add (simulateOneRun (numCust));
+ }
+
+ public class ObservationTrace implements ObservationListener {
+ private int step;
+
+ public ObservationTrace (int step) { this.step = step; }
+
+ public void newObservation (StatProbe probe, double x) {
+ if (cust % step == 0)
+ System.out.println ("Customer " + cust + " waited "
+ + x + " time units.");
+ }
+ }
+
+ public class LargeWaitsCollector implements ObservationListener {
+ double threshold;
+ ArrayList<Double> largeWaits = new ArrayList<Double>();
+
+ public LargeWaitsCollector (double threshold) {
+ this.threshold = threshold;
+ }
+
+ public void newObservation (StatProbe probe, double x) {
+ if (x > threshold) largeWaits.add (x);
+ }
+
+ public String formatLargeWaits () {
+ // Should print the list largeWaits.
+ return "not yet implemented...";
+ }
+ }
+
+ public static void main (String[] args) {
+ QueueObs queue = new QueueObs (1.0, 2.0, 5);
+ queue.simulateRuns (2, 100);
+ System.out.println ("\n\n" + queue.averageWaits.report());
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/QueueObs2.java b/source/umontreal/iro/lecuyer/examples/QueueObs2.java
new file mode 100644
index 0000000..1126059
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/QueueObs2.java
@@ -0,0 +1,83 @@
+import java.util.Observer;
+import java.util.Observable;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import umontreal.iro.lecuyer.stat.*;
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.ExponentialDist;
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+
+public class QueueObs2 {
+ static final double meanArr = 10.0;
+ static final double meanServ = 9.0;
+ static final int numCust = 50000;
+
+ RandomVariateGen genArr = new RandomVariateGen
+ (new MRG32k3a(), new ExponentialDist (1.0/meanArr));
+ RandomVariateGen genServ = new RandomVariateGen
+ (new MRG32k3a(), new ExponentialDist (1.0/meanServ));
+ Tally waitingTime = new Tally
+ ("Customers waiting time");
+
+ public static void main (String[] args) {
+ QueueObs2 qb = new QueueObs2();
+ SpyWindow sw = new SpyWindow (qb);
+ qb.waitingTime.setBroadcasting (true);
+ qb.waitingTime.addObservationListener (sw);
+ sw.pack();
+ sw.setVisible(true);
+ qb.simulation();
+ System.out.println (qb.waitingTime.report());
+ System.exit (0);
+ }
+
+ public void simulation() {
+ double Wi = 0.0;
+ waitingTime.add (Wi);
+ for (int i = 2; i <= numCust; i++) {
+ Wi += genServ.nextDouble() - genArr.nextDouble();
+ if (Wi < 0.0)
+ Wi = 0.0;
+ waitingTime.add (Wi);
+ }
+ }
+}
+
+class SpyWindow extends JFrame implements ObservationListener {
+ private int nObs = 0;
+ private JTextArea obs = new JTextArea (25, 80);
+ private JScrollPane sp = new JScrollPane (obs);
+ private QueueObs2 qb;
+
+ public SpyWindow (QueueObs2 qb) {
+ super ("Observation spy");
+ this.qb = qb;
+ sp.getViewport().setScrollMode (JViewport.BACKINGSTORE_SCROLL_MODE);
+ setContentPane (sp);
+ setDefaultCloseOperation (WindowConstants.DO_NOTHING_ON_CLOSE);
+ addWindowListener (new WindowClose());
+ }
+
+ public void newObservation (StatProbe probe, double x) {
+ Rectangle vrect = sp.getViewport().getViewRect();
+ Dimension vsize = sp.getViewport().getViewSize();
+ boolean rescroll = vrect.y+vrect.height == vsize.height;
+ obs.append
+ ("Customer " + (nObs++) + " waited " + x + " minutes.\n");
+ synchronized (sp) {
+ JViewport vp = sp.getViewport();
+ vsize = vp.getViewSize();
+ vp.scrollRectToVisible
+ (new Rectangle (0, vsize.height - 15, vsize.width, vsize.height));
+ }
+ }
+
+ class WindowClose extends WindowAdapter {
+ public void windowClosing (WindowEvent ev) {
+ qb.waitingTime.removeObservationListener (SpyWindow.this);
+ dispose();
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/QueueProc.java b/source/umontreal/iro/lecuyer/examples/QueueProc.java
new file mode 100644
index 0000000..c6f78a9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/QueueProc.java
@@ -0,0 +1,43 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.simprocs.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+
+public class QueueProc {
+ Resource server = new Resource (1, "Server");
+ RandomVariateGen genArr;
+ RandomVariateGen genServ;
+
+ public QueueProc (double lambda, double mu) {
+ genArr = new ExponentialGen (new MRG32k3a(), lambda);
+ genServ = new ExponentialGen (new MRG32k3a(), mu);
+ }
+
+ public void simulateOneRun (double timeHorizon) {
+ SimProcess.init();
+ server.setStatCollecting (true);
+ new EndOfSim().schedule (timeHorizon);
+ new Customer().schedule (genArr.nextDouble());
+ Sim.start();
+ }
+
+ class Customer extends SimProcess {
+ public void actions() {
+ new Customer().schedule (genArr.nextDouble());
+ server.request (1);
+ delay (genServ.nextDouble());
+ server.release (1);
+ }
+ }
+
+ class EndOfSim extends Event {
+ public void actions() { Sim.stop(); }
+ }
+
+ public static void main (String[] args) {
+ QueueProc queue = new QueueProc (1.0, 2.0);
+ queue.simulateOneRun (1000.0);
+ System.out.println (queue.server.report());
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/QueueProc.res b/source/umontreal/iro/lecuyer/examples/QueueProc.res
new file mode 100644
index 0000000..8e81515
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/QueueProc.res
@@ -0,0 +1,10 @@
+REPORT ON RESOURCE : Server
+ From time : 0.00 to time : 1000.00
+ min max average standard dev. nb. obs.
+ Capacity 1 1 1.000
+ Utilization 0 1 0.530
+ Queue Size 0 10 0.513
+ Wait 0.000 6.262 0.495 0.835 1037
+ Service 6.5E-4 3.437 0.511 0.512 1037
+ Sojourn 8.2E-4 6.466 1.005 0.979 1037
+
diff --git a/source/umontreal/iro/lecuyer/examples/TimeShared.java b/source/umontreal/iro/lecuyer/examples/TimeShared.java
new file mode 100644
index 0000000..70bef66
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/TimeShared.java
@@ -0,0 +1,87 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.simprocs.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.Tally;
+import java.io.*;
+
+public class TimeShared {
+ int nbTerminal = 20; // Number of terminals.
+ double quantum; // Quantum size.
+ double overhead = 0.001; // Amount of overhead (h).
+ double meanThink = 5.0; // Mean thinking time.
+ double alpha = 0.5; // Parameters of the Weibull service times.
+ double lambda = 1.0; // ''
+ double delta = 0.0; // ''
+ int N = 1100; // Total number of tasks to simulate.
+ int N0 = 100; // Number of tasks for warmup.
+ int nbTasks; // Number of tasks ended so far.
+
+ RandomStream streamThink = new MRG32k3a();
+ RandomVariateGen genThink = new ExponentialGen (streamThink, 1.0/meanThink);
+ RandomStream streamServ = new MRG32k3a ("Gen. for service requirements");
+ RandomVariateGen genServ = new WeibullGen (streamServ, alpha, lambda, delta);
+ Resource server = new Resource (1, "The server");
+ Tally meanInRep = new Tally ("Average for current run");
+ Tally statDiff = new Tally ("Diff. on mean response times");
+
+ class Terminal extends SimProcess {
+ public void actions() {
+ double arrivTime; // Arrival time of current request.
+ double timeNeeded; // Server's time still needed for it.
+ while (nbTasks < N) {
+ delay (genThink.nextDouble());
+ arrivTime = Sim.time();
+ timeNeeded = genServ.nextDouble();
+ while (timeNeeded > quantum) {
+ server.request (1);
+ delay (quantum + overhead);
+ timeNeeded -= quantum;
+ server.release (1);
+ }
+ server.request (1); // Here, timeNeeded <= quantum.
+ delay (timeNeeded + overhead);
+ server.release (1);
+ nbTasks++;
+ if (nbTasks > N0)
+ meanInRep.add (Sim.time() - arrivTime);
+ // Take the observation if warmup is over.
+ }
+ Sim.stop(); // N tasks have now completed.
+ }
+ }
+
+ private void simulOneRun() {
+ SimProcess.init();
+ server.init();
+ meanInRep.init();
+ nbTasks = 0;
+ for (int i=1; i <= nbTerminal; i++)
+ new Terminal().schedule (0.0);
+ Sim.start();
+ }
+
+ // Simulate numRuns pairs of runs and prints a confidence interval
+ // on the difference of perf. for quantum sizes q1 and q2.
+ public void simulateConfigs (double numRuns, double q1, double q2) {
+ double mean1; // To memorize average for first configuration.
+ for (int rep = 0; rep < numRuns; rep++) {
+ quantum = q1;
+ simulOneRun();
+ mean1 = meanInRep.average();
+ streamThink.resetStartSubstream();
+ streamServ.resetStartSubstream();
+ quantum = q2;
+ simulOneRun();
+ statDiff.add (mean1 - meanInRep.average());
+ streamThink.resetNextSubstream();
+ streamServ.resetNextSubstream();
+ }
+ statDiff.setConfidenceIntervalStudent();
+ System.out.println (statDiff.report (0.9, 3));
+ }
+
+ public static void main (String[] args) {
+ new TimeShared().simulateConfigs (10, 0.1, 0.2);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/TimeShared.res b/source/umontreal/iro/lecuyer/examples/TimeShared.res
new file mode 100644
index 0000000..973040a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/TimeShared.res
@@ -0,0 +1,5 @@
+REPORT on Tally stat. collector ==> Diff. on mean response times
+ num. obs. min max average standard dev.
+ 10 -0.134 0.369 0.168 0.174
+ 90.0% confidence interval for mean (student): ( 0.067, 0.269 )
+
diff --git a/source/umontreal/iro/lecuyer/examples/TimeShared.tex b/source/umontreal/iro/lecuyer/examples/TimeShared.tex
new file mode 100644
index 0000000..ebcfce4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/TimeShared.tex
@@ -0,0 +1,173 @@
+\section {A time-shared computer system}
+\label {sec:timeshared}
+
+This example is adapted from \cite{sLAW00a}, Section~2.4.
+Consider a simplified time-shared computer system comprised of $T$
+identical and independent terminals, all busy, using a common server
+(e.g., for database requests, or central processing unit (CPU)
+consumption, etc.).
+Each terminal user sends a task to the server at some random time and
+waits for the response. After receiving the response, he thinks
+for some random time before submitting a new task, and so on.
+
+We assume that the thinking time is an exponential random variable
+with mean $\mu$, whereas the server's time needed for a request is a
+Weibull random variable with parameters $\alpha$, $\lambda$ and $\delta$.
+The tasks waiting for the server form a single queue with a
+{\em round robin\/} service policy with {\em quantum size\/} $q$,
+which operates as follows.
+When a task obtains the server, if it can be completed in less than $q$
+seconds, then it keeps the server until completion.
+Otherwise, it gets the server for $q$ seconds and returns to the back
+of the queue to be continued later.
+In both cases, there is also $h$ additional seconds of {\em overhead\/}
+for changing the task that has the server's attention.
+
+
+\unitlength=1in
+\begin{picture}(6.0, 2.9)(0.0,0.3)
+\thicklines\bf
+\put(1.5,1.0){\circle{0.4}}
+\put(1.5,1.5){\circle{0.4}}
+\put(1.5,2.5){\circle{0.4}}
+\put(1.5,1.0){\makebox(0,0){1}}
+\put(1.5,1.5){\makebox(0,0){2}}
+\put(1.5,2.5){\makebox(0,0){T}}
+\multiput(1.5,1.85)(0.0,0.15){3}{\circle*{0.05}}
+\put(3.9,1.7){\framebox(0.8,0.4){CPU}}
+\multiput(3.5,1.75)(0.1,0.0){4}{\line(0,1){0.3}}
+\put(1.1,1.2){\line(0,1){1.1}}
+\put(1.9,1.2){\line(0,1){1.1}}
+\put(0.7,2.2){\line(0,1){0.5}}
+\put(0.9,2.0){\vector(1,0){0.2}}
+\put(1.9,2.0){\vector(1,0){1.5}}
+\put(3.0,1.8){\vector(1,0){0.4}}
+\put(3.0,1.4){\line(1,0){2.1}}
+\put(4.7,1.9){\line(1,0){0.4}}
+\put(0.9,2.9){\line(1,0){4.2}}
+\put(1.3,1.2){\oval(0.4,0.4)[bl]}
+\put(1.3,1.7){\oval(0.4,0.4)[bl]}
+\put(1.3,2.3){\oval(0.4,0.4)[tl]}
+\put(1.7,2.3){\oval(0.4,0.4)[tr]}
+\put(1.7,1.7){\oval(0.4,0.4)[br]}
+\put(1.7,1.2){\oval(0.4,0.4)[br]}
+\put(0.9,2.2){\oval(0.4,0.4)[bl]}
+\put(0.9,2.7){\oval(0.4,0.4)[tl]}
+\put(3.0,1.6){\oval(0.4,0.4)[l]}
+\put(5.1,1.65){\oval(0.4,0.5)[r]}
+\put(5.1,2.4){\oval(0.4,1.0)[r]}
+\small\rm
+\put(1.5,0.65){\makebox(0,0){Terminals}}
+\put(4.1,1.25){\makebox(0,0){End of quantum}}
+\put(3.2,2.75){\makebox(0,0){End of task}}
+\put(3.65,2.2){\makebox(0,0){Waiting queue}}
+\end{picture}
+
+
+The {\em response time\/} of a task is defined as the difference
+between the time when the task ends (including the overhead $h$ at the
+end) and the arrival time of the task to the server.
+We are interested in the {\em mean response time}, in steady-state.
+We will simulate the system until $N$ tasks have ended, with all
+terminals initially in the ``thinking'' state.
+To reduce the initial bias, we will start collecting statistics only
+after $N_0$ tasks have ended (so the first $N_0$ response times are
+not counted by our estimator, and we take the average response
+time for the $N-N_0$ response times that remain).
+This entire simulation is repeated $R$ times, independently,
+so we can estimate the variance of our estimator.
+% and compute a confidence interval for the true mean response time.
+
+
+Suppose we want to compare the mean response times for two
+different configurations of this system, where a configuration is
+characterized by the vector of parameters $(T, q, h, \mu, \alpha, \lambda, \delta)$.
+We will make $R$ independent simulation runs (replications)
+for each configuration.
+To compare the two configurations, we want to use {\em common random
+numbers}, i.e., the same streams of random numbers
+across the two configurations.
+We couple the simulation runs by pairs:
+for run number $i$, let $R_{1i}$ and $R_{2i}$ be the mean response times
+for configurations 1 and 2, and let
+ $$D_i = R_{1i} - R_{2i}.$$
+We use the same random numbers to obtain $R_{1i}$ and $R_{2i}$,
+for each $i$.
+The $D_i$ are nevertheless independent random variables (under the blunt
+assumption that the random streams really produce independent uniform
+random variables) and we can use them to compute a confidence interval
+for the difference $d$ between the theoretical mean response times of the
+two systems.
+Using common random numbers across $R_{1i}$ and $R_{2i}$ should reduce
+the variance of the $D_i$ and the size of the confidence interval.
+
+\bigskip
+\lstinputlisting[label=lst:TimeShared,caption={Simulation of a time shared system}]{TimeShared.java}
+
+The program of Listing~\ref{lst:TimeShared} performs this simulation.
+In \texttt{TimeShared}, the variable \texttt{conf} indicates the current
+configuration number. For each configuration,
+% we read the configuration data and
+we make \texttt{nbRep} simulation runs.
+The array \texttt{meanConf1} memorizes the values of $R_{1i}$, and the
+statistical probe \texttt{statDiff} collect the differences $D_i$, in
+order to compute a confidence interval for $d$.
+After all the runs for the first configuration have been completed,
+the random number streams are reset to their initial seeds,
+so that the two configurations get the same random numbers.
+The random streams are also reset to the beginning of their next
+substream after each run, to make sure that for the corresponding runs
+for the two configurations, the generators start from exactly the same
+seeds and generate the same numbers.
+
+For each simulation run, the statistical probe \texttt{meanInRep}
+is used to compute the average response time for the $N - N_0$ tasks
+that terminate after the warm-up.
+It is initialized before each run and updated with a new observation
+at the $i$th task termination, for $i = N_0+1,\dots,N$.
+At the beginning of a run, a \texttt{Terminal} process is activated for
+each terminal. When the $N$th task terminates, the corresponding
+process invokes \texttt{Sim.stop} to stop the simulation and
+to return the control to
+the instruction that follows the call to \texttt{simulOneRun} in
+\texttt{TimeShared}.
+
+\setbox3=\vbox {\hsize = 6.0in
+\begin{verbatim}
+REPORT on Tally stat. collector ==> Differences on mean response times
+ min max average standard dev. nb. obs.
+ -0.134 0.369 0.168 0.175 10
+
+ 90.0 confidence interval for mean ( 0.067, 0.269 )
+\end{verbatim}
+}
+
+\begin{figure}[ht]
+\centerline{\boxit{\box3}}
+\caption {Difference in the mean response times for $q=0.1$ and $q=0.2$
+ for the time shared system.}
+\label {fig:timeshared-res}
+\end{figure}
+
+For a concrete example, let $T=20$, $h=.001$, $\mu=5$ sec., $\alpha=1/2$,
+$\lambda=1$ and $\delta=0$ for the two configurations.
+With these parameters, the mean of the Weibull distribution is 2.
+Take $q=0.1$ for configuration 1 and $q=0.2$ for configuration 2.
+We also choose $N_0=100$, $N=1100$, and $R=10$ runs.
+With these numbers in the data file, the program gives the results of
+Figure~\ref{fig:timeshared-res}.
+The confidence interval on the difference between the response time
+with $q=0.1$ and that with $q=0.2$ contains only positive numbers.
+We can therefore conclude that
+the mean response time is significantly shorter (statistically)
+with $q=0.2$ than with $q = 0.1$ (assuming that we can neglect the
+bias due to the choice of the initial state).
+To gain better confidence in this conclusion, we could repeat the
+simulation with larger values of $N_0$ and $N$.
+
+Of course, the model could be made more realistic by considering,
+for example, different types of terminals, with different parameters,
+a number of terminals that changes with time,
+different classes of tasks with priorities, etc.
+SSJ offers the tools to implement these generalizations easily.
+The program would be more elaborate but its structure would be similar.
diff --git a/source/umontreal/iro/lecuyer/examples/Visits.java b/source/umontreal/iro/lecuyer/examples/Visits.java
new file mode 100644
index 0000000..056c026
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Visits.java
@@ -0,0 +1,95 @@
+import umontreal.iro.lecuyer.simevents.*;
+import umontreal.iro.lecuyer.simprocs.*;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+// import umontreal.iro.lecuyer.simprocs.dsol.SimProcess;
+
+public class Visits {
+ int queueSize; // Size of waiting queue.
+ int nbLost; // Number of visitors lost so far today.
+ Bin visitReady = new Bin ("Visit ready");
+ // A token becomes available when there
+ // are enough visitors to start a new visit.
+ Tally avLost = new Tally ("Nb. of visitors lost per day");
+ RandomVariateGen genArriv = new ExponentialGen (new MRG32k3a(), 20.0);
+ // Interarriv.
+ RandomStream streamSize = new MRG32k3a(); // Group sizes.
+ RandomStream streamBalk = new MRG32k3a(); // Balking decisions.
+
+ private void oneDay() {
+ queueSize = 0; nbLost = 0;
+ SimProcess.init();
+ visitReady.init();
+ closing.schedule (16.0);
+ new Arrival().schedule (9.75);
+ for (int i=1; i<=3; i++) new Guide().schedule (10.0);
+ Sim.start();
+ avLost.add (nbLost);
+ }
+
+ Event closing = new Event() {
+ public void actions() {
+ if (visitReady.waitList().size() == 0)
+ nbLost += queueSize;
+ Sim.stop();
+ }
+ };
+
+ class Guide extends SimProcess {
+ public void actions() {
+ boolean lunchDone = false;
+ while (true) {
+ if (Sim.time() > 12.0 && !lunchDone) {
+ delay (0.5); lunchDone = true;
+ }
+ visitReady.take (1); // Starts the next visit.
+ if (queueSize > 15) queueSize -= 15;
+ else queueSize = 0;
+ if (queueSize >= 8) visitReady.put (1);
+ // Enough people for another visit.
+ delay (0.75);
+ }
+ }
+ }
+
+ class Arrival extends SimProcess {
+ public void actions() {
+ while (true) {
+ delay (genArriv.nextDouble());
+ // A new group of visitors arrives.
+ int groupSize; // number of visitors in group.
+ double u = streamSize.nextDouble();
+ if (u <= 0.2) groupSize = 1;
+ else if (u <= 0.8) groupSize = 2;
+ else if (u <= 0.9) groupSize = 3;
+ else groupSize = 4;
+ if (!balk()) {
+ queueSize += groupSize;
+ if (queueSize >= 8 &&
+ visitReady.getAvailable() == 0)
+ // A token is now available.
+ visitReady.put (1);
+ }
+ else nbLost += groupSize;
+ }
+ }
+
+ private boolean balk() {
+ if (queueSize <= 10) return false;
+ if (queueSize >= 40) return true;
+ return (streamBalk.nextDouble() <
+ ((queueSize - 10.0) / 30.0));
+ }
+ }
+
+ public void simulateRuns (int numRuns) {
+ for (int i = 1; i <= numRuns; i++) oneDay();
+ avLost.setConfidenceIntervalStudent();
+ System.out.println (avLost.report (0.9, 3));
+ }
+
+ static public void main (String[] args) {
+ new Visits().simulateRuns (100);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/examples/Visits.res b/source/umontreal/iro/lecuyer/examples/Visits.res
new file mode 100644
index 0000000..68a78dc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Visits.res
@@ -0,0 +1,5 @@
+REPORT on Tally stat. collector ==> Nb. of visitors lost per day
+ num. obs. min max average standard dev.
+ 100 3.000 48.000 21.780 10.639
+ 90.0% confidence interval for mean (student): ( 20.014, 23.546 )
+
diff --git a/source/umontreal/iro/lecuyer/examples/Visits.tex b/source/umontreal/iro/lecuyer/examples/Visits.tex
new file mode 100644
index 0000000..27b2dc7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/Visits.tex
@@ -0,0 +1,108 @@
+\section {Guided visits}
+\label {sec:visits}
+
+This example is translated from \cite{sLEC88a}.
+A touristic attraction offers guided visits, using three guides.
+The site opens at 10:00 and closes at 16:00.
+Visitors arrive in small groups (e.g., families) and the arrival
+process of
+those groups is assumed to be a Poisson process
+with rate of 20 groups per hour, from 9:45 until 16:00.
+The visitors arriving before 10:00 must wait for the opening.
+After 16:00, the visits already under way can be completed,
+but no new visit is undertaken, so that all the visitors still
+waiting cannot visit the site and are lost.
+
+The size of each arriving group of visitors is a discrete random
+variable taking the value $i$ with probability $p_i$ given in the
+following table:
+\begin{center}
+\begin{tabular}{r|rrrr} \hline
+ $i$ \ \ & 1 & 2 & 3 & 4\\ \hline
+ $p_i$ \ & \ .2 & \ .6 & \ .1 & \ .1\\ \hline
+\end{tabular}
+\end{center}
+
+Visits are normally made by groups of 8 to 15 visitors.
+Each visit requires one guide and lasts 45 minutes.
+People waiting for guides form a single queue.
+When a guide becomes free, if there is less than 8 people
+in the queue, the guide waits until the queue grows to at
+least 8 people, otherwise she starts a new visit right away.
+If the queue contains more than 15 people, the first 15 will
+go on this visit.
+At 16:00, if there is less than 8 people in the queue
+and a guide is free, she starts a visit with the remaining
+people.
+At noon, each free guide takes 30 minutes for lunch.
+The guides that are busy at that time will take 30 minutes
+for lunch as soon as they complete their on-going visit.
+
+Sometimes, an arriving group of visitors may decide to just
+go away (balk) because the queue is too long.
+% These visitors are lost.
+We assume that the probability of balking when the queue
+size is $n$ is given by
+$$
+ R(n) = \cases {0 & for $n\le 10;$\cr
+ (n-10)/30 & for $10< n< 40$;\cr
+ 1 & for $n\ge 40$.}
+$$
+
+The aim is to estimate the average number of visitors lost
+per day, in the long run.
+The visitors lost are those that balk or are still in the
+queue after 16:00.
+
+A simulation program for this model is given in
+Listing~\ref{lst:Visits}.
+Here, time is measured in hours, starting at midnight.
+At time 9:45, for example, the simulation clock is at 9.75.
+The (process) class \texttt{Guide} describes the daily
+behavior of a guide (each guide is an instance of this
+class), whereas \texttt{Arrival} generates the arrivals
+according to a Poisson process, the group sizes,
+and the balking decisions.
+The event \texttt{closing} closes the site at 16:00.
+
+The \texttt{Bin} mechanism \texttt{visitReady} is used to
+synchronize the \texttt{Guide} processes.
+The number of tokens in this bin is 1 if there is
+enough visitors in the queue to start a visit (8 or more)
+and is 0 otherwise.
+When the queue size reaches 8 due to a new arrival,
+the \texttt{Arrival} process puts a token into the bin.
+This wakes up a guide if one is free.
+A guide must take a token from the bin to start a new
+visit. If there is still 8 people or more in the queue
+when she starts the visit, she puts the token back to
+the bin immediately, to indicate that another visit is
+ready to be undertaken by the next available guide.
+
+\lstinputlisting[label=lst:Visits,caption={Simulation of guided visits}, lineskip=-1pt]{Visits.java}
+
+\setbox3=\vbox {\hsize = 6.0in
+\begin{verbatim}
+REPORT on Tally stat. collector ==> Nb. of visitors lost per day
+ min max average standard dev. nb. obs.
+ 3 48 21.78 10.639 100
+
+ 90.0% confidence interval for mean (student): ( 20.014, 23.546 )
+\end{verbatim}
+}
+
+\begin{figure}[ht]
+\centerline{\boxit{\box3}}
+\caption {Simulation results for the guided visits model.}
+\label {fig:visits-res}
+\end{figure}
+
+The simulation results are in Figure~\ref{fig:visits-res}.
+
+Could we have used a \texttt{Condition} instead of a \texttt{Bin}
+for synchronizing the \texttt{Guide} processes?
+The problem would be that if several guides are waiting for a
+condition indicating that the queue size has reached 8,
+{\em all\/} these guides (not only the first one)
+would resume their execution
+simultaneously when the condition becomes true.
diff --git a/source/umontreal/iro/lecuyer/examples/event.tex b/source/umontreal/iro/lecuyer/examples/event.tex
new file mode 100644
index 0000000..71e9d04
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/event.tex
@@ -0,0 +1,543 @@
+\section {Discrete-Event Simulation}
+\label {sec:event}
+
+Examples of discrete-event simulation programs, based on the event view
+supported by the package \texttt{simevents}, are given in this section.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {The single-server queue with an event view}
+\label {sec:queue-event}
+
+We return to the single-server queue considered in
+Section~\ref{sec:queue-lindley}.
+This time, instead of simulating a fixed number of customers,
+we simulate the system for a fixed time horizon of 1000.
+
+%%%%%%%%%%%%%%%%%
+\lstinputlisting[label=lst:QueueEv,%
+caption={Event-oriented simulation of an $M/M/1$ queue},%
+lineskip=-1pt,emph={simulateOneRun,actions,main}]{QueueEv.java}
+
+% \bigskip
+
+Listing~\ref{lst:QueueEv} gives an event-oriented simulation program,
+where a subclass of the class \texttt{Event} is defined for each type
+of event that can occur in the simulation:
+arrival of a customer (\texttt{Arrival}),
+departure of a customer (\texttt{Departure}),
+and end of the simulation (\texttt{EndOfSim}).
+Each event {\em instance\/} is inserted into the {\em event list\/}
+upon its creation, with a scheduled time of occurrence, and is
+{\em executed\/} when the simulation clock reaches this time.
+Executing an event means invoking its \texttt{actions} method.
+Each event subclass must implement this method.
+The simulation clock and the event list (i.e., the list of events
+scheduled to occur in the future) are maintained behind the
+scenes by the class \texttt{Sim} of package \texttt{simevents}.
+
+When \texttt{QueueEv} is instantiated by the \texttt{main} method,
+the program creates
+two streams of random numbers,
+two random variate generators, two
+lists, and two statistical probes (or collectors).
+The random number streams
+% can be viewed as virtual random number generators that generate random
+% numbers in the interval $[0,1)$ according to the uniform probability
+are attached to random variate generators
+\texttt{genArr} and \texttt{genServ} which are used to generate the times
+between successive arrivals and the service times, respectively.
+We can use such an attached generator because the means (parameters)
+do not change during simulation.
+The lists \texttt{waitList} and \texttt{servList} contain the customers
+waiting in the queue and the customer in service (if any), respectively.
+Maintaining a list for the customer in service may seem exaggerated,
+because this list never contains more than one object, but the current
+design has the advantage of working with very little change if the
+queuing model has more than one server, and in other more general
+situations.
+Note that we could have used the class \texttt{LinkedListStat} from package
+\texttt{simevents} instead of \texttt{java.util.LinkedList}.
+However, with our current implementation,
+the automatic statistical collection in that \texttt{LinkedListStat}
+class would not count the customers whose waiting time is zero, because
+they are never placed in the list.
+\begin{comment}
+Here we use the class \texttt{List} from package \texttt{simevents}.
+This class is equivalent to the standard class \texttt{java.util.LinkedList},
+except that its implementation is more efficient than the current one
+in JDK and it can also collect statistics automatically.
+However, the automatic statistical collection on \texttt{waitList}
+would not count the customers whose waiting time is zero, because
+they are never placed in this list, so we do not use this facility.
+\end{comment}
+
+The statistical probe \texttt{custWaits} collects statistics on the
+customer's waiting times. It is of the class \texttt{Tally}, which
+is appropriate when the statistical data of interest is a sequence
+of observations $X_1, X_2, \dots$ of which we might want to compute
+the sample mean, variance, and so on.
+A new observation is given to this probe by the \texttt{add} method
+each time a customer starts its service.
+Every \texttt{add} to a \texttt{Tally} probe brings a new observation $X_i$,
+which corresponds here to a customer's waiting time in the queue.
+The other statistical probe, \texttt{totWait}, is of the class
+\texttt{Accumulate}, which means that it computes the integral
+(and, eventually, the time-average) of a continuous-time
+stochastic process with piecewise-constant trajectory.
+Here, the stochastic process of interest is the length of the queue
+as a function of time. One must call \texttt{totWait.update} whenever
+there is a change in the queue size, to update the (hidden)
+{\em accumulator\/} that keeps the current value of the integral
+of the queue length. This integral is equal, after each update,
+to the total waiting time in the queue, for all the customers,
+since the beginning of the simulation.
+
+Each customer is an object with two fields: \texttt{arrivTime}
+memorizes this customer's arrival time to the system, and
+\texttt{servTime} memorizes its service time.
+This object is created, and its fields are initialized,
+when the customer arrives.
+
+The method \texttt{simulateOneRun} simulates this system for a fixed
+time horizon. It first invokes \texttt{Sim.init},
+which initializes the clock and the event list.
+The method \texttt{Sim.start} actually starts the simulation
+by advancing the clock to the time of
+the first event in the event list, removing this event
+from the list, and executing it. This is repeated until either
+\texttt{Sim.stop} is called or the event list becomes empty.
+\texttt{Sim.time} returns the current time on the simulation clock.
+Here, two events are scheduled before starting the simulation:
+the end of the simulation at time horizon, and the
+arrival of the first customer at a random time that has the exponential
+distribution with \emph{rate} $\lambda$ (i.e., \emph{mean} $1/\lambda$),
+generated by \texttt{genArr} using inversion and its attached random stream.
+The method \texttt{genArr.nextDouble} returns this exponential random variate.
+
+The method \texttt{actions} of the class \texttt{Arrival} describes what happens
+when an arrival occurs.
+Arrivals are scheduled by a domino effect:
+the first action of each arrival event schedules the next event in
+a random number of time units, generated from the exponential distribution
+with rate $\lambda$.
+Then, the newly arrived customer is created,
+its arrival time is set to the current simulation time,
+and its service time is generated from the exponential distribution
+with mean $1/\mu$, using the random variate generator \texttt{genServ}.
+If the server is busy, this customer is inserted at the end of the
+queue (the list \texttt{waitList}) and the statistical probe
+\texttt{totWait}, that keeps track of the size of the queue, is updated.
+Otherwise, the customer is inserted in the server's list \texttt{servList},
+its departure is scheduled to happen in a number of time units
+equal to its service time, and a new observation of 0.0 is given to the
+statistical probe \texttt{custWaits} that collects the waiting times.
+
+When a \texttt{Departure} event occurs, the customer in service is
+removed from the list (and disappears).
+If the queue is not empty, the first customer is removed from
+the queue (\texttt{waitList}) and inserted in the server's list,
+and its departure is scheduled.
+The waiting time of that customer (the current time minus its
+arrival time) is given as a new observation to the probe
+\texttt{custWaits}, and the probe \texttt{totWait} is also updated
+with the new (reduced) size of the queue.
+
+The event \texttt{EndOfSim} stops the simulation.
+Then the \texttt{main} routine regains control and prints statistical
+reports for the two probes.
+The results are shown in Listing~\ref{res:QueueEv}.
+When calling \texttt{report} on an \texttt{Accumulate} object, an implicit
+update is done using the current simulation time and the last
+value given to \texttt{update}. In this example, this ensures
+that the \texttt{totWait} accumulator will integrate the total wait
+until the time horizon, because the simulation clock is still at that
+time when the report is printed.
+Without such an automatic update, the accumulator would integrate
+only up to the last update time before the time horizon.
+
+
+%%%%%%%%%%%%%%%%%
+\lstinputlisting[label=res:QueueEv,%
+caption={Results of the program \texttt{QueueEv}},%
+lineskip=-1pt]{QueueEv.res}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {Continuous simulation: A prey-predator system}
+\label {sec:preypred}
+
+We consider a classical prey-predator system, where the preys
+are food for the predators (see, e.g., \cite{sLAW00a}, page 87).
+Let $x(t)$ and $z(t)$ be the numbers of preys and predators
+at time $t$, respectively.
+These numbers are integers, but as an approximation,
+we shall assume that they are real-valued variables evolving
+according to the differential equations
+\begin{eqnarray*}
+ x'(t) &= &\ r x(t) - c x(t) z(t)\\
+ z'(t) &= & -s z(t) + d x(t) z(t)
+\end{eqnarray*}
+with initial values $x(0)=x_0>0$ et $z(0)=z_0>0$.
+This is a \emph{Lotka-Volterra} system of differential
+equations, which has a known analytical solution.
+Here, in the program of Listing~\ref{lst:PreyPred},
+we simply simulate its evolution, to illustrate the continuous
+simulation facilities of SSJ.
+
+\lstinputlisting[label=lst:PreyPred,%
+caption={Simulation of the prey-predator system},
+emph={derivative,actions,main}
+]{PreyPred.java}
+
+% \bigskip
+Note that, instead of using the default simulator, this program
+explicitly creates a discrete-event \class{Simulator} object to manage the
+execution of the simulation, unlike the other examples in this section.
+
+The program prints the triples $(t, x(t), z(t))$ at values of
+$t$ that are multiples of \texttt{h}, one triple per line.
+This is done by an event of class \texttt{PrintPoint}, which is
+rescheduled at every \texttt{h} units of time.
+This output can be redirected to a file for later use,
+for example to plot a graph of the trajectory.
+The continuous variables \texttt{x} and \texttt{z} are instances of the
+classes \texttt{Preys} and \texttt{Preds}, whose method \texttt{derivative}
+give their derivative $x'(t)$ and $z'(t)$, respectively.
+The differential equations are integrated by a Runge-Kutta method
+of order 4.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {A simplified bank}
+\label {sec:bank}
+
+This is Example~1.4.1 of \cite{sBRA87a}, page~14.
+% Bratley, Fox, and Schrage (1987).
+A bank has a random number of tellers every morning.
+On any given day, the bank has $t$ tellers with probability $q_t$,
+where $q_3 = 0.80$, $q_2 = 0.15$, and $q_1 = 0.05$.
+All the tellers are assumed to be identical from the modeling viewpoint.
+
+
+%%%%%%%%%%%%%
+\setbox0=\vbox{\hsize=6.0in
+%% {Arrival rate of customers to the bank.}
+\beginpicture
+\setcoordinatesystem units <1.8cm,2cm>
+%% 72.27pt = 1in
+\setplotarea x from 0 to 6.5, y from 0 to 1
+\axis left
+ label {\lines {arrival \ \cr rate }}
+ ticks length <2pt> withvalues 0.5 1 / at 0.5 1 / /
+\axis bottom
+ label {\hbox to 5.4in {\hfill time}}
+ ticks length <2pt> withvalues 9:00 9:45 11:00 14:00 15:00 /
+ at 0.0 0.75 2.0 5.0 6.0 / /
+\shaderectangleson
+\putrectangle corners at 0.75 0.0 and 2.0 0.5
+\putrectangle corners at 2.0 0.0 and 5.0 1.0
+\putrectangle corners at 5.0 0.0 and 6.0 0.5
+\endpicture
+}
+
+\begin{figure}[htb]
+\box0
+\caption {Arrival rate of customers to the bank.}
+\label {fig:blambda}
+\end{figure}
+
+\lstinputlisting[label=lst:BankEv,
+caption={Event-oriented simulation of the bank model},
+emph={simulOneDay,simulateDays,actions,balk,main}]%
+{BankEv.java}
+
+% \bigskip
+
+The bank opens at 10:00 and closes at 15:00 (i.e., {\sc 3 p.m.}).
+The customers arrive randomly according to a Poisson process
+with piecewise constant rate $\lambda(t)$, $t\ge 0$.
+The arrival rate $\lambda(t)$ (see Fig.{}~\ref{fig:blambda})
+is 0.5 customer per minute from
+9:45 until 11:00 and from 14:00 until 15:00, and
+one customer per minute from 11:00 until 14:00.
+The customers who arrive between 9:45 and 10:00 join a FIFO queue
+and wait for the bank to open.
+At 15:00, the door is closed, but all the customers already in will be served.
+Service starts at 10:00.
+
+Customers form a FIFO queue for the tellers, with balking.
+An arriving customer will balk (walk out) with probability $p_k$ if there
+are $k$ customers ahead of him in the queue (not counting the people
+receiving service), where
+ $$ p_k = \cases { 0 & if $k\le 5$;\cr
+ (n-5)/5 & if $5 < k < 10$;\cr
+ 1 & if $k\ge 10$.\cr }$$
+The customer service times are independent Erlang random
+variables: Each service time is the sum of
+two independent exponential random variables with mean one.
+
+We want to estimate the expected number of customers served in a
+day, and the expected average wait for the customers
+served on a day.
+% We could also be interested in the effect of changing the number of tellers,
+% changing their speed, and so on.
+
+Listing~\ref{lst:BankEv} gives and event-oriented simulation
+program for this bank model.
+There are events at the fixed times 9:45, 10:00, etc.
+At 9:45, the counters are initialized and the arrival process
+is started. The time until the first arrival,
+or the time between one arrival and the next one, is (tentatively)
+an exponential with a mean of 2 minutes.
+However, as soon as an arrival turns out to be past 11:00,
+its time must be readjusted to take into account the increase of the
+arrival rate at 11:00.
+The event 11:00 takes care of this readjustment,
+and the event at 14:00 makes a similar readjustment
+when the arrival rate decreases.
+We give the specific name \texttt{nextArriv} to the next planned
+arrival event in order to be able to reschedule
+that particular event to a different time.
+Note that a {\em single\/} arrival event is
+created at the beginning and this same event is scheduled over and
+over again. This can be done because there is never more than one
+arrival event in the event list.
+(We could have done that as well for the $M/M/1$ queue in
+Listing \ref{lst:QueueEv}.)
+
+At the bank opening at 10:00, an event generates the number
+of tellers and starts the service for the corresponding customers.
+The event at 15:00 cancels the next arrival.
+
+Upon arrival, a customer checks if a teller is free.
+If so, one teller becomes busy and the customer generates its
+service time and schedules his departure, otherwise the
+customer either balks or joins the queue.
+The balking decision is computed by the method \texttt{balk},
+using the random number stream \texttt{streamBalk}.
+The arrival event also generates the next scheduled arrival.
+Upon departure, the customer frees the teller, and the first
+customer in the queue, if any, can start its service.
+The generator \texttt{genServ} is an \texttt{ErlangConvolutionGen} generator,
+so that the Erlang variates are generated by adding two exponentials instead
+of using inversion.
+
+The method \texttt{simulateDays} simulates the bank
+for \texttt{numDays} days and prints a statistical report.
+If $X_i$ is the number of customers served on day $i$ and
+$Q_i$ the total waiting time on day $i$, the program estimates
+$E[X_i]$ and $E[Q_i]$ by their sample averages $\bar X_n$ and
+$\bar Q_n$ with $n = $\texttt{numDays}.
+For each simulation run (each day), \texttt{simulOneDay} initializes
+the clock, event list, and statistical probe for the waiting times,
+schedules the deterministic events, and runs the simulation.
+After 15:00, no more arrival occurs and the event list becomes
+empty when the last customer departs.
+At that point, the program returns to right after the \texttt{Sim.start()}
+statement and updates the statistical counters for the number of
+customers served during the day and their total waiting time.
+
+The results are given in Listing~\ref{res:Bank}.
+
+\lstinputlisting[label=res:Bank,
+caption={Results of the \texttt{BankEv} program}]%
+{Bank.res}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {A call center}
+\label {sec:call-center}
+
+We consider here a simplified model of a telephone contact center
+(or \emph{call center}) where agents answer incoming calls.
+% in FIFO order.
+Each day, the center operates for $m$ hours.
+The number of agents answering calls and the arrival rate
+of calls vary during the day; we shall assume that
+they are constant within each hour of operation but depend on the hour.
+% We number the hours of operations staring from zero.
+Let $n_j$ be the number of agents in the center during hour $j$,
+for $j=0,\dots,m-1$.
+For example, if the center operates from 8 {\sc am} to 9 {\sc pm},
+then $m=13$ and hour $j$ starts at ($j+8$) o'clock.
+All agents are assumed to be identical.
+When the number of occupied agents at the end of hour $j$ is larger
+than $n_{j+1}$, ongoing calls are all completed but new calls are
+answered only when there are less than $n_{j+1}$ agents busy.
+After the center closes, ongoing calls are completed and calls already
+in the queue are answered, but no additional incoming call is taken.
+
+The calls arrive according to a Poisson process with piecewise constant rate,
+equal to $R_j = B \lambda_j$ during hour $j$, where the $\lambda_j$
+are constants and $B$ is a random variable having the gamma distribution
+with parameters $(\alpha_0,\alpha_0)$.
+% $E[B] = \alpha/\lambda$ and $\Var[B] = \alpha /\lambda^2$.
+Thus, $B$ has mean 1 and variance $1/\alpha_0$, and it
+represents the \emph{busyness} of the day; it is more busy than usual
+when $B > 1$ and less busy when $B < 1$.
+The Poisson process assumption means that conditional on $B$,
+the number of incoming calls during any subinterval $(t_1, t_2]$
+of hour $j$ is a Poisson random variable with
+mean $(t_2 - t_1) B \lambda_j$ and that the arrival counts in
+any disjoint time intervals are independent random variables.
+This arrival process model is motivated and studied in
+\cite{ccWHI99c} and \cite{ccAVR04a}.
+
+Incoming calls form a FIFO queue for the agents.
+% with impatient customers.
+A call is \emph{lost} (abandons the queue) when its waiting time
+exceed its \emph{patience time}.
+The patience times of calls are assumed to be i.i.d.{} random variables
+with the following distribution: with probability $p$ the patience
+time is 0 (so the person hangs up unless there is an agent
+available immediately), and with probability $1-p$ it is exponential
+with mean $1/\nu$.
+The service times
+% (times to handle the calls)
+are i.i.d.{} gamma random variables with parameters $(\alpha,\beta)$.
+% Or perhaps Erlang?
+
+We want to estimate the following quantities
+\emph{in the long run} (i.e., over an infinite number of days):
+(a) $w$, the average waiting time per call,
+(b) $g(s)$, the fraction of calls whose waiting time is less than
+ $s$ seconds for a given threshold $s$, and
+(c) $\ell$, the fraction of calls lost due to abandonment.
+
+Suppose we simulate the model for $n$ days. For each day $i$, let
+$A_i$ be the number of arrivals,
+$W_i$ the total waiting time of all calls,
+$G_i(s)$ the number of calls who waited less than $s$ seconds,
+and $L_i$ the number of abandonments.
+For this model, the expected number of incoming calls in a day is
+$a = E[A_i] = \sum_{j=0}^{m-1} \lambda_j$.
+% We have that $w = E[W_i]/a$, $g(s) = E[G_i(s)]/a$, and $\ell = E[L_i]/a$.
+Then, $W_i/a$, $G_i(s)/a$, and $L_i/a$, $i=1,\dots,n$,
+are i.i.d.{} unbiased estimators of $w$, $g(s)$, and $\ell$, respectively,
+and can be used to compute confidence intervals for these quantities
+in a standard way if $n$ is large.
+
+
+\lstinputlisting[label=lst:CallCenter,
+caption={Simulation of a simplified call center},
+emph={simulateOneDay,actions,generPatience,checkQueue,endWait,readData,main}
+]%
+{CallCenter.java}
+
+% \bigskip
+
+Listing~\ref{lst:CallCenter} gives an event-oriented simulation
+program for this call center model.
+When the \texttt{CallCenter} class is instantiated by the \texttt{main} method,
+the random streams, list, and statistical probes are created,
+and the model parameters are read from a file by the method \texttt{readData}.
+The line \texttt{Locale.setDefault(Locale.US)} is added because
+real numbers in the data file are read in the anglo-saxon form 8.3
+instead of the form 8,3 used by most countries in the world.
+The \texttt{main} program then simulates $n = 1000$ operating days and
+prints the value of $a$, as well as 90\%{} confidence intervals on
+$a$, $w$, $g(s)$, and $\ell$, based on their estimators
+$\bar A_n$, $\bar W_n/a$, $\bar G_n(s)/a$, and $\bar L_n/a$,
+assuming that these estimators have approximately the Student distribution.
+This is justified by the fact that $W_i$, and $G_i(s)$, and $L_i$
+are themselves ``averages'' over several observations, so we may expect
+their distribution to be not far from a normal.
+
+To generate the service times, we use a gamma random variate
+generator called \texttt{genServ}, created in the constructor
+after the parameters $(\alpha,\beta)$ of the service time distribution
+have been read from the data file.
+For the other random variables in the model, we simply create random
+streams of i.i.d.{} uniforms (in the preamble) and apply inversion
+explicitly to generate the random variates.
+The latter approach is more convenient, e.g., for patience times
+because their distribution is not standard and for the inter-arrival
+times because their mean changes every period.
+For the gamma service time distribution, on the other hand,
+the parameters always remain the same and inversion is rather slow,
+so we decided to create a generator that uses a faster special method.
+
+The method \texttt{simulateOneDay} simulates one day of operation.
+It initializes the simulation clock, event list, and counters,
+schedules the center's opening
+% (start of the first period)
+and the first arrival, and starts the simulation.
+When the day is over, it updates the statistical collectors.
+Note that there are two versions of this method; one that generates the random
+variate $B$ and the other that takes its value as an input parameter.
+This is convenient in case one wishes to simulate the center with
+a fixed value of $B$.
+
+An event \texttt{NextPeriod(j)} marks the beginning of each period $j$.
+The first of these events (for $j=0$) is scheduled by \texttt{simulateOneDay};
+then the following ones schedule each other successively,
+until the end of the day.
+This type of event updates the number of agents in the center and
+the arrival rate for the next period.
+If the number of agents has just increased and the queue is not empty,
+some calls in the queue can now be answered.
+The method \texttt{checkQueue} verifies this and starts service for the
+appropriate number of calls.
+The time until the next planned arrival is readjusted to take into account
+the change of arrival rate, as follows.
+The inter-arrival times are i.i.d.{} exponential with
+mean $1/R_{j-1}$ when the arrival rate is fixed at $R_{j-1}$.
+But when the arrival rate changes from $R_{j-1}$ to $R_j$,
+the residual time until the next arrival should be modified from an
+exponential with mean $1/R_{j-1}$ (already generated)
+to an exponential with mean $1/R_j$.
+Multiplying the residual time by $\lambda_{j-1}/\lambda_j$ is an easy
+way to achieve this.
+We give the specific name \texttt{nextArrival} to the next arrival
+event in order to be able to reschedule it to a different time.
+Note that there is a \emph{single} arrival event which is scheduled
+over and over again during the entire simulation.
+This is more efficient than creating a new arrival event for each
+call, and can be done here because there is never more than one arrival
+event at a time in the event list.
+At the end of the day, simply canceling the next arrival makes sure
+that no more calls will arrive.
+
+Each arrival event first schedules the next one.
+Then it increments the arrivals counter and creates the new call that just
+arrived. The call's constructor generates its service time and decides
+where the incoming call should go.
+If an agent is available, the call is answered immediately
+(its waiting time is zero), and an event is scheduled for the completion
+of the call. Otherwise, the call must join the queue;
+its patience time is generated by \texttt{generPatience} and memorized,
+together with its arrival time, for future reference.
+
+Upon completion of a call, the number of busy agents is decremented
+and one must verify if a waiting call can now be answered.
+The method \texttt{checkQueue} verifies that and if the answer is yes,
+it removes the first call from the queue and activates its \texttt{endWait}
+method.
+This method first compares the call's waiting time with its patience time,
+to see if this call is still waiting or has been lost (by abandonment).
+If the call was lost, we consider its waiting time
+as being equal to its patience time (i.e., the time that the caller
+has really waited), for the statistics.
+If the call is still there, the number of busy agents is incremented
+and an event is scheduled for the call completion.
+
+The results of this program, with the data in file
+\texttt{CallCenter.dat}, are shown in Listing~\ref{res:CallCenter}.
+
+\lstinputlisting[label=res:CallCenter,
+caption={Simulation of a simplified call center},
+float=tp]%
+{CallCenter.res}
+
+% \bigskip
+
+This model is certainly an oversimplification of actual call centers.
+It can be embellished and made more realistic by considering
+different types of agents, different types of calls,
+agents taking breaks for lunch, coffee, or going to the restroom,
+agents making outbound calls to reach customers when the inbound
+traffic is low (e.g., for marketing purpose or for returning calls),
+and so on. One could also model the revenue generated by calls and
+the operating costs for running the center,
+and use the simulation model to compare alternative operating strategies
+in terms of the expected net revenue, for example.
diff --git a/source/umontreal/iro/lecuyer/examples/examples.bbl b/source/umontreal/iro/lecuyer/examples/examples.bbl
new file mode 100644
index 0000000..107eecf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/examples.bbl
@@ -0,0 +1,97 @@
+\begin{thebibliography}{10}
+
+\bibitem{ccAVR04a}
+A.~N. Avramidis, A.~Deslauriers, and P.~L'Ecuyer.
+\newblock Modeling daily arrivals to a telephone call center.
+\newblock {\em Management Science}, 50(7):896--908, 2004.
+
+\bibitem{sBRA87a}
+P.~Bratley, B.~L. Fox, and L.~E. Schrage.
+\newblock {\em A Guide to Simulation}.
+\newblock Springer-Verlag, New York, NY, second edition, 1987.
+
+\bibitem{sFIS96a}
+G.~S. Fishman.
+\newblock {\em {M}onte {C}arlo: Concepts, Algorithms, and Applications}.
+\newblock Springer Series in Operations Research. Springer-Verlag, New York,
+ NY, 1996.
+
+\bibitem{fGLA04a}
+P.~Glasserman.
+\newblock {\em Monte {C}arlo Methods in Financial Engineering}.
+\newblock Springer-Verlag, New York, 2004.
+
+\bibitem{iHOS04a}
+Wolfgang Hoschek.
+\newblock {\em The Colt Distribution: Open Source Libraries for High
+ Performance Scientific and Technical Computing in Java}.
+\newblock CERN, Geneva, 2004.
+\newblock Available at \url{http://acs.lbl.gov/software/colt/}.
+
+\bibitem{iMOR80a}
+{J. J. Mor\'e and B. S. Garbow and K. E. Hillstrom}.
+\newblock {\em User Guide for MINPACK-1, Report ANL-80-74}.
+\newblock Argonne, Illinois, USA, 1980.
+\newblock See
+ \url{http://www-fp.mcs.anl.gov/otc/Guide/softwareGuide/Blurbs/minpack.html}.
+
+\bibitem{pKLE75a}
+L.~Kleinrock.
+\newblock {\em Queueing Systems, Vol. 1}.
+\newblock Wiley, New York, NY, 1975.
+
+\bibitem{sLAW00a}
+A.~M. Law and W.~D. Kelton.
+\newblock {\em Simulation Modeling and Analysis}.
+\newblock McGraw-Hill, New York, NY, third edition, 2000.
+
+\bibitem{sLEC88a}
+P.~L'{E}cuyer.
+\newblock {SIMOD}: D{\'e}finition fonctionnelle et guide d'utilisation (version
+ 2.0).
+\newblock Technical Report DIUL-RT-8804, D{\'e}partement d'informatique,
+ Universit\'e Laval, Sept 1988.
+
+\bibitem{sLEC05a}
+P.~L'Ecuyer and E.~Buist.
+\newblock Simulation in {J}ava with {SSJ}.
+\newblock In M.~E. Kuhl, N.~M. Steiger, F.~B. Armstrong, and J.~A. Joines,
+ editors, {\em Proceedings of the 2005 Winter Simulation Conference}, pages
+ 611--620, Piscataway, NJ, 2005. {IEEE} Press.
+
+\bibitem{sLEC02a}
+P.~L'Ecuyer, L.~Meliani, and J.~Vaucher.
+\newblock {SSJ}: A framework for stochastic simulation in {J}ava.
+\newblock In E.~Y\"ucesan, C.-H. Chen, J.~L. Snowdon, and J.~M. Charnes,
+ editors, {\em Proceedings of the 2002 Winter Simulation Conference}, pages
+ 234--242. {IEEE} Press, 2002.
+
+\bibitem{sLEC09a}
+P.~L'Ecuyer and A.~B. Owen, editors.
+\newblock {\em {M}onte {C}arlo and Quasi-{M}onte {C}arlo Methods 2008}.
+\newblock Springer-Verlag, Berlin, 2010.
+
+\bibitem{iLEY02a}
+J.~Leydold and W.~H\"ormann.
+\newblock {\em {UNU.RAN}---A Library for Universal Non-Uniform Random Number
+ Generators}, 2002.
+\newblock Available at \url{http://statistik.wu-wien.ac.at/unuran}.
+
+\bibitem{iSCHa}
+R.~B. Schnabel.
+\newblock {\em {UNCMIN}---Unconstrained Optimization Package, FORTRAN}.
+\newblock University of Colorado at Boulder.
+\newblock See \url{http://www.ici.ro/camo/unconstr/uncmin.htm}.
+
+\bibitem{ccWHI99c}
+W.~Whitt.
+\newblock Dynamic staffing in a telephone call center aiming to immediately
+ answer all calls.
+\newblock {\em Operations Research Letters}, 24:205--212, 1999.
+
+\bibitem{pWOL89a}
+R.~W. Wolff.
+\newblock {\em Stochastic Modeling and the Theory of Queues}.
+\newblock Prentice-Hall, New York, NY, 1989.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/examples/examples.tex b/source/umontreal/iro/lecuyer/examples/examples.tex
new file mode 100644
index 0000000..13cd294
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/examples.tex
@@ -0,0 +1,152 @@
+\documentclass[twoside,12pt]{article}
+
+\usepackage{ssj}
+\usepackage[procnames]{listings}
+\usepackage{lstpatch}
+\usepackage{url}
+\usepackage{color}
+\usepackage{crayola}
+\usepackage{amsfonts}
+% \usepackage{mycal}
+% \usepackage{mybold}
+% \usepackage{mymathbb}
+\usepackage{pictexwd}
+
+
+\lstloadlanguages{Java}
+\lstset{language=Java,
+float=tbhp,
+captionpos=t,
+frame=trbl,
+abovecaptionskip=1.5em,
+belowskip=2em,
+basicstyle=\small\ttfamily,
+stringstyle=\color{OliveGreen},
+commentstyle=\color{red},
+identifierstyle=\color{Bittersweet},
+basewidth={0.5em},
+showstringspaces=false,
+framerule=0.8pt,
+procnamestyle=\bfseries\color{blue},
+emphstyle=\bfseries\color{Cerulean},
+procnamekeys={class,extends,interface,implements}
+}
+
+% listings lacks a label feature to refer to listings.
+% The code was inspired from the \label LaTeX command definition.
+% \makeatletter
+% \newcommand{\lstlabel}[1]{{
+% \let\@currentlabel=\thelstlisting
+% \label{#1}}}
+% \makeatother
+
+\def\g {{\;\leftarrow\;}}
+\def\tbu {\tilde{\mbox{\boldmath $u$}}}
+\def\bu {\mbox{\boldmath $u$}}
+
+%%%%%%%%%%%%%%
+%begin{latexonly}
+%\def\fiverm {}
+%\input prepictex.tex \input pictex.tex \input postpictex.tex
+%\valuestolabelleading = -0.1\baselineskip
+%end{latexonly}
+\vbadness=10000
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+
+\null\vfill
+\begin {center}
+{\Large\bf SSJ User's Guide } \\[12pt]
+{\Large Overview and Examples} \\[20pt]
+% of Simulation programs using SSJ} \\[20pt]
+ Version: \today \\
+\vfill
+ {\sc Pierre L'Ecuyer}
+\footnote {\normalsize
+ SSJ was designed and implemented in the Simulation laboratory of the
+ D\'epartement d'Informa\-tique et de Recherche
+ Op\'erationnelle (DIRO), at the Universit\'e de Montr\'eal,
+ under the supervision of
+ Pierre L'Ecuyer, with the contribution of
+% (until August 2004):
+%\begin{verse}
+ Mathieu Bague,
+ Sylvain Bonnet,
+ \'Eric Buist,
+% Chiheb Dkhil,
+ Yves Edel,
+ Regina H.{} S.{} Hong,
+ Alexander Keller,
+ Pierre L'Ecuyer,
+ \'Etienne Marcotte,
+ Lakhdar Meliani,
+ Abdelazziz Milib,
+ Fran\c{c}ois Panneton,
+ Richard Simard,
+ Pierre-Alexandre Tremblay,
+ and Jean Vaucher.
+%\end{verse}
+%
+Its development has been supported by NSERC-Canada grant No.\ ODGP0110050,
+NATEQ-Qu\'ebec grant No.\ 02ER3218, a Killam fellowship,
+and a Canada Research Chair to P.~L'Ecuyer.
+
+% listed at the end of the \emph{SSJ overview} document.
+% The first implementation of SSJ was done by Lakhdar Meliani
+% for his master's thesis.
+} \\[10pt]
+ Canada Research Chair in Stochastic Simulation and Optimization \\
+% Chaire du Canada en simulation et optimisation stochastiques \\
+ D\'epartement d'Informatique et de Recherche Op\'erationnelle \\
+ Universit\'e de Montr\'eal, Canada
+\vfill\vfill
+\end {center}
+
+\begin {quotation}
+\noindent
+SSJ stands for \emph{stochastic simulation in Java}.
+The SSJ library provides facilities for generating uniform and nonuniform random
+variates, computing different measures related to probability
+distributions, performing goodness-of-fit tests, applying
+quasi-Monte Carlo methods, collecting statistics,
+and programming discrete-event simulations with both events and processes.
+This document provides a very brief overview of this library
+and presents several examples of small simulation programs in Java,
+based on this library. The examples are commented in detail.
+They can serve as a good starting point for learning how to use SSJ.
+The first part of the guide gives very simple examples that do not
+need event or process scheduling.
+The second part contains examples of discrete-event simulation
+programs implemented with an \emph{event view},
+% using the package \texttt{simevents}.
+while the third part gives examples of implementations based
+on the \emph{process view}.
+% supported by the package \texttt{simprocs}.
+\end {quotation}
+
+\vfill
+\end{titlepage}
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+\include{intro}
+\include{overview}
+\include{simple}
+\include{event}
+\include{process}
+
+%\include{Queue}
+%\include{PreyPred}
+%\include{Jobshop}
+%\include{TimeShared}
+%\include{Bank}
+%\include{Visits}
+
+\bibliography{simul,random,ift,stat,prob,fin,callc}
+\bibliographystyle{plain}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/examples/intro.tex b/source/umontreal/iro/lecuyer/examples/intro.tex
new file mode 100644
index 0000000..3466b0b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/intro.tex
@@ -0,0 +1,71 @@
+\section{Introduction}
+% \addcontentsline{toc}{section}{Introduction}}
+
+The aim of this document is to provide an introduction to SSJ
+via a brief overview and a series of examples.
+The examples are collected in three groups:
+\begin{itemize}
+\itemsep=0pt
+\item[(1)]
+ those that need no event or process scheduling;
+\item[(2)]
+ those based on the discrete-event simulation paradigm
+ and implemented with an \emph{event view} using the package
+ \texttt{simevents};
+\item[(3)]
+ those implemented with the \emph{process view},
+ supported by the package \texttt{simprocs}.
+\end{itemize}
+Sections~\ref{sec:simple} to \ref{sec:process}
+of this guide correspond to these three groups.
+Some examples (e.g., the single-server queue) are carried across
+two or three sections to illustrate different ways of implementing
+the same model.
+The Java code of all these examples is available on-line from the
+SSJ web page (just type ``SSJ iro'' in Google).
+
+While studying the examples, the reader can refer to the functional
+definitions (the APIs) of the SSJ classes and methods in the guides of the
+corresponding packages.
+Each package in SSJ has its own user's guide in the form of a \texttt{.pdf}
+document that contains the detailed API and complete documentation,
+and starts with an overview of one or two pages.
+We strongly recommend reading each of these overviews.
+We also recommend to refer to the \texttt{.pdf} versions of the guides,
+because they contain a more detailed and complete documentation
+than the \texttt{.html} versions, which are better suited for quick
+on-line referencing for those who are already familiar with SSJ.
+
+
+%%%%%%%%%%%%%%%%%%%%
+\begin{comment}
+
+In Section~\ref{sec:queue}, we start with a very simple classical
+example: a single queue.
+We give different variants of this example, illustrating the mixture
+of processes and events.
+%
+In Section~\ref{sec:preypred}, we give a small example of a deterministic
+continuous simulation.
+%
+In Sections~\ref{sec:jobshop} and \ref{sec:timeshared},
+we give examples of a job shop model and a time-shared computer model,
+adapted from \cite{sLAW00a}.
+%
+A queuing model of a bank, taken from \cite{sBRA87a}, is programmed in
+Section~\ref{sec:bank}, with both the process and event views.
+%
+In Section~\ref{sec:visits}, we simulate a model of guided tours
+for groups of people, where the process synchronization is slightly
+more complicated than for the earlier models.
+%
+In Section~\ref{sec:ingots}, we give an example
+of a mixed discrete-continuous simulation.
+%
+In Section~\ref{sec:robot}, we give a more elaborate example,
+for a model discussed in \cite{sLEC91a}, where a robot maintains
+a series of machines subject to random failures.
+It illustrates the idea of modular design.
+% for large simulation models.
+
+\end{comment}
diff --git a/source/umontreal/iro/lecuyer/examples/overview.tex b/source/umontreal/iro/lecuyer/examples/overview.tex
new file mode 100644
index 0000000..7791107
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/overview.tex
@@ -0,0 +1,235 @@
+
+\section{Overview of SSJ}
+
+SSJ is an organized set of packages whose purpose is to facilitate
+stochastic simulation programming in the Java language.
+The facilities offered are grouped into different packages,
+each one having its own user's guide as a \texttt{.pdf} file.
+This is the official documentation.
+There is also a simplified on-line documentation in HTML
+format, produced via \texttt{javadoc}.
+Early descriptions of SSJ are given in \cite{sLEC02a,sLEC05a}.
+Some of the tools can also be used for modeling (e.g., selecting
+and fitting distributions).
+SSJ is still growing actively.
+New packages, classes, and methods will be added in
+forthcoming years and others will be refined.
+
+
+
+\input{packdep.tex}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{comment}
+
+% Three packages are used for uniform and non-uniform random number
+% generation, one for the uniform random number generators, one for the
+% probability distributions and one for the non-uniform random number
+% generation. It is also possible to replace the streams of uniform
+% random numbers with highly uniform point sets for Quasi-Monte Carlo
+% \cite{vLEC02a} simulation.
+% As opposed to most random number generation libraries, it
+% is also possible to compute densities, distribution functions and
+% inverse distribution functions for all supported distributions.
+% The \externalclass{umontreal.iro.lecuyer}{simevents}
+% and \externalclass{umontreal.iro.lecuyer}{simprocs} packages make the
+% heart of the SSJ library.
+% They provide an efficient framework for stochastic simulation,
+% supporting the event view, proces view, continuous simulation, and arbitrary
+% mixtures of these.
+% The other packages allow one to perform miscellaneous tasks, such as
+% statistical collection and goodness of fit tests.
+
+\subsection*{random number generation}
+
+Random numbers feed simulation models and allow one to compute
+statistics. To generate random numbers from
+any probability distribution, uniform random numbers are required.
+Such numbers are uniformly distributed in the $[0,1)$ interval, i.e.,
+the probability of getting a given number $x$ in that interval is the same
+for all values of $x\in[0,1)$. Any generated number $x$ is also
+independent from any previous or future generated numbers. Although
+the generated uniforms are not truly independent since one uniform is
+obtained from the previous uniforms by a mathematical formula, one can
+consider them independent for simulation purposes. Selection of a
+random number generator is based on several criteria such as
+uniformity, performance, and portability \cite{rLEC01d}.
+The package
+\externalclass{umontreal.iro.lecuyer}{rng} contains the needed tools
+to generate such numbers. It defines an interface called
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} implemented by
+any random number generator supported by SSJ. This interface allows
+one to easily interchange random number generators since they are
+accessed through the same set of methods specified by the interface.
+Only the random number generator setup depends on the type of
+generator that was chosen.
+
+If one wants to replace uniform random numbers with low-discrepancy
+point sets for variance reduction, the package
+\externalclass{umontreal.iro.lecuyer}{hups} contains all the necessary
+facilities. Such highly uniform point sets all inherit from the
+\externalclass{umontreal.iro.lecuyer.hups}{PointSet} which provides a
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetIterator} extending
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}. The
+replacement can be easily done without modifying the model
+implementation, except the setup-time code.
+
+To generate non-uniform random numbers, one must select a probability
+distribution based on the empirical data \cite{sLAW00a}.
+SSJ does not provide
+probability distribution estimation tools, but goodness of fit tests are
+included to help in model validation. The package
+\externalclass{umontreal.iro.lecuyer}{probdist} contains several
+standard, commonly-used, probability distributions. It supports
+discrete and continuous distributions through two different abstract
+base classes:
+\externalclass{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}
+and
+\externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistribution},
+respectively. Again, since the distributions inherit from a common
+class, their access can be independent from the selected distribution,
+except for the setup case. One can compute the density/mass, distribution,
+complementary, and inverse distribution functions. These facilities
+are also accessible through static methods implemented in each
+distribution class if one does not want to create objects or needs
+distributions whose parameters vary in time.
+However, setup-time operations must be performed for each operation,
+which can be inefficient for certain distributions.
+
+To generate non-uniform random numbers, the packages
+\externalclass{umontreal.iro.lecuyer}{rng} (or
+\externalclass{umontreal.iro.lecuyer}{hups}) and
+\externalclass{umontreal.iro.lecuyer}{probdist} must be used
+together. The simplest generation method is to generate a uniform
+random number using a generator implementing
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} (or get a
+coordinate using a point set iterator) and to apply
+inversion by using the selected
+\externalclass{umontreal.iro.lecuyer}{probdist} distribution's
+\externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{inverseF}{} method.
+However, inversion is not the only generation method and sometimes not
+the most efficient. For some distributions, closed-form inverse functions
+or fast inversion algorithms exist.
+For others, inversion is performed using
+binary or even linear search. In such cases, the performance
+and precision depends on the complexity of the distribution function
+which is calculated several times for one inverse.
+The package
+\externalclass{umontreal.iro.lecuyer}{randvar} acts as glue between
+uniform random number generators and probability distributions. Continuous
+or discrete random number generators also inherits from common base
+classes, namely
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} and
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}.
+All generators use a random stream and a probability
+distribution for their construction. As opposed to
+\externalclass{umontreal.iro.lecuyer}{probdist}, one can directly
+instantiate
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} or
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}.
+However, in such cases, only inversion generation method will be
+available. To use an alternate generation method, one must
+instantiate a specialized generator class and switch to the given
+generation algorithm using an object method.
+Each specialized class also provides static method which perform
+the same action. Although they allow one to avoid object creation,
+their signatures are specific to the used distribution and they have
+to perform setup-time operations on each variate generation, which
+can become inefficient.
+The \externalclass{umontreal.iro.lecuyer}{randvar} package also
+provides the class
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateTrans} to
+apply transformations to a generator. This allows, for example, to
+generate variates from a truncated distribution.
+
+\subsection*{Performing simulation}
+
+SSJ supports discrete-event, process-driven, continuous or mixed
+simulation. The discrete-event and continuous simulation are managed
+by the package \externalclass{umontreal.iro.lecuyer}{simevents}. This
+package manages the simulation clock and the event list, two essential
+components for all discrete-event simulations. The simulation clock
+tracks the simulation time whereas the event list stores the
+scheduled events to execute them in the right order.
+Events are user-defined subclasses of
+\externalclass{umontreal.iro.lecuyer.simevents}{Event}. When an event
+occurs, any type of actions can then be taken. The package
+provides a class called
+\externalclass{umontreal.iro.lecuyer.simevents}{List} which implements
+a linked list supporting statistical collection. Continuous
+simulation can be performed using the class
+\externalclass{umontreal.iro.lecuyer.simevents}{Continuous}. It uses
+the event framework to resolve differential equations numerically at
+fixed steps in the simulation time.
+
+Process-driven simulation requires a separate package called
+\externalclass{umontreal.iro.lecuyer}{simprocs}. This package
+provides the base class
+\externalclass{umontreal.iro.lecuyer.simprocs}{SimProcess} which must
+be extended by any process. A simulation process is an autonomous
+object which executes tasks and interacts with other simulation
+processes. To allow such interactions, the package provides some
+synchronization tools such as a resource, a bin, and a condition.
+The process-driven simulation framework relies on the event-driven
+framework. Processes start, resume and wake up on scheduled events.
+One can then easily mix event-driven and process-driven simulation.
+
+\subsection*{Other tools}
+
+The package \externalclass{umontreal.iro.lecuyer}{stat} provides basic
+tools for statistical collection. Statistics are collected using
+statistical probes, i.e, objects implementing the abstract class
+\externalclass{umontreal.iro.lecuyer.stat}{StatProbe}. Two types of
+probes are supported. The
+\externalclass{umontreal.iro.lecuyer.stat}{Tally} allows to collect
+observations of the form $X_1,\dots,X_n$ whereas
+\externalclass{umontreal.iro.lecuyer.simevents}{Accumulate} collects
+statistics for a continuous variable evolving in simulation time.
+During the simulation, one can add observations to such probes. After
+the simulation, measures can be obtained, such as sample average,
+sample standard deviation or confidence interval. A statistical
+report can be obtained for all probes. The package also provides
+a way to detach statistical collection from the model implementation
+by using bound properties.
+
+To test a proposed model against empirical data, goodness of fit tests
+are provided in the package
+\externalclass{umontreal.iro.lecuyer}{gof}.
+Such tests, e.g.\ Kolmogorov-Smirnov
+or Anderson-Darling, compute a statistic using the
+empirical observations and the proposed distribution. The empirical
+observations are given as an array whereas the
+distribution is given as a
+\externalclass{umontreal.iro.lecuyer}{probdist} object. From the computed
+statistic, it is possible to compute the $p$-value which is useful to
+evaluate the significance of the test.
+
+\subsection*{Related documentation}
+
+The \texttt{example.pdf} file, in the \texttt{doc/pdf} subdirectory of
+the SSJ distribution, explains simulation examples implemented using
+SSJ. This may be the best starting point to learn SSJ.
+\begin{htmlonly}
+% This should be removed if we find a way to display blbliographical
+% references in HTML
+% LaTeX2HTML can do it when it is run for a LaTeX document, but it does not
+% work for a single bibliography shared by several documents.
+% Texjava processes each Java class as a separate, standalone, LaTeX document.
+One can find additional information and references in the PDF version
+of this documentation, available in the {\tt doc/pdf} subdirectory
+of the SSJ distribution.
+\end{htmlonly}
+\begin{latexonly}
+% In Javadoc, this is visible to the user as hyperlinks. In LaTeX,
+% this is separated filed.
+Every package introduced here contains its own reference documentation
+as a PDF file, in the \texttt{doc/pdf} subdirectory.
+This documentation describes in more details how to
+use the package and provides a description of each class and method.
+\end{latexonly}
+
+
+\end{comment}
+%%%%%%%%%%%%%%%%%%%%%%
diff --git a/source/umontreal/iro/lecuyer/examples/packdep.tex b/source/umontreal/iro/lecuyer/examples/packdep.tex
new file mode 100644
index 0000000..d2b158a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/packdep.tex
@@ -0,0 +1,170 @@
+
+\begin{latexonly} %%%%%%%
+
+The packages currently offered are the following:
+%
+\begin{verse}
+\externalclass{umontreal.iro.lecuyer}{util}
+ contains utility classes used in the implementation of SSJ,
+ and which are often useful elsewhere.
+ For example, there are timers (for CPU usage),
+ utilities to read or format numbers and arrays from/to text,
+ operations on binary vectors and matrices,
+ some mathematical functions and constants,
+ root-finding tools,
+ facilities for SQL database interface, and so on.
+
+\externalclass{umontreal.iro.lecuyer}{probdist}
+ contains a set of Java classes providing methods to compute mass,
+ density, distribution, complementary distribution,
+ and inverse distribution functions for many discrete and continuous
+ probability distributions, as well as estimating the parameters of
+ these distributions.
+
+\externalclass{umontreal.iro.lecuyer}{probdistmulti}
+ contains a set of Java classes providing methods to compute mass,
+ density, distribution, complementary distribution,
+ for some multi-dimensionnal discrete and continuous
+ probability distributions.
+
+\externalclass{umontreal.iro.lecuyer}{rng}
+ provides facilities for generating uniform random numbers over the
+ interval $(0,1)$, or over a given range of integer values, and other
+ types of simple random objects such as random permutations.
+
+\externalclass{umontreal.iro.lecuyer}{hups}
+ provides classes implementing highly uniform point sets and
+ sequences (HUPS), also called low-discrepancy sets and sequences,
+ and tools for their randomization.
+
+\externalclass{umontreal.iro.lecuyer}{randvar}
+ provides a collection of classes for non-uniform random variate
+ generation, primarily from standard distributions.
+
+\externalclass{umontreal.iro.lecuyer}{randvarmulti}
+ provides a collection of classes for
+ random number generators for some multi-dimensional distributions.
+
+\externalclass{umontreal.iro.lecuyer}{gof}
+ contains tools for performing univariate goodness-of-fit
+ (GOF) statistical tests.
+
+\externalclass{umontreal.iro.lecuyer}{stat}
+ provides elementary tools for collecting statistics
+ and computing confidence intervals.
+
+\externalclass{umontreal.iro.lecuyer}{stat.list}
+ this subpackage of \texttt{stat}
+ provides support to manage lists of statistical collectors.
+% Statistical operations may be applied on all the collectors easily.
+
+\externalclass{umontreal.iro.lecuyer}{simevents}
+ provides and manages the event-driven simulation facilities as well
+ as the simulation clock. Can manage several simulations in parallel,
+ in the same program.
+
+\externalclass{umontreal.iro.lecuyer}{simevents.eventlist}
+ this subpackage of \texttt{simevents} offers several kinds of event
+ list implementations.
+
+\externalclass{umontreal.iro.lecuyer}{simprocs}
+ provides and manages the process-driven simulation facilities.
+% This package requires \emph{green threads}, which are available
+% only in JDK version 1.3.1 or earlier (unfortunately).
+%
+% simprocs ne nécessite pas les green threads absolument.
+% Premièrement, si l'interpréteur D-SOL est employé, tout fonctionne #1, même
+% sans green threads. Avec l'implantation par défaut de SimProcess, qui utilise
+% les threads, c'est mieux avec les green threads, mais cela peut fonctionner
+% (quoique pas toujours stable) avec les native threads. Il faudrait donc dire,
+% dans le texte, qu'il est recommandé d'utiliser une JVM avec green threads,
+% comme Sun JRE <=1.3.1.
+
+\externalclass{umontreal.iro.lecuyer}{functions}
+ contains classes that allow one to pass an arbitrary function of one variable
+ as argument to a method
+ and to apply elementary mathematical operations on generic functions.
+
+\externalclass{umontreal.iro.lecuyer}{functionfit}
+provides basic facilities for curve fitting and interpolation
+with polynomials. % for example, least square fit, spline interpolation.
+
+\externalclass{umontreal.iro.lecuyer}{charts}
+provides tools for easy construction, visualization, and customization
+of $xy$ plots, histograms, and empirical styled charts
+from a Java program.
+
+\externalclass{umontreal.iro.lecuyer}{stochprocess}
+implements different kinds of stochastic processes.
+%%
+\end{verse}
+\end{latexonly} %%%%%%%%%
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection*{Dependence on other libraries}
+
+SSJ uses some classes from other free Java libraries.
+
+The \htmladdnormallink{Colt library}{http://acs.lbl.gov/software/colt/},
+ developed at the Centre Europ\'een de Recherche
+Nucl\'eaire (CERN) in Geneva \cite{iHOS04a},
+is a large library that provides a wide range of facilities for
+high performance scientific and technical computing in Java.
+SSJ uses the class \externalclass{cern.colt.list}{DoubleArrayList}
+from Colt in a few of its classes, namely in packages
+\externalclass{umontreal.iro.lecuyer}{stat} and
+\externalclass{umontreal.iro.lecuyer}{hups}.
+The reason is that this class provides a very efficient and convenient
+implementation of an (automatically) extensible array of {\tt double},
+together with several methods for computing statistics for the observations
+% Javadoc does not find cern.jet.stat.Descriptive and issues
+% a warning; maybe a bug in Javadoc.
+stored in the array (see, e.g., {\tt Descriptive}).
+% The Colt library must be installed (see the installation instructions
+% of SSJ) if one wishes to use a class relying on it.
+% Otherwise, Colt is not needed.
+The Colt library is distributed with the SSJ package as \textbf{colt.jar}.
+It must be added in the CLASSPATH environment variable.
+
+
+The \textbf{linear\_algebra} library is based on public domain LINPACK routines.
+They were translated from Fortran to Java by Steve Verrill at the
+ USDA Forest Products Laboratory
+ Madison, Wisconsin, USA.
+This software is also in the public domain and is included in the
+SSJ distribution as the \textbf{Blas.jar} archive. It is used only in the
+\texttt{probdist} package to compute maximum likelihood estimators.
+
+
+The optimization package of Steve Verrill includes Java translations of the
+\htmladdnormallink{MINPACK}{http://simul.iro.umontreal.ca/Uncmin_f77/Minpack_f77.html}
+ routines \cite{iMOR80a} for nonlinear least squares problems as well as
+\htmladdnormallink{UNCMIN}{http://simul.iro.umontreal.ca/Uncmin_f77/Uncmin_f77.html}
+ routines \cite{iSCHa} for
+unconstrained optimization. They were translated from Fortran to Java by
+Steve Verrill and are in the public domain. They are included in the SSJ
+distribution as the \textbf{optimization.jar} archive. It is used only in the \texttt{probdist}
+ package to compute maximum likelihood estimators.
+
+
+\htmladdnormallink{JFreeChart}{http://www.jfree.org/jfreechart/index.html} is a free
+Java library that can generate a wide variety of charts and plots for use in
+applications, applets and servlets. \textbf{JFreeChart} currently supports, amongst
+others, bar charts, pie charts, line charts, XY-plots, histograms, scatter plots and
+time series plots. It is distributed with SSJ as \textbf{jfreechart-*.jar}.
+\htmladdnormallink{JCommon}{http://www.jfree.org/jcommon/index.php} is a free
+general purpose Java library containing many useful classes used by JFreeChart and
+ other Java packages. It is distributed with SSJ as \textbf{jcommon-*.jar}.
+JFreeChart (and JCommon) are used in the SSJ package \textbf{charts} to create
+different kinds of charts.
+
+SSJ also provides an interface to the
+\htmladdnormallink{UNURAN}{http://statistik.wu-wien.ac.at/unuran/}
+library for nonuniform random number generation \cite{iLEY02a}, in the
+\externalclass{umontreal.iro.lecuyer}{randvar} package.
+UNURAN does not have to be installed to be used with SSJ, because it is
+linked statically with the appropriate SSJ native library.
+However, the UNURAN documentation will be required
+to take full advantage of the library.
diff --git a/source/umontreal/iro/lecuyer/examples/process.tex b/source/umontreal/iro/lecuyer/examples/process.tex
new file mode 100644
index 0000000..297f113
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/process.tex
@@ -0,0 +1,627 @@
+\section {Process-Oriented Programs}
+\label {sec:process}
+
+The process-oriented programs discussed in this section are
+based on the \texttt{simprocs} package and were initially designed
+to run using \emph{Java green threads}, which are actually \emph{simulated}
+threads and have been available only in JDK versions 1.3.1 or earlier,
+unfortunately.
+The green threads in these early versions of JDK were obtained
+by running the program with the ``\texttt{java -classic}'' option.
+
+With \emph{native threads}, these programs run more slowly
+and may crash if too many threads are initiated.
+This is a serious limitation of the package \texttt{simprocs},
+due to the fact that Java threads are \emph{not} designed for simulation.
+
+A second implementation of \texttt{simprocs}, available in DSOL
+(see the class \texttt{DSOLProcess\-Simulator})
+and provided to us by Peter Jacobs, does not use the Java threads.
+It is based on a Java reflection mechanism
+that interprets the code of processes at runtime and transforms everything into events.
+A program using the process view and implemented with the DSOL interpreter
+can be 500 to 1000 times slower than the corresponding event-based program.
+On the other hand, it is a good and safe teaching tool for process-oriented programming,
+and the number of processes is limited only by the available memory.
+To use the DSOL system with a program that imports \texttt{simprocs},
+it suffices to run the program with
+the ``\texttt{java -Dssj.withDSOL ...}'' option.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {The single queue}
+
+\lstinputlisting[%
+label=lst:QueueProc,
+caption={Process-oriented simulation of an $M/M/1$ queue},float=tp,
+emph={simulateOneRun,actions,main}
+]%
+{QueueProc.java}
+
+Typical simulation languages offer higher-level constructs than those used
+in the program of Listing~\ref{lst:QueueEv}, and so does SSJ.
+This is illustrated by our second implementation of the single-server
+queue model, in Listing~\ref{lst:QueueProc}, based on a
+paradigm called the {\em process-oriented\/} approach.
+% initiated in the early sixties by the GPSS language (\cite {sSCH90a}).
+
+In the event-oriented implementation, each customer was a {\em passive\/}
+object, storing two real numbers, and performing no action by itself.
+In the process-oriented implementation given in Listing~\ref{lst:QueueProc},
+each customer (instance of the class \texttt{Customer}) is a
+\emph{process} whose activities are described by its \texttt{actions} method.
+This is implemented by associating a Java \texttt{Thread} to each
+\texttt{SimProcess}.
+The server is an object of the class \texttt{Resource}, created when
+\texttt{QueueProc} is instantiated by \texttt{main}.
+It is a {\em passive\/} object, in the sense that it executes no code.
+Active resources, when needed, can be implemented as processes.
+
+When it starts executing its actions, a customer first schedules
+the arrival of the next customer, as in the event-oriented case.
+(Behind the scenes, this effectively schedules an event, in the
+event list, that will start a new customer instance.
+The class \texttt{SimProcess} contains all scheduling facilities of {\tt
+ Event}, which permits one
+to schedule processes just like events.)
+The customer then requests the server by invoking \texttt{server.request}.
+If the server is free, the customer gets it and can continue its
+execution immediately. Otherwise, the customer is automatically
+(behind the scenes) placed in the server's queue, is suspended,
+and resumes its execution only when it obtains the server.
+When its service can start, the customer invokes \texttt{delay} to
+freeze itself for a duration equal to its service time, which is
+again generated from the exponential distribution with mean $1/\mu$
+using the random variate generator \texttt{genServ}.
+After this delay has elapsed, the customer
+releases the server and ends its life.
+Invoking \texttt{delay(d)} can be interpreted as scheduling an event that
+% (behind the scenes)
+will resume the execution of the process in \texttt{d} units of time.
+Note that several distinct customers can
+co-exist in the simulation at any given point in time, and
+be at different phases of their \texttt{actions} method.
+However, only one process is executing at a time.
+
+The constructor \texttt{QueueProc} initializes the simulation,
+invokes \texttt{setStatCollecting (true)} to specify that detailed statistical
+collection must be performed automatically for the resource \texttt{server},
+schedules an event \texttt{EndOfSim} at the time horizon,
+schedules the first customer's arrival, and starts the simulation.
+The \texttt{EndOfSim} event stops the simulation.
+The \texttt{main} program then regains control and prints a detailed
+statistical report on the resource \texttt{server}.
+
+It should be pointed out that in the \texttt{QueueProc} program,
+the service time of a customer is generated
+only when the customer starts its service, whereas for \texttt{QueueEv},
+it was generated at customer's arrival.
+For this particular model, it turns out that this makes no difference
+in the results, because the customers are served in a FIFO order
+and because one random number stream is dedicated to the generation
+of service times.
+However, this may have an impact in other situations.
+
+The process-oriented program here is more compact and
+more elegant than its event-oriented counterpart.
+This tends to be often true: Process-oriented programming
+frequently gives less cumbersome and better looking programs.
+On the other hand, the process-oriented implementations also tend to
+execute more slowly, because they involve more overhead.
+For example, the process-driven single-server queue simulation
+is two to three times slower than its event-driven counterpart.
+In fact, process management is done via the event list:
+processes are started, suspended, reactivated, and so on,
+by hidden events.
+If the execution speed of a simulation program is really important,
+it may be better to stick to an event-oriented implementation.
+
+%%%%%%%%%%%%%%%
+\lstinputlisting[label=res:QueueProc,
+caption={Results of the program \texttt{QueueProc}}]%
+{QueueProc.res}
+
+% \bigskip
+
+\iffalse %%%%%%%%%%%%
+\setbox5=\vbox{\hsize=5.8in
+\begin{verbatim}
+REPORT ON RESOURCE : Server
+ From time : 0.0 to time : 1000.0
+ min max average Std. Dev. num.obs.
+ Capacity 1 1 1
+ Utilisation 0 1 0.999
+ Queue Size 0 12 4.85
+ Wait 0 113.721 49.554 22.336 97
+ Service 0.065 41.021 10.378 10.377 96
+ Sojourn 12.828 124.884 60.251 21.352 96
+
+\end{verbatim}
+}
+
+\begin{figure}[htb]
+\centerline {\boxit{\box5}}
+\caption {Results of the program \texttt{QueueProc}.}
+\label {fig:quresProc}
+\end{figure}
+\fi %%%%%%%%%%%%
+
+Listing~\ref{res:QueueProc} shows the output of the program \texttt{QueueProc}.
+It contains more information than the output of \texttt{QueueEv}.
+It gives statistics on the server utilization, queue size,
+waiting times, service times, and sojourn times in the system.
+(The sojourn time of a customer is its waiting time plus its service time.)
+
+We see that by time $T =1000$, 1037 customers have completed their waiting
+and all of them have completed their service.
+The maximal queue size has been 10 and its average length
+between time 0 and time 1000 was 0.513.
+The waiting times were in the range from 0 to 6.262, with an average of 0.495,
+while the service times were from 0.00065 to 3.437, with an average of 0.511
+(recall that the theoretical mean service time is $1/\mu = 0.5$).
+Clearly, the largest waiting time and largest service time
+belong to different customers.
+% The average waiting and service times do not sum up to
+% the average sojourn time because there is one more observation for the
+% waiting times.
+
+The report also gives the empirical standard deviations of the waiting,
+service, and sojourn times. It is important to note that these
+standard deviations should {\em not\/} be used to compute confidence
+intervals for the expected average waiting times or sojourn times
+in the standard way, because the observations here (e.g., the successive
+waiting times) are strongly dependent, and also not identically distributed.
+Appropriate techniques for computing confidence intervals in this type of
+situation are described, e.g., in \cite{sFIS96a,sLAW00a}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {A job shop model}
+\label {sec:jobshop}
+
+This example is adapted from \cite[Section 2.6]{sLAW00a},
+and from \cite{sLEC88a}.
+A job shop contains $M$ groups of machines, the $m$th group having
+$s_m$ identical machines, for $m=1,\dots,M$.
+It is modeled as a network of queues:
+each group has a single FIFO queue, with $s_m$ identical servers for the
+$m$th group. There are $N$ types of tasks arriving to the shop
+at random. Tasks of type $n$ arrive according to a Poisson process
+with rate $\lambda_n$ per hour, for $n=1,\dots,N$.
+Each type of task requires a fixed sequence of operations,
+where each operation must be performed on a specific type of machine
+and has a deterministic duration.
+A task of type $n$ requires $p_n$ operations, to be performed on
+machines $m_{n,1},m_{n,2},\dots,m_{n,p_n}$, in that order, and whose
+respective durations are $d_{n,1},d_{n,2},\dots,d_{n,p_n}$, in hours.
+A task can pass more than once on the same machine type, so $p_n$ may
+exceed $M$.
+
+We want to simulate the job shop for $T$ hours,
+assuming that it is initially empty, and start collecting statistics
+only after a warm-up period of $T_0$ hours.
+We want to compute: (a) the average sojourn time in the shop for
+each type of task and
+(b) the average utilization rate, average length of the waiting
+queue, and average waiting time, for each type of machine,
+over the time interval $[T_0,T]$.
+For the average sojourn times and waiting times, the counted
+observations are the sojourn times and waits that {\em end\/} during
+the time interval $[T_0,T]$.
+Note that the only randomness in this model is in the task
+arrival process.
+
+The class \texttt{Jobshop} in Listing~\ref{lst:Jobshop} performs this
+simulation. Each group of machine is viewed as a resource, with
+capacity $s_m$ for the group $m$.
+The different {\em types\/} of task are objects of the class \texttt{TaskType}.
+This class is used to store the parameters of the different types:
+their arrival rate, the number of operations, the machine type
+and duration for each operation, and a statistical collector for
+their sojourn times in the shop.
+(In the program, the machine types and task types are numbered
+from 0 to $M-1$ and from 0 to $N-1$, respectively,
+because array indices in Java start at 0.)
+
+The tasks that circulate in the shop are objects of the class \texttt{Task}.
+The \texttt{actions} method in class \texttt{Task} describes the behavior
+of a task from its arrival until it exits the shop.
+Each task, upon arrival, schedules the arrival of the next task of the
+same type. The task then runs through the list of its operations.
+For each operation, it requests the appropriate type of machine,
+keeps it for the duration of the operation, and releases it.
+When the task terminates, it sends its sojourn time as a new observation
+to the collector \texttt{statSojourn}.
+
+Before starting the simulation, the method \texttt{simulateOneRun}
+schedules an event for the end of the simulation and another one
+for the end of the warm-up period. The latter simply starts the
+statistical collection.
+It also schedules the arrival of a task of each type.
+Each task will in turn schedules the arrival of the next task of
+its own type.
+
+With this implementation, the event list always
+contain $N$ ``task arrival'' events, one for each type of task.
+An alternative implementation would be that each task schedules
+another task arrival in a number of hours that is an exponential r.v.\
+with rate $\lambda$, where $\lambda = \lambda_0 + \cdots + \lambda_{N-1}$
+is the global arrival rate, and then the type of each arriving task is
+$n$ with probability $\lambda_n/\lambda$, independently of the others.
+Initially, a {\em single\/} arrival would be scheduled by the class
+\texttt{Jobshop}.
+This approach is stochastically equivalent to the current implementation
+(see, e.g., \cite{sBRA87a,pWOL89a}), but the event list contains only
+one ``task arrival'' event at a time.
+On the other hand, there is the additional work of generating the task
+type on each arrival.
+
+At the end of the simulation, the \texttt{main} program prints the
+statistical reports. Note that if one wanted to perform several independent
+simulation runs with this program, the statistical collectors would
+have to be reinitialized before each run, and additional collectors
+would be required to collect the run averages and compute confidence
+intervals.
+
+
+%%%%%%%%%%%%%%%%
+\lstinputlisting[label=lst:Jobshop,
+caption={A job shop simulation},
+emph={printReportOneRun,simulateOneRun,actions,readData,performTask,main}
+]{Jobshop.java}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {A time-shared computer system}
+\label {sec:timeshared}
+
+This example is adapted from \cite{sLAW00a}, Section~2.4.
+Consider a simplified time-shared computer system comprised of $T$
+identical and independent terminals, all busy, using a common server
+(e.g., for database requests, or central processing unit (CPU)
+consumption, etc.).
+Each terminal user sends a task to the server at some random time and
+waits for the response. After receiving the response, he thinks
+for some random time before submitting a new task, and so on.
+
+We assume that the thinking time is an exponential random variable
+with mean $\mu$, whereas the server's time needed for a request is a
+Weibull random variable with parameters $\alpha$, $\lambda$ and $\delta$.
+The tasks waiting for the server form a single queue with a
+{\em round robin\/} service policy with {\em quantum size\/} $q$,
+which operates as follows.
+When a task obtains the server, if it can be completed in less than $q$
+seconds, then it keeps the server until completion.
+Otherwise, it gets the server for $q$ seconds and returns to the back
+of the queue to be continued later.
+In both cases, there is also $h$ additional seconds of {\em overhead\/}
+for changing the task that has the server's attention.
+
+
+The {\em response time\/} of a task is defined as the difference
+between the time when the task ends (including the overhead $h$ at the
+end) and the arrival time of the task to the server.
+We are interested in the {\em mean response time}, in steady-state.
+We will simulate the system until $N$ tasks have ended, with all
+terminals initially in the ``thinking'' state.
+To reduce the initial bias, we will start collecting statistics only
+after $N_0$ tasks have ended (so the first $N_0$ response times are
+not counted by our estimator, and we take the average response
+time for the $N-N_0$ response times that remain).
+This entire simulation is repeated $R$ times, independently,
+so we can estimate the variance of our estimator.
+% and compute a confidence interval for the true mean response time.
+
+
+\unitlength=1in
+\begin{picture}(6.0, 2.9)(0.0,0.3)
+\thicklines\bf
+\put(1.5,1.0){\circle{0.4}}
+\put(1.5,1.5){\circle{0.4}}
+\put(1.5,2.5){\circle{0.4}}
+\put(1.5,1.0){\makebox(0,0){1}}
+\put(1.5,1.5){\makebox(0,0){2}}
+\put(1.5,2.5){\makebox(0,0){T}}
+\multiput(1.5,1.85)(0.0,0.15){3}{\circle*{0.05}}
+\put(3.9,1.7){\framebox(0.8,0.4){CPU}}
+\multiput(3.5,1.75)(0.1,0.0){4}{\line(0,1){0.3}}
+\put(1.1,1.2){\line(0,1){1.1}}
+\put(1.9,1.2){\line(0,1){1.1}}
+\put(0.7,2.2){\line(0,1){0.5}}
+\put(0.9,2.0){\vector(1,0){0.2}}
+\put(1.9,2.0){\vector(1,0){1.5}}
+\put(3.0,1.8){\vector(1,0){0.4}}
+\put(3.0,1.4){\line(1,0){2.1}}
+\put(4.7,1.9){\line(1,0){0.4}}
+\put(0.9,2.9){\line(1,0){4.2}}
+\put(1.3,1.2){\oval(0.4,0.4)[bl]}
+\put(1.3,1.7){\oval(0.4,0.4)[bl]}
+\put(1.3,2.3){\oval(0.4,0.4)[tl]}
+\put(1.7,2.3){\oval(0.4,0.4)[tr]}
+\put(1.7,1.7){\oval(0.4,0.4)[br]}
+\put(1.7,1.2){\oval(0.4,0.4)[br]}
+\put(0.9,2.2){\oval(0.4,0.4)[bl]}
+\put(0.9,2.7){\oval(0.4,0.4)[tl]}
+\put(3.0,1.6){\oval(0.4,0.4)[l]}
+\put(5.1,1.65){\oval(0.4,0.5)[r]}
+\put(5.1,2.4){\oval(0.4,1.0)[r]}
+\small\rm
+\put(1.5,0.65){\makebox(0,0){Terminals}}
+\put(4.1,1.25){\makebox(0,0){End of quantum}}
+\put(3.2,2.75){\makebox(0,0){End of task}}
+\put(3.65,2.2){\makebox(0,0){Waiting queue}}
+\end{picture}
+
+
+Suppose we want to compare the mean response times for two
+different configurations of this system, where a configuration is
+characterized by the vector of parameters
+$(T, q, h, \mu, \alpha, \lambda, \delta)$.
+We will make $R$ independent simulation runs (replications)
+for each configuration.
+To compare the two configurations, we want to use {\em common random
+numbers}, i.e., the same streams of random numbers
+across the two configurations.
+We couple the simulation runs by pairs:
+for run number $i$, let $R_{1i}$ and $R_{2i}$ be the mean response times
+for configurations 1 and 2, and let
+ $$D_i = R_{1i} - R_{2i}.$$
+We use the same random numbers to obtain $R_{1i}$ and $R_{2i}$,
+for each $i$.
+The $D_i$ are nevertheless independent random variables (under the blunt
+assumption that the random streams really produce independent uniform
+random variables) and we can use them to compute a confidence interval
+for the difference $d$ between the theoretical mean response times of the
+two systems.
+Using common random numbers across $R_{1i}$ and $R_{2i}$ should reduce
+the variance of the $D_i$ and the size of the confidence interval.
+
+
+\lstinputlisting[label=lst:TimeShared,
+caption={Simulation of a time shared system},%
+emph={simulateConfigs,actions,simulOneRun,main}
+]%
+{TimeShared.java}
+
+% \bigskip
+
+The program of Listing~\ref{lst:TimeShared} performs this simulation.
+In the \texttt{simulateConfigs} method, we perform \texttt{numRuns}
+pairs of simulation runs, one with quantum size \texttt{q1} and one
+with quantum size {q2}.
+Each random stream is reset to the beginning of its \emph{current} substream
+after the first run, and to the beginning of its \emph{next} substream
+after the second run, to make sure that for each pair of runs,
+the generators start from exactly the same
+seeds and generate exactly the same random numbers in the same order.
+The variable \texttt{mean1} memorizes the values of $R_{1i}$ after the
+first run, and the statistical probe \texttt{statDiff} collects the
+differences $D_i$, in order to compute a confidence interval for $d$.
+
+For each simulation run, the statistical probe \texttt{meanInRep}
+is used to compute the average response time for the $N - N_0$ tasks
+that terminate after the warm-up.
+It is initialized before each run and updated with a new observation
+at the $i$th task termination, for $i = N_0+1,\dots,N$.
+At the beginning of a run, a \texttt{Terminal} process is activated for
+each terminal. When the $N$th task terminates, the corresponding
+process invokes \texttt{Sim.stop} to stop the simulation and
+to return the control to
+the instruction that follows the call to \texttt{simulOneRun}.
+
+% \bigskip
+
+\iffalse %%%%%%%%%
+\setbox3=\vbox {\hsize = 6.0in
+\begin{verbatim}
+REPORT on Tally stat. collector ==> Differences on mean response times
+ min max average standard dev. nb. obs.
+ -0.134 0.369 0.168 0.175 10
+
+ 90.0% confidence interval for mean (student): ( 0.067, 0.269 )
+\end{verbatim}
+}
+
+\begin{figure}[ht]
+\centerline{\boxit{\box3}}
+\caption {Difference in the mean response times for $q=0.1$ and $q=0.2$
+ for the time shared system.}
+\label {fig:timeshared-res}
+\end{figure}
+\fi %%%%%%
+
+For a concrete example, let $T=20$, $h=.001$, $\mu=5$ sec., $\alpha=1/2$,
+$\lambda=1$ and $\delta=0$ for the two configurations.
+With these parameters, the mean of the Weibull distribution is 2.
+Take $q=0.1$ for configuration 1 and $q=0.2$ for configuration 2.
+We also choose $N_0=100$, $N=1100$, and $R=10$ runs.
+With these numbers, the program gives the results of
+Listing~\ref{res:TimeShared}.
+The confidence interval on the difference between the response time
+with $q=0.1$ and that with $q=0.2$ contains only positive numbers.
+We can therefore conclude that
+the mean response time is significantly shorter (statistically)
+with $q=0.2$ than with $q = 0.1$ (assuming that we can neglect the
+bias due to the choice of the initial state).
+To gain better confidence in this conclusion, we could repeat the
+simulation with larger values of $N_0$ and $N$.
+
+\lstinputlisting[label=res:TimeShared,
+caption={Difference in mean response times
+ for $q=0.1$ and $q=0.2$, for time shared system}]%
+{TimeShared.res}
+
+Of course, the model could be made more realistic by considering,
+for example, different types of terminals, with different parameters,
+a number of terminals that changes with time,
+different classes of tasks with priorities, etc.
+SSJ offers the tools to implement these generalizations easily.
+% The program would be more elaborate but its structure would be similar.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {Guided visits}
+\label {sec:visits}
+
+This example is translated from \cite{sLEC88a}.
+A touristic attraction offers guided visits, using three guides.
+The site opens at 10:00 and closes at 16:00.
+Visitors arrive in small groups (e.g., families) and the arrival
+process of
+those groups is assumed to be a Poisson process
+with rate of 20 groups per hour, from 9:45 until 16:00.
+The visitors arriving before 10:00 must wait for the opening.
+After 16:00, the visits already under way can be completed,
+but no new visit is undertaken, so that all the visitors still
+waiting cannot visit the site and are lost.
+
+The size of each arriving group of visitors is a discrete random
+variable taking the value $i$ with probability $p_i$ given in the
+following table:
+\begin{center}
+\begin{tabular}{r|rrrr} \hline
+ $i$ \ \ & 1 & 2 & 3 & 4\\ \hline
+ $p_i$ \ & \ .2 & \ .6 & \ .1 & \ .1\\ \hline
+\end{tabular}
+\end{center}
+
+Visits are normally made by groups of 8 to 15 visitors.
+Each visit requires one guide and lasts 45 minutes.
+People waiting for guides form a single queue.
+When a guide becomes free, if there is less than 8 people
+in the queue, the guide waits until the queue grows to at
+least 8 people, otherwise she starts a new visit right away.
+If the queue contains more than 15 people, the first 15 will
+go on this visit.
+At 16:00, if there is less than 8 people in the queue
+and a guide is free, she starts a visit with the remaining
+people.
+At noon, each free guide takes 30 minutes for lunch.
+The guides that are busy at that time will take 30 minutes
+for lunch as soon as they complete their on-going visit.
+
+Sometimes, an arriving group of visitors may decide to just
+go away (balk) because the queue is too long.
+% These visitors are lost.
+We assume that the probability of balking when the queue
+size is $n$ is given by
+$$
+ R(n) = \cases {0 & for $n\le 10;$\cr
+ (n-10)/30 & for $10< n< 40$;\cr
+ 1 & for $n\ge 40$.}
+$$
+
+The aim is to estimate the average number of visitors lost
+per day, in the long run.
+The visitors lost are those that balk or are still in the
+queue after 16:00.
+
+A simulation program for this model is given in
+Listing~\ref{lst:Visits}.
+Here, time is measured in hours, starting at midnight.
+At time 9:45, for example, the simulation clock is at 9.75.
+The (process) class \texttt{Guide} describes the daily
+behavior of a guide (each guide is an instance of this
+class), whereas \texttt{Arrival} generates the arrivals
+according to a Poisson process, the group sizes,
+and the balking decisions.
+The event \texttt{closing} closes the site at 16:00.
+
+The \texttt{Bin} mechanism \texttt{visitReady} is used to
+synchronize the \texttt{Guide} processes.
+The number of tokens in this bin is 1 if there are
+enough visitors in the queue to start a visit (8 or more)
+and is 0 otherwise.
+When the queue size reaches 8 due to a new arrival,
+the \texttt{Arrival} process puts a token into the bin.
+This wakes up a guide if one is free.
+A guide must take a token from the bin to start a new
+visit. If there are still 8 people or more in the queue
+when she starts the visit, she puts the token back to
+the bin immediately, to indicate that another visit is
+ready to be undertaken by the next available guide.
+
+%%%%%%%%%%%%%%%%
+\lstinputlisting[label=lst:Visits,
+caption={Simulation of guided visits},
+emph={simulateRuns,actions,oneDay,balk,main},%
+lineskip=-1pt]{Visits.java}
+
+% \clearpage
+
+The simulation results are in Listing~\ref{res:Visits}.
+
+
+%%%%%%%%%%%%%%%%
+\lstinputlisting[label=res:Visits,
+caption={Simulation results for the guided visits model},
+ lineskip=-1pt]{Visits.res}
+
+%\bigskip
+
+\iffalse %%%%%%%%%%%%
+\setbox3=\vbox {\hsize = 6.0in
+\begin{verbatim}
+REPORT on Tally stat. collector ==> Nb. of visitors lost per day
+ min max average standard dev. nb. obs.
+ 3 48 21.78 10.639 100
+
+ 90.0 confidence interval for mean ( 20.014, 23.546 )
+\end{verbatim}
+}
+
+\begin{figure}[ht]
+\centerline{\boxit{\box3}}
+\caption {Simulation results for the guided visits model.}
+\label {fig:visits-res}
+\end{figure}
+\fi %%%%%%%%%%
+
+Could we have used a \texttt{Condition} instead of a \texttt{Bin}
+for synchronizing the \texttt{Guide} processes?
+The problem would be that if several guides are waiting for a
+condition indicating that the queue size has reached 8,
+{\em all\/} these guides (not only the first one)
+would resume their execution
+simultaneously when the condition becomes true.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {Return to the simplified bank}
+\label {sec:bank-proc}
+
+Listing~\ref{lst:BankProc} gives a process-oriented simulation
+program for the bank model of Section~\ref{sec:bank}.
+Here, the customers are viewed as processes.
+There are still events at the fixed times 9:45, 10:00, etc.,
+and these events do the same thing,
+but they are implicit in the process-oriented implementation.
+The next planned arrival event \texttt{nextArriv} is replaced by the
+next planned arriving customer \texttt{nextCust}.
+
+Instead of using the default process simulator as in the previous
+examples, here one creates a \class{ThreadProcessSimulator} object that
+will manage the processes of this simulation.
+
+The process-oriented version of the program is shorter,
+because certain aspects (such as the details of an arrival
+or departure event) are taken care of automatically by the
+process/resource construct, and the events 9:45, 10:00, etc.,
+are replaced by a single process.
+At 10 o'clock, the \texttt{setCapacity} statement that fixes the
+number of tellers also takes
+care of starting service for the appropriate number of customers.
+The two programs produce exactly the same results, given in
+Listing~\ref{lst:BankEv}.
+However, the process-oriented program take approximately 4 to 5 times
+longer to run than its event-oriented counterpart.
+
+% \clearpage
+
+\lstinputlisting[label=lst:BankProc,
+caption={Process-oriented simulation of the bank model},
+emph={simulOneDay,simulateDays,actions,balk,main}]%
+{BankProc.java}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/source/umontreal/iro/lecuyer/examples/simple.tex b/source/umontreal/iro/lecuyer/examples/simple.tex
new file mode 100644
index 0000000..b69f122
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/examples/simple.tex
@@ -0,0 +1,659 @@
+
+\section {Some Elementary Examples}
+\label {sec:simple}
+
+We start with elementary examples that illustrate how to
+generate uniform and nonuniform random numbers,
+construct probability distributions,
+collect elementary statistics,
+and compute confidence intervals,
+compare similar systems,
+and use randomized quasi-Monte Carlo point sets,
+with SSJ.
+
+The models considered here are quite simple and some of the
+performance measures can be computed by (more accurate) numerical
+methods rather than by simulation.
+The fact that we use these models to give a first tasting of SSJ
+should not be interpreted to mean that
+simulation is necessarily the best tool for them.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Collisions in a hashing system}
+\label{sec:collision}
+
+% In this small example,
+We want to estimate the expected number of collisions in a hashing system.
+There are $k$ locations (or addresses) and $m$ distinct items.
+Each item is assigned a random location, independently of the other items.
+A \emph{collision} occurs each time an item is assigned a location already
+occupied. Let $C$ be the number of collisions.
+We want to estimate $\mathbb{E}[C]$, the expected number of collisions, by simulation.
+A theoretical result states that when $k\to\infty$ while $\lambda = m^2/(2k)$ is fixed,
+$C$ converges in distribution to a Poisson random variable with mean $\lambda$.
+For finite values of $k$ and $m$, we may want to approximate the distribution of $C$
+by the Poisson distribution with mean $\lambda$, and
+use Monte Carlo simulation to assess the quality of this approximation.
+To do that, we can generate $n$ independent realizations of $C$, say $C_1,\dots,C_n$,
+compute their empirical distribution and empirical mean, and compare with the
+Poisson distribution.
+
+The Java program in Listing~\ref{lst:Collision} simulates $C_1,\dots,C_n$
+and computes a 95\%{} confidence interval on $\mathbb{E}[C]$.
+The results for $k = 10000$, $m = 500$, and $n = 100000$, are in Listing~\ref{res:Collision}.
+The reported confidence interval is $(12.25,\, 12.29)$, whereas $\lambda = 12.5$.
+This indicates that the asymptotic result underestimates $\mathbb{E}[C]$ by nearly 2\%.
+
+
+The Java program imports the SSJ packages \texttt{rng} and \texttt{stat}.
+It uses only two types of objects from SSJ:
+a \texttt{RandomStream} object, defined in the \texttt{rng} package,
+that generates a stream of independent random numbers from the uniform distribution,
+and a \texttt{Tally} object, from the \texttt{stat} package,
+to collect statistical observations and produce the report.
+In SSJ, \texttt{RandomStream} is actually just an interface that specifies all
+the methods that must be provided by its different implementations, which
+correspond to different brands of random streams (i.e., different types of
+uniform random number generators).
+The class \texttt{MRG32k3a}, whose constructor is invoked in the main program,
+is one such implementation of \texttt{RandomStream}. This is the one we use here.
+The class \texttt{Tally} provides the simplest type of statistical collector.
+It receives observations one by one, and after each new observation,
+it updates the number, average,
+variance, minimum, and maximum of the observations.
+At any time, it can return these statistics or compute a confidence interval
+for the theoretical mean of these observations, assuming that they are
+independent and identically distributed with the normal distribution.
+Other types of collectors that memorize the observations are also available in SSJ.
+
+
+\lstinputlisting[label=lst:Collision,
+caption={Simulating the number of collisions in a hashing system},%
+lineskip=-1pt,%
+emph={generateC,simulateRuns,main}
+]
+{Collision.java}
+
+The class \texttt{Collision} offers the facilities to simulate copies of $C$.
+Its constructor specifies $k$ and $m$, computes $\lambda$, and constructs
+a boolean array of size $k$ to memorize the locations used so far,
+in order to detect the collisions.
+The method \texttt{generateC} initializes the boolean array to \texttt{false},
+generates the $m$ locations, and computes $C$.
+The method \texttt{simulateRuns} first resets the statistical collector \texttt{statC},
+then generates $n$ independent copies of $C$ and pass these $n$ observations
+to the collector via the method \texttt{add}.
+The method \texttt{statC.report} computes a confidence interval from
+these $n$ observations and returns a statistical report in the form of a character string.
+This report is printed, together with the value of $\lambda$.
+
+
+\lstinputlisting[label=res:Collision,
+caption={Results of the program {\tt Collision}},%
+float=hbt,lineskip=-1pt]{Collision.res}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {Nonuniform variate generation and simple quantile estimates}
+\label {sec:nonuniform}
+
+The program of Listing~\ref{lst:Nonuniform} simulates the following artificial model.
+Define the random variable
+\[
+ X = Y_1 + \cdots + Y_N + W_1 + \dots + W_M,
+\]
+where $N$ is Poisson with mean $\lambda$, $M$ is geometric with parameter $p$,
+the $Y_j$'s are gamma with parameters $(\alpha, \beta)$,
+the $W_j$'s are lognormal with parameters $(\mu,\sigma)$,
+and all these random variables are independent.
+We want to generate $n$ copies of $X$, say $X_1,\dots,X_n$, and estimate
+the 0.10, 0.50, 0.90, and 0.99 quantiles of the distribution of $X$,
+simply from the quantiles of the empirical distribution.
+
+The method \texttt{simulateRuns} generates $n$ copies of $X$ and pass
+them to a statistical collector of class \texttt{TallyStore},
+that stores the individual observations. % in a special type of array
+% called \texttt{DoubleArrayList}, provided by the COLT library.
+These observations are sorted in increasing order by invoking \texttt{quickSort},
+and the appropriate empirical quantiles are printed,
+together with a short report.
+
+
+\lstinputlisting[label=lst:Nonuniform,
+caption={Simulating nonuniform variates and observing quantiles},%
+lineskip=-1pt,%
+emph={generateX,simulateRuns,main}
+]
+{Nonuniform.java}
+
+\lstinputlisting[label=res:Nonuniform,
+caption={Results of the program {\tt Nonuniform}},%
+float=hbt,lineskip=-1pt]{Nonuniform.res}
+
+
+To simplify the program, all the parameters are fixed as constants at the
+beginning of the class. This is simpler, but not recommended in general
+because it does not permit one to perform experiments with different parameter
+sets in a single program.
+Passing the parameters to the constructor as in Listing~\ref{lst:Collision}
+would require more lines of code, but would provide more flexibility.
+
+The class initialization also constructs a \texttt{RandomStream} of type
+\texttt{LFSR113} (this is a faster uniform generator that \texttt{MRG32k3a}),
+used to generate all the random numbers.
+For the generation of $N$, we construct a Poisson distribution
+with mean $\lambda$ (without giving it a name),
+and pass it together with the random stream to
+the constructor of class \texttt{PoissonGen}.
+The returned object \texttt{genN} is random number generator that generate
+Poisson random variables with mean $\lambda$, via inversion.
+As similar procedure is used to construct \texttt{genY} and \texttt{genW},
+which generate gamma and lognormal random variates, respectively.
+Note that a \texttt{RandomVariateGenInt} produces integer-valued random variates,
+while a \texttt{RandomVariateGen} produces real-valued random variates.
+For the gamma distribution, we use a special type of random number generator
+based on a rejection method, which is faster than inversion.
+These constructors precompute some (hidden) constants once for all,
+to speedup the random variate generation.
+For the Poisson distribution with mean $\lambda$, the constructor of \texttt{PoissonDist}
+actually precomputes the distribution function in a table, and uses
+this table to compute the inverse distribution function each time a Poisson
+random variate needs to be generated with this particular distribution.
+This is possible because all Poisson random variates have the same parameter $\lambda$.
+If a different $\lambda$ was used for each variate, then we would use
+the static method of \texttt{PoissonDist} instead of constructing a Poisson distribution,
+otherwise we would have to reconstruct the distribution each time.
+The static method reconstructs part of the table each time, with the given $\lambda$,
+so it is slower if we want to generate several Poisson variates with the same $\lambda$.
+As an illustration, we use the static method to generate the geometric random variates
+(in \texttt{generateX}), instead of constructing a geometric distribution and variate generator.
+To generate $M$, we invoke the static method \texttt{inverseF} of the class
+\texttt{GeometricDist}, which evaluates the inverse geometric distribution function
+for a given parameter $p$ and a given uniform random variate.
+
+The results of this program, with $n = 10000$, are in Listing~\ref{res:Nonuniform}.
+We see that $X$ has a coefficient of variation larger than 1,
+and the quantiles indicate that the distribution is skewed,
+with a long thick tail to the right.
+We have $X < 553$ about half the time, but values over several thousands are not uncommon.
+This probably happens when $N$ or $M$ takes a large value.
+There are also cases where $N=M=0$, in which case $X=0$.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {A discrete-time inventory system}
+\label {sec:inventory}
+
+Consider a simple inventory system where the demands for a given product
+on successive days are independent Poisson random variables with mean
+$\lambda$. If $X_j$ is the stock level at the beginning of day $j$ and
+$D_j$ is the demand on that day, then there are $\min(D_j, X_j)$ sales,
+$\max(0, D_j - X_j)$ lost sales, and the stock at the end of the day
+is $Y_j = \max(0, X_j - D_j)$.
+There is a revenue $c$ for each sale and a cost $h$ for each unsold
+item at the end of the day.
+The inventory is controlled using a $(s,S)$ policy:
+If $Y_j < s$, order $S - Y_j$ items, otherwise do not order.
+When an order is made in the evening, with probability $p$
+it arrives during the night and can be used for the next day,
+and with probability $1-p$ it never arrives (in which case a new order
+will have to be made the next evening).
+When the order arrives, there is a fixed cost $K$ plus a marginal cost
+of $k$ per item.
+The stock at the beginning of the first day is $X_0 = S$.
+
+We want to simulate this system for $m$ days, for a given set of
+parameters and a given control policy $(s,S)$, and replicate this
+simulation $n$ times independently to estimate the
+expected profit per day over a time horizon of $m$ days.
+Eventually, we might want to \emph{optimize} the values of the decision
+parameters $(s,S)$ via simulation, but we do not do that here.
+(In practice, this is usually done for more complicated models.)
+
+
+\lstinputlisting[label=lst:Inventory,
+caption={A simulation program for the simple inventory system},%
+lineskip=-1pt,%
+emph={simulateOneRun,simulateRuns,main}
+]
+{Inventory.java}
+
+%\bigskip
+
+Listing~\ref{lst:Inventory} gives a Java program, based on the SSJ
+library, that performs the required simulation for $n=500$, $m=2000$,
+$s=80$, $S=200$, $\lambda=100$, $c=2$, $h=0.1$, $K=10$, $k=1$, and $p=0.95$.
+
+The \texttt{import} statements at the beginning of the program
+retrieve the SSJ packages/classes that are needed.
+The \texttt{Inventory} class has a constructor that initializes the
+model parameters (saving their values in class variables) and constructs
+the required random number generators and the statistical collector.
+To generate the demands $D_j$ on successive days, we create
+(in the last line of the constructor) a random number stream and
+a Poisson distribution with mean $\lambda$, and then a Poisson random
+variate generator \texttt{genDemand} that uses this stream and this
+distribution. This mechanism will (automatically) precompute tables
+to ensure that the Poisson variate generation is efficient.
+This can be done because the value of $\lambda$ does not change during
+the simulation.
+The random number stream \texttt{streamOrder}, used to decide which
+orders are received, and the statistical collector \texttt{statProfit},
+are also created when the \texttt{Inventory} constructor is invoked.
+The code that invokes their constructors is outside the
+\texttt{Inventory} constructor, but it could have been inside as well.
+On the other hand, \texttt{genDemand} must be constructed inside
+the \texttt{Inventory} constructor, because the value of $\lambda$
+is not yet defined outside.
+The \emph{random number streams} can be viewed as virtual random number
+generators that generate random numbers in the interval $[0,1)$
+according to the uniform probability distribution.
+
+The method \texttt{simulateOneRun} simulates the system for $m$ days,
+with a given policy, and returns the average profit per day.
+For each day, we generate the demand $D_j$, compute the stock $Y_j$
+at the end of the day, and add the sales revenues minus the leftover
+inventory costs to the profit. If $Y_j < s$, we generate a uniform
+random variable $U$ over the interval $(0,1)$ and an order of size
+$S - Y_j$ is received the next morning if $U < p$ (that is, with
+probability $p$). In case of a successful order, we pay for it and
+the stock level is reset to $S$.
+
+The method \texttt{simulateRuns} performs $n$ independent
+simulation runs of this system and returns a report that contains
+a 90\%{} confidence interval for the expected profit.
+The main program constructs an \texttt{Inventory} object with the
+desired parameters, asks for $n$ simulation runs, and prints the report.
+It also creates a timer that computes the total CPU time to execute
+the program, and prints it.
+The results are in Listing~\ref{res:Inventory}.
+The average profit per day is approximately 85.
+It took 0.39 seconds (on a 2.4 GHz computer running Linux)
+to simulate the system for 2000 days,
+compute the statistics, and print the results.
+
+\lstinputlisting[label=res:Inventory,
+caption={Results of the program \texttt{Inventory}},%
+float=hbt,lineskip=-1pt]{Inventory.res}
+
+
+\iffalse %%%%%%%%%%
+\setbox1=\vbox{\hsize=5.8in
+\begin{verbatim}
+
+REPORT on Tally stat. collector ==> stats on profit
+ min max average standard dev. num. obs
+ 185.701 188.606 187.114 0.510 500
+ 90.0% confidence interval for mean (student): ( 187.076, 187.151 )
+
+Total CPU time: 0:0:0.60
+
+\end{verbatim}
+}
+
+\begin{figure}[htb]
+\centerline {\boxit{\box1}}
+\
+\label {fig:Inventory}
+\end{figure}
+\fi %%%%%%%%%%%%
+
+% \bigskip
+
+In Listing~\ref{lst:InventoryCRN}, we extend the \texttt{Inventory} class
+to a class \texttt{InventoryCRN} that compares two sets of values
+of the inventory control policy parameters $(s,S)$.
+
+% \bigskip
+
+\lstinputlisting[label=lst:InventoryCRN,
+caption={Comparing two inventory policies with common random numbers},%
+lineskip=-1pt,%
+emph={simulateDiffCRN,simulateDiff,main}]
+{InventoryCRN.java}
+
+The method \texttt{simulateDiff} simulates the system with policies
+$(s_1, S_1)$ and $(s_2, S_2)$ independently, computes the difference
+in profits, and repeats this $n$ times. These $n$ differences are
+tallied in statistical collector \texttt{statDiff}, to estimate the
+expected difference in average daily profits between the two policies.
+
+The method \texttt{simulateDiffCRN} does the same, but using
+\emph{common random numbers} across pairs of simulation runs.
+After running the simulation with policy $(s_1, S_1)$, the two random
+number streams are reset to the start of their current substream,
+so that they produce exactly the same sequence of random numbers
+when the simulation is run with policy $(s_2, S_2)$.
+Then the difference in profits is given to the statistical collector
+\texttt{statDiff} as before and the two streams are reset to a
+new substream for the next pair of simulations.
+
+Why not use the same stream for both the demands and orders?
+In this example, we need one random number to generate the demand each day,
+and also one random number to know if the order arrives, but only on the days
+where we make an order. These days where we make an order are not
+necessarily the same for the two policies.
+So if we use a single stream for both the demands and orders,
+the random numbers will not necessarily be used for the same purpose across
+the two policies: a random number used to decide if the order arrives in one case
+may end up being used to generate a demand in the other case.
+This can greatly diminish the power of the common random numbers technology.
+Using two different streams as in Listing~\ref{lst:InventoryCRN} ensures
+at least that the random numbers are used for the same purpose for the
+two policies. For more explanations and examples about common random numbers,
+see \cite{sLAW00a,sLEC09a}.
+
+The main program estimates the expected difference in average
+daily profits for policies $(s_1, S_1) = (80, 198)$ and
+$(s_2, S_2) = (80, 200)$, first with independent random numbers,
+then with common random numbers.
+The other parameters are the same as before.
+The results are in Listing~\ref{res:InventoryCRN}.
+We see that use of common random numbers reduces the variance by
+a factor of approximately 19 in this case.
+
+\lstinputlisting[label=res:InventoryCRN,
+caption={Results of the program \texttt{InventoryCRN}},%
+lineskip=-1pt]{InventoryCRN.res}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {A single-server queue with Lindley's recurrence}
+\label {sec:queue-lindley}
+
+We consider here a {\em single-server queue}, where customers
+arrive randomly and are served one by one in their order of arrival,
+i.e., {\em first in, first out\/} (FIFO).
+We suppose that the times between successive arrivals are exponential
+random variables with mean $1/\lambda$, that the service times are exponential
+random variables with mean $1/\mu$, and that all these random variables are
+mutually independent.
+The customers arriving while the server is busy must join the queue.
+The system initially starts empty. We want to simulate the first
+$m$ customers in the system and compute the mean waiting time per customer.
+
+This simple model is well-known in queuing theory: It is called
+an $M/M/1$ queue. Simple formulas are available for this model to
+compute the average waiting time per customer, average queue length,
+etc., over an {\em infinite\/} time horizon \cite{pKLE75a}.
+For a finite number of customers or a finite time horizon,
+these expectations can also be computed by numerical methods,
+but here we just want to show how it can be simulated.
+
+In a single-server queue,
+if $W_i$ and $S_i$ are the waiting time and service time of the
+$i$th customer, and $A_i$ is the time between the arrivals of
+the $i$th and $(i+1)$th customers, we have $W_1=0$ and the $W_i$'s
+follow the recurrence
+\eq
+ W_{i+1} = \max(0,\; W_i + S_i - A_i), \label {eq:lindley}
+\endeq
+known as \emph{Lindley's equation} \cite {pKLE75a}.
+
+
+\lstinputlisting[label=lst:QueueLindley,
+caption={A simulation based on Lindley's recurrence},
+emph={simulateOneRun,simulateRuns,main}
+]{QueueLindley.java}
+
+% \bigskip
+
+The program of Listing~\ref{lst:QueueLindley} exploits (\ref{eq:lindley})
+to compute the average waiting time of the first $m$ customers in
+the queue, repeats it $n$ times independently, and prints a summary of
+the results.
+Here, for a change, we pass the model parameters to the methods
+instead of to the constructor, and
+the random variates are generated by static methods
+instead of via a \texttt{RandomVariateGen} object as in the
+\emph{Inventory} class (previous example).
+This illustrates various ways of doing the same thing.
+The instruction ``\texttt{Wi += \dots}'' could also be replaced by
+\begin{code}
+ Wi += - Math.log (1.0 - streamServ.nextDouble()) / mu
+ + Math.log (1.0 - streamArr.nextDouble()) / lambda;
+\end{code}
+which directly implements inversion of the exponential distribution.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Using the observer design pattern}
+\label {sec:observer}
+
+Listing~\ref{lst:QueueObs} adds a few ingredients to the program
+\texttt{QueueLindley}, in order to illustrate the \emph{observer}
+design pattern implemented in package \texttt{stat}.
+This mechanism permits one to separate data generation from data
+processing. It can be very helpful in large simulation programs or
+libraries, where different objects may need to process the same data
+in different ways. These objects may have the task of storing observations
+or displaying statistics in different formats, for example, and they are
+not necessarily fixed in advance.
+
+\iffalse
+The \emph{observer} pattern, supported by the \texttt{Observable}
+class and \texttt{Observer} interface in Java,
+offers the appropriate flexibility for that kind of situation.
+An \texttt{Observable} object acts in a sense like a \emph{broadcasting}
+\emph{distribution agency} that maintains a list of registered
+\texttt{Observer} objects, and sends information to all its registered
+observers whenever appropriate.
+\fi
+
+The \emph{observer} pattern, supported by the
+\externalclass{umontreal.iro.lecuyer.stat}{ObservationListener}
+ interface in SSJ,
+offers the appropriate flexibility for that kind of situation.
+A statistical probe maintains a list of registered
+\externalclass{umontreal.iro.lecuyer.stat}{ObservationListener}
+objects, and broadcasts information to all its registered
+observers whenever appropriate. Any object that implements the interface
+\externalclass{umontreal.iro.lecuyer.stat}{ObservationListener}
+can register as an observer.
+
+\texttt{StatProbe} in package \texttt{stat}, as well as its subclasses
+\texttt{Tally} and \texttt{Accumulate}, contains a list of
+ \texttt{ObservationListener}'s.
+Whenever they receive a new statistical observation, e.g., via
+\texttt{Tally.add} or \texttt{Accumulate.update}, they send the new value
+to all registered observers.
+To register as an observer, an object must implement the interface
+\externalclass{umontreal.iro.lecuyer.stat}{ObservationListener}
+This implies that it must provide an implementation of the method
+\texttt{newObservation}, whose purpose is to recover the information
+that the object has registered for.
+
+In the example, the statistical collector \texttt{waitingTimes}
+ transmits to all its registered
+listeners each new statistical observation that it receives via
+its \texttt{add} method.
+More specifically, each call to \texttt{waitingTimes.add(x)} generates
+in the background a call to \texttt{o.newObservation(waitingTimes, x)}
+for all registered observers \texttt{o}.
+
+\begin{comment}
+The method \texttt{notifyObs} is used to
+turn the tally into such an agency. In fact, the collector is both a
+tally and a distribution agency, but its tally functionality can be
+disabled using the \texttt{stopCollectStat} method. This can be useful when
+the registered observers already perform statistical collection.
+\end{comment}
+
+Two observers register to receive observations from
+\texttt{waitingTimes} in the example.
+They are anonymous objects of classes \texttt{ObservationTrace} and
+\texttt{LargeWaitsCollector}, respectively. Each one is informed of any new
+observation $W_i$ via its \texttt{newObservation} method.
+The task of the \texttt{ObservationTrace} observer is to
+print the waiting times $W_5$, $W_{10}$, $W_{15}$, \dots, whereas
+the \texttt{LargeWaitsCollector} observer stores in an array all waiting
+times that exceed 2.
+The statistical collector \texttt{waitingTimes} itself also stores
+appropriate information to be able to provide a statistical report
+when required.
+
+The \texttt{ObservationListener} interface specifies that \texttt{newObservation}
+ must have two formal parameters, of classes \texttt{StatProbe} and
+\texttt{double}, respectively. The second parameter is the value of the
+observation.
+In the case where the observer registers to several \texttt{ObservationListener}
+objects, the first parameter of \texttt{newObservation} tells it which one
+is sending the information, so it can adopt the correct behavior for
+this sender.
+
+
+\lstinputlisting[label=lst:QueueObs,
+caption={A simulation of Lindley's recurrence using observers},
+emph={simulateOneRun,simulateRuns,updateLargeWaitsCollector,formatLargeWaits,update,main}
+]{QueueObs.java}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection {Pricing an Asian option}
+\label {sec:asian}
+
+A \emph{geometric Brownian motion} (GBM) $\{S(\zeta),\,\zeta\ge 0\}$ satisfies
+\[
+ S(\zeta) = S(0) \exp\left[(r - \sigma^2/2)\zeta + \sigma B(\zeta)\right]
+\]
+where $r$ is the \emph{risk-free appreciation rate},
+$\sigma$ is the \emph{volatility parameter}, and
+$B$ is a standard Brownian motion, i.e.,
+a stochastic process whose increments over disjoint intervals
+are independent normal random variables, with mean 0 and variance
+$\delta$ over an interval of length $\delta$ (see, e.g., \cite{fGLA04a}).
+The GBM process is a popular model for the evolution in time of the
+market price of financial assets.
+A discretely-monitored \emph{Asian option} on the arithmetic
+average of a given asset has discounted payoff
+\eq \label{eq:payasian}
+ X = e^{-rT} \max[\bar S - K,\, 0]
+\endeq
+where $K$ is a constant called the \emph{strike price} and
+\begin{equation} \label{eq:arithmetic-average}
+ \bar S = \frac{1}{t} \sum_{j=1}^t S(\zeta_j),
+\end{equation}
+for some fixed observation times $0 < \zeta_1 < \cdots < \zeta_t = T$.
+The value (or fair price) of the Asian option is $c = E[X]$ where
+the expectation is taken under the so-called risk-neutral measure
+(which means that the parameters $r$ and $\sigma$ have to be selected
+in a particular way; see \cite{fGLA04a}).
+
+This value $c$ can be estimated by simulation as follows.
+Generate $t$ independent and identically distributed
+(i.i.d.) $N(0,1)$ random variables $Z_1,\dots,Z_t$
+and put $B(\zeta_j) = B(\zeta_{j-1}) + \sqrt{\zeta_j - \zeta_{j-1}} Z_j$,
+for $j=1,\dots,t$, where $B(\zeta_0) = \zeta_0 = 0$. Then,
+\eq
+ S(\zeta_j) = S(0) e^{(r-\sigma^2/2)\zeta_j + \sigma B(\zeta_j)}
+ = S(\zeta_{j-1}) e^{(r-\sigma^2/2)(\zeta_j-\zeta_{j-1})
+ + \sigma \sqrt{\zeta_j - \zeta_{j-1}} Z_j}
+ \label{eq:Szetaj}
+\endeq
+for $j = 1,\dots,t$ and the payoff can be computed via (\ref{eq:payasian}).
+This can be replicated $n$ times, independently, and the
+option value is estimated by the average discounted payoff.
+The Java program of Listing~\ref{lst:Asian} implement this procedure.
+
+Note that generating the sample path and computing the payoff is done
+in two different methods. This way, other methods could eventually be
+added to compute payoffs that are defined differently (e.g., based on
+the geometric average, or with barriers, etc.) over the same generated
+sample path.
+
+\lstinputlisting[label=lst:Asian,
+caption={Pricing an Asian option on a GMB process},
+lineskip=-1pt,
+emph={generatePath,getPayoff,simulateRuns,main}
+]{Asian.java}
+
+% \bigskip
+
+The method \texttt{simulateRuns} performs $n$ independent simulation
+runs using the given random number stream and put the $n$ observations
+of the net payoff in the statistical collector \texttt{statValue}.
+In the \texttt{main} program, we first specify the 12 observation
+times $\zeta_j = j/12$ for $j=1,\dots,12$, and put them in the array
+\texttt{zeta} (of size 13) together with $\zeta_0=0$.
+We then construct an \texttt{Asian} object with parameters
+$r=0.05$, $\sigma=0.5$, $K = 100$, $S(0)=100$, $t=12$, and the
+observation times contained in array \texttt{zeta}.
+We then create the statistical collector \texttt{statValue},
+perform $10^5$ simulation runs, and print the results.
+The discount factor $e^{-rT}$ and the
+constants $\sigma \sqrt{\zeta_j - \zeta_{j-1}}$ and
+$(r-\sigma^2/2)(\zeta_j - \zeta_{j-1})$ are precomputed in the
+constructor \texttt{Asian}, to speed up the simulation.
+
+
+The program in Listing~\ref{lst:AsianQMC} extends the class \texttt{Asian}
+to \texttt{AsianQMC}, whose method \texttt{simulate\-QMC}
+estimates the option value via randomized quasi-Monte Carlo.
+It uses $m$ independently randomized copies of digital net \texttt{p}
+and puts the results in statistical collector \texttt{statAverage}.
+The randomization is a left matrix scramble followed by a digital
+random shift, applied before each batch of $n$ simulation runs.
+
+% \bigskip
+
+\lstinputlisting[label=lst:AsianQMC,
+caption={Pricing an Asian option on a GMB process with quasi-Monte Carlo},
+lineskip=-1pt,
+lineskip=-1pt,
+emph={simulateQMC,main}
+]{AsianQMC.java}
+
+% \bigskip
+
+The random number stream passed to the method
+\texttt{simulateRuns} is an iterator that enumerates the points and
+coordinates of the randomized point set \texttt{p}.
+These point set iterators, available for each type of point set
+in package \texttt{hups}, implement the \texttt{RandomStream} interface
+and permit one to easily replace the uniform random numbers by (randomized)
+highly-uniform point sets or sequences, without changing the code of
+the model itself.
+The method \texttt{resetStartStream}, invoked immediately after each
+randomization, resets the iterator to the first coordinate of the
+first point of the point set \texttt{p}.
+The number $n$ of simulation runs is equal to the number of points.
+The points correspond to substreams in the \texttt{RandomStream} interface.
+The method \texttt{resetNextSubstream}, invoked after each simulation
+run in \texttt{simulateRuns}, resets the iterator to the first
+coordinate of the next point.
+Each generation of a uniform random number (directly or indirectly)
+with this stream during the simulation moves the iterator to the next
+coordinate of the current point.
+
+The point set used in this example is a \emph{Sobol' net} with
+$n = 2^{16}$ points in $t$ dimensions.
+The \texttt{main} program passes this point set to \texttt{simulateQMC}
+and asks for $m=20$ independent randomizations.
+It then computes the empirical variance and CPU time
+\emph{per simulation run} for both MC and randomized QMC.
+It prints the ratio of variances, which can be
+interpreted as the estimated \emph{variance reduction factor} obtained
+when using QMC instead of MC in this example, and the ratio of
+efficiencies, which can be interpreted
+as the estimated \emph{efficiency improvement factor}.
+(The efficiency of an estimator is defined as
+1/(variance $\times$ CPU time per run.)
+The results are in Listing~\ref{res:AsianQMC}:
+QMC reduces the variance by a factor of around 250 and improves the
+efficiency by a factor of over 500.
+Randomized QMC not only reduces the variance, it also runs faster than MC.
+The main reason for this is the call to \texttt{resetNextSubstream}
+in \texttt{simulateRuns}, which is rather costly for a random
+number stream of class \texttt{MRG32k3a} (with the current implementation)
+and takes negligible time for an iterator over a digital net in base 2.
+In fact, in the the case of MC, the call to \texttt{resetNextSubstream}
+is not really needed. Removing it for that case reduces the CPU
+time by more than 40\%.
+
+\lstinputlisting[label=res:AsianQMC,
+caption={Results of the program \texttt{AsianQMC}},%
+float=t,lineskip=-1pt]{AsianQMC.res}
diff --git a/source/umontreal/iro/lecuyer/functionfit/BSpline.java b/source/umontreal/iro/lecuyer/functionfit/BSpline.java
new file mode 100644
index 0000000..1a6e13c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/BSpline.java
@@ -0,0 +1,611 @@
+
+
+/*
+ * Class: BSpline
+ * Description: B-spline
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functionfit;
+
+import umontreal.iro.lecuyer.util.Misc;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import umontreal.iro.lecuyer.functions.MathFunctionWithIntegral;
+import umontreal.iro.lecuyer.functions.MathFunctionWithDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionWithFirstDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+import cern.colt.matrix.linalg.Algebra;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.DoubleFactory2D;
+import java.util.Arrays;
+import java.util.Random;
+import java.io.*;
+
+
+/**
+ * Represents a B-spline with control points at
+ *
+ * <SPAN CLASS="MATH">(<I>X</I><SUB>i</SUB>, <I>Y</I><SUB>i</SUB>)</SPAN>.
+ * Let
+ * <SPAN CLASS="MATH"><B>P</B><SUB><B>i</B></SUB> = (<I>X</I><SUB>i</SUB>, <I>Y</I><SUB>i</SUB>)</SPAN>, for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>, be a <SPAN CLASS="textit">control point</SPAN> and
+ * let <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN>, for
+ * <SPAN CLASS="MATH"><I>j</I> = 0,…, <I>m</I> - 1</SPAN> be a <SPAN CLASS="textit">knot</SPAN>.
+ * A B-spline of degree <SPAN CLASS="MATH"><I>p</I> = <I>m</I> - <I>n</I> - 1</SPAN> is a parametric curve defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>P</B>(<B>t</B>) = ∑<SUB>i=0</SUB><SUP>n-1</SUP><I>N</I><SUB>i, p</SUB>(<I>t</I>)<B>P</B><SUB><B>i</B></SUB>, for <I>t</I><SUB>p</SUB> <= <I>t</I> <= <I>t</I><SUB>m-p-1</SUB>.
+ * </DIV><P></P>
+ * Here,
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>N</I><SUB>i, p</SUB>(<I>t</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>((<I>t</I> - <I>t</I><SUB>i</SUB>)/(<I>t</I><SUB>i+p</SUB> - <I>t</I><SUB>i</SUB>))<I>N</I><SUB>i, p-1</SUB>(<I>t</I>) + ((<I>t</I><SUB>i+p+1</SUB> - <I>t</I>)/(<I>t</I><SUB>i+p+1</SUB> - <I>t</I><SUB>i+1</SUB>))<I>N</I><SUB>i+1, p-1</SUB>(<I>t</I>)</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>N</I><SUB>i, 0</SUB>(<I>t</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>{1#1</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * <P>
+ * This class provides methods to evaluate
+ *
+ * <SPAN CLASS="MATH"><B>P</B>(<B>t</B>) = (<I>X</I>(<I>t</I>), <I>Y</I>(<I>t</I>))</SPAN> at any value of <SPAN CLASS="MATH"><I>t</I></SPAN>,
+ * for a B-spline of any degree <SPAN CLASS="MATH"><I>p</I> >= 1</SPAN>.
+ * Note that the
+ * {@link #evaluate(double) evaluate} method
+ * of this class can be slow, since it
+ * uses a root finder to determine
+ * the value of <SPAN CLASS="MATH"><I>t</I><SUP>*</SUP></SPAN> for which <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUP>*</SUP>) = <I>x</I></SPAN>
+ * before it computes <SPAN CLASS="MATH"><I>Y</I>(<I>t</I><SUP>*</SUP>)</SPAN>.
+ *
+ */
+public class BSpline implements MathFunction
+
+,
+MathFunctionWithIntegral, MathFunctionWithDerivative, MathFunctionWithFirstDerivative{
+ // Formula taken from http://www.ibiblio.org/e-notes/Splines/Basis.htm
+ // and http://en.wikipedia.org/wiki/De_Boor_algorithm
+ private double[] x; //x original
+ private double[] y; //y original
+
+ private int degree;
+
+ //variables sur lesquelles on travaille
+ private double[] myX;
+ private double[] myY;
+ private double[] knots;
+
+
+
+ /**
+ * Constructs a new uniform B-spline of degree <TT>degree</TT>
+ * with control points at (<TT>x[i]</TT>, <TT>y[i]</TT>).
+ * The knots of the resulting B-spline are set uniformly from
+ * <TT>x[0]</TT> to <TT>x[n-1]</TT>.
+ *
+ * @param x the values of <SPAN CLASS="MATH"><I>X</I></SPAN>.
+ *
+ * @param y the values of <SPAN CLASS="MATH"><I>Y</I></SPAN>.
+ *
+ * @param degree the degree of the B-spline.
+ *
+ *
+ */
+ public BSpline (final double[] x, final double[] y, final int degree) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ this.degree = degree;
+ this.x = x.clone();
+ this.y = y.clone();
+ init(x, y, null);
+ }
+
+
+ /**
+ * Constructs a new uniform B-spline
+ * with control points at (<TT>x[i]</TT>, <TT>y[i]</TT>), and
+ * knot vector given by the array <TT>knots</TT>.
+ *
+ * @param x the values of <SPAN CLASS="MATH"><I>X</I></SPAN>.
+ *
+ * @param y the values of <SPAN CLASS="MATH"><I>Y</I></SPAN>.
+ *
+ * @param knots the knots of the B-spline.
+ *
+ */
+ public BSpline (final double[] x, final double[] y, final double[] knots) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ if (knots.length <= x.length + 1)
+ throw new IllegalArgumentException("The number of knots must be at least n+2");
+
+ this.x = x.clone();
+ this.y = y.clone();
+ this.knots = knots.clone();
+ init(x, y, knots);
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>X</I><SUB>i</SUB></SPAN> coordinates for this spline.
+ *
+ * @return the <SPAN CLASS="MATH"><I>X</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ */
+ public double[] getX() {
+ return myX.clone ();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>Y</I><SUB>i</SUB></SPAN> coordinates for this spline.
+ *
+ * @return the <SPAN CLASS="MATH"><I>Y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ */
+ public double[] getY() {
+ return myY.clone ();
+ }
+
+
+ /**
+ * Returns the knot maximal value.
+ *
+ * @return the <SPAN CLASS="MATH"><I>Y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ */
+ public double getMaxKnot() {
+ return knots[knots.length-1];
+ }
+
+
+ /**
+ * Returns the knot minimal value.
+ *
+ * @return the <SPAN CLASS="MATH"><I>Y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ */
+ public double getMinKnot() {
+ return knots[0];
+ }
+
+
+ /**
+ * Returns an array containing the knot vector
+ * <SPAN CLASS="MATH">(<I>t</I><SUB>0</SUB>, <I>t</I><SUB>m-1</SUB>)</SPAN>.
+ *
+ * @return the knot vector.
+ *
+ */
+ public double[] getKnots() {
+ return knots.clone ();
+ }
+
+
+ /**
+ * Returns a B-spline curve of degree <TT>degree</TT> interpolating the
+ *
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN> points.
+ * This method uses the uniformly spaced method for interpolating
+ * points with a B-spline curve, and a uniformed clamped knot vector,
+ * as described in <TT><A NAME="tex2html1"
+ * HREF="http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/">http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/</A></TT>.
+ *
+ * @param x the values of <SPAN CLASS="MATH"><I>X</I></SPAN>.
+ *
+ * @param y the values of <SPAN CLASS="MATH"><I>Y</I></SPAN>.
+ *
+ * @param degree the degree of the B-spline.
+ *
+ * @return the B-spline curve.
+ *
+ */
+ public static BSpline createInterpBSpline (double[] x, double[] y,
+ int degree) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ if (x.length <= degree)
+ throw new IllegalArgumentException("The arrays length must be greater than degree");
+
+ int n = x.length-1;
+ //compute t : parameters vector uniformly from 0 to 1
+ double[] t = new double[x.length];
+ for(int i =0; i<t.length; i++) {
+ t[i] = (double)i/(t.length-1);
+ }
+
+ //compute U : clamped knots vector uniformly from 0 to 1
+ double U[] = new double[x.length + degree + 1];
+ int m = U.length-1;
+ for(int i =0; i<=degree; i++)
+ U[i] = 0;
+ for(int i =1; i<x.length-degree; i++)
+ U[i+degree] = (double)i/(x.length-degree);
+ for(int i = U.length-1-degree; i<U.length; i++)
+ U[i] = 1;
+
+
+ //compute matrix N : made of BSpline coefficients
+ double [][] N = new double[x.length][x.length];
+ for(int i = 0; i<x.length; i++) {
+ N[i] = computeN(U, degree, t[i], x.length);
+ }
+
+ //initialize D : initial points matrix
+ double [][] D = new double[x.length][2];
+ for(int i =0; i<x.length; i++) {
+ D[i][0] = x[i];
+ D[i][1] = y[i];
+ }
+
+ //solve the linear equation system using colt library
+ DoubleMatrix2D coltN = DoubleFactory2D.dense.make(N);
+ DoubleMatrix2D coltD = DoubleFactory2D.dense.make(D);
+ DoubleMatrix2D coltP;
+ coltP = Algebra.ZERO.solve(coltN, coltD);
+
+ return new BSpline(coltP.viewColumn(0).toArray(), coltP.viewColumn(1).toArray(), U);
+ }
+
+
+ /**
+ * Returns a B-spline curve of degree <TT>degree</TT> smoothing
+ *
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN>, for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I></SPAN> points. The
+ * precision depends on the parameter <SPAN CLASS="MATH"><I>h</I></SPAN>:
+ * <SPAN CLASS="MATH">1 <= <texttt>degree</texttt> <= <I>h</I> < <I>n</I></SPAN>, which
+ * represents the number of control points used by the new B-spline curve,
+ * minimizing the quadratic error
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>L</I> = ∑<SUB>i=0</SUB><SUP>n</SUP>(2#2)<SUP>2</SUP>.
+ * </DIV><P></P>
+ * This method uses the uniformly spaced method for interpolating
+ * points with a B-spline curve and a uniformed clamped knot vector,
+ * as described in <TT><A NAME="tex2html2"
+ * HREF="http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/">http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/</A></TT>.
+ *
+ * @param x the values of <SPAN CLASS="MATH"><I>X</I></SPAN>.
+ *
+ * @param y the values of <SPAN CLASS="MATH"><I>Y</I></SPAN>.
+ *
+ * @param degree the degree of the B-spline.
+ *
+ * @param h the desired number of control points.
+ *
+ * @return the B-spline curve.
+ *
+ */
+ public static BSpline createApproxBSpline (double[] x, double[] y,
+ int degree, int h) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ if (x.length <= degree)
+ throw new IllegalArgumentException("The arrays length must be greater than degree");
+
+ //compute t : parameters vector uniformly from 0 to 1
+ double[] t = new double[x.length];
+ for(int i =0; i<t.length; i++) {
+ t[i] = (double)i/(t.length-1);
+ }
+
+ //compute U : clamped knots vector uniformly from 0 to 1
+ double U[] = new double[x.length + degree + 1];
+ int m = U.length-1;
+ for(int i =0; i<=degree; i++)
+ U[i] = 0;
+ for(int i =1; i<x.length-degree; i++)
+ U[i+degree] = (double)i/(x.length-degree);
+ for(int i = U.length-1-degree; i<U.length; i++)
+ U[i] = 1;
+
+
+ //compute matrix N : composed of BSpline coefficients
+ double [][] N = new double[x.length][x.length];
+ for(int i = 1; i<x.length; i++) {
+ N[i] = computeN(U, degree, t[i], x.length);
+ }
+
+ //initialize D : initial points matrix
+ double [][] D = new double[x.length][2];
+ for(int i =0; i<x.length; i++) {
+ D[i][0] = x[i];
+ D[i][1] = y[i];
+ }
+
+ //compute Q :
+ double[][] tempQ = new double[x.length][2];
+ for(int k = 1; k < x.length-1; k++) {
+ tempQ[k][0] = D[k][0] - N[k][0]*D[0][0] - N[k][h]*D[D.length-1][0];
+ tempQ[k][1] = D[k][1] - N[k][0]*D[0][1] - N[k][h]*D[D.length-1][1];
+ }
+ double[][] Q = new double[h-1][2];
+ for(int i = 1; i < h; i++) {
+ Q[i-1][0] = 0;
+ Q[i-1][1] = 0;
+ for(int k = 1; k<x.length; k++) {
+ Q[i-1][0] += N[k][i]*tempQ[k][0];
+ Q[i-1][1] += N[k][i]*tempQ[k][1];
+ }
+ }
+
+ //initialize P : new control point matrix
+ double [][] P = new double[h+1][2];
+
+ //solve the linear equation system using colt library
+ DoubleMatrix2D coltQ = DoubleFactory2D.dense.make(Q);
+ DoubleMatrix2D coltN = Algebra.ZERO.subMatrix(DoubleFactory2D.dense.make(N), 1, x.length-1, 1, h-1).copy();
+ DoubleMatrix2D coltM = Algebra.ZERO.mult(Algebra.ZERO.transpose(coltN), coltN);
+ DoubleMatrix2D coltP;
+ coltP = Algebra.ZERO.solve(coltM, coltQ);
+ double[] pxTemp = coltP.viewColumn(0).toArray();
+ double[] pyTemp = coltP.viewColumn(1).toArray();
+ double[] px = new double[h+1];
+ double[] py = new double[h+1];
+ px[0] = D[0][0];
+ py[0] = D[0][1];
+ px[h] = D[D.length-1][0];
+ py[h] = D[D.length-1][1];
+ for(int i =0; i< pxTemp.length; i++) {
+ px[i+1] = pxTemp[i];
+ py[i+1] = pyTemp[i];
+ }
+/*
+ Writer dos = null;
+ try {
+ dos = new FileWriter("resss");
+
+ int j = 0;
+ //BSpline bs = new BSpline(x, y, 5);
+ for (int i = 0; i < px.length; i++) {
+ dos.write(px[i] + " " + py[i] + PrintfFormat.NEWLINE);
+ }
+ }
+ catch (FileNotFoundException e) {e.printStackTrace(); }
+ catch (IOException e) { e.printStackTrace();}
+ finally {
+ try {
+ dos.close();
+ }
+ catch (IOException e) {}
+ }*/
+ return new BSpline(px, py, U);
+ }
+
+
+ /**
+ * Returns the derivative B-spline object of the current variable.
+ * Using this function and the returned object, instead of the
+ * <TT>derivative</TT> method,
+ * is strongly recommended if one wants to compute many derivative values.
+ *
+ * @return the derivative B-spline of the current variable.
+ *
+ */
+ public BSpline derivativeBSpline() {
+ double xTemp[] = new double[this.myX.length-1];
+ double yTemp[] = new double[this.myY.length-1];
+ for(int i = 0; i<xTemp.length; i++) {
+ xTemp[i] = (myX[i+1]-myX[i])*degree/(knots[i+degree+1]-knots[i+1]);
+ yTemp[i] = (myY[i+1]-myY[i])*degree/(knots[i+degree+1]-knots[i+1]);
+ }
+
+ double [] newKnots = new double[knots.length-2];
+ for(int i = 0; i < newKnots.length; i++) {
+ newKnots[i] = knots[i+1];
+ }
+
+ //tri pas optimise du tout
+ double xTemp2[] = new double[this.myX.length-1];
+ double yTemp2[] = new double[this.myY.length-1];
+ for(int i = 0; i<xTemp.length; i++) {
+ int k=0;
+ for(int j = 0; j<xTemp.length; j++) {
+ if(xTemp[i] > xTemp[j])
+ k++;
+ }
+ while(xTemp2[k] != 0)
+ k++;
+ xTemp2[k] = xTemp[i];
+ yTemp2[k] = yTemp[i];
+ }
+
+ return new BSpline(xTemp2, yTemp2, newKnots);
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>i</I></SPAN>th derivative B-spline object of the current variable;
+ * <SPAN CLASS="MATH"><I>i</I></SPAN> must be less than the degree of the original B-spline.
+ * Using this function and the returned object, instead of the
+ * <TT>derivative</TT> method,
+ * is strongly recommended if one wants to compute many derivative values.
+ *
+ * @param i the degree of the derivative.
+ *
+ * @return the ith derivative.
+ *
+ */
+ public BSpline derivativeBSpline (int i) {
+ BSpline bs = this;
+ while(i > 0) {
+ i--;
+ bs = bs.derivativeBSpline();
+ }
+ return bs;
+ }
+
+ public double evaluate(final double u) {
+ final MathFunction xFunction = new MathFunction () {
+ public double evaluate (double t) {
+ return evalX(t) - u;
+ }
+ };
+ final double t = RootFinder.brentDekker (0, 1-1.0E-6, xFunction, 1e-6);
+ return evalY(t);
+ }
+
+ public double integral (double a, double b) {
+ return MathFunctionUtil.simpsonIntegral (this, a, b, 500);
+ }
+
+ public double derivative(double u) {
+ return derivativeBSpline().evaluate(u);
+ }
+
+ public double derivative(double u, int n) {
+ return derivativeBSpline(n).evaluate(u);
+ }
+
+ private void init(double[] x, double[] y, double [] initialKnots) {
+ if(initialKnots == null) {
+ //Cree un vecteur de noeud uniforme entre 0 et 1
+ knots = new double[x.length+degree+1];
+ for(int i = degree; i < this.knots.length-degree; i++)
+ this.knots[i]= (double)(i-degree)/(knots.length - (2.0*degree) -1);
+ for(int i = this.knots.length-degree; i < this.knots.length; i++)
+ this.knots[i]=this.knots[i-1];
+ for(int i = degree; i > 0; i--)
+ this.knots[i-1]=this.knots[i];
+
+ // cree notre vecteur interne de Points de controle
+ // ici, aucune modification a faire sur les tableaux originaux
+ myX = x;
+ myY = y;
+ }
+ else {
+ degree = initialKnots.length - x.length -1;
+
+ // on adapte le tableau des noeuds a notre algorithme
+ // le tableau knot necessite d'avoir degree fois la meme valeur en debut et en fin de tableau
+ // on adapte la taille des tableau X et Y en consequence afin de continuer a respecter la condition :
+ // x.length + degree + 1 = this.knots.length
+ // Cette modification n'influence pas le resultat et permet de fermer notre courbe
+
+ //Compute the number of values wich need to be added
+ int iBorneInf = 1, iBorneSup = initialKnots.length-2;
+ while(initialKnots[iBorneInf] == initialKnots[0])
+ iBorneInf++;
+ if(iBorneInf <= degree)
+ iBorneInf = degree-iBorneInf+1;
+ else
+ iBorneInf=0;//on a alors iBorneInf valeurs a rajouter en debut de tableau
+
+ while(initialKnots[iBorneSup] == initialKnots[initialKnots.length-1])
+ iBorneSup--;
+ if(iBorneSup >= initialKnots.length-1-degree)
+ iBorneSup = degree+1-(initialKnots.length-1-iBorneSup);
+ else
+ iBorneSup = 0; //on a alors iBorneSup valeurs a rajouter en fin de tableau
+
+ //add computed values
+ this.knots = new double[initialKnots.length + iBorneInf + iBorneSup];
+ myX = new double[x.length + iBorneInf + iBorneSup];
+ myY = new double[y.length + iBorneInf + iBorneSup];
+ for(int i = 0; i<iBorneInf; i++) {
+ this.knots[i] = initialKnots[0];
+ myX[i] = x[0];
+ myY[i] = y[0];
+ }
+ for(int i = 0; i<initialKnots.length; i++)
+ this.knots[iBorneInf + i] = initialKnots[i];
+ for(int i = 0; i<x.length; i++) {
+ myX[iBorneInf + i] = x[i];
+ myY[iBorneInf + i] = y[i];
+ }
+ for(int i = 0; i<iBorneSup; i++) {
+ this.knots[this.knots.length-1 - i] = initialKnots[initialKnots.length-1];
+ myX[myX.length-1-i] = x[x.length-1];
+ myY[myY.length-1-i] = y[y.length-1];
+ }
+ }
+ }
+
+ public double evalX(double u) {
+ int k = Misc.getTimeInterval (knots, 0, knots.length - 1, u);
+ double[][] X = new double[degree+1][myX.length];
+
+ for(int i = k-degree; i<=k; i++)
+ X[0][i] = myX[i];
+ for(int j=1; j<= degree; j++) {
+ for(int i = k-degree+j; i <= k; i++) {
+ double aij = (u - this.knots[i]) / (this.knots[i+1+degree-j] - this.knots[i]);
+ X[j][i] = (1-aij) * X[j-1][i-1] + aij * X[j-1][i];
+ }
+ }
+ return X[degree][k];
+ }
+
+ public double evalY(double u) {
+ int k = Misc.getTimeInterval (knots, 0, knots.length - 1, u);
+ double[][] Y = new double[degree+1][myX.length];
+
+ for(int i = k-degree; i<=k; i++)
+ Y[0][i] = myY[i];
+ for(int j=1; j<= degree; j++) {
+ for(int i = k-degree+j; i <= k; i++) {
+ double aij = (u - this.knots[i]) / (this.knots[i+1+degree-j] - this.knots[i]);
+ Y[j][i] = (1-aij) * Y[j-1][i-1] + aij * Y[j-1][i];
+ }
+ }
+ return Y[degree][k];
+ }
+
+ private static double[] computeN(double[] U, int degree, double u, int n) {
+ double[] N = new double[n];
+
+ //cas particuliers
+ if(u == U[0]) {
+ N[0] = 1.0;
+ return N;
+ }
+ else if (u == U[U.length-1]) {
+ N[N.length-1] = 1.0;
+ return N;
+ }
+
+ //trouve l'intervalle de u
+ int k = Misc.getTimeInterval (U, 0, U.length - 1, u);
+
+ //calcule N, tableaux des coefficients des BSplines
+ N[k] = 1.0;
+ for(int d = 1; d <= degree; d++) {
+ N[k-d] = N[k-d+1] * (U[k+1] - u) / (U[k+1] - U[k-d+1]);
+ for(int i = k-d+1; i<= k-1; i++)
+ N[i] = (u - U[i]) / (U[i+d]-U[i]) * N[i] + (U[i+d+1] - u)/(U[i+d+1] - U[i+1]) * N[i+1];
+ N[k] = (u - U[k]) / (U[k+d] - U[k]) * N[k];
+ }
+ return N;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/functionfit/BSpline.tex b/source/umontreal/iro/lecuyer/functionfit/BSpline.tex
new file mode 100644
index 0000000..e1551c7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/BSpline.tex
@@ -0,0 +1,607 @@
+\defmodule{BSpline}
+
+Represents a B-spline with control points at
+$(X_i, Y_i)$.
+Let $\mathbf{P_i}=(X_i, Y_i)$, for $i=0,\ldots,n-1$, be a \emph{control point} and
+let $t_j$, for $j=0,\ldots,m-1$ be a \emph{knot}.
+A B-spline \cite{mDEB78a} of degree $p=m-n-1$ is a parametric curve defined as
+\[\mathbf{P(t)} = \sum_{i=0}^{n-1} N_{i, p}(t) \mathbf{P_i},\mbox{ for }t_p\le t\le t_{m-p-1}.\]
+Here,
+\begin{latexonly}%
+\begin{eqnarray*}
+N_{i, p}(t) &=&
+\frac{t-t_i}{t_{i+p} - t_i}N_{i, p-1}(t)
++ \frac{t_{i+p+1} - t}{t_{i+p+1} - t_{i+1}}N_{i+1, p-1}(t)\\[7pt]
+N_{i, 0}(t) &=& \left\{\begin{array}{ll}
+1&\mbox{ for }t_i\le t\le t_{i+1},\\
+0&\mbox{ elsewhere}.
+\end{array}\right.
+\end{eqnarray*}
+\end{latexonly}%
+\begin{htmlonly}
+\begin{eqnarray*}
+N_{i, p}(t) &=&
+((t-t_i)/(t_{i+p} - t_i))N_{i, p-1}(t)
++ ((t_{i+p+1} - t)/(t_{i+p+1} - t_{i+1})) N_{i+1, p-1}(t)\\[7pt]
+N_{i, 0}(t) &=& \left\{\begin{array}{ll}
+1&\mbox{ for }t_i\le t\le t_{i+1},\\
+0&\mbox{ elsewhere}.
+\end{array}\right.
+\end{eqnarray*}
+\end{htmlonly}%
+
+
+This class provides methods to evaluate
+$\mathbf{P(t)}=(X(t), Y(t))$ at any value of $t$,
+for a B-spline of any degree $p\ge 1$.
+Note that the
+\method{evaluate}{double} method
+of this class can be slow, since it
+uses a root finder to determine
+the value of $t^*$ for which $X(t^*)=x$
+before it computes $Y(t^*)$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BSpline
+ * Description: B-spline
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functionfit;\begin{hide}
+
+import umontreal.iro.lecuyer.util.Misc;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import umontreal.iro.lecuyer.functions.MathFunctionWithIntegral;
+import umontreal.iro.lecuyer.functions.MathFunctionWithDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionWithFirstDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+import cern.colt.matrix.linalg.Algebra;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.DoubleFactory2D;
+import java.util.Arrays;
+import java.util.Random;
+import java.io.*;
+\end{hide}
+
+public class BSpline implements MathFunction\begin{hide}
+
+,
+MathFunctionWithIntegral, MathFunctionWithDerivative, MathFunctionWithFirstDerivative{
+ // Formula taken from http://www.ibiblio.org/e-notes/Splines/Basis.htm
+ // and http://en.wikipedia.org/wiki/De_Boor_algorithm
+ private double[] x; //x original
+ private double[] y; //y original
+
+ private int degree;
+
+ //variables sur lesquelles on travaille
+ private double[] myX;
+ private double[] myY;
+ private double[] knots;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BSpline (final double[] x, final double[] y, final int degree)\begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ this.degree = degree;
+ this.x = x.clone();
+ this.y = y.clone();
+ init(x, y, null);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new uniform B-spline of degree \texttt{degree}
+ with control points at (\texttt{x[i]}, \texttt{y[i]}).
+ The knots of the resulting B-spline are set uniformly from
+ \texttt{x[0]} to \texttt{x[n-1]}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the values of $X$.}
+ \param{y}{the values of $Y$.}
+ \param{degree}{the degree of the B-spline.}
+\end{htmlonly}
+\begin{code}
+
+ public BSpline (final double[] x, final double[] y, final double[] knots)\begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ if (knots.length <= x.length + 1)
+ throw new IllegalArgumentException("The number of knots must be at least n+2");
+
+ this.x = x.clone();
+ this.y = y.clone();
+ this.knots = knots.clone();
+ init(x, y, knots);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new uniform B-spline
+ with control points at (\texttt{x[i]}, \texttt{y[i]}), and
+ knot vector given by the array \texttt{knots}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the values of $X$.}
+ \param{y}{the values of $Y$.}
+ \param{knots}{the knots of the B-spline.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public double[] getX()\begin{hide} {
+ return myX.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $X_i$ coordinates for this spline.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $X_i$ coordinates.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getY()\begin{hide} {
+ return myY.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $Y_i$ coordinates for this spline.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $Y_i$ coordinates.}
+\end{htmlonly}
+\begin{code}
+
+ public double getMaxKnot()\begin{hide} {
+ return knots[knots.length-1];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the knot maximal value.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $Y_i$ coordinates.}
+\end{htmlonly}
+\begin{code}
+
+ public double getMinKnot()\begin{hide} {
+ return knots[0];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the knot minimal value.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $Y_i$ coordinates.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getKnots()\begin{hide} {
+ return knots.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an array containing the knot vector $(t_0, t_{m-1})$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the knot vector.}
+\end{htmlonly}
+\begin{code}
+
+ public static BSpline createInterpBSpline (double[] x, double[] y,
+ int degree) \begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ if (x.length <= degree)
+ throw new IllegalArgumentException("The arrays length must be greater than degree");
+
+ int n = x.length-1;
+ //compute t : parameters vector uniformly from 0 to 1
+ double[] t = new double[x.length];
+ for(int i =0; i<t.length; i++) {
+ t[i] = (double)i/(t.length-1);
+ }
+
+ //compute U : clamped knots vector uniformly from 0 to 1
+ double U[] = new double[x.length + degree + 1];
+ int m = U.length-1;
+ for(int i =0; i<=degree; i++)
+ U[i] = 0;
+ for(int i =1; i<x.length-degree; i++)
+ U[i+degree] = (double)i/(x.length-degree);
+ for(int i = U.length-1-degree; i<U.length; i++)
+ U[i] = 1;
+
+
+ //compute matrix N : made of BSpline coefficients
+ double [][] N = new double[x.length][x.length];
+ for(int i = 0; i<x.length; i++) {
+ N[i] = computeN(U, degree, t[i], x.length);
+ }
+
+ //initialize D : initial points matrix
+ double [][] D = new double[x.length][2];
+ for(int i =0; i<x.length; i++) {
+ D[i][0] = x[i];
+ D[i][1] = y[i];
+ }
+
+ //solve the linear equation system using colt library
+ DoubleMatrix2D coltN = DoubleFactory2D.dense.make(N);
+ DoubleMatrix2D coltD = DoubleFactory2D.dense.make(D);
+ DoubleMatrix2D coltP;
+ coltP = Algebra.ZERO.solve(coltN, coltD);
+
+ return new BSpline(coltP.viewColumn(0).toArray(), coltP.viewColumn(1).toArray(), U);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a B-spline curve of degree \texttt{degree} interpolating the
+ $(x_i, y_i)$ points \cite{mDEB78a}.
+ This method uses the uniformly spaced method for interpolating
+ points with a B-spline curve, and a uniformed clamped knot vector,
+ as described in \url{http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/}.
+ %On pourrait peut etre implementer les autes methodes ?
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the values of $X$.}
+ \param{y}{the values of $Y$.}
+ \param{degree}{the degree of the B-spline.}
+ \return{the B-spline curve.}
+\end{htmlonly}
+
+\begin{code}
+
+ public static BSpline createApproxBSpline (double[] x, double[] y,
+ int degree, int h) \begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException("The arrays x and y must share the same length");
+ if (x.length <= degree)
+ throw new IllegalArgumentException("The arrays length must be greater than degree");
+
+ //compute t : parameters vector uniformly from 0 to 1
+ double[] t = new double[x.length];
+ for(int i =0; i<t.length; i++) {
+ t[i] = (double)i/(t.length-1);
+ }
+
+ //compute U : clamped knots vector uniformly from 0 to 1
+ double U[] = new double[x.length + degree + 1];
+ int m = U.length-1;
+ for(int i =0; i<=degree; i++)
+ U[i] = 0;
+ for(int i =1; i<x.length-degree; i++)
+ U[i+degree] = (double)i/(x.length-degree);
+ for(int i = U.length-1-degree; i<U.length; i++)
+ U[i] = 1;
+
+
+ //compute matrix N : composed of BSpline coefficients
+ double [][] N = new double[x.length][x.length];
+ for(int i = 1; i<x.length; i++) {
+ N[i] = computeN(U, degree, t[i], x.length);
+ }
+
+ //initialize D : initial points matrix
+ double [][] D = new double[x.length][2];
+ for(int i =0; i<x.length; i++) {
+ D[i][0] = x[i];
+ D[i][1] = y[i];
+ }
+
+ //compute Q :
+ double[][] tempQ = new double[x.length][2];
+ for(int k = 1; k < x.length-1; k++) {
+ tempQ[k][0] = D[k][0] - N[k][0]*D[0][0] - N[k][h]*D[D.length-1][0];
+ tempQ[k][1] = D[k][1] - N[k][0]*D[0][1] - N[k][h]*D[D.length-1][1];
+ }
+ double[][] Q = new double[h-1][2];
+ for(int i = 1; i < h; i++) {
+ Q[i-1][0] = 0;
+ Q[i-1][1] = 0;
+ for(int k = 1; k<x.length; k++) {
+ Q[i-1][0] += N[k][i]*tempQ[k][0];
+ Q[i-1][1] += N[k][i]*tempQ[k][1];
+ }
+ }
+
+ //initialize P : new control point matrix
+ double [][] P = new double[h+1][2];
+
+ //solve the linear equation system using colt library
+ DoubleMatrix2D coltQ = DoubleFactory2D.dense.make(Q);
+ DoubleMatrix2D coltN = Algebra.ZERO.subMatrix(DoubleFactory2D.dense.make(N), 1, x.length-1, 1, h-1).copy();
+ DoubleMatrix2D coltM = Algebra.ZERO.mult(Algebra.ZERO.transpose(coltN), coltN);
+ DoubleMatrix2D coltP;
+ coltP = Algebra.ZERO.solve(coltM, coltQ);
+ double[] pxTemp = coltP.viewColumn(0).toArray();
+ double[] pyTemp = coltP.viewColumn(1).toArray();
+ double[] px = new double[h+1];
+ double[] py = new double[h+1];
+ px[0] = D[0][0];
+ py[0] = D[0][1];
+ px[h] = D[D.length-1][0];
+ py[h] = D[D.length-1][1];
+ for(int i =0; i< pxTemp.length; i++) {
+ px[i+1] = pxTemp[i];
+ py[i+1] = pyTemp[i];
+ }
+/*
+ Writer dos = null;
+ try {
+ dos = new FileWriter("resss");
+
+ int j = 0;
+ //BSpline bs = new BSpline(x, y, 5);
+ for (int i = 0; i < px.length; i++) {
+ dos.write(px[i] + " " + py[i] + PrintfFormat.NEWLINE);
+ }
+ }
+ catch (FileNotFoundException e) {e.printStackTrace(); }
+ catch (IOException e) { e.printStackTrace();}
+ finally {
+ try {
+ dos.close();
+ }
+ catch (IOException e) {}
+ }*/
+ return new BSpline(px, py, U);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a B-spline curve of degree \texttt{degree} smoothing
+ $(x_i, y_i)$, for $i=0,\ldots,n$ points. The
+ precision depends on the parameter $h$: $1 \le \texttt{degree} \le h<n$, which
+ represents the number of control points used by the new B-spline curve,
+ minimizing the quadratic error
+ \[L = \sum_{i=0}^{n}\left( \frac{Y_i - S_i(X_i)}{W_i}\right)^2.\]
+ This method uses the uniformly spaced method for interpolating
+ points with a B-spline curve and a uniformed clamped knot vector,
+ as described in \url{http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the values of $X$.}
+ \param{y}{the values of $Y$.}
+ \param{degree}{the degree of the B-spline.}
+ \param{h}{the desired number of control points.}
+ \return{the B-spline curve.}
+\end{htmlonly}
+\begin{code}
+
+ public BSpline derivativeBSpline() \begin{hide} {
+ double xTemp[] = new double[this.myX.length-1];
+ double yTemp[] = new double[this.myY.length-1];
+ for(int i = 0; i<xTemp.length; i++) {
+ xTemp[i] = (myX[i+1]-myX[i])*degree/(knots[i+degree+1]-knots[i+1]);
+ yTemp[i] = (myY[i+1]-myY[i])*degree/(knots[i+degree+1]-knots[i+1]);
+ }
+
+ double [] newKnots = new double[knots.length-2];
+ for(int i = 0; i < newKnots.length; i++) {
+ newKnots[i] = knots[i+1];
+ }
+
+ //tri pas optimise du tout
+ double xTemp2[] = new double[this.myX.length-1];
+ double yTemp2[] = new double[this.myY.length-1];
+ for(int i = 0; i<xTemp.length; i++) {
+ int k=0;
+ for(int j = 0; j<xTemp.length; j++) {
+ if(xTemp[i] > xTemp[j])
+ k++;
+ }
+ while(xTemp2[k] != 0)
+ k++;
+ xTemp2[k] = xTemp[i];
+ yTemp2[k] = yTemp[i];
+ }
+
+ return new BSpline(xTemp2, yTemp2, newKnots);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the derivative B-spline object of the current variable.
+ Using this function and the returned object, instead of the
+ \texttt{derivative} method,
+ is strongly recommended if one wants to compute many derivative values.
+\end{tabb}
+\begin{htmlonly}
+ \return{the derivative B-spline of the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public BSpline derivativeBSpline (int i) \begin{hide} {
+ BSpline bs = this;
+ while(i > 0) {
+ i--;
+ bs = bs.derivativeBSpline();
+ }
+ return bs;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $i$th derivative B-spline object of the current variable;
+ $i$ must be less than the degree of the original B-spline.
+ Using this function and the returned object, instead of the
+ \texttt{derivative} method,
+ is strongly recommended if one wants to compute many derivative values.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the degree of the derivative.}
+ \return{the ith derivative.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+ public double evaluate(final double u) {
+ final MathFunction xFunction = new MathFunction () {
+ public double evaluate (double t) {
+ return evalX(t) - u;
+ }
+ };
+ final double t = RootFinder.brentDekker (0, 1-1.0E-6, xFunction, 1e-6);
+ return evalY(t);
+ }
+
+ public double integral (double a, double b) {
+ return MathFunctionUtil.simpsonIntegral (this, a, b, 500);
+ }
+
+ public double derivative(double u) {
+ return derivativeBSpline().evaluate(u);
+ }
+
+ public double derivative(double u, int n) {
+ return derivativeBSpline(n).evaluate(u);
+ }
+
+ private void init(double[] x, double[] y, double [] initialKnots) {
+ if(initialKnots == null) {
+ //Cree un vecteur de noeud uniforme entre 0 et 1
+ knots = new double[x.length+degree+1];
+ for(int i = degree; i < this.knots.length-degree; i++)
+ this.knots[i]= (double)(i-degree)/(knots.length - (2.0*degree) -1);
+ for(int i = this.knots.length-degree; i < this.knots.length; i++)
+ this.knots[i]=this.knots[i-1];
+ for(int i = degree; i > 0; i--)
+ this.knots[i-1]=this.knots[i];
+
+ // cree notre vecteur interne de Points de controle
+ // ici, aucune modification a faire sur les tableaux originaux
+ myX = x;
+ myY = y;
+ }
+ else {
+ degree = initialKnots.length - x.length -1;
+
+ // on adapte le tableau des noeuds a notre algorithme
+ // le tableau knot necessite d'avoir degree fois la meme valeur en debut et en fin de tableau
+ // on adapte la taille des tableau X et Y en consequence afin de continuer a respecter la condition :
+ // x.length + degree + 1 = this.knots.length
+ // Cette modification n'influence pas le resultat et permet de fermer notre courbe
+
+ //Compute the number of values wich need to be added
+ int iBorneInf = 1, iBorneSup = initialKnots.length-2;
+ while(initialKnots[iBorneInf] == initialKnots[0])
+ iBorneInf++;
+ if(iBorneInf <= degree)
+ iBorneInf = degree-iBorneInf+1;
+ else
+ iBorneInf=0;//on a alors iBorneInf valeurs a rajouter en debut de tableau
+
+ while(initialKnots[iBorneSup] == initialKnots[initialKnots.length-1])
+ iBorneSup--;
+ if(iBorneSup >= initialKnots.length-1-degree)
+ iBorneSup = degree+1-(initialKnots.length-1-iBorneSup);
+ else
+ iBorneSup = 0; //on a alors iBorneSup valeurs a rajouter en fin de tableau
+
+ //add computed values
+ this.knots = new double[initialKnots.length + iBorneInf + iBorneSup];
+ myX = new double[x.length + iBorneInf + iBorneSup];
+ myY = new double[y.length + iBorneInf + iBorneSup];
+ for(int i = 0; i<iBorneInf; i++) {
+ this.knots[i] = initialKnots[0];
+ myX[i] = x[0];
+ myY[i] = y[0];
+ }
+ for(int i = 0; i<initialKnots.length; i++)
+ this.knots[iBorneInf + i] = initialKnots[i];
+ for(int i = 0; i<x.length; i++) {
+ myX[iBorneInf + i] = x[i];
+ myY[iBorneInf + i] = y[i];
+ }
+ for(int i = 0; i<iBorneSup; i++) {
+ this.knots[this.knots.length-1 - i] = initialKnots[initialKnots.length-1];
+ myX[myX.length-1-i] = x[x.length-1];
+ myY[myY.length-1-i] = y[y.length-1];
+ }
+ }
+ }
+
+ public double evalX(double u) {
+ int k = Misc.getTimeInterval (knots, 0, knots.length - 1, u);
+ double[][] X = new double[degree+1][myX.length];
+
+ for(int i = k-degree; i<=k; i++)
+ X[0][i] = myX[i];
+ for(int j=1; j<= degree; j++) {
+ for(int i = k-degree+j; i <= k; i++) {
+ double aij = (u - this.knots[i]) / (this.knots[i+1+degree-j] - this.knots[i]);
+ X[j][i] = (1-aij) * X[j-1][i-1] + aij * X[j-1][i];
+ }
+ }
+ return X[degree][k];
+ }
+
+ public double evalY(double u) {
+ int k = Misc.getTimeInterval (knots, 0, knots.length - 1, u);
+ double[][] Y = new double[degree+1][myX.length];
+
+ for(int i = k-degree; i<=k; i++)
+ Y[0][i] = myY[i];
+ for(int j=1; j<= degree; j++) {
+ for(int i = k-degree+j; i <= k; i++) {
+ double aij = (u - this.knots[i]) / (this.knots[i+1+degree-j] - this.knots[i]);
+ Y[j][i] = (1-aij) * Y[j-1][i-1] + aij * Y[j-1][i];
+ }
+ }
+ return Y[degree][k];
+ }
+
+ private static double[] computeN(double[] U, int degree, double u, int n) {
+ double[] N = new double[n];
+
+ //cas particuliers
+ if(u == U[0]) {
+ N[0] = 1.0;
+ return N;
+ }
+ else if (u == U[U.length-1]) {
+ N[N.length-1] = 1.0;
+ return N;
+ }
+
+ //trouve l'intervalle de u
+ int k = Misc.getTimeInterval (U, 0, U.length - 1, u);
+
+ //calcule N, tableaux des coefficients des BSplines
+ N[k] = 1.0;
+ for(int d = 1; d <= degree; d++) {
+ N[k-d] = N[k-d+1] * (U[k+1] - u) / (U[k+1] - U[k-d+1]);
+ for(int i = k-d+1; i<= k-1; i++)
+ N[i] = (u - U[i]) / (U[i+d]-U[i]) * N[i] + (U[i+d+1] - u)/(U[i+d+1] - U[i+1]) * N[i+1];
+ N[k] = (u - U[k]) / (U[k+d] - U[k]) * N[k];
+ }
+ return N;
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functionfit/LeastSquares.java b/source/umontreal/iro/lecuyer/functionfit/LeastSquares.java
new file mode 100644
index 0000000..d1a374d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/LeastSquares.java
@@ -0,0 +1,316 @@
+
+
+/*
+ * Class: LeastSquares
+ * Description: General linear regression with the least squares method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2013 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since April 2013
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functionfit;
+
+import java.io.Serializable;
+import cern.colt.matrix.DoubleMatrix1D;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.QRDecomposition;
+import cern.colt.matrix.linalg.SingularValueDecomposition;
+import cern.colt.matrix.linalg.Algebra;
+
+
+/**
+ * This class implements different <SPAN CLASS="textit">linear regression</SPAN> models, using the
+ * least squares method to estimate the regression coefficients. Given
+ * input data <SPAN CLASS="MATH"><I>x</I><SUB>ij</SUB></SPAN> and response <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN>, one want to find the
+ * coefficients <SPAN CLASS="MATH"><I>β</I><SUB>j</SUB></SPAN> that minimize the residuals of the form
+ * (using matrix notation)
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>r</I> = min<SUB><I>β</I></SUB>| <I>Y</I> - <I>Xβ</I>|<SUB>2</SUB>,
+ * </DIV><P></P>
+ * where the <SPAN CLASS="MATH"><I>L</I><SUB>2</SUB></SPAN> norm is used. Particular cases are
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>r</I> = min<SUB><I>β</I></SUB>∑<SUB>i</SUB>(<I>y</I><SUB>i</SUB> - <I>β</I><SUB>0</SUB> - ∑<SUB>j=1</SUB><SUP>k</SUP><I>β</I><SUB>j</SUB><I>x</I><SUB>ij</SUB>)<SUP>2</SUP>.
+ * </DIV><P></P>
+ * for <SPAN CLASS="MATH"><I>k</I></SPAN> regressor variables <SPAN CLASS="MATH"><I>x</I><SUB>j</SUB></SPAN>. The well-known case of the single
+ * variable <SPAN CLASS="MATH"><I>x</I></SPAN> is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>r</I> = min<SUB><I>α</I>, <I>β</I></SUB>∑<SUB>i</SUB>(<I>y</I><SUB>i</SUB> - <I>α</I> - <I>βx</I><SUB>i</SUB>)<SUP>2</SUP>.
+ * </DIV><P></P>
+ *
+ * <P>
+ * Sometimes, one wants to use a basis of general functions <SPAN CLASS="MATH"><I>ψ</I><SUB>j</SUB>(<I>t</I>)</SPAN>
+ * with a minimization of the form
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>r</I> = min<SUB><I>β</I></SUB>∑<SUB>i</SUB>(<I>y</I><SUB>i</SUB> - ∑<SUB>j=1</SUB><SUP>k</SUP><I>β</I><SUB>j</SUB><I>ψ</I><SUB>j</SUB>(<I>t</I><SUB>i</SUB>))<SUP>2</SUP>.
+ * </DIV><P></P>
+ * For example, we could have
+ * <SPAN CLASS="MATH"><I>ψ</I><SUB>j</SUB>(<I>t</I>) = <I>e</I><SUP>-<I>λ</I><SUB>j</SUB>t</SUP></SPAN> or some other
+ * functions.
+ * In that case, one has to choose the points <SPAN CLASS="MATH"><I>t</I><SUB>i</SUB></SPAN> at which to
+ * compute the basis functions, and use a method below with
+ *
+ * <SPAN CLASS="MATH"><I>x</I><SUB>ij</SUB> = <I>ψ</I><SUB>j</SUB>(<I>t</I><SUB>i</SUB>)</SPAN>.
+ *
+ */
+public class LeastSquares {
+
+ private static double[] solution (DoubleMatrix2D X, DoubleMatrix2D Y, int k) {
+ // Solve X * Beta = Y for Beta
+ // Only the first column of Y is used
+ // k is number of beta coefficients
+
+ QRDecomposition qr = new QRDecomposition(X);
+
+ if (qr.hasFullRank()) {
+ DoubleMatrix2D B = qr.solve(Y);
+ return B.viewColumn(0).toArray();
+
+ } else {
+ DoubleMatrix1D Y0 = Y.viewColumn(0); // first column of Y
+ SingularValueDecomposition svd = new SingularValueDecomposition(X);
+ DoubleMatrix2D S = svd.getS();
+ DoubleMatrix2D V = svd.getV();
+ DoubleMatrix2D U = svd.getU();
+ Algebra alg = new Algebra();
+ DoubleMatrix2D Ut = alg.transpose(U);
+ DoubleMatrix1D g = alg.mult(Ut, Y0); // Ut*Y0
+
+ for (int j = 0; j < k; j++) {
+ // solve S*p = g for p; S is a diagonal matrix
+ double x = S.getQuick(j, j);
+ if (x > 0.) {
+ x = g.getQuick(j) / x; // p[j] = g[j]/S[j]
+ g.setQuick(j, x); // overwrite g by p
+ } else
+ g.setQuick(j, 0.);
+ }
+ DoubleMatrix1D beta = alg.mult(V, g); // V*p
+ return beta.toArray();
+ }
+}
+
+
+ /**
+ * Computes the regression coefficients using the
+ * least squares method. This is a simple linear regression with
+ * 2 regression coefficients, <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>. The model is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>y</I> = <I>α</I> + <I>βx</I>.
+ * </DIV><P></P>
+ * Given the <SPAN CLASS="MATH"><I>n</I></SPAN> data points
+ * <SPAN CLASS="MATH">(<I>X</I><SUB>i</SUB>, <I>Y</I><SUB>i</SUB>)</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…,(<I>n</I> - 1)</SPAN>,
+ * the method computes and returns the array
+ *
+ * <SPAN CLASS="MATH">[<I>α</I>, <I>β</I>]</SPAN>.
+ *
+ * @param X the regressor variables
+ *
+ * @param Y the response
+ *
+ * @return the regression coefficients
+ *
+ */
+ public static double[] calcCoefficients (double[] X, double[] Y) {
+ if (X.length != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ final int n = X.length;
+ double[][] Xa = new double[n][1];
+ for (int i = 0; i < n; i++)
+ Xa[i][0] = X[i];
+
+ return calcCoefficients0 (Xa, Y);
+ }
+
+
+ /**
+ * Computes the regression coefficients using the
+ * least squares method. This is a linear regression with a polynomial of
+ * degree <TT>deg</TT> <SPAN CLASS="MATH">= <I>k</I></SPAN> and <SPAN CLASS="MATH"><I>k</I> + 1</SPAN> regression coefficients <SPAN CLASS="MATH"><I>β</I><SUB>j</SUB></SPAN>.
+ * The model is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>y</I> = <I>β</I><SUB>0</SUB> + ∑<SUB>j=1</SUB><SUP>k</SUP><I>β</I><SUB>j</SUB><I>x</I><SUP>j</SUP>.
+ * </DIV><P></P>
+ * Given the <SPAN CLASS="MATH"><I>n</I></SPAN> data points
+ * <SPAN CLASS="MATH">(<I>X</I><SUB>i</SUB>, <I>Y</I><SUB>i</SUB>)</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…,(<I>n</I> - 1)</SPAN>,
+ * the method computes and returns the array
+ *
+ * <SPAN CLASS="MATH">[<I>β</I><SUB>0</SUB>, <I>β</I><SUB>1</SUB>,…, <I>β</I><SUB>k</SUB>]</SPAN>. Restriction: <SPAN CLASS="MATH"><I>n</I> > <I>k</I></SPAN>.
+ *
+ * @param X the regressor variables
+ *
+ * @param Y the response
+ *
+ * @return the regression coefficients
+ *
+ */
+ public static double[] calcCoefficients (double[] X, double[] Y, int deg) {
+ final int n = X.length;
+ if (n != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ if (n < deg + 1)
+ throw new IllegalArgumentException ("Not enough points");
+
+ final double[] xSums = new double[2 * deg + 1];
+ final double[] xySums = new double[deg + 1];
+ xSums[0] = n;
+ for (int i = 0; i < n; i++) {
+ double xv = X[i];
+ xySums[0] += Y[i];
+ for (int j = 1; j <= 2 * deg; j++) {
+ xSums[j] += xv;
+ if (j <= deg)
+ xySums[j] += xv * Y[i];
+ xv *= X[i];
+ }
+ }
+ final DoubleMatrix2D A = new DenseDoubleMatrix2D (deg + 1, deg + 1);
+ final DoubleMatrix2D B = new DenseDoubleMatrix2D (deg + 1, 1);
+ for (int i = 0; i <= deg; i++) {
+ for (int j = 0; j <= deg; j++) {
+ final int d = i + j;
+ A.setQuick (i, j, xSums[d]);
+ }
+ B.setQuick (i, 0, xySums[i]);
+ }
+
+ return solution(A, B, deg + 1);
+ }
+
+
+ /**
+ * Computes the regression coefficients using the
+ * least squares method. This is a model for multiple linear regression.
+ * There are <SPAN CLASS="MATH"><I>k</I> + 1</SPAN> regression coefficients <SPAN CLASS="MATH"><I>β</I><SUB>j</SUB></SPAN>, and
+ * <SPAN CLASS="MATH"><I>k</I></SPAN> regressors variables <SPAN CLASS="MATH"><I>x</I><SUB>j</SUB></SPAN>. The model is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>y</I> = <I>β</I><SUB>0</SUB> + ∑<SUB>j=1</SUB><SUP>k</SUP><I>β</I><SUB>j</SUB><I>x</I><SUB>j</SUB>.
+ * </DIV><P></P>
+ * There are <SPAN CLASS="MATH"><I>n</I></SPAN> data points <SPAN CLASS="MATH"><I>Y</I><SUB>i</SUB></SPAN>, <SPAN CLASS="MATH"><I>X</I><SUB>ij</SUB></SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…,(<I>n</I> - 1)</SPAN>, and each
+ * <SPAN CLASS="MATH"><I>X</I><SUB>i</SUB></SPAN> is a <SPAN CLASS="MATH"><I>k</I></SPAN>-dimensional point.
+ * Given the response <TT>Y[i]</TT> and the regressor variables <TT>X[i][j]</TT>,
+ *
+ * <SPAN CLASS="MATH"><texttt>i</texttt> = 0, 1,…,(<I>n</I> - 1)</SPAN>,
+ * <SPAN CLASS="MATH"><texttt>j</texttt> = 0, 1,…,(<I>k</I> - 1)</SPAN>, the method
+ * computes and returns the array
+ * <SPAN CLASS="MATH">[<I>β</I><SUB>0</SUB>, <I>β</I><SUB>1</SUB>,…, <I>β</I><SUB>k</SUB>]</SPAN>.
+ * Restriction: <SPAN CLASS="MATH"><I>n</I> > <I>k</I> + 1</SPAN>.
+ *
+ * @param X the regressor variables
+ *
+ * @param Y the response
+ *
+ * @return the regression coefficients
+ *
+ */
+ public static double[] calcCoefficients0 (double[][] X, double[] Y) {
+ if (X.length != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ if (Y.length <= X[0].length + 1)
+ throw new IllegalArgumentException ("Not enough points");
+
+ final int n = Y.length;
+ final int k = X[0].length;
+
+ DoubleMatrix2D Xa = new DenseDoubleMatrix2D(n, k+1);
+ DoubleMatrix2D Ya = new DenseDoubleMatrix2D(n, 1);
+
+ for (int i = 0; i < n; i++) {
+ Xa.setQuick (i, 0, 1.);
+ for (int j = 1; j <= k; j++) {
+ Xa.setQuick (i, j, X[i][j-1]);
+ }
+ Ya.setQuick (i, 0, Y[i]);
+ }
+
+ return solution(Xa, Ya, k + 1);
+ }
+
+
+
+ /**
+ * Computes the regression coefficients using the
+ * least squares method. This is a model for multiple linear regression.
+ * There are <SPAN CLASS="MATH"><I>k</I></SPAN> regression coefficients <SPAN CLASS="MATH"><I>β</I><SUB>j</SUB></SPAN>,
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…,(<I>k</I> - 1)</SPAN> and
+ * <SPAN CLASS="MATH"><I>k</I></SPAN> regressors variables <SPAN CLASS="MATH"><I>x</I><SUB>j</SUB></SPAN>. The model is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>y</I> = ∑<SUB>j=0</SUB><SUP>k-1</SUP><I>β</I><SUB>j</SUB><I>x</I><SUB>j</SUB>.
+ * </DIV><P></P>
+ * There are <SPAN CLASS="MATH"><I>n</I></SPAN> data points <SPAN CLASS="MATH"><I>Y</I><SUB>i</SUB></SPAN>, <SPAN CLASS="MATH"><I>X</I><SUB>ij</SUB></SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…,(<I>n</I> - 1)</SPAN>, and each
+ * <SPAN CLASS="MATH"><I>X</I><SUB>i</SUB></SPAN> is a <SPAN CLASS="MATH"><I>k</I></SPAN>-dimensional point.
+ * Given the response <TT>Y[i]</TT> and the regressor variables <TT>X[i][j]</TT>,
+ *
+ * <SPAN CLASS="MATH"><texttt>i</texttt> = 0, 1,…,(<I>n</I> - 1)</SPAN>,
+ * <SPAN CLASS="MATH"><texttt>j</texttt> = 0, 1,…,(<I>k</I> - 1)</SPAN>, the method
+ * computes and returns the array
+ * <SPAN CLASS="MATH">[<I>β</I><SUB>0</SUB>, <I>β</I><SUB>1</SUB>,…, <I>β</I><SUB>k-1</SUB>]</SPAN>.
+ * Restriction: <SPAN CLASS="MATH"><I>n</I> > <I>k</I></SPAN>.
+ *
+ * @param X the regressor variables
+ *
+ * @param Y the response
+ *
+ * @return the regression coefficients
+ *
+ */
+ public static double[] calcCoefficients (double[][] X, double[] Y) {
+ if (X.length != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ if (Y.length <= X[0].length + 1)
+ throw new IllegalArgumentException ("Not enough points");
+
+ final int n = Y.length;
+ final int k = X[0].length;
+
+ DoubleMatrix2D Xa = new DenseDoubleMatrix2D(n, k);
+ DoubleMatrix2D Ya = new DenseDoubleMatrix2D(n, 1);
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < k; j++) {
+ Xa.setQuick (i, j, X[i][j]);
+ }
+ Ya.setQuick (i, 0, Y[i]);
+ }
+
+ return solution(Xa, Ya, k);
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/functionfit/LeastSquares.tex b/source/umontreal/iro/lecuyer/functionfit/LeastSquares.tex
new file mode 100644
index 0000000..e6532e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/LeastSquares.tex
@@ -0,0 +1,288 @@
+\defmodule{LeastSquares}
+
+This class implements different \emph{linear regression} models, using the
+least squares method to estimate the regression coefficients. Given
+input data $x_{ij}$ and response $y_i$, one want to find the
+coefficients $\beta_j$ that minimize the residuals of the form
+(using matrix notation)
+%
+\[
+r = \min_\beta \|Y - X\beta\|_2,
+\]
+%
+where the $L_2$ norm is used. Particular cases are
+\[
+ r = \min_\beta \sum_i \left(y_i - \beta_0 - \sum_{j=1}^k \beta_j x_{ij}\right)^2.
+\]
+%
+for $k$ regressor variables $x_j$. The well-known case of the single
+variable $x$ is
+\[
+ r = \min_{\alpha,\beta} \sum_i \left(y_i - \alpha - \beta x_{i}\right)^2.
+\]
+%
+
+Sometimes, one wants to use a basis of general functions $\psi_j(t)$
+with a minimization of the form
+\[
+r = \min_\beta \sum_i \left(y_i - \sum_{j=1}^k \beta_j\psi_j(t_{i})\right)^2.
+\]
+For example, we could have $\psi_j(t) = e^{-\lambda_j t}$ or some other
+functions.
+In that case, one has to choose the points $t_i$ at which to
+compute the basis functions, and use a method below with
+$x_{ij} = \psi_j(t_{i})$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LeastSquares
+ * Description: General linear regression with the least squares method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2013 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since April 2013
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functionfit; \begin{hide}
+
+import java.io.Serializable;
+import cern.colt.matrix.DoubleMatrix1D;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.QRDecomposition;
+import cern.colt.matrix.linalg.SingularValueDecomposition;
+import cern.colt.matrix.linalg.Algebra;
+\end{hide}
+
+public class LeastSquares\begin{hide} {
+
+ private static double[] solution (DoubleMatrix2D X, DoubleMatrix2D Y, int k) {
+ // Solve X * Beta = Y for Beta
+ // Only the first column of Y is used
+ // k is number of beta coefficients
+
+ QRDecomposition qr = new QRDecomposition(X);
+
+ if (qr.hasFullRank()) {
+ DoubleMatrix2D B = qr.solve(Y);
+ return B.viewColumn(0).toArray();
+
+ } else {
+ DoubleMatrix1D Y0 = Y.viewColumn(0); // first column of Y
+ SingularValueDecomposition svd = new SingularValueDecomposition(X);
+ DoubleMatrix2D S = svd.getS();
+ DoubleMatrix2D V = svd.getV();
+ DoubleMatrix2D U = svd.getU();
+ Algebra alg = new Algebra();
+ DoubleMatrix2D Ut = alg.transpose(U);
+ DoubleMatrix1D g = alg.mult(Ut, Y0); // Ut*Y0
+
+ for (int j = 0; j < k; j++) {
+ // solve S*p = g for p; S is a diagonal matrix
+ double x = S.getQuick(j, j);
+ if (x > 0.) {
+ x = g.getQuick(j) / x; // p[j] = g[j]/S[j]
+ g.setQuick(j, x); // overwrite g by p
+ } else
+ g.setQuick(j, 0.);
+ }
+ DoubleMatrix1D beta = alg.mult(V, g); // V*p
+ return beta.toArray();
+ }
+} \end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static double[] calcCoefficients (double[] X, double[] Y)\begin{hide} {
+ if (X.length != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ final int n = X.length;
+ double[][] Xa = new double[n][1];
+ for (int i = 0; i < n; i++)
+ Xa[i][0] = X[i];
+
+ return calcCoefficients0 (Xa, Y);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the regression coefficients using the
+least squares method. This is a simple linear regression with
+ 2 regression coefficients, $\alpha$ and $\beta$. The model is
+\[
+ y = \alpha + \beta x.
+\]
+Given the $n$ data points $(X_i, Y_i)$, $i=0,1,\ldots,(n-1)$,
+ the method computes and returns the array
+$[\alpha, \beta]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{X}{the regressor variables}
+ \param{Y}{the response}
+ \return{the regression coefficients}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] calcCoefficients (double[] X, double[] Y, int deg) \begin{hide} {
+ final int n = X.length;
+ if (n != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ if (n < deg + 1)
+ throw new IllegalArgumentException ("Not enough points");
+
+ final double[] xSums = new double[2 * deg + 1];
+ final double[] xySums = new double[deg + 1];
+ xSums[0] = n;
+ for (int i = 0; i < n; i++) {
+ double xv = X[i];
+ xySums[0] += Y[i];
+ for (int j = 1; j <= 2 * deg; j++) {
+ xSums[j] += xv;
+ if (j <= deg)
+ xySums[j] += xv * Y[i];
+ xv *= X[i];
+ }
+ }
+ final DoubleMatrix2D A = new DenseDoubleMatrix2D (deg + 1, deg + 1);
+ final DoubleMatrix2D B = new DenseDoubleMatrix2D (deg + 1, 1);
+ for (int i = 0; i <= deg; i++) {
+ for (int j = 0; j <= deg; j++) {
+ final int d = i + j;
+ A.setQuick (i, j, xSums[d]);
+ }
+ B.setQuick (i, 0, xySums[i]);
+ }
+
+ return solution(A, B, deg + 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the regression coefficients using the
+least squares method. This is a linear regression with a polynomial of
+degree \texttt{deg} $= k$ and $k+1$ regression coefficients $\beta_j$.
+The model is
+\[
+ y = \beta_0 + \sum_{j=1}^k \beta_j x^j.
+\]
+Given the $n$ data points $(X_i, Y_i)$, $i=0,1,\ldots,(n-1)$,
+ the method computes and returns the array
+$[\beta_0, \beta_1, \ldots, \beta_k]$. Restriction: $n > k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{X}{the regressor variables}
+ \param{Y}{the response}
+ \return{the regression coefficients}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] calcCoefficients0 (double[][] X, double[] Y)\begin{hide} {
+ if (X.length != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ if (Y.length <= X[0].length + 1)
+ throw new IllegalArgumentException ("Not enough points");
+
+ final int n = Y.length;
+ final int k = X[0].length;
+
+ DoubleMatrix2D Xa = new DenseDoubleMatrix2D(n, k+1);
+ DoubleMatrix2D Ya = new DenseDoubleMatrix2D(n, 1);
+
+ for (int i = 0; i < n; i++) {
+ Xa.setQuick (i, 0, 1.);
+ for (int j = 1; j <= k; j++) {
+ Xa.setQuick (i, j, X[i][j-1]);
+ }
+ Ya.setQuick (i, 0, Y[i]);
+ }
+
+ return solution(Xa, Ya, k + 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the regression coefficients using the
+least squares method. This is a model for multiple linear regression.
+There are $k+1$ regression coefficients $\beta_j$, and
+$k$ regressors variables $x_j$. The model is
+\[
+ y = \beta_0 + \sum_{j=1}^k \beta_j x_j.
+\]
+There are $n$ data points $Y_i$, $X_{ij}$, $i=0,1,\ldots,(n-1)$, and each
+$X_{i}$ is a $k$-dimensional point.
+Given the response \texttt{Y[i]} and the regressor variables \texttt{X[i][j]},
+$\texttt{i} =0,1,\ldots,(n-1)$, $\texttt{j} =0,1,\ldots,(k-1)$, the method
+computes and returns the array $[\beta_0, \beta_1, \ldots, \beta_k]$.
+Restriction: $n > k+1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{X}{the regressor variables}
+ \param{Y}{the response}
+ \return{the regression coefficients}
+\end{htmlonly}
+\begin{code}
+
+
+ public static double[] calcCoefficients (double[][] X, double[] Y)\begin{hide} {
+ if (X.length != Y.length)
+ throw new IllegalArgumentException ("Lengths of X and Y are not equal");
+ if (Y.length <= X[0].length + 1)
+ throw new IllegalArgumentException ("Not enough points");
+
+ final int n = Y.length;
+ final int k = X[0].length;
+
+ DoubleMatrix2D Xa = new DenseDoubleMatrix2D(n, k);
+ DoubleMatrix2D Ya = new DenseDoubleMatrix2D(n, 1);
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < k; j++) {
+ Xa.setQuick (i, j, X[i][j]);
+ }
+ Ya.setQuick (i, 0, Y[i]);
+ }
+
+ return solution(Xa, Ya, k);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the regression coefficients using the
+least squares method. This is a model for multiple linear regression.
+There are $k$ regression coefficients $\beta_j$, $j=0,1,\ldots,(k-1)$ and
+$k$ regressors variables $x_j$. The model is
+\[
+ y = \sum_{j=0}^{k-1} \beta_j x_j.
+\]
+There are $n$ data points $Y_i$, $X_{ij}$, $i=0,1,\ldots,(n-1)$, and each
+$X_{i}$ is a $k$-dimensional point.
+Given the response \texttt{Y[i]} and the regressor variables \texttt{X[i][j]},
+$\texttt{i} =0,1,\ldots,(n-1)$, $\texttt{j} =0,1,\ldots,(k-1)$, the method
+computes and returns the array $[\beta_0, \beta_1, \ldots, \beta_{k-1}]$.
+Restriction: $n > k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{X}{the regressor variables}
+ \param{Y}{the response}
+ \return{the regression coefficients}
+\end{htmlonly}
+
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functionfit/PolInterp.java b/source/umontreal/iro/lecuyer/functionfit/PolInterp.java
new file mode 100644
index 0000000..12c93a4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/PolInterp.java
@@ -0,0 +1,177 @@
+
+
+/*
+ * Class: PolInterp
+ * Description: polynomial that interpolates through a set of points
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functionfit;
+
+import java.io.Serializable;
+import umontreal.iro.lecuyer.functions.Polynomial;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.Algebra;
+
+
+/**
+ * Represents a polynomial that interpolates through a set of points. More
+ * specifically, let
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>0</SUB>, <I>y</I><SUB>0</SUB>),…,(<I>x</I><SUB>n</SUB>, <I>y</I><SUB>n</SUB>)</SPAN> be a set of points and
+ * <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN> the constructed polynomial of degree <SPAN CLASS="MATH"><I>n</I></SPAN>. Then, for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>p</I>(<I>x</I><SUB>i</SUB>) = <I>y</I><SUB>i</SUB></SPAN>.
+ *
+ */
+public class PolInterp extends Polynomial implements Serializable {
+ private static final long serialVersionUID = -710451931485296501L;
+ private static final Algebra alg = new Algebra ();
+ private double[] x;
+ private double[] y;
+
+
+
+ /**
+ * Constructs a new polynomial interpolating through the given points
+ * <TT>(x[0], y[0]), ..., (x[n], y[n])</TT>. This constructs a polynomial of
+ * degree <TT>n</TT> from <TT>n+1</TT> points.
+ *
+ * @param x the <SPAN CLASS="MATH"><I>x</I></SPAN> coordinates of the points.
+ *
+ * @param y the <SPAN CLASS="MATH"><I>y</I></SPAN> coordinates of the points.
+ *
+ * @exception NullPointerException if <TT>x</TT> or <TT>y</TT> are <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the lengths of <TT>x</TT> and <TT>y</TT> are different,
+ * or if less than two points are specified.
+ *
+ */
+ public PolInterp (double[] x, double[] y) {
+ super (getCoefficients (x, y));
+ this.x = x.clone ();
+ this.y = y.clone ();
+ }
+
+
+ /**
+ * Computes and returns the coefficients the polynomial interpolating
+ * through the given points <TT>(x[0], y[0]), ..., (x[n], y[n])</TT>.
+ * This polynomial has degree <TT>n</TT> and there are <TT>n+1</TT> coefficients.
+ *
+ * @param x the <SPAN CLASS="MATH"><I>x</I></SPAN> coordinates of the points.
+ *
+ * @param y the <SPAN CLASS="MATH"><I>y</I></SPAN> coordinates of the points.
+ *
+ * @return the coefficients the interpolating polynomial.
+ *
+ */
+ public static double[] getCoefficients (double[] x, double[] y) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException (
+ "x and y must have the same length");
+ if (x.length <= 1)
+ throw new IllegalArgumentException ("At least two points are needed");
+ final DoubleMatrix2D u = new DenseDoubleMatrix2D (x.length, x.length);
+ for (int i = 0; i < x.length; i++) {
+ double v = 1;
+ for (int j = 0; j < x.length; j++) {
+ u.setQuick (i, j, v);
+ v *= x[i];
+ }
+ }
+ final DoubleMatrix2D yMat = new DenseDoubleMatrix2D (x.length, 1);
+ yMat.viewColumn (0).assign (y);
+ final DoubleMatrix2D bMat = alg.solve (u, yMat);
+ return bMat.viewColumn (0).toArray ();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>x</I></SPAN> coordinates of the interpolated points.
+ *
+ * @return the <SPAN CLASS="MATH"><I>x</I></SPAN> coordinates of the interpolated points.
+ *
+ */
+ public double[] getX() {
+ return x.clone ();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>y</I></SPAN> coordinates of the interpolated points.
+ *
+ * @return the <SPAN CLASS="MATH"><I>y</I></SPAN> coordinates of the interpolated points.
+ *
+ */
+ public double[] getY() {
+ return y.clone ();
+ }
+
+
+ /**
+ * Makes a string representation of a set of points.
+ *
+ * @param x the <SPAN CLASS="MATH"><I>x</I></SPAN> coordinates of the points.
+ *
+ * @param y the <SPAN CLASS="MATH"><I>y</I></SPAN> coordinates of the points.
+ *
+ * @return the string representing the points.
+ *
+ */
+ public static String toString (double[] x, double[] y) {
+ final StringBuilder sb = new StringBuilder ("Points: ");
+ for (int i = 0; i < x.length; i++) {
+ if (i > 0)
+ sb.append (", ");
+ final String xstr = PrintfFormat.format (8, 3, 3, x[i]);
+ final String ystr = PrintfFormat.format (8, 3, 3, y[i]);
+ sb.append ('(').append (xstr).append (", ").append (ystr).append (')');
+ }
+ return sb.toString ();
+ }
+
+ @Override
+
+
+
+ /**
+ * Calls {@link #toString( ) toString}<TT>(double[], double[])</TT> with the
+ * associated points.
+ *
+ * @return a string containing the points.
+ *
+ */
+ public String toString() {
+ return toString (x, y);
+ }
+
+
+ public PolInterp clone() {
+ final PolInterp p = (PolInterp) super.clone ();
+ p.x = x.clone ();
+ p.y = y.clone ();
+ return p;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functionfit/PolInterp.tex b/source/umontreal/iro/lecuyer/functionfit/PolInterp.tex
new file mode 100644
index 0000000..9867f5d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/PolInterp.tex
@@ -0,0 +1,178 @@
+\defmodule{PolInterp}
+
+Represents a polynomial that interpolates through a set of points. More
+specifically, let $(x_0,y_0), \ldots, (x_n, y_n)$ be a set of points and
+$p(x)$ the constructed polynomial of degree $n$. Then, for $i=0,\ldots,n$,
+$p(x_i)=y_i$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PolInterp
+ * Description: polynomial that interpolates through a set of points
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functionfit;\begin{hide}
+
+import java.io.Serializable;
+import umontreal.iro.lecuyer.functions.Polynomial;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.Algebra;
+\end{hide}
+
+public class PolInterp extends Polynomial implements Serializable\begin{hide} {
+ private static final long serialVersionUID = -710451931485296501L;
+ private static final Algebra alg = new Algebra ();
+ private double[] x;
+ private double[] y;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public PolInterp (double[] x, double[] y)\begin{hide} {
+ super (getCoefficients (x, y));
+ this.x = x.clone ();
+ this.y = y.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new polynomial interpolating through the given points
+ \texttt{(x[0], y[0]), ..., (x[n], y[n])}. This constructs a polynomial of
+ degree \texttt{n} from \texttt{n+1} points.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the $x$ coordinates of the points.}
+ \param{y}{the $y$ coordinates of the points.}
+ \exception{NullPointerException}{if \texttt{x} or \texttt{y} are \texttt{null}.}
+ \exception{IllegalArgumentException}{if the lengths of \texttt{x} and \texttt{y} are different,
+ or if less than two points are specified.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static double[] getCoefficients (double[] x, double[] y)\begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException (
+ "x and y must have the same length");
+ if (x.length <= 1)
+ throw new IllegalArgumentException ("At least two points are needed");
+ final DoubleMatrix2D u = new DenseDoubleMatrix2D (x.length, x.length);
+ for (int i = 0; i < x.length; i++) {
+ double v = 1;
+ for (int j = 0; j < x.length; j++) {
+ u.setQuick (i, j, v);
+ v *= x[i];
+ }
+ }
+ final DoubleMatrix2D yMat = new DenseDoubleMatrix2D (x.length, 1);
+ yMat.viewColumn (0).assign (y);
+ final DoubleMatrix2D bMat = alg.solve (u, yMat);
+ return bMat.viewColumn (0).toArray ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the coefficients the polynomial interpolating
+ through the given points \texttt{(x[0], y[0]), ..., (x[n], y[n])}.
+ This polynomial has degree \texttt{n} and there are \texttt{n+1} coefficients.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the $x$ coordinates of the points.}
+ \param{y}{the $y$ coordinates of the points.}
+ \return{the coefficients the interpolating polynomial.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getX()\begin{hide} {
+ return x.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $x$ coordinates of the interpolated points.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $x$ coordinates of the interpolated points.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getY()\begin{hide} {
+ return y.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $y$ coordinates of the interpolated points.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $y$ coordinates of the interpolated points.}
+\end{htmlonly}
+\begin{code}
+
+ public static String toString (double[] x, double[] y)\begin{hide} {
+ final StringBuilder sb = new StringBuilder ("Points: ");
+ for (int i = 0; i < x.length; i++) {
+ if (i > 0)
+ sb.append (", ");
+ final String xstr = PrintfFormat.format (8, 3, 3, x[i]);
+ final String ystr = PrintfFormat.format (8, 3, 3, y[i]);
+ sb.append ('(').append (xstr).append (", ").append (ystr).append (')');
+ }
+ return sb.toString ();
+ }
+
+ @Override
+\end{hide}
+\end{code}
+\begin{tabb} Makes a string representation of a set of points.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the $x$ coordinates of the points.}
+ \param{y}{the $y$ coordinates of the points.}
+ \return{the string representing the points.}
+\end{htmlonly}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ return toString (x, y);
+ }\end{hide}
+\end{code}
+\begin{tabb} Calls \method{toString}~\texttt{(double[], double[])} with the
+ associated points.
+\end{tabb}
+\begin{htmlonly}
+ \return{a string containing the points.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public PolInterp clone() {
+ final PolInterp p = (PolInterp) super.clone ();
+ p.x = x.clone ();
+ p.y = y.clone ();
+ return p;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functionfit/SmoothingCubicSpline.java b/source/umontreal/iro/lecuyer/functionfit/SmoothingCubicSpline.java
new file mode 100644
index 0000000..c67b241
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/SmoothingCubicSpline.java
@@ -0,0 +1,482 @@
+
+
+/*
+ * Class: SmoothingCubicSpline
+ * Description: smoothing cubic spline algorithm of Schoenberg
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functionfit;
+ import umontreal.iro.lecuyer.functions.*;
+ import umontreal.iro.lecuyer.functions.Polynomial;
+import umontreal.iro.lecuyer.functions.MathFunctionWithFirstDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionWithDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionWithIntegral;
+import umontreal.iro.lecuyer.functions.SquareMathFunction;
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+
+
+/**
+ * Represents a cubic spline with nodes at
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN> computed with
+ * the smoothing cubic spline algorithm of Schoenberg.
+ * A smoothing cubic spline
+ * is made of <SPAN CLASS="MATH"><I>n</I> + 1</SPAN> cubic polynomials. The <SPAN CLASS="MATH"><I>i</I></SPAN>th polynomial of such a spline,
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>n</I> - 1</SPAN>, is defined as <SPAN CLASS="MATH"><I>S</I><SUB>i</SUB>(<I>x</I>)</SPAN> while the complete spline is
+ * defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>S</I>(<I>x</I>) = <I>S</I><SUB>i</SUB>(<I>x</I>), for <I>x</I>∈[<I>x</I><SUB>i-1</SUB>, <I>x</I><SUB>i</SUB>].
+ * </DIV><P></P>
+ * For <SPAN CLASS="MATH"><I>x</I> < <I>x</I><SUB>0</SUB></SPAN> and
+ * <SPAN CLASS="MATH"><I>x</I> > <I>x</I><SUB>n-1</SUB></SPAN>, the spline is not precisely defined, but this class performs
+ * extrapolation by using <SPAN CLASS="MATH"><I>S</I><SUB>0</SUB></SPAN> and <SPAN CLASS="MATH"><I>S</I><SUB>n</SUB></SPAN> linear polynomials.
+ * The algorithm which calculates the smoothing spline is a
+ * generalization of the algorithm for an interpolating spline.
+ * <SPAN CLASS="MATH"><I>S</I><SUB>i</SUB></SPAN> is linked to <SPAN CLASS="MATH"><I>S</I><SUB>i+1</SUB></SPAN> at <SPAN CLASS="MATH"><I>x</I><SUB>i+1</SUB></SPAN> and keeps continuity properties for
+ * first and second derivatives at this point, therefore
+ *
+ * <SPAN CLASS="MATH"><I>S</I><SUB>i</SUB>(<I>x</I><SUB>i+1</SUB>) = <I>S</I><SUB>i+1</SUB>(<I>x</I><SUB>i+1</SUB>)</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>S'</I><SUB>i</SUB>(<I>x</I><SUB>i+1</SUB>) = <I>S'</I><SUB>i+1</SUB>(<I>x</I><SUB>i+1</SUB>)</SPAN> and
+ * <SPAN CLASS="MATH"><I>S''</I><SUB>i</SUB>(<I>x</I><SUB>i+1</SUB>) = <I>S''</I><SUB>i+1</SUB>(<I>x</I><SUB>i+1</SUB>)</SPAN>.
+ *
+ * <P>
+ * The spline is computed with a smoothing parameter
+ * <SPAN CLASS="MATH"><I>ρ</I>∈[0, 1]</SPAN> which represents its accuracy with respect to the initial
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN> nodes. The smoothing spline minimizes
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>L</I> = <I>ρ</I>∑<SUB>i=0</SUB><SUP>n-1</SUP><I>w</I><SUB>i</SUB>(<I>y</I><SUB>i</SUB>-<I>S</I><SUB>i</SUB>(<I>x</I><SUB>i</SUB>))<SUP>2</SUP> + (1 - <I>ρ</I>)∫<SUB>x<SUB>0</SUB></SUB><SUP>x<SUB>n-1</SUB></SUP>(<I>S''</I>(<I>x</I>))<SUP>2</SUP><I>dx</I>
+ * </DIV><P></P>
+ * In fact, by setting <SPAN CLASS="MATH"><I>ρ</I> = 1</SPAN>, we obtain the interpolating spline; and
+ * we obtain a linear function by setting <SPAN CLASS="MATH"><I>ρ</I> = 0</SPAN>.
+ * The weights <SPAN CLASS="MATH"><I>w</I><SUB>i</SUB> > 0</SPAN>, which default to 1, can be used to change the contribution
+ * of each point in the error term. A large value <SPAN CLASS="MATH"><I>w</I><SUB>i</SUB></SPAN> will give a large weight
+ * to the <SPAN CLASS="MATH"><I>i</I></SPAN>th point, so the spline will pass closer to it.
+ * Here is a small example that uses smoothing splines:
+ *
+ * <P>
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ *
+ * <BR> int n;
+ * <BR> double[] X = new double[n];
+ * <BR> double[] Y = new double[n];
+ * <BR> // here, fill arrays X and Y with n data points (x_i, y_i)
+ * <BR> // The points must be sorted with respect to x_i.
+ * <BR>
+ * <BR> double rho = 0.1;
+ * <BR> SmoothingCubicSpline fit = new SmoothingCubicSpline(X, Y, rho);
+ * <BR>
+ * <BR> int m = 40;
+ * <BR> double[] Xp = new double[m+1]; // Xp, Yp are spline points
+ * <BR> double[] Yp = new double[m+1];
+ * <BR> double h = (X[n-1] - X[0]) / m; // step
+ * <BR>
+ * <BR> for (int i = 0; i <= m; i++) {
+ * <BR> double z = X[0] + i * h;
+ * <BR> Xp[i] = z;
+ * <BR> Yp[i] = fit.evaluate(z); // evaluate spline at z
+ * <BR> }
+ * <BR>
+ * <BR></TT>
+ * </DIV>
+ *
+ */
+public class SmoothingCubicSpline implements MathFunction,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral {
+
+ private Polynomial[] splineVector;
+ private double[] x, y, weight;
+ private double rho;
+
+
+
+ /**
+ * Constructs a spline with nodes at
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN>,
+ * with weights <SPAN CLASS="MATH"><I>w</I><SUB>i</SUB></SPAN> and smoothing factor <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>.
+ * The <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> <SPAN CLASS="textit">must</SPAN> be sorted in increasing order.
+ *
+ * @param x the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y the <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param w the weight for each point, must be <SPAN CLASS="MATH">> 0</SPAN>.
+ *
+ * @param rho the smoothing parameter
+ *
+ * @exception IllegalArgumentException if <TT>x</TT>, <TT>y</TT> and
+ * <TT>z</TT> do not have the same length, if rho has wrong value, or if
+ * the spline cannot be calculated.
+ *
+ *
+ */
+ public SmoothingCubicSpline (double[] x, double[] y, double[] w,
+ double rho) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException ("x.length != y.length");
+ if (w != null && x.length != w.length)
+ throw new IllegalArgumentException ("x.length != w.length");
+ if (rho < 0 || rho > 1)
+ throw new IllegalArgumentException ("rho not in [0, 1]");
+
+ splineVector = new Polynomial[x.length+1];
+ this.rho = rho;
+ this.x = x.clone();
+ this.y = y.clone();
+
+ weight = new double[x.length];
+ if (w == null) {
+ for (int i = 0; i < weight.length; i++)
+ weight[i] = 1.0;
+ } else {
+ for (int i = 0; i < weight.length; i++)
+ weight[i] = w[i];
+ }
+
+ resolve();
+ }
+
+
+ /**
+ * Constructs a spline with nodes at
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>y</I><SUB>i</SUB>)</SPAN>,
+ * with weights <SPAN CLASS="MATH">= 1</SPAN> and smoothing factor <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>.
+ * The <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> <SPAN CLASS="textit">must</SPAN> be sorted in increasing order.
+ *
+ * @param x the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param y the <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ * @param rho the smoothing parameter
+ *
+ * @exception IllegalArgumentException if <TT>x</TT> and <TT>y</TT> do
+ * not have the same length, if rho has wrong value, or if the spline
+ * cannot be calculated.
+ *
+ */
+ public SmoothingCubicSpline (double[] x, double[] y, double rho) {
+ this (x, y, null, rho);
+ }
+
+
+ /**
+ * Evaluates and returns the value of the spline at <SPAN CLASS="MATH"><I>z</I></SPAN>.
+ *
+ * @param z argument of the spline.
+ *
+ * @return value of spline.
+ *
+ */
+ public double evaluate (double z) {
+ int i = getFitPolynomialIndex(z);
+ if (i == 0)
+ return splineVector[i].evaluate(z-x[0]);
+ else
+ return splineVector[i].evaluate(z-x[i-1]);
+ }
+
+
+ /**
+ * Evaluates and returns the value of the integral of the
+ * spline from <SPAN CLASS="MATH"><I>a</I></SPAN> to <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @param a lower limit of integral.
+ *
+ * @param b upper limit of integral.
+ *
+ * @return value of integral.
+ *
+ */
+ public double integral (double a, double b) {
+ int iA = getFitPolynomialIndex(a);
+ int iB = getFitPolynomialIndex(b);
+ double retour;
+ int i = 1;
+
+ // Calcule l'integrale
+ if (iA == iB) { // les deux valeurs sont sur le meme polynome
+ retour = splineVector[iB].integral(a-x[iB], b-x[iB]);
+ } else {
+ if (iA == 0)
+ retour = splineVector[iA].integral(a-x[iA], 0);
+ else
+ retour = splineVector[iA].integral(a-x[iA], x[iA+1]-x[iA]);
+ for (i = iA+1; i<iB; i++) {
+ retour += splineVector[i].integral(0, x[i+1]-x[i]);
+ }
+ retour += splineVector[iB].integral(0, b-x[iB]);
+ }
+ return retour;
+ }
+
+
+ /**
+ * Evaluates and returns the value of the <SPAN CLASS="textit">first</SPAN> derivative of
+ * the spline at <SPAN CLASS="MATH"><I>z</I></SPAN>.
+ *
+ * @param z argument of the spline.
+ *
+ * @return value of first derivative.
+ *
+ */
+ public double derivative (double z) {
+ int i = getFitPolynomialIndex(z);
+ if (i == 0)
+ return splineVector[i].derivative(z-x[0]);
+ else
+ return splineVector[i].derivative(z-x[i-1]);
+ }
+
+
+ /**
+ * Evaluates and returns the value of the <SPAN CLASS="textit">n</SPAN>-th derivative of
+ * the spline at <SPAN CLASS="MATH"><I>z</I></SPAN>.
+ *
+ * @param z argument of the spline.
+ *
+ * @param n order of the derivative.
+ *
+ * @return value of n-th derivative.
+ *
+ */
+ public double derivative (double z, int n) {
+ int i = getFitPolynomialIndex(z);
+ if (i == 0)
+ return splineVector[i].derivative(z - x[0], n);
+ else
+ return splineVector[i].derivative(z - x[i-1], n);
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates for this spline.
+ *
+ * @return the <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ */
+ public double[] getX() {
+ return x.clone ();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates for this spline.
+ *
+ * @return the <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB></SPAN> coordinates.
+ *
+ */
+ public double[] getY() {
+ return y.clone ();
+ }
+
+
+ /**
+ * Returns the weights of the points.
+ *
+ * @return the weights of the points.
+ *
+ */
+ public double[] getWeights() {
+ return weight;
+ }
+
+
+ /**
+ * Returns the smoothing factor used to construct the spline.
+ *
+ * @return the smoothing factor.
+ *
+ */
+ public double getRho() {
+ return rho;
+ }
+
+
+ /**
+ * Returns a table containing all fitting polynomials.
+ *
+ * @return Table containing the fitting polynomials.
+ *
+ */
+ public Polynomial[] getSplinePolynomials() {
+ return splineVector.clone();
+ }
+
+
+ /**
+ * Returns the index of <SPAN CLASS="MATH"><I>P</I></SPAN>, the {@link Polynomial} instance used to evaluate
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>, in an <TT>ArrayList</TT> table instance returned by
+ * <TT>getSplinePolynomials()</TT>. This index <SPAN CLASS="MATH"><I>k</I></SPAN> gives also the interval in
+ * table <SPAN CLASS="textbf">X</SPAN> which contains the value <SPAN CLASS="MATH"><I>x</I></SPAN>
+ * (i.e. such that
+ * <SPAN CLASS="MATH"><I>x</I><SUB>k</SUB> < <I>x</I> <= <I>x</I><SUB>k+1</SUB></SPAN>).
+ *
+ * @return Index of the polynomial check with x in the Polynomial list returned by methodgetSplinePolynomials
+ *
+ */
+ public int getFitPolynomialIndex (double x) {
+ // Algorithme de recherche binaire legerement modifie
+ int j = this.x.length-1;
+ if (x > this.x[j])
+ return j+1;
+ int tmp = 0;
+ int i = 0;
+
+ while (i+1 != j) {
+ if (x > this.x[tmp]) {
+ i = tmp;
+ tmp = i+(j-i)/2;
+ } else {
+ j = tmp;
+ tmp = i+(j-i)/2;
+ }
+ if (j == 0) // le point est < a x_0, on sort
+ i--;
+ }
+ return i+1;
+ }
+
+
+ private void resolve () {
+ /*
+ taken from D.S.G Pollock's paper, "Smoothing with Cubic Splines",
+ Queen Mary, University of London (1993)
+ http://www.qmw.ac.uk/~ugte133/PAPERS/SPLINES.PDF
+ */
+
+ double[] h = new double[x.length];
+ double[] r = new double[x.length];
+ double[] u = new double[x.length];
+ double[] v = new double[x.length];
+ double[] w = new double[x.length];
+ double[] q = new double[x.length+1];
+ double[] sigma = new double[weight.length];
+
+ for (int i = 0; i < weight.length; i++) {
+ if (weight[i] <= 0.0)
+ sigma[i] = 1.0e100;
+ else
+ sigma[i] = 1.0/Math.sqrt(weight[i]);
+ }
+
+ double mu;
+ int n = x.length-1;
+
+ if (rho <= 0)
+ mu = 1.0e100; // arbitrary large number to avoid 1/0
+ else
+ mu = 2 * (1 - rho)/(3 * rho);
+
+ h[0] = x[1] - x[0];
+ r[0] = 3/h[0];
+ for (int i = 1; i < n; i++) {
+ h[i] = x[i+1] - x[i];
+ r[i] = 3/h[i];
+ q[i] = 3 * (y[i+1] - y[i])/h[i] - 3 * (y[i] - y[i-1])/h[i - 1];
+ }
+
+ for (int i = 1; i < n; i++) {
+ u[i] = r[i-1]*r[i-1] * sigma[i-1]
+ + (r[i - 1] + r[i])*(r[i - 1] + r[i]) * sigma[i] +
+ r[i]*r[i] * sigma[i+1];
+ u[i] = mu * u[i] + 2 * (x[i+1] - x[i-1]);
+ v[i] = -(r[i - 1] + r[i]) * r[i] * sigma[i] - r[i] * (r[i] +
+ r[i+1]) * sigma[i+1];
+ v[i] = mu * v[i] + h[i];
+ w[i] = mu * r[i] * r[i+1] * sigma[i+1];
+ }
+ q = Quincunx(u, v, w, q);
+
+ // extrapolation a gauche
+ double[] params = new double[4];
+ double dd;
+ params[0] = y[0] - mu * r[0] * q[1] * sigma[0];
+ dd = y[1] - mu * ((-r[0] - r[1]) * q[1] + r[1] * q[2]) * sigma[1];
+ params[1] = (dd - params[0])/h[0] - q[1] * h[0]/3;
+ splineVector[0] = new Polynomial(params);
+
+ // premier polynome
+ params[0] = y[0] - mu * r[0] * q[1] * sigma[0];
+ dd = y[1] - mu * ((-r[0] - r[1]) * q[1] + r[1] * q[2]) * sigma[1];
+ params[3] = q[1]/(3 * h[0]);
+ params[2] = 0;
+ params[1] = (dd - params[0])/h[0] - q[1] * h[0]/3;
+ splineVector[1] = new Polynomial(params);
+
+ // les polynomes suivants
+ int j;
+ for (j = 1; j < n; j++) {
+ params[3] = (q[j + 1] - q[j])/(3 * h[j]);
+ params[2] = q[j];
+ params[1] = (q[j] + q[j - 1]) * h[j - 1] + splineVector[j].getCoefficient(1);
+ params[0] = r[j - 1] * q[j - 1] + (-r[j-1] - r[j]) * q[j] + r[j] * q[j + 1];
+ params[0] = y[j] - mu * params[0] * sigma[j];
+ splineVector[j+1] = new Polynomial(params);
+ }
+
+ // extrapolation a droite
+ j = n;
+ params[3] = 0;
+ params[2] = 0;
+ params[1] = splineVector[j].derivative(x[x.length-1]-x[x.length-2]);
+ params[0] = splineVector[j].evaluate(x[x.length-1]-x[x.length-2]);
+ splineVector[n+1] = new Polynomial(params);
+ }
+
+
+ private static double[] Quincunx (double[] u, double[] v,
+ double[] w, double[] q) {
+ u[0] = 0;
+ v[1] = v[1]/u[1];
+ w[1] = w[1]/u[1];
+ int j;
+
+ for (j = 2; j < u.length-1; j++) {
+ u[j] = u[j] - u[j - 2] * w[j-2]*w[j-2] - u[j - 1] * v[j-1]*v[j-1];
+ v[j] = (v[j] - u[j - 1] * v[j-1] * w[j-1])/u[j];
+ w[j] = w[j]/u[j];
+ }
+
+ // forward substitution
+ q[1] = q[1] - v[0] * q[0];
+ for (j = 2; j < u.length-1; j++) {
+ q[j] = q[j] - v[j - 1] * q[j - 1] - w[j - 2] * q[j - 2];
+ }
+ for (j = 1; j < u.length-1; j++) {
+ q[j] = q[j]/u[j];
+ }
+
+ // back substitution
+ q[u.length-1] = 0;
+ for (j = u.length-3; j > 0; j--) {
+ q[j] = q[j] - v[j] * q[j + 1] - w[j] * q[j + 2];
+ }
+ return q;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functionfit/SmoothingCubicSpline.tex b/source/umontreal/iro/lecuyer/functionfit/SmoothingCubicSpline.tex
new file mode 100644
index 0000000..518fbd3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/SmoothingCubicSpline.tex
@@ -0,0 +1,468 @@
+\defmodule{SmoothingCubicSpline}
+
+
+Represents a cubic spline with nodes at $(x_i, y_i)$ computed with
+the smoothing cubic spline algorithm of Schoenberg \cite{mDEB78a,mPOL93a}.
+ A smoothing cubic spline
+is made of $n+1$ cubic polynomials. The $i$th polynomial of such a spline,
+for $i=1,\ldots,n-1$, is defined as $S_i(x)$ while the complete spline is
+defined as
+ \[
+S(x) = S_i(x), \qquad\mbox{ for }x\in[x_{i-1}, x_{i}].
+\]
+For $x<x_0$ and
+$x>x_{n-1}$, the spline is not precisely defined, but this class performs
+extrapolation by using $S_0$ and $S_{n}$ linear polynomials.
+The algorithm which calculates the smoothing spline is a
+generalization of the algorithm for an interpolating spline.
+$S_i$ is linked to $S_{i+1}$ at $x_{i+1}$ and keeps continuity properties for
+first and second derivatives at this point, therefore
+ $S_i(x_{i+1})=S_{i+1}(x_{i+1})$,
+$S'_i(x_{i+1})=S'_{i+1}(x_{i+1})$ and $S''_i(x_{i+1})=S''_{i+1}(x_{i+1})$.
+
+The spline is computed with a smoothing parameter $\rho\in[0, 1]$ which represents its accuracy with respect to the initial $(x_i, y_i)$ nodes. The smoothing spline minimizes
+\[
+L = \rho\sum_{i=0}^{n-1}{w_i}\left({y_i - S_i(x_i)}\right)^2 +
+(1-\rho)\int_{x_0}^{x_{n-1}}\left(S''(x)\right)^2dx
+\]
+In fact, by setting $\rho = 1$, we obtain the interpolating spline; and
+we obtain a linear function by setting $\rho = 0$.
+The weights $w_i>0$, which default to 1, can be used to change the contribution
+of each point in the error term. A large value $w_i$ will give a large weight
+ to the $i$th point, so the spline will pass closer to it.
+Here is a small example that uses smoothing splines:
+
+\begin{vcode}
+
+ int n;
+ double[] X = new double[n];
+ double[] Y = new double[n];
+ // here, fill arrays X and Y with n data points (x_i, y_i)
+ // The points must be sorted with respect to x_i.
+
+ double rho = 0.1;
+ SmoothingCubicSpline fit = new SmoothingCubicSpline(X, Y, rho);
+
+ int m = 40;
+ double[] Xp = new double[m+1]; // Xp, Yp are spline points
+ double[] Yp = new double[m+1];
+ double h = (X[n-1] - X[0]) / m; // step
+
+ for (int i = 0; i <= m; i++) {
+ double z = X[0] + i * h;
+ Xp[i] = z;
+ Yp[i] = fit.evaluate(z); // evaluate spline at z
+ }
+
+\end{vcode}
+
+\bigskip\hrule
+% \newpage
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: SmoothingCubicSpline
+ * Description: smoothing cubic spline algorithm of Schoenberg
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functionfit;
+ import umontreal.iro.lecuyer.functions.*;
+ import umontreal.iro.lecuyer.functions.Polynomial;\begin{hide}
+import umontreal.iro.lecuyer.functions.MathFunctionWithFirstDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionWithDerivative;
+import umontreal.iro.lecuyer.functions.MathFunctionWithIntegral;
+import umontreal.iro.lecuyer.functions.SquareMathFunction;
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+\end{hide}
+
+public class SmoothingCubicSpline implements MathFunction,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral\begin{hide} {
+
+ private Polynomial[] splineVector;
+ private double[] x, y, weight;
+ private double rho;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public SmoothingCubicSpline (double[] x, double[] y, double[] w,
+ double rho)\begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException ("x.length != y.length");
+ if (w != null && x.length != w.length)
+ throw new IllegalArgumentException ("x.length != w.length");
+ if (rho < 0 || rho > 1)
+ throw new IllegalArgumentException ("rho not in [0, 1]");
+
+ splineVector = new Polynomial[x.length+1];
+ this.rho = rho;
+ this.x = x.clone();
+ this.y = y.clone();
+
+ weight = new double[x.length];
+ if (w == null) {
+ for (int i = 0; i < weight.length; i++)
+ weight[i] = 1.0;
+ } else {
+ for (int i = 0; i < weight.length; i++)
+ weight[i] = w[i];
+ }
+
+ resolve();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a spline with nodes at $(x_i, y_i)$,
+ with weights $w_i$ and smoothing factor $\rho$ = \texttt{rho}.
+ The $x_i$ \emph{must} be sorted in increasing order.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the $x_i$ coordinates.}
+ \param{y}{the $y_i$ coordinates.}
+ \param{w}{the weight for each point, must be $> 0$.}
+ \param{rho}{the smoothing parameter}
+ \exception{IllegalArgumentException}{if \texttt{x}, \texttt{y} and
+ \texttt{z} do not have the same length, if rho has wrong value, or if
+ the spline cannot be calculated.}
+\end{htmlonly}
+\begin{code}
+
+ public SmoothingCubicSpline (double[] x, double[] y, double rho) \begin{hide} {
+ this (x, y, null, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a spline with nodes at $(x_i, y_i)$,
+ with weights $= 1$ and smoothing factor $\rho$ = \texttt{rho}.
+ The $x_i$ \emph{must} be sorted in increasing order.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the $x_i$ coordinates.}
+ \param{y}{the $y_i$ coordinates.}
+ \param{rho}{the smoothing parameter}
+ \exception{IllegalArgumentException}{if \texttt{x} and \texttt{y} do
+ not have the same length, if rho has wrong value, or if the spline
+ cannot be calculated.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public double evaluate (double z)\begin{hide} {
+ int i = getFitPolynomialIndex(z);
+ if (i == 0)
+ return splineVector[i].evaluate(z-x[0]);
+ else
+ return splineVector[i].evaluate(z-x[i-1]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Evaluates and returns the value of the spline at $z$.
+\end{tabb}
+\begin{htmlonly}
+ \param{z}{argument of the spline.}
+ \return{value of spline.}
+\end{htmlonly}
+\begin{code}
+
+ public double integral (double a, double b)\begin{hide} {
+ int iA = getFitPolynomialIndex(a);
+ int iB = getFitPolynomialIndex(b);
+ double retour;
+ int i = 1;
+
+ // Calcule l'integrale
+ if (iA == iB) { // les deux valeurs sont sur le meme polynome
+ retour = splineVector[iB].integral(a-x[iB], b-x[iB]);
+ } else {
+ if (iA == 0)
+ retour = splineVector[iA].integral(a-x[iA], 0);
+ else
+ retour = splineVector[iA].integral(a-x[iA], x[iA+1]-x[iA]);
+ for (i = iA+1; i<iB; i++) {
+ retour += splineVector[i].integral(0, x[i+1]-x[i]);
+ }
+ retour += splineVector[iB].integral(0, b-x[iB]);
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}Evaluates and returns the value of the integral of the
+ spline from $a$ to $b$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{lower limit of integral.}
+ \param{b}{upper limit of integral.}
+ \return{value of integral.}
+\end{htmlonly}
+\begin{code}
+
+ public double derivative (double z)\begin{hide} {
+ int i = getFitPolynomialIndex(z);
+ if (i == 0)
+ return splineVector[i].derivative(z-x[0]);
+ else
+ return splineVector[i].derivative(z-x[i-1]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Evaluates and returns the value of the \emph{first} derivative of
+ the spline at $z$.
+\end{tabb}
+\begin{htmlonly}
+ \param{z}{argument of the spline.}
+ \return{value of first derivative.}
+\end{htmlonly}
+\begin{code}
+
+ public double derivative (double z, int n)\begin{hide} {
+ int i = getFitPolynomialIndex(z);
+ if (i == 0)
+ return splineVector[i].derivative(z - x[0], n);
+ else
+ return splineVector[i].derivative(z - x[i-1], n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Evaluates and returns the value of the \emph{n}-th derivative of
+ the spline at $z$.
+\end{tabb}
+\begin{htmlonly}
+ \param{z}{argument of the spline.}
+ \param{n}{order of the derivative.}
+ \return{value of n-th derivative.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getX()\begin{hide} {
+ return x.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $x_i$ coordinates for this spline.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $x_i$ coordinates.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getY()\begin{hide} {
+ return y.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $y_i$ coordinates for this spline.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $y_i$ coordinates.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getWeights()\begin{hide} {
+ return weight;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the weights of the points.
+\end{tabb}
+\begin{htmlonly}
+ \return{the weights of the points.}
+\end{htmlonly}
+\begin{code}
+
+ public double getRho()\begin{hide} {
+ return rho;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the smoothing factor used to construct the spline.
+\end{tabb}
+\begin{htmlonly}
+ \return{the smoothing factor.}
+\end{htmlonly}
+\begin{code}
+
+ public Polynomial[] getSplinePolynomials()\begin{hide} {
+ return splineVector.clone();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a table containing all fitting polynomials.
+\end{tabb}
+\begin{htmlonly}
+ \return{Table containing the fitting polynomials.}
+\end{htmlonly}
+\begin{code}
+
+ public int getFitPolynomialIndex (double x)\begin{hide} {
+ // Algorithme de recherche binaire legerement modifie
+ int j = this.x.length-1;
+ if (x > this.x[j])
+ return j+1;
+ int tmp = 0;
+ int i = 0;
+
+ while (i+1 != j) {
+ if (x > this.x[tmp]) {
+ i = tmp;
+ tmp = i+(j-i)/2;
+ } else {
+ j = tmp;
+ tmp = i+(j-i)/2;
+ }
+ if (j == 0) // le point est < a x_0, on sort
+ i--;
+ }
+ return i+1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the index of $P$, the \class{Polynomial} instance used to evaluate
+ $x$, in an \texttt{ArrayList} table instance returned by
+ \texttt{getSplinePolynomials()}. This index $k$ gives also the interval in
+ table \textbf{X} which contains the value $x$
+ (i.e. such that $x_k < x \leq x_{k+1}$).
+\end{tabb}
+\begin{htmlonly}
+ \return{Index of the polynomial check with x in the Polynomial list returned by method{getSplinePolynomials}}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private void resolve () {
+ /*
+ taken from D.S.G Pollock's paper, "Smoothing with Cubic Splines",
+ Queen Mary, University of London (1993)
+ http://www.qmw.ac.uk/~ugte133/PAPERS/SPLINES.PDF
+ */
+
+ double[] h = new double[x.length];
+ double[] r = new double[x.length];
+ double[] u = new double[x.length];
+ double[] v = new double[x.length];
+ double[] w = new double[x.length];
+ double[] q = new double[x.length+1];
+ double[] sigma = new double[weight.length];
+
+ for (int i = 0; i < weight.length; i++) {
+ if (weight[i] <= 0.0)
+ sigma[i] = 1.0e100;
+ else
+ sigma[i] = 1.0/Math.sqrt(weight[i]);
+ }
+
+ double mu;
+ int n = x.length-1;
+
+ if (rho <= 0)
+ mu = 1.0e100; // arbitrary large number to avoid 1/0
+ else
+ mu = 2 * (1 - rho)/(3 * rho);
+
+ h[0] = x[1] - x[0];
+ r[0] = 3/h[0];
+ for (int i = 1; i < n; i++) {
+ h[i] = x[i+1] - x[i];
+ r[i] = 3/h[i];
+ q[i] = 3 * (y[i+1] - y[i])/h[i] - 3 * (y[i] - y[i-1])/h[i - 1];
+ }
+
+ for (int i = 1; i < n; i++) {
+ u[i] = r[i-1]*r[i-1] * sigma[i-1]
+ + (r[i - 1] + r[i])*(r[i - 1] + r[i]) * sigma[i] +
+ r[i]*r[i] * sigma[i+1];
+ u[i] = mu * u[i] + 2 * (x[i+1] - x[i-1]);
+ v[i] = -(r[i - 1] + r[i]) * r[i] * sigma[i] - r[i] * (r[i] +
+ r[i+1]) * sigma[i+1];
+ v[i] = mu * v[i] + h[i];
+ w[i] = mu * r[i] * r[i+1] * sigma[i+1];
+ }
+ q = Quincunx(u, v, w, q);
+
+ // extrapolation a gauche
+ double[] params = new double[4];
+ double dd;
+ params[0] = y[0] - mu * r[0] * q[1] * sigma[0];
+ dd = y[1] - mu * ((-r[0] - r[1]) * q[1] + r[1] * q[2]) * sigma[1];
+ params[1] = (dd - params[0])/h[0] - q[1] * h[0]/3;
+ splineVector[0] = new Polynomial(params);
+
+ // premier polynome
+ params[0] = y[0] - mu * r[0] * q[1] * sigma[0];
+ dd = y[1] - mu * ((-r[0] - r[1]) * q[1] + r[1] * q[2]) * sigma[1];
+ params[3] = q[1]/(3 * h[0]);
+ params[2] = 0;
+ params[1] = (dd - params[0])/h[0] - q[1] * h[0]/3;
+ splineVector[1] = new Polynomial(params);
+
+ // les polynomes suivants
+ int j;
+ for (j = 1; j < n; j++) {
+ params[3] = (q[j + 1] - q[j])/(3 * h[j]);
+ params[2] = q[j];
+ params[1] = (q[j] + q[j - 1]) * h[j - 1] + splineVector[j].getCoefficient(1);
+ params[0] = r[j - 1] * q[j - 1] + (-r[j-1] - r[j]) * q[j] + r[j] * q[j + 1];
+ params[0] = y[j] - mu * params[0] * sigma[j];
+ splineVector[j+1] = new Polynomial(params);
+ }
+
+ // extrapolation a droite
+ j = n;
+ params[3] = 0;
+ params[2] = 0;
+ params[1] = splineVector[j].derivative(x[x.length-1]-x[x.length-2]);
+ params[0] = splineVector[j].evaluate(x[x.length-1]-x[x.length-2]);
+ splineVector[n+1] = new Polynomial(params);
+ }
+
+
+ private static double[] Quincunx (double[] u, double[] v,
+ double[] w, double[] q) {
+ u[0] = 0;
+ v[1] = v[1]/u[1];
+ w[1] = w[1]/u[1];
+ int j;
+
+ for (j = 2; j < u.length-1; j++) {
+ u[j] = u[j] - u[j - 2] * w[j-2]*w[j-2] - u[j - 1] * v[j-1]*v[j-1];
+ v[j] = (v[j] - u[j - 1] * v[j-1] * w[j-1])/u[j];
+ w[j] = w[j]/u[j];
+ }
+
+ // forward substitution
+ q[1] = q[1] - v[0] * q[0];
+ for (j = 2; j < u.length-1; j++) {
+ q[j] = q[j] - v[j - 1] * q[j - 1] - w[j - 2] * q[j - 2];
+ }
+ for (j = 1; j < u.length-1; j++) {
+ q[j] = q[j]/u[j];
+ }
+
+ // back substitution
+ q[u.length-1] = 0;
+ for (j = u.length-3; j > 0; j--) {
+ q[j] = q[j] - v[j] * q[j + 1] - w[j] * q[j + 2];
+ }
+ return q;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functionfit/guidefunctionfit.bbl b/source/umontreal/iro/lecuyer/functionfit/guidefunctionfit.bbl
new file mode 100644
index 0000000..68049a1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/guidefunctionfit.bbl
@@ -0,0 +1,15 @@
+\begin{thebibliography}{1}
+
+\bibitem{mDEB78a}
+C.~{de Boor}.
+\newblock {\em A Practical Guide to Splines}.
+\newblock Number~27 in Applied Mathematical Sciences Series. Springer-Verlag,
+ New York, 1978.
+
+\bibitem{mPOL93a}
+D.~S.~G. Pollock.
+\newblock Smoothing with cubic splines.
+\newblock Technical report, University of London, Queen Mary and Westfield
+ College, London, 1993.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/functionfit/guidefunctionfit.tex b/source/umontreal/iro/lecuyer/functionfit/guidefunctionfit.tex
new file mode 100644
index 0000000..1d2dbac
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functionfit/guidefunctionfit.tex
@@ -0,0 +1,31 @@
+\documentclass[12pt]{article}
+\usepackage{ssj}
+\mytwoheads
+\dateheadtrue
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+\begin{titlepage}
+\title{functionfit}{Function fit utilities}
+
+\vfill
+This package provides basic facilities for curve fitting and interpolation
+with polynomials as, for example, least square fit and spline interpolation.\\
+\vfill
+\end{titlepage}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\pagenumbering{roman}
+\tableofcontents
+
+\pagenumbering{arabic}
+\include{PolInterp}
+\include{LeastSquares}
+\include{BSpline}
+\include{SmoothingCubicSpline}
+
+\bibliographystyle{plain}
+\bibliography{math}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/functions/AverageMathFunction.java b/source/umontreal/iro/lecuyer/functions/AverageMathFunction.java
new file mode 100644
index 0000000..53d7c6c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/AverageMathFunction.java
@@ -0,0 +1,107 @@
+
+
+/*
+ * Class: AverageMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents a function computing the average of several functions.
+ * Let
+ * <SPAN CLASS="MATH"><I>f</I><SUB>0</SUB>(<I>x</I>),…, <I>f</I><SUB>n-1</SUB>(<I>x</I>)</SPAN> be a set of <SPAN CLASS="MATH"><I>n</I></SPAN> functions.
+ * This function represents the average
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 1#1∑<SUB>i=0</SUB><SUP>n-1</SUP><I>f</I><SUB>i</SUB>(<I>x</I>).
+ * </DIV><P></P>
+ *
+ */
+public class AverageMathFunction implements MathFunction
+
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral {
+ private MathFunction[] func;
+
+
+ /**
+ * Constructs a function computing the average
+ * of the functions in the array <TT>func</TT>.
+ *
+ * @param func the array of functions to average.
+ *
+ *
+ */
+ public AverageMathFunction (MathFunction... func) {
+ if (func == null)
+ throw new NullPointerException();
+ this.func = func.clone ();
+ }
+
+
+ /**
+ * Returns the functions being averaged.
+ *
+ * @return the averaged functions.
+ *
+ */
+ public MathFunction[] getFunctions() {
+ return func.clone ();
+ }
+
+
+ public double evaluate (double x) {
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += fi.evaluate (x);
+ return sum / func.length;
+ }
+
+ public double derivative (double x, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be greater than or equal to 0");
+ if (n == 0)
+ return evaluate (x);
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += MathFunctionUtil.derivative (fi, x, n);
+ return sum / func.length;
+ }
+
+ public double derivative (double x) {
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += MathFunctionUtil.derivative (fi, x);
+ return sum / func.length;
+ }
+
+ public double integral (double a, double b) {
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += MathFunctionUtil.integral (fi, a, b);
+ return sum / func.length;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/AverageMathFunction.tex b/source/umontreal/iro/lecuyer/functions/AverageMathFunction.tex
new file mode 100644
index 0000000..d35cfc8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/AverageMathFunction.tex
@@ -0,0 +1,104 @@
+\defmodule{AverageMathFunction}
+
+Represents a function computing the average of several functions.
+Let $f_0(x), \ldots, f_{n-1}(x)$ be a set of $n$ functions.
+This function represents the average \[f(x)=\frac1n\sum_{i=0}^{n-1} f_i(x).\]
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: AverageMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public class AverageMathFunction implements MathFunction\begin{hide}
+
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral {
+ private MathFunction[] func;
+\end{hide}
+
+ public AverageMathFunction (MathFunction... func)\begin{hide} {
+ if (func == null)
+ throw new NullPointerException();
+ this.func = func.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a function computing the average
+ of the functions in the array \texttt{func}.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the array of functions to average.}
+\end{htmlonly}
+\begin{code}
+
+ public MathFunction[] getFunctions()\begin{hide} {
+ return func.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the functions being averaged.
+\end{tabb}
+\begin{htmlonly}
+ \return{the averaged functions.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double evaluate (double x) {
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += fi.evaluate (x);
+ return sum / func.length;
+ }
+
+ public double derivative (double x, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be greater than or equal to 0");
+ if (n == 0)
+ return evaluate (x);
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += MathFunctionUtil.derivative (fi, x, n);
+ return sum / func.length;
+ }
+
+ public double derivative (double x) {
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += MathFunctionUtil.derivative (fi, x);
+ return sum / func.length;
+ }
+
+ public double integral (double a, double b) {
+ double sum = 0;
+ for (final MathFunction fi : func)
+ sum += MathFunctionUtil.integral (fi, a, b);
+ return sum / func.length;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/IdentityMathFunction.java b/source/umontreal/iro/lecuyer/functions/IdentityMathFunction.java
new file mode 100644
index 0000000..ab01e1b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/IdentityMathFunction.java
@@ -0,0 +1,55 @@
+
+
+/*
+ * Class: IdentityMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents the identity function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = <I>x</I></SPAN>.
+ *
+ */
+public class IdentityMathFunction implements MathFunction
+
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral {
+ public double evaluate (double x) {
+ return x;
+ }
+
+ public double derivative (double x) {
+ return 1;
+ }
+
+ public double derivative (double x, int n) {
+ return n > 1 ? 0 : 1;
+ }
+
+ public double integral (double a, double b) {
+ return (b*b - a*a) / 2;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/IdentityMathFunction.tex b/source/umontreal/iro/lecuyer/functions/IdentityMathFunction.tex
new file mode 100644
index 0000000..ebc57bf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/IdentityMathFunction.tex
@@ -0,0 +1,58 @@
+\defmodule{IdentityMathFunction}
+
+Represents the identity function $f(x)=x$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: IdentityMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public class IdentityMathFunction implements MathFunction\begin{hide}
+
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral {
+ public double evaluate (double x) {
+ return x;
+ }
+
+ public double derivative (double x) {
+ return 1;
+ }
+
+ public double derivative (double x, int n) {
+ return n > 1 ? 0 : 1;
+ }
+
+ public double integral (double a, double b) {
+ return (b*b - a*a) / 2;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunction.java b/source/umontreal/iro/lecuyer/functions/MathFunction.java
new file mode 100644
index 0000000..de02b8f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunction.java
@@ -0,0 +1,50 @@
+
+
+/*
+ * Class: MathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+/**
+ * This interface should be implemented by classes which represent univariate
+ * mathematical functions. It is used to pass an arbitrary function of one
+ * variable as argument to another function. For example, it is used
+ * in {@link umontreal.iro.lecuyer.util.RootFinder RootFinder} to find
+ * the zeros of a function.
+ *
+ */
+public interface MathFunction {
+
+
+ /**
+ * Returns the value of the function evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ * @param x value at which the function is evaluated
+ *
+ * @return function evaluated at <TT>x</TT>
+ *
+ */
+ public double evaluate (double x);
+
+}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunction.tex b/source/umontreal/iro/lecuyer/functions/MathFunction.tex
new file mode 100644
index 0000000..0d41c24
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunction.tex
@@ -0,0 +1,54 @@
+\defmodule {MathFunction}
+
+This interface should be implemented by classes which represent univariate
+mathematical functions. It is used to pass an arbitrary function of one
+variable as argument to another function. For example, it is used
+in \externalclass{umontreal.iro.lecuyer.util}{RootFinder} to find
+ the zeros of a function.
+
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;
+
+public interface MathFunction\begin{hide} {\end{hide}
+\end{code}
+\begin{code}
+
+ public double evaluate (double x);
+\end{code}
+\begin{tabb}
+ Returns the value of the function evaluated at $x$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the function is evaluated}
+ \return{function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionUtil.java b/source/umontreal/iro/lecuyer/functions/MathFunctionUtil.java
new file mode 100644
index 0000000..6ff67bd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionUtil.java
@@ -0,0 +1,535 @@
+
+
+/*
+ * Class: MathFunctionUtil
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+import umontreal.iro.lecuyer.util.Misc;
+
+
+/**
+ * Provides utility methods for computing
+ * derivatives and integrals of functions.
+ *
+ */
+public class MathFunctionUtil {
+
+
+ /**
+ * Step length in <SPAN CLASS="MATH"><I>x</I></SPAN> to compute derivatives. Default: <SPAN CLASS="MATH">10<SUP>-6</SUP></SPAN>.
+ *
+ */
+ public static double H = 1e-6;
+
+ private MathFunctionUtil() {}
+
+ // For Gauss-Lobatto: nodes Cg and weights Wg
+ private static final double[] Cg = { 0, 0.17267316464601142812, 0.5,
+ 0.82732683535398857188, 1 };
+ private static final double[] Wg = { 0.05, 0.27222222222222222222,
+ 0.35555555555555555555, 0.27222222222222222222, 0.05 };
+
+
+ private static double[] fixBounds (MathFunction func, double a,
+ double b, int numIntervals) {
+ // For functions which are 0 on parts of [a, b], shorten the interval
+ // [a, b] to the non-zero part of f(x). Returns the shortened interval.
+
+ final double h = (b - a)/numIntervals;
+ double x = b;
+ while ((0 == func.evaluate (x)) && (x > a))
+ x -= h;
+ if (x < b)
+ b = x + h;
+
+ x = a;
+ while ((0 == func.evaluate (x)) && (x < b))
+ x += h;
+ if (x > a)
+ a = x - h;
+ double[] D = {a, b};
+ return D;
+ }
+
+ /**
+ * Default number of intervals for Simpson's integral.
+ *
+ */
+ public static int NUMINTERVALS = 1024;
+
+
+ /**
+ * Returns the first derivative of the function <TT>func</TT>
+ * evaluated at <TT>x</TT>. If the given function implements
+ * {@link MathFunctionWithFirstDerivative}, this method calls
+ * {@link MathFunctionWithFirstDerivative#derivative MathFunctionWithFirstDerivative.derivative} <TT>(double)</TT>.
+ * Otherwise, if the function implements {@link MathFunctionWithDerivative},
+ * this method calls
+ * {@link MathFunctionWithDerivative#derivative MathFunctionWithDerivative.derivative} <TT>(double, int)</TT>.
+ * If the function does not implement any of these two interfaces, the method
+ * uses {@link #finiteCenteredDifferenceDerivative finiteCenteredDifferenceDerivative} <TT>(MathFunction,
+ * double, double)</TT> to obtain an estimate of the derivative.
+ *
+ * @param func the function to derivate.
+ *
+ * @param x the evaluation point.
+ *
+ * @return the first derivative.
+ *
+ */
+ public static double derivative (MathFunction func, double x) {
+ if (func instanceof MathFunctionWithFirstDerivative)
+ return ((MathFunctionWithFirstDerivative)func).derivative (x);
+ else if (func instanceof MathFunctionWithDerivative)
+ return ((MathFunctionWithDerivative)func).derivative (x, 1);
+ else
+ return finiteCenteredDifferenceDerivative (func, x, H);
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative of function
+ * <TT>func</TT> evaluated at <TT>x</TT>.
+ * If <SPAN CLASS="MATH"><I>n</I> = 0</SPAN>, this returns <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ * If <SPAN CLASS="MATH"><I>n</I> = 1</SPAN>, this calls
+ * {@link #derivative derivative} <TT>(MathFunction, double)</TT>
+ * and returns the resulting first derivative.
+ * Otherwise,
+ * if the function implements
+ * {@link MathFunctionWithDerivative},
+ * this method calls
+ * {@link MathFunctionWithDerivative#derivative MathFunctionWithDerivative.derivative} <TT>(double, int)</TT>.
+ * If the function does not implement this
+ * interface,
+ * the method uses
+ * {@link #finiteCenteredDifferenceDerivative finiteCenteredDifferenceDerivative} <TT>(MathFunction, double, int, double)</TT>
+ * if <SPAN CLASS="MATH"><I>n</I></SPAN> is even, or
+ * {@link #finiteDifferenceDerivative finiteDifferenceDerivative} <TT>(MathFunction, double, int, double)</TT>
+ * if <SPAN CLASS="MATH"><I>n</I></SPAN> is odd,
+ * to obtain a numerical approximation of the derivative.
+ *
+ * @param func the function to derivate.
+ *
+ * @param x the evaluation point.
+ *
+ * @param n the order of the derivative.
+ *
+ * @return the <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative.
+ *
+ */
+ public static double derivative (MathFunction func, double x, int n) {
+ if (n == 0)
+ return func.evaluate (x);
+ else if (n == 1)
+ return derivative (func, x);
+ else if (func instanceof MathFunctionWithDerivative)
+ return ((MathFunctionWithDerivative)func).derivative (x, n);
+ else if (n % 2 == 0)
+ return finiteCenteredDifferenceDerivative (func, x, n, H);
+ else
+ return finiteDifferenceDerivative (func, x, n, H);
+ }
+
+
+ /**
+ * Computes and returns an estimate
+ * of the <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative of the
+ * function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ * This method estimates
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * 1#1,
+ * </DIV><P></P>
+ * the <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative of <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>
+ * evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ * This method first computes
+ *
+ * <SPAN CLASS="MATH"><I>f</I><SUB>i</SUB> = <I>f</I> (<I>x</I> + <I>iε</I>)</SPAN>, for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I></SPAN>, with
+ *
+ * <SPAN CLASS="MATH"><I>ε</I> = <I>h</I><SUP>1/n</SUP></SPAN>.
+ * The estimate is then given by
+ *
+ * <SPAN CLASS="MATH"><I>Δ</I><SUP>n</SUP><I>f</I><SUB>0</SUB>/<I>h</I></SPAN>, where
+ *
+ * <SPAN CLASS="MATH"><I>Δ</I><SUP>n</SUP><I>f</I><SUB>i</SUB> = <I>Δ</I><SUP>n-1</SUP><I>f</I><SUB>i+1</SUB> - <I>Δ</I><SUP>n-1</SUP><I>f</I><SUB>i</SUB></SPAN>, and
+ *
+ * <SPAN CLASS="MATH"><I>Δf</I><SUB>i</SUB> = <I>f</I><SUB>i+1</SUB> - <I>f</I><SUB>i</SUB></SPAN>.
+ *
+ * @param func the function to derivate.
+ *
+ * @param x the evaluation point.
+ *
+ * @param n the order of the derivative.
+ *
+ * @param h the error.
+ *
+ * @return the estimate of the derivative.
+ *
+ */
+ public static double finiteDifferenceDerivative (
+ MathFunction func, double x, int n, double h) {
+ if (n < 0)
+ throw new IllegalArgumentException
+ ("n must not be negative");
+ if (n == 0)
+ return func.evaluate (x);
+ final double err = Math.pow (h, 1.0 / n);
+ final double[] values = new double[n+1];
+ for (int i = 0; i < values.length; i++)
+ values[i] = func.evaluate (x + i*err);
+ for (int j = 0; j < n; j++) {
+ for (int i = 0; i < n - j; i++)
+ values[i] = values[i + 1] - values[i];
+ }
+ return values[0] / h;
+ }
+
+
+ /**
+ * Returns
+ * <SPAN CLASS="MATH">(<I>f</I> (<I>x</I> + <I>h</I>) - <I>f</I> (<I>x</I> - <I>h</I>))/(2<I>h</I>)</SPAN>,
+ * an estimate of the first derivative of <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>
+ * using centered differences.
+ *
+ * @param func the function to derivate.
+ *
+ * @param x the evaluation point.
+ *
+ * @param h the error.
+ *
+ * @return the estimate of the first derivative.
+ *
+ */
+ public static double finiteCenteredDifferenceDerivative (
+ MathFunction func, double x, double h) {
+ final double fplus = func.evaluate (x + h);
+ final double fminus = func.evaluate (x - h);
+ return (fplus - fminus) / (2*h);
+ }
+
+
+ /**
+ * Computes and returns an estimate of the <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative of the
+ * function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> using finite centered differences.
+ * If <SPAN CLASS="MATH"><I>n</I></SPAN> is even, this method returns
+ * {@link #finiteDifferenceDerivative(MathFunction, double, int, double) finiteDifferenceDerivative} <TT>(func, x - <SPAN CLASS="MATH"><I>ε</I></SPAN>*n/2, n, h)</TT>, with
+ * <SPAN CLASS="MATH"><I>h</I> = <I>ε</I><SUP>n</SUP></SPAN>.
+ *
+ * @param func the function to derivate.
+ *
+ * @param x the evaluation point.
+ *
+ * @param n the order of the derivative.
+ *
+ * @param h the error.
+ *
+ * @return the estimate of the derivative.
+ *
+ */
+ public static double finiteCenteredDifferenceDerivative (
+ MathFunction func, double x, int n, double h) {
+ if (n < 0)
+ throw new IllegalArgumentException
+ ("n must not be negative");
+ if (n == 0)
+ return func.evaluate (x);
+ if (n % 2 == 1)
+ throw new IllegalArgumentException ("n must be even");
+ final double err = Math.pow (h, 1.0 / n);
+ return finiteDifferenceDerivative (func, x - n*err / 2, n, h);
+ }
+
+
+ /**
+ * Removes any point <TT>(NaN, y)</TT> or <TT>(x, NaN)</TT> from
+ * <TT>x</TT> and <TT>y</TT>, and returns a 2D array containing the filtered
+ * points. This method filters each pair (<TT>x[i]</TT>, <TT>y[i]</TT>)
+ * containing at least one NaN element. It constructs a 2D array containing
+ * the two filtered arrays, whose size is smaller than or equal to
+ * <TT>x.length</TT>.
+ *
+ * @param x the <SPAN CLASS="MATH"><I>X</I></SPAN> coordinates.
+ *
+ * @param y the <SPAN CLASS="MATH"><I>Y</I></SPAN> coordinates.
+ *
+ * @return the filtered <SPAN CLASS="MATH"><I>X</I></SPAN> and <SPAN CLASS="MATH"><I>Y</I></SPAN> arrays.
+ *
+ */
+ public static double[][] removeNaNs (double[] x, double[] y) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException();
+ int numNaNs = 0;
+ for (int i = 0; i < x.length; i++)
+ if (Double.isNaN (x[i]) || Double.isNaN (y[i]))
+ ++numNaNs;
+ if (numNaNs == 0)
+ return new double[][] { x, y };
+ final double[] nx = new double[x.length - numNaNs];
+ final double[] ny = new double[y.length - numNaNs];
+ int j = 0;
+ for (int i = 0; i < x.length; i++)
+ if (!Double.isNaN (x[i]) && !Double.isNaN (y[i])) {
+ nx[j] = x[i];
+ ny[j++] = y[i];
+ }
+ return new double[][] { nx, ny };
+ }
+
+
+ /**
+ * Returns the integral of the function <TT>func</TT> over <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ * If the given function implements {@link MathFunctionWithIntegral},
+ * this returns
+ * {@link umontreal.iro.lecuyer.functions.MathFunctionWithIntegral#integral MathFunctionWithIntegral.integral} <TT>(double, double)</TT>.
+ * Otherwise, this calls
+ * {@link #simpsonIntegral simpsonIntegral} <TT>(MathFunction, double, double, int)</TT>
+ * with {@link #NUMINTERVALS NUMINTERVALS} intervals.
+ *
+ * @param func the function to integrate.
+ *
+ * @param a the lower bound.
+ *
+ * @param b the upper bound.
+ *
+ * @return the value of the integral.
+ *
+ */
+ public static double integral (MathFunction func, double a, double b) {
+ if (func instanceof MathFunctionWithIntegral)
+ return ((MathFunctionWithIntegral)func).integral (a, b);
+ else
+ return simpsonIntegral (func, a, b, NUMINTERVALS);
+ }
+
+
+ /**
+ * Computes and returns an approximation of the integral of <TT>func</TT> over
+ * <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>, using the Simpson's <SPAN CLASS="MATH">1/3</SPAN> method with <TT>numIntervals</TT>
+ * intervals. This method estimates
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * ∫<SUB>a</SUB><SUP>b</SUP><I>f</I> (<I>x</I>)<I>dx</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> is the function defined by <TT>func</TT> evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>,
+ * by dividing <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> in <SPAN CLASS="MATH"><I>n</I> =</SPAN> <TT>numIntervals</TT> intervals of length
+ *
+ * <SPAN CLASS="MATH"><I>h</I> = (<I>b</I> - <I>a</I>)/<I>n</I></SPAN>. The integral is estimated by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * 2#2(<I>f</I> (<I>a</I>) + 4<I>f</I> (<I>a</I> + <I>h</I>) + 2<I>f</I> (<I>a</I> + 2<I>h</I>) + 4<I>f</I> (<I>a</I> + 3<I>h</I>) + <SUP> ... </SUP> + <I>f</I> (<I>b</I>))
+ * </DIV><P></P>
+ * This method assumes that
+ * <SPAN CLASS="MATH"><I>a</I> <= <I>b</I> < ∞</SPAN>, and <SPAN CLASS="MATH"><I>n</I></SPAN> is even.
+ *
+ * @param func the function being integrated.
+ *
+ * @param a the left bound
+ *
+ * @param b the right bound.
+ *
+ * @param numIntervals the number of intervals.
+ *
+ * @return the approximate value of the integral.
+ *
+ */
+ public static double simpsonIntegral (MathFunction func, double a,
+ double b, int numIntervals) {
+ if (numIntervals % 2 != 0)
+ throw new IllegalArgumentException
+ ("numIntervals must be an even number");
+ if (Double.isInfinite (a) || Double.isInfinite (b) ||
+ Double.isNaN (a) || Double.isNaN (b))
+ throw new IllegalArgumentException
+ ("a and b must not be infinite or NaN");
+ if (b < a)
+ throw new IllegalArgumentException ("b < a");
+ if (a == b)
+ return 0;
+ double[] D = fixBounds (func, a, b, numIntervals);
+ a = D[0];
+ b = D[1];
+ final double h = (b - a) / numIntervals;
+ final double h2 = 2*h;
+ final int m = numIntervals / 2;
+ double sum = 0;
+ for (int i = 0; i < m - 1; i++) {
+ final double x = a + h + h2*i;
+ sum += 4*func.evaluate (x) + 2*func.evaluate (x + h);
+ }
+ sum += func.evaluate (a) + func.evaluate (b) + 4*func.evaluate (b - h);
+ return sum * h / 3;
+ }
+
+
+ /**
+ * Computes and returns a numerical approximation of the integral of
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> over <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>, using Gauss-Lobatto adaptive quadrature with
+ * 5 nodes, with tolerance <TT>tol</TT>. This method estimates
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * ∫<SUB>a</SUB><SUP>b</SUP><I>f</I> (<I>x</I>)<I>dx</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> is the function defined by <TT>func</TT>.
+ * Whenever the estimated error is larger than <TT>tol</TT>, the interval
+ * <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> will be halved in two smaller intervals, and the method will
+ * recursively call itself on the two smaller intervals until the estimated
+ * error is smaller than <TT>tol</TT>.
+ *
+ * @param func the function being integrated.
+ *
+ * @param a the left bound
+ *
+ * @param b the right bound.
+ *
+ * @param tol error.
+ *
+ * @return the approximate value of the integral.
+ *
+ */
+ public static double gaussLobatto (MathFunction func, double a, double b,
+ double tol) {
+ if (b < a)
+ throw new IllegalArgumentException ("b < a");
+ if (Double.isInfinite (a) || Double.isInfinite (b) ||
+ Double.isNaN (a) || Double.isNaN (b))
+ throw new IllegalArgumentException ("a or b is infinite or NaN");
+ if (a == b)
+ return 0;
+ double r0 = simpleGaussLob (func, a, b);
+ final double h = (b - a)/2;
+ double r1 = simpleGaussLob (func, a, a + h) +
+ simpleGaussLob (func, a + h, b);
+ double maxi = Math.max(1.0, Math.abs(r1));
+ if (Math.abs(r0 - r1) <= tol*maxi)
+ return r1;
+ return gaussLobatto (func, a, a + h, tol) +
+ gaussLobatto (func, a + h, b, tol);
+ }
+
+
+ private static double simpleGaussLob (MathFunction func, double a, double b) {
+ // Gauss-Lobatto integral over [a, b] with 5 nodes
+ if (a == b)
+ return 0;
+ final double h = b - a;
+ double sum = 0;
+ for (int i = 0; i < 5; i++) {
+ sum += Wg[i] * func.evaluate(a + h*Cg[i]);
+ }
+ return h*sum;
+ }
+
+
+ /**
+ * Similar to method
+ * {@link #gaussLobatto gaussLobatto}<TT>(MathFunction, double, double, double)</TT>, but
+ * also returns in <TT>T[0]</TT> the subintervals of integration, and in
+ * <TT>T[1]</TT>, the partial values of the integral over the corresponding
+ * subintervals. Thus <TT>T[0][0]</TT> <SPAN CLASS="MATH">= <I>x</I><SUB>0</SUB> = <I>a</I></SPAN> and <TT>T[0][n]</TT>
+ * <SPAN CLASS="MATH">= <I>x</I><SUB>n</SUB> = <I>b</I></SPAN>; <TT>T[1][i]</TT> contains the value of the integral over
+ * the subinterval
+ * <SPAN CLASS="MATH">[<I>x</I><SUB>i-1</SUB>, <I>x</I><SUB>i</SUB>]</SPAN>; we also have <TT>T[1][0]</TT> <SPAN CLASS="MATH">= 0</SPAN>.
+ * The sum over all <TT>T[1][i]</TT>, for
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>n</I></SPAN> gives the
+ * value of the integral over <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>, which is the value returned by this
+ * method. <SPAN CLASS="textit">WARNING:</SPAN> The user <SPAN CLASS="textit">must reserve</SPAN> the 2 elements
+ * of the first dimension (<TT>T[0]</TT> and <TT>T[1]</TT>)
+ * before calling this method.
+ *
+ * @param func function being integrated
+ *
+ * @param a left bound of interval
+ *
+ * @param b right bound of interval
+ *
+ * @param tol error
+ *
+ * @param T <SPAN CLASS="MATH">(<I>x</I>, <I>y</I>)</SPAN> = (values of partial intervals,partial values of integral)
+ *
+ * @return value of the integral
+ *
+ */
+ public static double gaussLobatto (MathFunction func, double a, double b,
+ double tol, double[][] T) {
+ if (b < a)
+ throw new IllegalArgumentException ("b < a");
+ if (a == b) {
+ T[0] = new double [1];
+ T[1] = new double [1];
+ T[0][0] = a;
+ T[1][0] = 0;
+ return 0;
+ }
+
+ int n = 1; // initial capacity
+ T[0] = new double [n];
+ T[1] = new double [n];
+ T[0][0] = a;
+ T[1][0] = 0;
+ int[] s = {0}; // actual number of intervals
+ double res = innerGaussLob (func, a, b, tol, T, s);
+ n = s[0] + 1;
+ double[] temp = new double[n];
+ System.arraycopy (T[0], 0, temp, 0, n);
+ T[0] = temp;
+ temp = new double[n];
+ System.arraycopy (T[1], 0, temp, 0, n);
+ T[1] = temp;
+ return res;
+ }
+
+
+ private static double innerGaussLob (MathFunction func, double a, double b,
+ double tol, double[][] T, int[] s) {
+ double r0 = simpleGaussLob (func, a, b);
+ final double h = (b - a) / 2;
+ double r1 = simpleGaussLob (func, a, a + h) +
+ simpleGaussLob (func, a + h, b);
+ if (Math.abs(r0 - r1) <= tol) {
+ ++s[0];
+ int len = s[0];
+ if (len >= T[0].length) {
+ double[] temp = new double[2 * len];
+ System.arraycopy (T[0], 0, temp, 0, len);
+ T[0] = temp;
+ temp = new double[2 * len];
+ System.arraycopy (T[1], 0, temp, 0, len);
+ T[1] = temp;
+ }
+ T[0][len] = b;
+ T[1][len] = r1;
+ return r1;
+ }
+
+ return innerGaussLob (func, a, a + h, tol, T, s) +
+ innerGaussLob (func, a + h, b, tol, T, s);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionUtil.tex b/source/umontreal/iro/lecuyer/functions/MathFunctionUtil.tex
new file mode 100644
index 0000000..08b3f63
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionUtil.tex
@@ -0,0 +1,483 @@
+\defmodule{MathFunctionUtil}
+
+Provides utility methods for computing
+derivatives and integrals of functions.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MathFunctionUtil
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+import umontreal.iro.lecuyer.util.Misc;
+\end{hide}
+
+public class MathFunctionUtil\begin{hide} {
+\end{hide}
+
+ public static double H = 1e-6;
+\end{code}
+\begin{tabb} Step length in $x$ to compute derivatives. Default: $10^{-6}$.
+\end{tabb}
+\begin{code}\begin{hide}
+ private MathFunctionUtil() {}
+
+ // For Gauss-Lobatto: nodes Cg and weights Wg
+ private static final double[] Cg = { 0, 0.17267316464601142812, 0.5,
+ 0.82732683535398857188, 1 };
+ private static final double[] Wg = { 0.05, 0.27222222222222222222,
+ 0.35555555555555555555, 0.27222222222222222222, 0.05 };
+
+
+ private static double[] fixBounds (MathFunction func, double a,
+ double b, int numIntervals) {
+ // For functions which are 0 on parts of [a, b], shorten the interval
+ // [a, b] to the non-zero part of f(x). Returns the shortened interval.
+
+ final double h = (b - a)/numIntervals;
+ double x = b;
+ while ((0 == func.evaluate (x)) && (x > a))
+ x -= h;
+ if (x < b)
+ b = x + h;
+
+ x = a;
+ while ((0 == func.evaluate (x)) && (x < b))
+ x += h;
+ if (x > a)
+ a = x - h;
+ double[] D = {a, b};
+ return D;
+ }\end{hide}
+
+ public static int NUMINTERVALS = 1024;
+\end{code}
+\begin{tabb} Default number of intervals for Simpson's integral.
+\end{tabb}
+\begin{code}
+
+ public static double derivative (MathFunction func, double x)\begin{hide} {
+ if (func instanceof MathFunctionWithFirstDerivative)
+ return ((MathFunctionWithFirstDerivative)func).derivative (x);
+ else if (func instanceof MathFunctionWithDerivative)
+ return ((MathFunctionWithDerivative)func).derivative (x, 1);
+ else
+ return finiteCenteredDifferenceDerivative (func, x, H);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the first derivative of the function \texttt{func}
+ evaluated at \texttt{x}. If the given function implements
+ \class{MathFunctionWithFirstDerivative}, this method calls
+\clsexternalmethod{}{MathFunctionWithFirst\-Derivative}{derivative}{}~\texttt{(double)}.
+ Otherwise, if the function implements \class{MathFunction\-WithDerivative},
+ this method calls
+\clsexternalmethod{}{MathFunctionWithDerivative}{derivative}{}~\texttt{(double, int)}.
+If the function does not implement any of these two interfaces, the method
+ uses \method{finiteCenteredDifferenceDerivative}{}~\texttt{(MathFunction,
+ double, double)} to obtain an estimate of the derivative.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function to derivate.}
+ \param{x}{the evaluation point.}
+ \return{the first derivative.}
+\end{htmlonly}
+\begin{code}
+
+ public static double derivative (MathFunction func, double x, int n)\begin{hide} {
+ if (n == 0)
+ return func.evaluate (x);
+ else if (n == 1)
+ return derivative (func, x);
+ else if (func instanceof MathFunctionWithDerivative)
+ return ((MathFunctionWithDerivative)func).derivative (x, n);
+ else if (n % 2 == 0)
+ return finiteCenteredDifferenceDerivative (func, x, n, H);
+ else
+ return finiteDifferenceDerivative (func, x, n, H);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $n$th derivative of function
+ \texttt{func} evaluated at \texttt{x}.
+ If $n=0$, this returns $f(x)$.
+ If $n=1$, this calls
+ \method{derivative}{}~\texttt{(MathFunction, double)}
+ and returns the resulting first derivative.
+ Otherwise,
+ if the function implements
+ \class{MathFunctionWithDerivative},
+ this method calls
+\clsexternalmethod{}{MathFunctionWithDerivative}{derivative}{}~\texttt{(double, int)}.
+ If the function does not implement this
+ interface,
+ the method uses
+ \method{finiteCenteredDifferenceDerivative}{}~\texttt{(MathFunction, double, int, double)}
+ if $n$ is even, or
+ \method{finiteDifferenceDerivative}{}~\texttt{(MathFunction, double, int, double)}
+ if $n$ is odd,
+ to obtain a numerical approximation of the derivative.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function to derivate.}
+ \param{x}{the evaluation point.}
+ \param{n}{the order of the derivative.}
+ \return{the $n$th derivative.}
+\end{htmlonly}
+\begin{code}
+
+ public static double finiteDifferenceDerivative (
+ MathFunction func, double x, int n, double h)\begin{hide} {
+ if (n < 0)
+ throw new IllegalArgumentException
+ ("n must not be negative");
+ if (n == 0)
+ return func.evaluate (x);
+ final double err = Math.pow (h, 1.0 / n);
+ final double[] values = new double[n+1];
+ for (int i = 0; i < values.length; i++)
+ values[i] = func.evaluate (x + i*err);
+ for (int j = 0; j < n; j++) {
+ for (int i = 0; i < n - j; i++)
+ values[i] = values[i + 1] - values[i];
+ }
+ return values[0] / h;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns an estimate
+ of the $n$th derivative of the
+ function $f(x)$.
+ This method estimates
+ \[\frac{d^nf(x)}{dx^n},\]
+ the $n$th derivative of $f(x)$
+ evaluated at $x$.
+ This method first computes
+ $f_i=f(x+i\epsilon)$, for $i=0,\ldots,n$, with
+ $\epsilon=h^{1/n}$.
+ The estimate is then given by
+ $\Delta^nf_0/h$, where
+ $\Delta^nf_i=\Delta^{n-1}f_{i+1} - \Delta^{n-1}f_i$, and
+ $\Delta f_i = f_{i+1} - f_i$.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function to derivate.}
+ \param{x}{the evaluation point.}
+ \param{n}{the order of the derivative.}
+ \param{h}{the error.}
+ \return{the estimate of the derivative.}
+\end{htmlonly}
+\begin{code}
+
+ public static double finiteCenteredDifferenceDerivative (
+ MathFunction func, double x, double h)\begin{hide} {
+ final double fplus = func.evaluate (x + h);
+ final double fminus = func.evaluate (x - h);
+ return (fplus - fminus) / (2*h);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $(f(x + h) - f(x - h))/(2h)$,
+ an estimate of the first derivative of $f(x)$
+ using centered differences.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function to derivate.}
+ \param{x}{the evaluation point.}
+ \param{h}{the error.}
+ \return{the estimate of the first derivative.}
+\end{htmlonly}
+\begin{code}
+
+ public static double finiteCenteredDifferenceDerivative (
+ MathFunction func, double x, int n, double h)\begin{hide} {
+ if (n < 0)
+ throw new IllegalArgumentException
+ ("n must not be negative");
+ if (n == 0)
+ return func.evaluate (x);
+ if (n % 2 == 1)
+ throw new IllegalArgumentException ("n must be even");
+ final double err = Math.pow (h, 1.0 / n);
+ return finiteDifferenceDerivative (func, x - n*err / 2, n, h);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns an estimate of the $n$th derivative of the
+ function $f(x)$ using finite centered differences.
+ If $n$ is even, this method returns
+\method{finiteDifferenceDerivative}{MathFunction, double, int, double}~\texttt{(func, x - $\epsilon$*n/2, n, h)}, with $h=\epsilon^n$.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function to derivate.}
+ \param{x}{the evaluation point.}
+ \param{n}{the order of the derivative.}
+ \param{h}{the error.}
+ \return{the estimate of the derivative.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] removeNaNs (double[] x, double[] y)\begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException();
+ int numNaNs = 0;
+ for (int i = 0; i < x.length; i++)
+ if (Double.isNaN (x[i]) || Double.isNaN (y[i]))
+ ++numNaNs;
+ if (numNaNs == 0)
+ return new double[][] { x, y };
+ final double[] nx = new double[x.length - numNaNs];
+ final double[] ny = new double[y.length - numNaNs];
+ int j = 0;
+ for (int i = 0; i < x.length; i++)
+ if (!Double.isNaN (x[i]) && !Double.isNaN (y[i])) {
+ nx[j] = x[i];
+ ny[j++] = y[i];
+ }
+ return new double[][] { nx, ny };
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes any point \texttt{(NaN, y)} or \texttt{(x, NaN)} from
+ \texttt{x} and \texttt{y}, and returns a 2D array containing the filtered
+ points. This method filters each pair (\texttt{x[i]}, \texttt{y[i]})
+ containing at least one NaN element. It constructs a 2D array containing
+ the two filtered arrays, whose size is smaller than or equal to
+ \texttt{x.length}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the $X$ coordinates.}
+ \param{y}{the $Y$ coordinates.}
+ \return{the filtered $X$ and $Y$ arrays.}
+\end{htmlonly}
+\begin{code}
+
+ public static double integral (MathFunction func, double a, double b) \begin{hide} {
+ if (func instanceof MathFunctionWithIntegral)
+ return ((MathFunctionWithIntegral)func).integral (a, b);
+ else
+ return simpsonIntegral (func, a, b, NUMINTERVALS);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the integral of the function \texttt{func} over $[a, b]$.
+ If the given function implements \class{MathFunctionWithIntegral},
+ this returns
+ \clsexternalmethod{umontreal.iro.lecuyer.functions}{MathFunctionWithIntegral}{integral}{}~\texttt{(double, double)}.
+ Otherwise, this calls
+ \method{simpsonIntegral}{}~\texttt{(MathFunction, double, double, int)}
+ with \method{NUMINTERVALS}{} intervals.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function to integrate.}
+ \param{a}{the lower bound.}
+ \param{b}{the upper bound.}
+ \return{the value of the integral.}
+\end{htmlonly}
+\begin{code}
+
+ public static double simpsonIntegral (MathFunction func, double a,
+ double b, int numIntervals) \begin{hide} {
+ if (numIntervals % 2 != 0)
+ throw new IllegalArgumentException
+ ("numIntervals must be an even number");
+ if (Double.isInfinite (a) || Double.isInfinite (b) ||
+ Double.isNaN (a) || Double.isNaN (b))
+ throw new IllegalArgumentException
+ ("a and b must not be infinite or NaN");
+ if (b < a)
+ throw new IllegalArgumentException ("b < a");
+ if (a == b)
+ return 0;
+ double[] D = fixBounds (func, a, b, numIntervals);
+ a = D[0];
+ b = D[1];
+ final double h = (b - a) / numIntervals;
+ final double h2 = 2*h;
+ final int m = numIntervals / 2;
+ double sum = 0;
+ for (int i = 0; i < m - 1; i++) {
+ final double x = a + h + h2*i;
+ sum += 4*func.evaluate (x) + 2*func.evaluate (x + h);
+ }
+ sum += func.evaluate (a) + func.evaluate (b) + 4*func.evaluate (b - h);
+ return sum * h / 3;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns an approximation of the integral of \texttt{func} over
+ $[a, b]$, using the Simpson's $1/3$ method with \texttt{numIntervals}
+ intervals. This method estimates
+\[\int_a^b f(x)dx,\]
+ where $f(x)$ is the function defined by \texttt{func} evaluated at $x$,
+ by dividing $[a, b]$ in $n=$~\texttt{numIntervals} intervals of length
+ $h=(b - a)/n$. The integral is estimated by
+\[\frac{h}{3}(f(a)+4f(a+h)+2f(a+2h)+4f(a+3h)+\cdots+f(b))\]
+This method assumes that $a\le b<\infty$, and $n$ is even.
+\end{tabb}
+\begin{htmlonly}
+\param{func}{the function being integrated.}
+\param{a}{the left bound}
+\param{b}{the right bound.}
+\param{numIntervals}{the number of intervals.}
+\return{the approximate value of the integral.}
+\end{htmlonly}
+\begin{code}
+
+ public static double gaussLobatto (MathFunction func, double a, double b,
+ double tol)\begin{hide} {
+ if (b < a)
+ throw new IllegalArgumentException ("b < a");
+ if (Double.isInfinite (a) || Double.isInfinite (b) ||
+ Double.isNaN (a) || Double.isNaN (b))
+ throw new IllegalArgumentException ("a or b is infinite or NaN");
+ if (a == b)
+ return 0;
+ double r0 = simpleGaussLob (func, a, b);
+ final double h = (b - a)/2;
+ double r1 = simpleGaussLob (func, a, a + h) +
+ simpleGaussLob (func, a + h, b);
+ double maxi = Math.max(1.0, Math.abs(r1));
+ if (Math.abs(r0 - r1) <= tol*maxi)
+ return r1;
+ return gaussLobatto (func, a, a + h, tol) +
+ gaussLobatto (func, a + h, b, tol);
+ }
+
+
+ private static double simpleGaussLob (MathFunction func, double a, double b) {
+ // Gauss-Lobatto integral over [a, b] with 5 nodes
+ if (a == b)
+ return 0;
+ final double h = b - a;
+ double sum = 0;
+ for (int i = 0; i < 5; i++) {
+ sum += Wg[i] * func.evaluate(a + h*Cg[i]);
+ }
+ return h*sum;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns a numerical approximation of the integral of
+ $f(x)$ over $[a, b]$, using Gauss-Lobatto adaptive quadrature with
+ 5 nodes, with tolerance \texttt{tol}. This method estimates
+\[
+\int_a^b f(x)dx,
+\]
+ where $f(x)$ is the function defined by \texttt{func}.
+Whenever the estimated error is larger than \texttt{tol}, the interval
+$[a, b]$ will be halved in two smaller intervals, and the method will
+ recursively call itself on the two smaller intervals until the estimated
+ error is smaller than \texttt{tol}.
+\end{tabb}
+\begin{htmlonly}
+\param{func}{the function being integrated.}
+\param{a}{the left bound}
+\param{b}{the right bound.}
+\param{tol}{error.}
+\return{the approximate value of the integral.}
+\end{htmlonly}
+\begin{code}
+
+ public static double gaussLobatto (MathFunction func, double a, double b,
+ double tol, double[][] T) \begin{hide} {
+ if (b < a)
+ throw new IllegalArgumentException ("b < a");
+ if (a == b) {
+ T[0] = new double [1];
+ T[1] = new double [1];
+ T[0][0] = a;
+ T[1][0] = 0;
+ return 0;
+ }
+
+ int n = 1; // initial capacity
+ T[0] = new double [n];
+ T[1] = new double [n];
+ T[0][0] = a;
+ T[1][0] = 0;
+ int[] s = {0}; // actual number of intervals
+ double res = innerGaussLob (func, a, b, tol, T, s);
+ n = s[0] + 1;
+ double[] temp = new double[n];
+ System.arraycopy (T[0], 0, temp, 0, n);
+ T[0] = temp;
+ temp = new double[n];
+ System.arraycopy (T[1], 0, temp, 0, n);
+ T[1] = temp;
+ return res;
+ }
+
+
+ private static double innerGaussLob (MathFunction func, double a, double b,
+ double tol, double[][] T, int[] s) {
+ double r0 = simpleGaussLob (func, a, b);
+ final double h = (b - a) / 2;
+ double r1 = simpleGaussLob (func, a, a + h) +
+ simpleGaussLob (func, a + h, b);
+ if (Math.abs(r0 - r1) <= tol) {
+ ++s[0];
+ int len = s[0];
+ if (len >= T[0].length) {
+ double[] temp = new double[2 * len];
+ System.arraycopy (T[0], 0, temp, 0, len);
+ T[0] = temp;
+ temp = new double[2 * len];
+ System.arraycopy (T[1], 0, temp, 0, len);
+ T[1] = temp;
+ }
+ T[0][len] = b;
+ T[1][len] = r1;
+ return r1;
+ }
+
+ return innerGaussLob (func, a, a + h, tol, T, s) +
+ innerGaussLob (func, a + h, b, tol, T, s);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Similar to method
+\method{gaussLobatto}{}\texttt{(MathFunction, double, double, double)}, but
+also returns in \texttt{T[0]} the subintervals of integration, and in
+\texttt{T[1]}, the partial values of the integral over the corresponding
+ subintervals. Thus \texttt{T[0][0]} $= x_0 = a$ and \texttt{T[0][n]}
+ $=x_n =b$; \texttt{T[1][i]} contains the value of the integral over
+ the subinterval $[x_{i-1}, x_i]$; we also have \texttt{T[1][0]} $=0$.
+ The sum over all \texttt{T[1][i]}, for $i=1, \ldots, n$ gives the
+ value of the integral over $[a,b]$, which is the value returned by this
+ method. \emph{WARNING:} The user \emph{must reserve} the 2 elements
+ of the first dimension (\texttt{T[0]} and \texttt{T[1]})
+ before calling this method.
+ % The user program must call
+ % double[][] T = new double[2][1];
+ % before calling gaussLobatto.
+\end{tabb}
+\begin{htmlonly}
+\param{func}{function being integrated}
+\param{a}{left bound of interval}
+\param{b}{right bound of interval}
+\param{tol}{error}
+\param{T}{$(x,y)$ = (values of partial intervals,partial values of integral)}
+\return{value of the integral}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionWithDerivative.java b/source/umontreal/iro/lecuyer/functions/MathFunctionWithDerivative.java
new file mode 100644
index 0000000..1ed4188
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionWithDerivative.java
@@ -0,0 +1,55 @@
+
+
+/*
+ * Class: MathFunctionWithDerivative
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents a mathematical function whose
+ * <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative can be computed using
+ * {@link #derivative(double, int) derivative}.
+ *
+ */
+public interface MathFunctionWithDerivative extends MathFunction {
+
+
+ /**
+ * Computes (or estimates) the <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative
+ * of the function at point <TT>x</TT>.
+ * For <SPAN CLASS="MATH"><I>n</I> = 0</SPAN>, this returns the result of
+ * {@link umontreal.iro.lecuyer.functions.MathFunction#evaluate(double) evaluate}.
+ *
+ * @param x the point to evaluate the derivate to.
+ *
+ * @param n the order of the derivative.
+ *
+ * @return the resulting derivative.
+ * @exception IllegalArgumentException if <TT>n</TT> is negative or 0.
+ *
+ */
+ public double derivative (double x, int n);
+}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionWithDerivative.tex b/source/umontreal/iro/lecuyer/functions/MathFunctionWithDerivative.tex
new file mode 100644
index 0000000..5adb7e8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionWithDerivative.tex
@@ -0,0 +1,56 @@
+\defmodule{MathFunctionWithDerivative}
+
+Represents a mathematical function whose
+$n$th derivative can be computed using
+\method{derivative}{double, int}.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MathFunctionWithDerivative
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public interface MathFunctionWithDerivative extends MathFunction\begin{hide} {
+\end{hide}
+
+ public double derivative (double x, int n);\begin{hide}
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Computes (or estimates) the $n$th derivative
+ of the function at point \texttt{x}.
+ For $n=0$, this returns the result of
+ \externalmethod{umontreal.iro.lecuyer.functions}{MathFunction}{evaluate}{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the point to evaluate the derivate to.}
+ \param{n}{the order of the derivative.}
+ \return{the resulting derivative.}
+ \exception{IllegalArgumentException}{if \texttt{n} is negative or 0.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionWithFirstDerivative.java b/source/umontreal/iro/lecuyer/functions/MathFunctionWithFirstDerivative.java
new file mode 100644
index 0000000..780640d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionWithFirstDerivative.java
@@ -0,0 +1,49 @@
+
+
+/*
+ * Class: MathFunctionWithFirstDerivative
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents a mathematical function whose
+ * derivative can be computed using
+ * {@link #derivative(double) derivative}.
+ *
+ */
+public interface MathFunctionWithFirstDerivative extends MathFunction {
+
+
+ /**
+ * Computes (or estimates) the first derivative
+ * of the function at point <TT>x</TT>.
+ *
+ * @param x the point to evaluate the derivative to.
+ *
+ * @return the value of the derivative.
+ */
+ public double derivative (double x);
+}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionWithFirstDerivative.tex b/source/umontreal/iro/lecuyer/functions/MathFunctionWithFirstDerivative.tex
new file mode 100644
index 0000000..049c3e6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionWithFirstDerivative.tex
@@ -0,0 +1,52 @@
+\defmodule{MathFunctionWithFirstDerivative}
+
+Represents a mathematical function whose
+derivative can be computed using
+\method{derivative}{double}.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MathFunctionWithFirstDerivative
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public interface MathFunctionWithFirstDerivative extends MathFunction\begin{hide} {
+\end{hide}
+
+ public double derivative (double x);\begin{hide}
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Computes (or estimates) the first derivative
+ of the function at point \texttt{x}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the point to evaluate the derivative to.}
+ \return{the value of the derivative.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionWithIntegral.java b/source/umontreal/iro/lecuyer/functions/MathFunctionWithIntegral.java
new file mode 100644
index 0000000..e355c58
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionWithIntegral.java
@@ -0,0 +1,51 @@
+
+
+/*
+ * Class: MathFunctionWithIntegral
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents a mathematical function whose
+ * integral can be computed by the
+ * {@link #integral(double, double) integral} method.
+ *
+ */
+public interface MathFunctionWithIntegral extends MathFunction {
+
+
+ /**
+ * Computes (or estimates) the integral of the
+ * function over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ *
+ * @param a the starting point of the interval.
+ *
+ * @param b the ending point of the interval.
+ *
+ * @return the value of the integral.
+ */
+ public double integral (double a, double b);
+}
diff --git a/source/umontreal/iro/lecuyer/functions/MathFunctionWithIntegral.tex b/source/umontreal/iro/lecuyer/functions/MathFunctionWithIntegral.tex
new file mode 100644
index 0000000..0801e49
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MathFunctionWithIntegral.tex
@@ -0,0 +1,53 @@
+\defmodule{MathFunctionWithIntegral}
+
+Represents a mathematical function whose
+integral can be computed by the
+\method{integral}{double, double} method.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MathFunctionWithIntegral
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public interface MathFunctionWithIntegral extends MathFunction\begin{hide} {
+\end{hide}
+
+ public double integral (double a, double b);\begin{hide}
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Computes (or estimates) the integral of the
+ function over the interval $[a, b]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the starting point of the interval.}
+ \param{b}{the ending point of the interval.}
+ \return{the value of the integral.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/functions/MultiFunction.java b/source/umontreal/iro/lecuyer/functions/MultiFunction.java
new file mode 100644
index 0000000..2a9120b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MultiFunction.java
@@ -0,0 +1,48 @@
+
+/*
+ * Class: MultiFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2013
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+/**
+ * This interface should be implemented by classes which represent <SPAN CLASS="textit">multivariate</SPAN>
+ * mathematical functions. It is used to pass an arbitrary function of a vector
+ * variable as argument to another function.
+ *
+ */
+public interface MultiFunction {
+
+
+ /**
+ * Returns the value of the function evaluated at <SPAN CLASS="MATH"><I>X</I></SPAN>.
+ *
+ * @param X point at which the function is evaluated
+ *
+ * @return value of function at <TT>X</TT>
+ *
+ */
+ public double evaluate (double[] X);
+
+}
diff --git a/source/umontreal/iro/lecuyer/functions/MultiFunction.tex b/source/umontreal/iro/lecuyer/functions/MultiFunction.tex
new file mode 100644
index 0000000..acc0aef
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/MultiFunction.tex
@@ -0,0 +1,52 @@
+\defmodule {MultiFunction}
+
+This interface should be implemented by classes which represent \emph{multivariate}
+mathematical functions. It is used to pass an arbitrary function of a vector
+variable as argument to another function.
+
+\bigskip\hrule\bigskip
+
+\begin{code}\begin{hide}
+/*
+ * Class: MultiFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since May 2013
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;
+
+
+public interface MultiFunction\begin{hide} {\end{hide}
+\end{code}
+\begin{code}
+
+ public double evaluate (double[] X);
+\end{code}
+\begin{tabb}
+ Returns the value of the function evaluated at $X$.
+\end{tabb}
+\begin{htmlonly}
+ \param{X}{point at which the function is evaluated}
+ \return{value of function at \texttt{X}}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/PiecewiseConstantFunction.java b/source/umontreal/iro/lecuyer/functions/PiecewiseConstantFunction.java
new file mode 100644
index 0000000..9d63d53
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/PiecewiseConstantFunction.java
@@ -0,0 +1,90 @@
+
+
+/*
+ * Class: PiecewiseConstantFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+import java.util.Arrays;
+
+
+/**
+ * Represents a piecewise-constant function.
+ *
+ */
+public class PiecewiseConstantFunction implements MathFunction {
+ private double[] x;
+ private double[] y;
+
+
+ /**
+ * Constructs a new piecewise-constant function
+ * with <SPAN CLASS="MATH"><I>X</I></SPAN> and <SPAN CLASS="MATH"><I>Y</I></SPAN> coordinates given
+ * by <TT>x</TT> and <TT>y</TT>.
+ *
+ * @param x the <SPAN CLASS="MATH"><I>X</I></SPAN> coordinates.
+ *
+ * @param y the <SPAN CLASS="MATH"><I>Y</I></SPAN> coordinates.
+ *
+ *
+ */
+ public PiecewiseConstantFunction (double[] x, double[] y) {
+ if (x.length != y.length)
+ throw new IllegalArgumentException();
+ this.x = x.clone ();
+ this.y = y.clone ();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>X</I></SPAN> coordinates of the function.
+ *
+ * @return the <SPAN CLASS="MATH"><I>X</I></SPAN> coordinates of the function.
+ *
+ */
+ public double[] getX() {
+ return x.clone ();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>Y</I></SPAN> coordinates of
+ * the function.
+ *
+ * @return the <SPAN CLASS="MATH"><I>Y</I></SPAN> coordinates of the function.
+ *
+ */
+ public double[] getY() {
+ return y.clone ();
+ }
+
+
+ public double evaluate (double x) {
+ final int idx = Arrays.binarySearch (this.x, x);
+ if (idx >= 0)
+ return y[idx];
+ final int insertionPoint = -(idx + 1);
+ return y[insertionPoint];
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/PiecewiseConstantFunction.tex b/source/umontreal/iro/lecuyer/functions/PiecewiseConstantFunction.tex
new file mode 100644
index 0000000..d498860
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/PiecewiseConstantFunction.tex
@@ -0,0 +1,92 @@
+\defmodule{PiecewiseConstantFunction}
+
+
+Represents a piecewise-constant function.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PiecewiseConstantFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+import java.util.Arrays;
+\end{hide}
+
+public class PiecewiseConstantFunction implements MathFunction\begin{hide} {
+ private double[] x;
+ private double[] y;
+\end{hide}
+
+ public PiecewiseConstantFunction (double[] x, double[] y)\begin{hide} {
+ if (x.length != y.length)
+ throw new IllegalArgumentException();
+ this.x = x.clone ();
+ this.y = y.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new piecewise-constant function
+ with $X$ and $Y$ coordinates given
+ by \texttt{x} and \texttt{y}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the $X$ coordinates.}
+ \param{y}{the $Y$ coordinates.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getX()\begin{hide} {
+ return x.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $X$ coordinates of the function.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $X$ coordinates of the function.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getY()\begin{hide} {
+ return y.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $Y$ coordinates of
+ the function.
+\end{tabb}
+\begin{htmlonly}
+ \return{the $Y$ coordinates of the function.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double evaluate (double x) {
+ final int idx = Arrays.binarySearch (this.x, x);
+ if (idx >= 0)
+ return y[idx];
+ final int insertionPoint = -(idx + 1);
+ return y[insertionPoint];
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/Polynomial.java b/source/umontreal/iro/lecuyer/functions/Polynomial.java
new file mode 100644
index 0000000..964aa92
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/Polynomial.java
@@ -0,0 +1,260 @@
+
+
+/*
+ * Class: Polynomial
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+import java.io.Serializable;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * Represents a polynomial of degree <SPAN CLASS="MATH"><I>n</I></SPAN> in power form. Such a polynomial is of
+ * the form
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = <I>c</I><SUB>0</SUB> + <I>c</I><SUB>1</SUB><I>x</I> + <SUP> ... </SUP> + <I>c</I><SUB>n</SUB><I>x</I><SUP>n</SUP>,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>c</I><SUB>0</SUB>,…, <I>c</I><SUB>n</SUB></SPAN> are the coefficients of the polynomial.
+ *
+ */
+public class Polynomial implements MathFunction
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral, Serializable, Cloneable {
+ private static final long serialVersionUID = -2911550952861456470L;
+ private double[] coeff;
+
+
+ /**
+ * Constructs a new polynomial with coefficients <TT>coeff</TT>. The value of
+ * <TT>coeff[i]</TT> in this array corresponds to <SPAN CLASS="MATH"><I>c</I><SUB>i</SUB></SPAN>.
+ *
+ * @param coeff the coefficients of the polynomial.
+ *
+ * @exception NullPointerException if <TT>coeff</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of <TT>coeff</TT> is 0.
+ *
+ *
+ */
+ public Polynomial (double... coeff) {
+ if (coeff == null)
+ throw new NullPointerException ();
+ if (coeff.length == 0)
+ throw new IllegalArgumentException (
+ "At least one coefficient is needed");
+ this.coeff = coeff.clone ();
+ }
+
+
+ /**
+ * Returns the degree of this polynomial.
+ *
+ * @return the degree of this polynomial.
+ *
+ */
+ public int getDegree () {
+ return coeff.length - 1;
+ }
+
+
+ /**
+ * Returns an array containing the coefficients of the polynomial.
+ *
+ * @return the array of coefficients.
+ *
+ */
+ public double[] getCoefficients () {
+ return coeff.clone ();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>i</I></SPAN>th coefficient of the polynomial.
+ *
+ * @return the array of coefficients.
+ *
+ */
+ public double getCoefficient (int i) {
+ return coeff[i];
+ }
+
+
+ /**
+ * Sets the array of coefficients of this polynomial to <TT>coeff</TT>.
+ *
+ * @param coeff the new array of coefficients.
+ *
+ * @exception NullPointerException if <TT>coeff</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of <TT>coeff</TT> is 0.
+ *
+ *
+ */
+ public void setCoefficients (double... coeff) {
+ if (coeff == null)
+ throw new NullPointerException ();
+ if (coeff.length == 0)
+ throw new IllegalArgumentException (
+ "At least one coefficient is needed");
+ this.coeff = coeff.clone ();
+ }
+
+
+ public double evaluate (double x) {
+ double res = coeff[coeff.length - 1];
+ for (int i = coeff.length - 2; i >= 0; i--)
+ res = coeff[i] + x * res;
+ return res;
+ }
+
+ public double derivative (double x) {
+ return derivative (x, 1);
+ }
+
+ public double derivative (double x, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+ if (n == 0)
+ return evaluate (x);
+ if (n >= coeff.length)
+ return 0;
+// double res = coeff[coeff.length - 1]*(coeff.length - 1);
+// for (int i = coeff.length - 2; i >= n; i--)
+// res = i*(coeff[i] + x * res);
+ double res = getCoeffDer (coeff.length - 1, n);
+ for (int i = coeff.length - 2; i >= n; i--)
+ res = getCoeffDer (i, n) + x * res;
+ return res;
+ }
+
+ /**
+ * Returns a polynomial corresponding to the <SPAN CLASS="MATH"><I>n</I></SPAN>th derivative of
+ * this polynomial.
+ *
+ * @param n the degree of the derivative.
+ *
+ * @return the derivative.
+ *
+ */
+ public Polynomial derivativePolynomial (int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+ if (n == 0)
+ return this;
+ if (n >= coeff.length)
+ return new Polynomial (0);
+ final double[] coeffDer = new double[coeff.length - n];
+ for (int i = coeff.length - 1; i >= n; i--)
+ coeffDer[i - n] = getCoeffDer (i, n);
+ return new Polynomial (coeffDer);
+ }
+
+
+ private double getCoeffDer (int i, int n) {
+ double coeffDer = coeff[i];
+ for (int j = i; j > i - n; j--)
+ coeffDer *= j;
+ return coeffDer;
+ }
+
+ public double integral (double a, double b) {
+ return integralA0 (b) - integralA0 (a);
+ }
+
+ private double integralA0 (double u) {
+ final int n = coeff.length - 1;
+ double res = u * coeff[n] / (n + 1);
+ for (int i = coeff.length - 2; i >= 0; i--)
+ res = coeff[i] * u / (i + 1) + u * res;
+ return res;
+ }
+
+ /**
+ * Returns a polynomial representing the integral of this polynomial.
+ * This integral is of the form
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * ∫<I>p</I>(<I>x</I>)<I>dx</I> = <I>c</I> + <I>c</I><SUB>0</SUB><I>x</I> + 1#1 + <SUP> ... </SUP> + 2#2,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>c</I></SPAN> is a user-defined constant.
+ *
+ * @param c the constant for the integral.
+ *
+ * @return the polynomial representing the integral.
+ *
+ */
+ public Polynomial integralPolynomial (double c) {
+ final double[] coeffInt = new double[coeff.length + 1];
+ coeffInt[0] = c;
+ for (int i = 0; i < coeff.length; i++)
+ coeffInt[i + 1] = coeff[i] / (i + 1);
+ return new Polynomial (coeffInt);
+ }
+
+ @Override
+
+
+
+ public String toString () {
+ final StringBuilder sb = new StringBuilder ();
+ for (int i = 0; i < coeff.length; i++) {
+ if (i > 0) {
+ if (coeff[i] == 0)
+ continue;
+ else if (coeff[i] > 0)
+ sb.append (" + ");
+ else
+ sb.append (" - ");
+ sb.append (PrintfFormat.format (8, 3, 3, Math.abs (coeff[i])));
+ }
+ else
+ sb.append (PrintfFormat.format (8, 3, 3, coeff[i]));
+ if (i > 0) {
+ sb.append ("*X");
+ if (i > 1)
+ sb.append ("^").append (i);
+ }
+ }
+ return sb.toString ();
+ }
+
+ @Override
+ public Polynomial clone () {
+ Polynomial pol;
+ try {
+ pol = (Polynomial) super.clone ();
+ }
+ catch (final CloneNotSupportedException cne) {
+ throw new IllegalStateException ("Clone not supported");
+ }
+ pol.coeff = coeff.clone ();
+ return pol;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/Polynomial.tex b/source/umontreal/iro/lecuyer/functions/Polynomial.tex
new file mode 100644
index 0000000..c0affbe
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/Polynomial.tex
@@ -0,0 +1,249 @@
+\defmodule{Polynomial}
+
+Represents a polynomial of degree $n$ in power form. Such a polynomial is of
+the form
+\begin{equation}
+p(x) = c_0 + c_1x + \cdots + c_nx^n,
+\end{equation}
+where $c_0, \ldots, c_n$ are the coefficients of the polynomial.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: Polynomial
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+import java.io.Serializable;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class Polynomial implements MathFunction\begin{hide}
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral, Serializable, Cloneable {
+ private static final long serialVersionUID = -2911550952861456470L;
+ private double[] coeff;
+\end{hide}
+
+ public Polynomial (double... coeff)\begin{hide} {
+ if (coeff == null)
+ throw new NullPointerException ();
+ if (coeff.length == 0)
+ throw new IllegalArgumentException (
+ "At least one coefficient is needed");
+ this.coeff = coeff.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new polynomial with coefficients \texttt{coeff}. The value of
+ \texttt{coeff[i]} in this array corresponds to $c_i$.
+\end{tabb}
+\begin{htmlonly}
+ \param{coeff}{the coefficients of the polynomial.}
+ \exception{NullPointerException}{if \texttt{coeff} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of \texttt{coeff} is 0.}
+\end{htmlonly}
+\begin{code}
+
+ public int getDegree ()\begin{hide} {
+ return coeff.length - 1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the degree of this polynomial.
+\end{tabb}
+\begin{htmlonly}
+ \return{the degree of this polynomial.}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getCoefficients ()\begin{hide} {
+ return coeff.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns an array containing the coefficients of the polynomial.
+\end{tabb}
+\begin{htmlonly}
+ \return{the array of coefficients.}
+\end{htmlonly}
+\begin{code}
+
+ public double getCoefficient (int i)\begin{hide} {
+ return coeff[i];
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $i$th coefficient of the polynomial.
+\end{tabb}
+\begin{htmlonly}
+ \return{the array of coefficients.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCoefficients (double... coeff)\begin{hide} {
+ if (coeff == null)
+ throw new NullPointerException ();
+ if (coeff.length == 0)
+ throw new IllegalArgumentException (
+ "At least one coefficient is needed");
+ this.coeff = coeff.clone ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the array of coefficients of this polynomial to \texttt{coeff}.
+\end{tabb}
+\begin{htmlonly}
+ \param{coeff}{the new array of coefficients.}
+ \exception{NullPointerException}{if \texttt{coeff} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of \texttt{coeff} is 0.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double evaluate (double x) {
+ double res = coeff[coeff.length - 1];
+ for (int i = coeff.length - 2; i >= 0; i--)
+ res = coeff[i] + x * res;
+ return res;
+ }
+
+ public double derivative (double x) {
+ return derivative (x, 1);
+ }
+
+ public double derivative (double x, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+ if (n == 0)
+ return evaluate (x);
+ if (n >= coeff.length)
+ return 0;
+// double res = coeff[coeff.length - 1]*(coeff.length - 1);
+// for (int i = coeff.length - 2; i >= n; i--)
+// res = i*(coeff[i] + x * res);
+ double res = getCoeffDer (coeff.length - 1, n);
+ for (int i = coeff.length - 2; i >= n; i--)
+ res = getCoeffDer (i, n) + x * res;
+ return res;
+ }\end{hide}
+
+ public Polynomial derivativePolynomial (int n)\begin{hide} {
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+ if (n == 0)
+ return this;
+ if (n >= coeff.length)
+ return new Polynomial (0);
+ final double[] coeffDer = new double[coeff.length - n];
+ for (int i = coeff.length - 1; i >= n; i--)
+ coeffDer[i - n] = getCoeffDer (i, n);
+ return new Polynomial (coeffDer);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a polynomial corresponding to the $n$th derivative of
+this polynomial.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{the degree of the derivative.}
+ \return{the derivative.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private double getCoeffDer (int i, int n) {
+ double coeffDer = coeff[i];
+ for (int j = i; j > i - n; j--)
+ coeffDer *= j;
+ return coeffDer;
+ }
+
+ public double integral (double a, double b) {
+ return integralA0 (b) - integralA0 (a);
+ }
+
+ private double integralA0 (double u) {
+ final int n = coeff.length - 1;
+ double res = u * coeff[n] / (n + 1);
+ for (int i = coeff.length - 2; i >= 0; i--)
+ res = coeff[i] * u / (i + 1) + u * res;
+ return res;
+ }\end{hide}
+
+ public Polynomial integralPolynomial (double c)\begin{hide} {
+ final double[] coeffInt = new double[coeff.length + 1];
+ coeffInt[0] = c;
+ for (int i = 0; i < coeff.length; i++)
+ coeffInt[i + 1] = coeff[i] / (i + 1);
+ return new Polynomial (coeffInt);
+ }
+
+ @Override
+\end{hide}
+\end{code}
+\begin{tabb} Returns a polynomial representing the integral of this polynomial.
+ This integral is of the form
+\[\int p(x)dx = c + c_0x + \frac{c_1 x^2}2 + \cdots + \frac{c_n x^{n+1}}{n+1},
+ \]
+where $c$ is a user-defined constant.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the constant for the integral.}
+ \return{the polynomial representing the integral.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public String toString () {
+ final StringBuilder sb = new StringBuilder ();
+ for (int i = 0; i < coeff.length; i++) {
+ if (i > 0) {
+ if (coeff[i] == 0)
+ continue;
+ else if (coeff[i] > 0)
+ sb.append (" + ");
+ else
+ sb.append (" - ");
+ sb.append (PrintfFormat.format (8, 3, 3, Math.abs (coeff[i])));
+ }
+ else
+ sb.append (PrintfFormat.format (8, 3, 3, coeff[i]));
+ if (i > 0) {
+ sb.append ("*X");
+ if (i > 1)
+ sb.append ("^").append (i);
+ }
+ }
+ return sb.toString ();
+ }
+
+ @Override
+ public Polynomial clone () {
+ Polynomial pol;
+ try {
+ pol = (Polynomial) super.clone ();
+ }
+ catch (final CloneNotSupportedException cne) {
+ throw new IllegalStateException ("Clone not supported");
+ }
+ pol.coeff = coeff.clone ();
+ return pol;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/PowerMathFunction.java b/source/umontreal/iro/lecuyer/functions/PowerMathFunction.java
new file mode 100644
index 0000000..56c37ca
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/PowerMathFunction.java
@@ -0,0 +1,138 @@
+
+
+/*
+ * Class: PowerMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents a function computing
+ * <SPAN CLASS="MATH">(<I>af</I> (<I>x</I>) + <I>b</I>)<SUP>p</SUP></SPAN> for a user-defined function
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> and power <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ */
+public class PowerMathFunction implements MathFunction
+
+,
+ MathFunctionWithFirstDerivative {
+ private MathFunction func;
+ private double a, b;
+ private double power;
+
+
+ /**
+ * Constructs a new power function for function <TT>func</TT> and power
+ * <TT>power</TT>. The values of the constants are <SPAN CLASS="MATH"><I>a</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>b</I> = 0</SPAN>.
+ *
+ * @param func the function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ * @param power the power <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ *
+ */
+ public PowerMathFunction (MathFunction func, double power) {
+ this (func, 1, 0, power);
+ }
+
+
+ /**
+ * Constructs a new power function for function <TT>func</TT>, power
+ * <TT>power</TT>, and constants <TT>a</TT> and <TT>b</TT>.
+ *
+ * @param func the function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ * @param power the power <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @param a the multiplicative constant.
+ *
+ * @param b the additive constant.
+ *
+ *
+ */
+ public PowerMathFunction (MathFunction func, double a, double b, double power) {
+ if (func == null)
+ throw new NullPointerException ();
+ this.func = func;
+ this.a = a;
+ this.b = b;
+ this.power = power;
+ }
+
+
+ /**
+ * Returns the function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ * @return the function.
+ *
+ */
+ public MathFunction getFunction () {
+ return func;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public double getA () {
+ return a;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB () {
+ return b;
+ }
+
+
+ /**
+ * Returns the power <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the power.
+ *
+ */
+ public double getPower () {
+ return power;
+ }
+
+
+ public double derivative (double x) {
+ final double fder = MathFunctionUtil.derivative (func, x);
+ return getA()*getPower()*Math.pow (getA() * func.evaluate (x) + getB(), getPower() - 1)*fder;
+ }
+
+ public double evaluate (double x) {
+ final double v = func.evaluate (x);
+ return Math.pow (a * v + b, power);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/PowerMathFunction.tex b/source/umontreal/iro/lecuyer/functions/PowerMathFunction.tex
new file mode 100644
index 0000000..33c25f4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/PowerMathFunction.tex
@@ -0,0 +1,134 @@
+\defmodule{PowerMathFunction}
+
+Represents a function computing $(af(x) + b)^p$ for a user-defined function
+$f(x)$ and power $p$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PowerMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public class PowerMathFunction implements MathFunction\begin{hide}
+
+,
+ MathFunctionWithFirstDerivative {
+ private MathFunction func;
+ private double a, b;
+ private double power;
+\end{hide}
+
+ public PowerMathFunction (MathFunction func, double power)\begin{hide} {
+ this (func, 1, 0, power);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new power function for function \texttt{func} and power
+ \texttt{power}. The values of the constants are $a=1$ and $b=0$.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function $f(x)$.}
+ \param{power}{the power $p$.}
+\end{htmlonly}
+\begin{code}
+
+ public PowerMathFunction (MathFunction func, double a, double b, double power)\begin{hide} {
+ if (func == null)
+ throw new NullPointerException ();
+ this.func = func;
+ this.a = a;
+ this.b = b;
+ this.power = power;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new power function for function \texttt{func}, power
+ \texttt{power}, and constants \texttt{a} and \texttt{b}.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function $f(x)$.}
+ \param{power}{the power $p$.}
+ \param{a}{the multiplicative constant.}
+ \param{b}{the additive constant.}
+\end{htmlonly}
+\begin{code}
+
+ public MathFunction getFunction ()\begin{hide} {
+ return func;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the function $f(x)$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the function.}
+\end{htmlonly}
+\begin{code}
+
+ public double getA ()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $a$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the value of $a$.}
+\end{htmlonly}
+\begin{code}
+
+ public double getB ()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $b$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the value of $b$.}
+\end{htmlonly}
+\begin{code}
+
+ public double getPower ()\begin{hide} {
+ return power;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the power $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the power.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double derivative (double x) {
+ final double fder = MathFunctionUtil.derivative (func, x);
+ return getA()*getPower()*Math.pow (getA() * func.evaluate (x) + getB(), getPower() - 1)*fder;
+ }
+
+ public double evaluate (double x) {
+ final double v = func.evaluate (x);
+ return Math.pow (a * v + b, power);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/ShiftedMathFunction.java b/source/umontreal/iro/lecuyer/functions/ShiftedMathFunction.java
new file mode 100644
index 0000000..548bdee
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/ShiftedMathFunction.java
@@ -0,0 +1,101 @@
+
+
+/*
+ * Class: ShiftedMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents a function computing
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) - <I>δ</I></SPAN> for a user-defined function
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> and shift <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+public class ShiftedMathFunction implements MathFunction
+
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral {
+ MathFunction func;
+ double delta;
+
+
+ /**
+ * Constructs a new function shifting the function <TT>func</TT> by
+ * a shift <TT>delta</TT>.
+ *
+ * @param func the function.
+ *
+ * @param delta the shift.
+ *
+ *
+ */
+ public ShiftedMathFunction (MathFunction func, double delta) {
+ if (func == null)
+ throw new NullPointerException ();
+ this.func = func;
+ this.delta = delta;
+ }
+
+
+ /**
+ * Returns the function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ * @return the function.
+ *
+ */
+ public MathFunction getFunction () {
+ return func;
+ }
+
+
+ /**
+ * Returns the shift <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT>.
+ *
+ * @return the shift.
+ *
+ */
+ public double getDelta () {
+ return delta;
+ }
+
+
+ public double evaluate (double x) {
+ return func.evaluate (x) - delta;
+ }
+
+ public double derivative (double x) {
+ return MathFunctionUtil.derivative (func, x);
+ }
+
+ public double derivative (double x, int n) {
+ return MathFunctionUtil.derivative (func, x, n);
+ }
+
+ public double integral (double a, double b) {
+ return MathFunctionUtil.integral (func, a, b) - (b - a)*getDelta();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/ShiftedMathFunction.tex b/source/umontreal/iro/lecuyer/functions/ShiftedMathFunction.tex
new file mode 100644
index 0000000..b02690f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/ShiftedMathFunction.tex
@@ -0,0 +1,101 @@
+\defmodule{ShiftedMathFunction}
+
+Represents a function computing $f(x) - \delta$ for a user-defined function
+$f(x)$ and shift $\delta$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ShiftedMathFunction
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public class ShiftedMathFunction implements MathFunction\begin{hide}
+
+,
+ MathFunctionWithFirstDerivative, MathFunctionWithDerivative,
+ MathFunctionWithIntegral {
+ MathFunction func;
+ double delta;
+\end{hide}
+
+ public ShiftedMathFunction (MathFunction func, double delta)\begin{hide} {
+ if (func == null)
+ throw new NullPointerException ();
+ this.func = func;
+ this.delta = delta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new function shifting the function \texttt{func} by
+ a shift \texttt{delta}.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function.}
+ \param{delta}{the shift.}
+\end{htmlonly}
+\begin{code}
+
+ public MathFunction getFunction ()\begin{hide} {
+ return func;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the function $f(x)$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the function.}
+\end{htmlonly}
+\begin{code}
+
+ public double getDelta ()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the shift $\delta$ = \texttt{delta}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the shift.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double evaluate (double x) {
+ return func.evaluate (x) - delta;
+ }
+
+ public double derivative (double x) {
+ return MathFunctionUtil.derivative (func, x);
+ }
+
+ public double derivative (double x, int n) {
+ return MathFunctionUtil.derivative (func, x, n);
+ }
+
+ public double integral (double a, double b) {
+ return MathFunctionUtil.integral (func, a, b) - (b - a)*getDelta();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/SqrtMathFunction.java b/source/umontreal/iro/lecuyer/functions/SqrtMathFunction.java
new file mode 100644
index 0000000..d8fa97f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/SqrtMathFunction.java
@@ -0,0 +1,71 @@
+
+
+/*
+ * Class: SqrtMathFunction
+ * Description: function computing the square root of another function
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+/**
+ * Represents a function computing
+ * the square root of another function
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ */
+public class SqrtMathFunction implements MathFunction {
+ private MathFunction func;
+
+
+ /**
+ * Computes and returns the square
+ * root of the function <TT>func</TT>.
+ *
+ * @param func the function to compute square root for.
+ *
+ *
+ */
+ public SqrtMathFunction (MathFunction func) {
+ super ();
+ if (func == null)
+ throw new NullPointerException();
+ this.func = func;
+ }
+
+
+ /**
+ * Returns the function associated with
+ * this object.
+ *
+ * @return the associated function.
+ *
+ */
+ public MathFunction getFunction() {
+ return func;
+ }
+
+
+ public double evaluate (double x) {
+ return Math.sqrt (func.evaluate (x));
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/SqrtMathFunction.tex b/source/umontreal/iro/lecuyer/functions/SqrtMathFunction.tex
new file mode 100644
index 0000000..82564a4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/SqrtMathFunction.tex
@@ -0,0 +1,73 @@
+\defmodule{SqrtMathFunction}
+
+Represents a function computing
+the square root of another function
+$f(x)$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: SqrtMathFunction
+ * Description: function computing the square root of another function
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+\end{hide}
+
+public class SqrtMathFunction implements MathFunction\begin{hide} {
+ private MathFunction func;
+\end{hide}
+
+ public SqrtMathFunction (MathFunction func)\begin{hide} {
+ super ();
+ if (func == null)
+ throw new NullPointerException();
+ this.func = func;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the square
+ root of the function \texttt{func}.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function to compute square root for.}
+\end{htmlonly}
+\begin{code}
+
+ public MathFunction getFunction()\begin{hide} {
+ return func;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the function associated with
+ this object.
+\end{tabb}
+\begin{htmlonly}
+ \return{the associated function.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double evaluate (double x) {
+ return Math.sqrt (func.evaluate (x));
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/SquareMathFunction.java b/source/umontreal/iro/lecuyer/functions/SquareMathFunction.java
new file mode 100644
index 0000000..8db5cc0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/SquareMathFunction.java
@@ -0,0 +1,122 @@
+
+
+/*
+ * Class: SquareMathFunction
+ * Description: function computing the square of another function
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.functions;
+
+
+
+/**
+ * Represents a function computing
+ *
+ * <SPAN CLASS="MATH">(<I>af</I> (<I>x</I>) + <I>b</I>)<SUP>2</SUP></SPAN> for a user-defined function
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ */
+public class SquareMathFunction implements MathFunctionWithFirstDerivative {
+ private MathFunction func;
+ private double a, b;
+
+
+ /**
+ * Constructs a new square function
+ * for function <TT>func</TT>.
+ * The values of the constants are
+ * <SPAN CLASS="MATH"><I>a</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>b</I> = 0</SPAN>.
+ *
+ * @param func the function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ *
+ */
+ public SquareMathFunction (MathFunction func) {
+ this (func, 1, 0);
+ }
+
+
+ /**
+ * Constructs a new power function
+ * for function <TT>func</TT>, and constants
+ * <TT>a</TT> and <TT>b</TT>.
+ *
+ * @param func the function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ * @param a <
+ * #30#>the multiplicative constant.
+ * @param b the additive constant.
+ *
+ *
+ */
+ public SquareMathFunction (MathFunction func, double a, double b) {
+ if (func == null)
+ throw new NullPointerException();
+ this.func = func;
+ this.a = a;
+ this.b = b;
+ }
+
+
+ /**
+ * Returns the function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ * @return the function.
+ *
+ */
+ public MathFunction getFunction() {
+ return func;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ public double evaluate (double x) {
+ final double v = a*func.evaluate (x) + b;
+ return v*v;
+ }
+
+ public double derivative (double x) {
+ final double fder = MathFunctionUtil.derivative (func, x);
+ return 2*a*(a*func.evaluate (x) + b)*fder;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/functions/SquareMathFunction.tex b/source/umontreal/iro/lecuyer/functions/SquareMathFunction.tex
new file mode 100644
index 0000000..254dc48
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/SquareMathFunction.tex
@@ -0,0 +1,120 @@
+\defmodule{SquareMathFunction}
+
+Represents a function computing
+$(af(x) + b)^2$ for a user-defined function
+$f(x)$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: SquareMathFunction
+ * Description: function computing the square of another function
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.functions;\begin{hide}
+
+\end{hide}
+
+public class SquareMathFunction implements MathFunctionWithFirstDerivative \begin{hide} {
+ private MathFunction func;
+ private double a, b;
+\end{hide}
+
+ public SquareMathFunction (MathFunction func)\begin{hide} {
+ this (func, 1, 0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new square function
+ for function \texttt{func}.
+ The values of the constants are
+ $a=1$ and $b=0$.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function $f(x)$.}
+\end{htmlonly}
+\begin{code}
+
+ public SquareMathFunction (MathFunction func, double a, double b)\begin{hide} {
+ if (func == null)
+ throw new NullPointerException();
+ this.func = func;
+ this.a = a;
+ this.b = b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new power function
+ for function \texttt{func}, and constants
+ \texttt{a} and \texttt{b}.
+\end{tabb}
+\begin{htmlonly}
+ \param{func}{the function $f(x)$.}
+ \param{a}{the multiplicative constant.
+ \param{b}{the additive constant.}
+\end{htmlonly}
+\begin{code}
+
+ public MathFunction getFunction()\begin{hide} {
+ return func;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the function $f(x)$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the function.}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $a$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the value of $a$.}
+\end{htmlonly}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $b$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the value of $b$.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double evaluate (double x) {
+ final double v = a*func.evaluate (x) + b;
+ return v*v;
+ }
+
+ public double derivative (double x) {
+ final double fder = MathFunctionUtil.derivative (func, x);
+ return 2*a*(a*func.evaluate (x) + b)*fder;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/functions/guidefunctions.bbl b/source/umontreal/iro/lecuyer/functions/guidefunctions.bbl
new file mode 100644
index 0000000..e69de29
diff --git a/source/umontreal/iro/lecuyer/functions/guidefunctions.tex b/source/umontreal/iro/lecuyer/functions/guidefunctions.tex
new file mode 100644
index 0000000..7959b7c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/functions/guidefunctions.tex
@@ -0,0 +1,48 @@
+\documentclass[12pt]{article}
+\usepackage{ssj}
+\mytwoheads
+\dateheadtrue
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+\begin{titlepage}
+\title{functions}{Functions utilities}
+
+\vfill
+This package contains a few utilities classes representing univariate
+mathematical functions. They are useful, for example, when one wants to pass
+an arbitrary function of one variable as argument to a method.
+They allow one to apply mathematical operations like squaring, power, etc.
+on generic functions. There are also utilities for numerical
+differentiation and integration.
+\vfill
+\end{titlepage}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+
+\pagenumbering{arabic}
+\include{MathFunction}
+\include{MathFunctionUtil}
+\include{MathFunctionWithFirstDerivative}
+\include{MathFunctionWithDerivative}
+\include{MathFunctionWithIntegral}
+\include{PowerMathFunction}
+\include{ShiftedMathFunction}
+\include{SqrtMathFunction}
+\include{SquareMathFunction}
+\include{AverageMathFunction}
+\include{IdentityMathFunction}
+\include{PiecewiseConstantFunction}
+\include{Polynomial}
+
+\include{MultiFunction}
+
+
+%\bibliographystyle{plain}
+%\bibliography{simul,random,ift,stat,prob,math} % Dans texmac.
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/gof/FBar.java b/source/umontreal/iro/lecuyer/gof/FBar.java
new file mode 100644
index 0000000..17c3a39
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/FBar.java
@@ -0,0 +1,262 @@
+
+
+/*
+ * Class: FBar
+ * Description: Complementary empirical distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.gof;
+
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class is similar to {@link FDist}, except that it provides static methods
+ * to compute or approximate the complementary distribution function of <SPAN CLASS="MATH"><I>X</I></SPAN>,
+ * which we define as
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>, instead of
+ * <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = <I>P</I>[<I>X</I> <= <I>x</I>]</SPAN>.
+ * Note that with our definition of <SPAN CLASS="MATH">bar(F)</SPAN>, one has
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = 1 - <I>F</I>(<I>x</I>)</SPAN> for continuous distributions and
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = 1 - <I>F</I>(<I>x</I> - 1)</SPAN> for discrete distributions over the integers.
+ *
+ */
+public class FBar {
+ private FBar() {}
+
+ private static final double EPSILONSCAN = 1.0E-7;
+
+ private static double scanGlaz (int n, double d, int m) {
+ int j, jmoy;
+ double temp;
+ double jr, jm1r, nr = n;
+ int signe;
+ double q = 1.0 - d;
+ double q4, q3, q2, q1;
+ double bin, binMoy;
+
+ jmoy = (int)((n + 1)*d); // max term of the Binomial
+ if (jmoy < m - 1)
+ jmoy = m - 1;
+
+ /*---------------------------------------------------------
+ * Compute q1: formula (2.5) in Glaz (1989)
+ * Compute q2: formula (A.6) in Berman and Eagleson (1985)
+ * Compute q3, q4 : Theorem (3.2) in Glaz (1989)
+ *---------------------------------------------------------*/
+
+ // compute the probability of term j = jmoy
+ q1 = 0.0;
+ for (j = 1; j <= jmoy; j++) {
+ jr = j;
+ q1 += Math.log (nr - jr + 1.0) - Math.log (jr);
+ }
+ q1 += jmoy*Math.log (d) + (nr - jmoy)*Math.log (q);
+ binMoy = Math.exp (q1);
+ q1 = binMoy;
+ jm1r = jmoy - m + 1;
+ if (((jmoy - m + 1) & 1) != 0)
+ signe = -1;
+ else
+ signe = 1;
+ q2 = signe*binMoy;
+ q3 = signe*binMoy*(2.0 - jm1r*jm1r + jm1r);
+ q4 = signe*binMoy*(jm1r + 1.0)*(jm1r + 2.0)*(6.0 + jm1r*jm1r -
+ 5.0*jm1r);
+
+ // compute the probability of terms j > jmoy
+ if (((jmoy - m + 1) & 1) != 0)
+ signe = -1;
+ else
+ signe = 1;
+
+ jm1r = jmoy - m + 1;
+ bin = binMoy;
+ for (j = jmoy + 1; j <= n; j++) {
+ jr = j;
+ jm1r += 1.0;
+ signe = -signe;
+ bin = (bin*(nr - jr + 1.0)*d)/(jr*q);
+ if (bin < EPSILONSCAN)
+ break;
+ q1 += bin;
+ q2 += signe*bin;
+ q3 += signe*bin*(2.0 - jm1r*jm1r + jm1r);
+ q4 += signe*bin*(jm1r + 1.0)*(jm1r + 2.0)*(6.0 + jm1r*jm1r -
+ 5.0*jm1r);
+ }
+
+ q1 = 1.0 - q1;
+ q3 /= 2.0;
+ q4 /= 12.0;
+ if (m == 3) {
+ // Problem with this formula; I do not get the same results as Glaz
+ q4 = ((nr*(nr - 1.0)*d*d*Math.pow (q, nr - 2.0))/8.0
+ + nr*d*2.0*Math.pow (1.0 - 2.0*d, nr - 1.0))
+ - 4.0*Math.pow (1.0 - 2.0*d, nr);
+ if (d < 1.0/3.0) {
+ q4 += nr*d*2.0*Math.pow (1.0 - 3.0*d, nr - 1.0)
+ + 4.0*Math.pow (1.0 - 3.0*d, nr);
+ }
+ }
+ // compute probability: Glaz, equations (3.2) and (3.3)
+ q3 = q1 - q2 - q3;
+ q4 = q3 - q4;
+ //when the approximation is bad, avoid overflow
+ temp = Math.log (q3) + (nr - m - 2.0)*Math.log (q4/q3);
+ if (temp >= 0.0)
+ return 0.0;
+ if (temp < -30.0)
+ return 1.0;
+ q4 = Math.exp (temp);
+ return 1.0 - q4;
+ }
+
+ //-----------------------------------------------------------------
+
+ private static double scanWNeff (int n, double d, int m) {
+ double q = 1.0 - d;
+ double temp;
+ double bin;
+ double sum;
+ int j;
+
+ /*--------------------------------------
+ * Anderson-Titterington: equation (4)
+ *--------------------------------------*/
+
+ // compute the probability of term j = m
+ sum = 0.0;
+ for (j = 1; j <= m; j++)
+ sum += Math.log ((double)(n - j + 1)) - Math.log ((double)j);
+
+ sum += m*Math.log (d) + (n - m)*Math.log (q);
+ bin = Math.exp (sum);
+ temp = (m/d - n - 1.0)*bin;
+ sum = bin;
+
+ // compute the probability of terms j > m
+ for (j = m + 1; j <= n; j++) {
+ bin *= (n - j + 1)*d/(j*q);
+ if (bin < EPSILONSCAN)
+ break;
+ sum += bin;
+ }
+ sum = 2.0*sum + temp;
+ return sum;
+ }
+
+ //----------------------------------------------------------------
+
+ private static double scanAsympt (int n, double d, int m) {
+ double kappa;
+ double temp;
+ double theta;
+ double sum;
+
+ /*--------------------------------------------------------------
+ * Anderson-Titterington: asymptotic formula after equation (4)
+ *--------------------------------------------------------------*/
+
+ theta = Math.sqrt (d/(1.0 - d));
+ temp = Math.sqrt ((double)n);
+ kappa = m/(d*temp) - temp;
+ temp = theta*kappa;
+ temp = temp*temp/2.0;
+ sum = 2.0*(1.0 - NormalDist.cdf01 (theta*kappa)) +
+ (kappa*theta*Math.exp (-temp))/(d*Math.sqrt (2.0*Math.PI));
+ return sum;
+ }
+
+ /**
+ * Return
+ * <SPAN CLASS="MATH"><I>P</I>[<I>S</I><SUB>N</SUB>(<I>d</I> ) >= <I>m</I>]</SPAN>, where <SPAN CLASS="MATH"><I>S</I><SUB>N</SUB>(<I>d</I> )</SPAN> is the scan
+ * statistic. It is defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>S</I><SUB>N</SUB>(<I>d</I> )= <I>sup</I><SUB>0 <= y <= 1-d</SUB><I>η</I>[<I>y</I>, <I>y</I> + <I>d</I>],
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>d</I></SPAN> is a constant in <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>η</I>[<I>y</I>, <I>y</I> + <I>d</I>]</SPAN> is the number of observations falling inside
+ * the interval <SPAN CLASS="MATH">[<I>y</I>, <I>y</I> + <I>d</I>]</SPAN>, from a sample of <SPAN CLASS="MATH"><I>N</I></SPAN> i.i.d. <SPAN CLASS="MATH"><I>U</I>(0, 1)</SPAN>
+ * random variables.
+ * The approximation returned by this function is generally good when
+ * it is close to 0, but is not very reliable when it exceeds, say, 0.4.
+ *
+ * Restrictions: <SPAN CLASS="MATH"><I>N</I> >= 2</SPAN> and <SPAN CLASS="MATH"><I>d</I> <= 1/2</SPAN>.
+ * <BR>
+ * @param n sample size (<SPAN CLASS="MATH"> >= 2</SPAN>)
+ *
+ * @param d length of the test interval (<SPAN CLASS="MATH">∈(0, 1)</SPAN>)
+ *
+ * @param m scan statistic
+ *
+ * @return the complementary distribution function of the statistic evaluated at <TT>m</TT>
+ */
+ public static double scan (int n, double d, int m) {
+ double mu;
+ double prob;
+
+ if (n < 2)
+ throw new IllegalArgumentException ("Calling scan with n < 2");
+ if (d <= 0.0 || d >= 1.0)
+ throw new IllegalArgumentException ("Calling scan with "+
+ "d outside (0,1)");
+
+ if (m > n)
+ return 0.0;
+ if (m <= 1)
+ return 1.0;
+ if (m <= 2) {
+ if ((n - 1)*d >= 1.0)
+ return 1.0;
+ return 1.0 - Math.pow (1.0 - (n - 1)*d, (double)n);
+ }
+ if (d >= 0.5 && m <= (n + 1)/2.0)
+ return 1.0;
+ if (d > 0.5)
+ return -1.0; // Error
+ // util_Assert (d <= 0.5, "Calling fbar_Scan with d > 1/2");
+
+ mu = n*d; // mean of a binomial
+ if (m <= mu + d)
+ return 1.0;
+ if (mu <= 10.0)
+ return scanGlaz (n, d, m);
+ prob = scanAsympt (n, d, m);
+ if ((d >= 0.3 && n >= 50.0) || (n*d*d >= 250.0 && d < 0.3)) {
+ if (prob <= 0.4)
+ return prob;
+ }
+ prob = scanWNeff (n, d, m);
+ if (prob <= 0.4)
+ return prob;
+ prob = scanGlaz (n, d, m);
+ if (prob > 0.4 && prob <= 1.0)
+ return prob;
+ return 1.0;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/gof/FBar.tex b/source/umontreal/iro/lecuyer/gof/FBar.tex
new file mode 100644
index 0000000..7eca240
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/FBar.tex
@@ -0,0 +1,307 @@
+\defmodule {FBar}
+
+This class is similar to \class{FDist}, except that it provides static methods
+to compute or approximate the complementary distribution function of $X$,
+which we define as $\bar F (x) = P[X\ge x]$, instead of $F (x)=P[X\le x]$.
+Note that with our definition of $\bar F$, one has
+$\bar F (x) = 1 - F (x)$ for continuous distributions and
+$\bar F (x) = 1 - F (x-1)$ for discrete distributions over the integers.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FBar
+ * Description: Complementary empirical distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.gof;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class FBar\begin{hide} {
+ private FBar() {}
+
+ private static final double EPSILONSCAN = 1.0E-7;
+
+ private static double scanGlaz (int n, double d, int m) {
+ int j, jmoy;
+ double temp;
+ double jr, jm1r, nr = n;
+ int signe;
+ double q = 1.0 - d;
+ double q4, q3, q2, q1;
+ double bin, binMoy;
+
+ jmoy = (int)((n + 1)*d); // max term of the Binomial
+ if (jmoy < m - 1)
+ jmoy = m - 1;
+
+ /*---------------------------------------------------------
+ * Compute q1: formula (2.5) in Glaz (1989)
+ * Compute q2: formula (A.6) in Berman and Eagleson (1985)
+ * Compute q3, q4 : Theorem (3.2) in Glaz (1989)
+ *---------------------------------------------------------*/
+
+ // compute the probability of term j = jmoy
+ q1 = 0.0;
+ for (j = 1; j <= jmoy; j++) {
+ jr = j;
+ q1 += Math.log (nr - jr + 1.0) - Math.log (jr);
+ }
+ q1 += jmoy*Math.log (d) + (nr - jmoy)*Math.log (q);
+ binMoy = Math.exp (q1);
+ q1 = binMoy;
+ jm1r = jmoy - m + 1;
+ if (((jmoy - m + 1) & 1) != 0)
+ signe = -1;
+ else
+ signe = 1;
+ q2 = signe*binMoy;
+ q3 = signe*binMoy*(2.0 - jm1r*jm1r + jm1r);
+ q4 = signe*binMoy*(jm1r + 1.0)*(jm1r + 2.0)*(6.0 + jm1r*jm1r -
+ 5.0*jm1r);
+
+ // compute the probability of terms j > jmoy
+ if (((jmoy - m + 1) & 1) != 0)
+ signe = -1;
+ else
+ signe = 1;
+
+ jm1r = jmoy - m + 1;
+ bin = binMoy;
+ for (j = jmoy + 1; j <= n; j++) {
+ jr = j;
+ jm1r += 1.0;
+ signe = -signe;
+ bin = (bin*(nr - jr + 1.0)*d)/(jr*q);
+ if (bin < EPSILONSCAN)
+ break;
+ q1 += bin;
+ q2 += signe*bin;
+ q3 += signe*bin*(2.0 - jm1r*jm1r + jm1r);
+ q4 += signe*bin*(jm1r + 1.0)*(jm1r + 2.0)*(6.0 + jm1r*jm1r -
+ 5.0*jm1r);
+ }
+
+ q1 = 1.0 - q1;
+ q3 /= 2.0;
+ q4 /= 12.0;
+ if (m == 3) {
+ // Problem with this formula; I do not get the same results as Glaz
+ q4 = ((nr*(nr - 1.0)*d*d*Math.pow (q, nr - 2.0))/8.0
+ + nr*d*2.0*Math.pow (1.0 - 2.0*d, nr - 1.0))
+ - 4.0*Math.pow (1.0 - 2.0*d, nr);
+ if (d < 1.0/3.0) {
+ q4 += nr*d*2.0*Math.pow (1.0 - 3.0*d, nr - 1.0)
+ + 4.0*Math.pow (1.0 - 3.0*d, nr);
+ }
+ }
+ // compute probability: Glaz, equations (3.2) and (3.3)
+ q3 = q1 - q2 - q3;
+ q4 = q3 - q4;
+ //when the approximation is bad, avoid overflow
+ temp = Math.log (q3) + (nr - m - 2.0)*Math.log (q4/q3);
+ if (temp >= 0.0)
+ return 0.0;
+ if (temp < -30.0)
+ return 1.0;
+ q4 = Math.exp (temp);
+ return 1.0 - q4;
+ }
+
+ //-----------------------------------------------------------------
+
+ private static double scanWNeff (int n, double d, int m) {
+ double q = 1.0 - d;
+ double temp;
+ double bin;
+ double sum;
+ int j;
+
+ /*--------------------------------------
+ * Anderson-Titterington: equation (4)
+ *--------------------------------------*/
+
+ // compute the probability of term j = m
+ sum = 0.0;
+ for (j = 1; j <= m; j++)
+ sum += Math.log ((double)(n - j + 1)) - Math.log ((double)j);
+
+ sum += m*Math.log (d) + (n - m)*Math.log (q);
+ bin = Math.exp (sum);
+ temp = (m/d - n - 1.0)*bin;
+ sum = bin;
+
+ // compute the probability of terms j > m
+ for (j = m + 1; j <= n; j++) {
+ bin *= (n - j + 1)*d/(j*q);
+ if (bin < EPSILONSCAN)
+ break;
+ sum += bin;
+ }
+ sum = 2.0*sum + temp;
+ return sum;
+ }
+
+ //----------------------------------------------------------------
+
+ private static double scanAsympt (int n, double d, int m) {
+ double kappa;
+ double temp;
+ double theta;
+ double sum;
+
+ /*--------------------------------------------------------------
+ * Anderson-Titterington: asymptotic formula after equation (4)
+ *--------------------------------------------------------------*/
+
+ theta = Math.sqrt (d/(1.0 - d));
+ temp = Math.sqrt ((double)n);
+ kappa = m/(d*temp) - temp;
+ temp = theta*kappa;
+ temp = temp*temp/2.0;
+ sum = 2.0*(1.0 - NormalDist.cdf01 (theta*kappa)) +
+ (kappa*theta*Math.exp (-temp))/(d*Math.sqrt (2.0*Math.PI));
+ return sum;
+ }\end{hide}
+
+ public static double scan (int n, double d, int m)\begin{hide} {
+ double mu;
+ double prob;
+
+ if (n < 2)
+ throw new IllegalArgumentException ("Calling scan with n < 2");
+ if (d <= 0.0 || d >= 1.0)
+ throw new IllegalArgumentException ("Calling scan with "+
+ "d outside (0,1)");
+
+ if (m > n)
+ return 0.0;
+ if (m <= 1)
+ return 1.0;
+ if (m <= 2) {
+ if ((n - 1)*d >= 1.0)
+ return 1.0;
+ return 1.0 - Math.pow (1.0 - (n - 1)*d, (double)n);
+ }
+ if (d >= 0.5 && m <= (n + 1)/2.0)
+ return 1.0;
+ if (d > 0.5)
+ return -1.0; // Error
+ // util_Assert (d <= 0.5, "Calling fbar_Scan with d > 1/2");
+
+ mu = n*d; // mean of a binomial
+ if (m <= mu + d)
+ return 1.0;
+ if (mu <= 10.0)
+ return scanGlaz (n, d, m);
+ prob = scanAsympt (n, d, m);
+ if ((d >= 0.3 && n >= 50.0) || (n*d*d >= 250.0 && d < 0.3)) {
+ if (prob <= 0.4)
+ return prob;
+ }
+ prob = scanWNeff (n, d, m);
+ if (prob <= 0.4)
+ return prob;
+ prob = scanGlaz (n, d, m);
+ if (prob > 0.4 && prob <= 1.0)
+ return prob;
+ return 1.0;
+ }
+}\end{hide}
+\end{code}
+ \begin{tabb} Return $P[S_N (d) \ge m]$, where $S_N (d)$ is the scan
+ statistic\latex{(see \cite{tGLA89a,tGLA01a} and
+ \externalmethod{}{GofStat}{scan}{int,double,int}),}\html{. It is} defined as
+ \eq
+ S_N (d) = \latex{\sup}\html{sup}_{0\le y\le 1-d} \eta[y,\,y+d], \latex{\eqlabel{eq:scan}}
+ \endeq
+ where $d$ is a constant in $(0, 1)$,
+ $\eta[y,\,y+d]$ is the number of observations falling inside
+ the interval $[y, y+d]$, from a sample of $N$ i.i.d.\ $U (0,1)$
+ random variables.
+\begin{latexonly}
+ One has (see \cite {tAND95b}),
+ \begin{eqnarray}
+ P[S_N (d) \ge m]
+ &\approx& \left (\frac{m}{d}-N-1\right) b (m)
+ + 2 \sum_{i=m}^{N} b (i) \eqlabel{DistScan1} \\[6pt]
+ &\approx& 2 (1-\Phi (\theta\kappa)) + \theta\kappa
+ \frac{\exp(-\theta^2\kappa^2 /2)}{d \sqrt{2\pi}}
+ \eqlabel {DistScan2}
+ \end{eqnarray}
+ where $\Phi$ is the standard normal distribution function.
+% (\ref{eq:cdfnormal}),
+ \begin{eqnarray*}
+ b (i) &=& \binom{N}{i} d^i (1-d)^{N-i}, \\[4pt]
+ \theta &=& \sqrt{\frac d{1-d}}, \\[4pt]
+ \kappa &=& \frac m{d \sqrt{N}} - \sqrt{N}.
+ \end{eqnarray*}
+ For $d \le 1/2$, (\ref{DistScan1}) is exact for $m > N/2$,
+ but only an approximation otherwise.
+ The approximation (\ref{DistScan2}) is good when
+ $N d^2$ is large or when $d > 0.3$ and $N>50$.
+ In other cases, this implementation sometimes use the approximation
+ proposed by Glaz \cite{tGLA89a}.
+ For more information, see \cite {tAND95b,tGLA89a,tWAL87a}.
+\end{latexonly}
+ The approximation returned by this function is generally good when
+ it is close to 0, but is not very reliable when it exceeds, say, 0.4.
+\begin{detailed} %%%%%%
+ If $m \le (N + 1)d$, the method returns 1.
+ Else, if $Nd \le 10$, it returns the approximation given by
+ Glaz \cite{tGLA89a}.
+ If $Nd > 10$, it computes (\ref{DistScan2}) or (\ref{DistScan1})
+ and returns the result if it does not exceed 0.4, otherwise it computes
+ the approximation from \cite{tGLA89a}, returns it if it is less than 1.0,
+% inside the interval $(0.4, 1.0)$,
+ and returns 1.0 otherwise.
+ \hpierre{Even if it 0.40001 in the first approximation, and 0.3999
+ in the second one?}
+ \hrichard{C'est ce qui est programm\'e. Probablement que dans ce cas,
+ l'approximation est tellement mauvaise que nous avons d\'ecid\'e de
+ retourner 1. On pourrait retourner 0 au lieu??}
+ \hpierre {\c Ca n'a pas de bon sens. Je ne vois pas pourquoi elle serait
+ ``tellement mauvaise'' dans le cas cit\'e avec 0.3999 et subitement
+ bonne si la seconde approximation retourne 0.4.
+ Il me semble que dans un tel cas on devrait retourner quelque chose
+ aux alentours de 0.4.
+ Et ce qui est g\^enant c'est qu'en retournant 0 ou 1 comme p-valeur,
+ on croira que l'hypoth\`ese nulle est rejet\'ee! }
+ The relative error can
+ reach 10\%\ when $Nd \le 10$ or when the returned value
+ is less than 0.4. For $m > Nd$ and $Nd > 10$, a returned value
+ that exceeds $0.4$ should be regarded as unreliable.
+ For $m = 3$, the returned values are totally unreliable.
+ (There may be an error in the original formulae in \cite{tGLA89a}).
+\end{detailed} %%%%
+ Restrictions: $N \ge 2$ and $d \le 1/2$.\\
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{sample size ($\ge 2$)}
+ \param{d}{length of the test interval ($\in(0,1)$)}
+ \param{m}{scan statistic}
+ \return{the complementary distribution function of the statistic evaluated at \texttt{m}}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/gof/FDist.java b/source/umontreal/iro/lecuyer/gof/FDist.java
new file mode 100644
index 0000000..1db6a96
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/FDist.java
@@ -0,0 +1,162 @@
+
+
+/*
+ * Class: FDist
+ * Description: Empirical distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.gof;
+
+
+/**
+ * This class provides methods to compute (or approximate)
+ * the distribution functions of special types of
+ * goodness-of-fit test statistics.
+ *
+ */
+public class FDist {
+ private FDist() {}
+
+
+ /**
+ * Similar to
+ * {@link umontreal.iro.lecuyer.probdist.KolmogorovSmirnovPlusDist KolmogorovSmirnovPlusDist}
+ * but for the case where the distribution function <SPAN CLASS="MATH"><I>F</I></SPAN> has a jump of size
+ * <SPAN CLASS="MATH"><I>a</I></SPAN> at a given point <SPAN CLASS="MATH"><I>x</I><SUB>0</SUB></SPAN>, is zero at the left of <SPAN CLASS="MATH"><I>x</I><SUB>0</SUB></SPAN>,
+ * and is continuous at the right of <SPAN CLASS="MATH"><I>x</I><SUB>0</SUB></SPAN>.
+ * Restriction: <SPAN CLASS="MATH">0 < <I>a</I> < 1</SPAN>.
+ *
+ * @param N sample size
+ *
+ * @param a size of the jump
+ *
+ * @param x positive or negative Kolmogorov-Smirnov statistic
+ *
+ * @return the distribution function of the statistic evaluated at <TT>x</TT>
+ *
+ */
+ public static double kolmogorovSmirnovPlusJumpOne (int N, double a,
+ double x) {
+ final double EPSILONLR = 1.E-15;
+ final double EPSILON = 1.0E-290;
+ final double NXAPARAM = 6.5; // frontier: alternating series
+ double LogCom;
+ double q, p1, q1;
+ double Sum = 0.0;
+ double term;
+ double Njreal;
+ double jreal;
+ int Sign;
+ int j;
+ int jmax;
+
+ if (N < 1)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlusJumpOne "+
+ "with N < 1");
+ if (a >= 1.0 || a <= 0.0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlusJumpOne "+
+ "with a outside (0, 1)");
+ if (x <= 0.0)
+ return 0.0;
+ if (x + a >= 1.0)
+ return 1.0;
+ LogCom = Math.log ((double)N);
+
+ //--------------------------------------------------------------------
+ // the alternating series is stable and fast for N*(x + a) very small
+ //--------------------------------------------------------------------
+ if (N*(x + a) < NXAPARAM && a + x < 0.5) {
+ jmax = (int)(N*(x + a));
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = N - j;
+ q = jreal/N - x;
+ if ((q < 0.0 && ((j & 1) != 0)) ||
+ ((q > 1.0) && (((N - j - 1) & 1) != 0)))
+ Sign = -1;
+ else
+ Sign = 1;
+
+ // we must avoid log (0.0)
+ q1 = Math.abs (q);
+ p1 = Math.abs (1.0 - q);
+ if (q1 > EPSILON && p1 > EPSILON) {
+ term = LogCom + jreal*Math.log (q1) +
+ (Njreal - 1.0)*Math.log (p1);
+ Sum += Sign*Math.exp (term);
+ }
+ LogCom += Math.log (Njreal/(jreal + 1.0));
+ }
+ // add the term j = 0
+ Sum += Math.exp ((N - 1)*Math.log (1.0 + x));
+ return Sum*x;
+ }
+
+ //---------------------------------------------
+ // For N (x + a) >= NxaParam or (a + x) > 0.5,
+ // use the non-alternating series.
+ //---------------------------------------------
+
+ // EpsilonLR because the distribution has a jump
+ jmax = (int)(N*(1.0 - a - x - EPSILONLR));
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = N - jreal;
+ q = jreal/N + x;
+ if (1.0 - q > EPSILON) {
+ term = LogCom + (jreal - 1.0)*Math.log (q) + Njreal*Math.log (1.0 - q);
+ Sum += Math.exp (term);
+ }
+ LogCom += Math.log (Njreal/(jreal + 1.0));
+ }
+ Sum *= x;
+
+ // add the term j = 0
+ if (1.0 - x > EPSILON)
+ Sum += Math.exp (N*Math.log (1.0 - x));
+ return 1.0 - Sum;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>F</I>(<I>m</I>)</SPAN>, the distribution function of the scan statistic
+ * with parameters <SPAN CLASS="MATH"><I>N</I></SPAN> and <SPAN CLASS="MATH"><I>d</I></SPAN>, evaluated at <SPAN CLASS="MATH"><I>m</I></SPAN>.
+ * For a description of this statistic and its distribution, see
+ * {@link FBar#scan(int,double,int) scan},
+ * which computes its complementary distribution
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>m</I>) = 1 - <I>F</I>(<I>m</I> - 1)</SPAN>.
+ *
+ * @param N sample size (<SPAN CLASS="MATH"> >= 2</SPAN>)
+ *
+ * @param d length of the test interval (<SPAN CLASS="MATH">∈(0, 1)</SPAN>)
+ *
+ * @param m scan statistic
+ *
+ * @return the distribution function of the statistic evaluated at <TT>m</TT>
+ */
+ public static double scan (int N, double d, int m) {
+ return 1.0 - FBar.scan (N, d, m);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/gof/FDist.tex b/source/umontreal/iro/lecuyer/gof/FDist.tex
new file mode 100644
index 0000000..42ad22f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/FDist.tex
@@ -0,0 +1,212 @@
+\defmodule {FDist}
+
+This class provides methods to compute (or approximate)
+the distribution functions of special types of
+goodness-of-fit test statistics.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FDist
+ * Description: Empirical distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.gof;
+
+
+public class FDist\begin{hide} {
+ private FDist() {}
+\end{hide}
+
+ public static double kolmogorovSmirnovPlusJumpOne (int N, double a,
+ double x)\begin{hide} {
+ final double EPSILONLR = 1.E-15;
+ final double EPSILON = 1.0E-290;
+ final double NXAPARAM = 6.5; // frontier: alternating series
+ double LogCom;
+ double q, p1, q1;
+ double Sum = 0.0;
+ double term;
+ double Njreal;
+ double jreal;
+ int Sign;
+ int j;
+ int jmax;
+
+ if (N < 1)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlusJumpOne "+
+ "with N < 1");
+ if (a >= 1.0 || a <= 0.0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlusJumpOne "+
+ "with a outside (0, 1)");
+ if (x <= 0.0)
+ return 0.0;
+ if (x + a >= 1.0)
+ return 1.0;
+ LogCom = Math.log ((double)N);
+
+ //--------------------------------------------------------------------
+ // the alternating series is stable and fast for N*(x + a) very small
+ //--------------------------------------------------------------------
+ if (N*(x + a) < NXAPARAM && a + x < 0.5) {
+ jmax = (int)(N*(x + a));
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = N - j;
+ q = jreal/N - x;
+ if ((q < 0.0 && ((j & 1) != 0)) ||
+ ((q > 1.0) && (((N - j - 1) & 1) != 0)))
+ Sign = -1;
+ else
+ Sign = 1;
+
+ // we must avoid log (0.0)
+ q1 = Math.abs (q);
+ p1 = Math.abs (1.0 - q);
+ if (q1 > EPSILON && p1 > EPSILON) {
+ term = LogCom + jreal*Math.log (q1) +
+ (Njreal - 1.0)*Math.log (p1);
+ Sum += Sign*Math.exp (term);
+ }
+ LogCom += Math.log (Njreal/(jreal + 1.0));
+ }
+ // add the term j = 0
+ Sum += Math.exp ((N - 1)*Math.log (1.0 + x));
+ return Sum*x;
+ }
+
+ //---------------------------------------------
+ // For N (x + a) >= NxaParam or (a + x) > 0.5,
+ // use the non-alternating series.
+ //---------------------------------------------
+
+ // EpsilonLR because the distribution has a jump
+ jmax = (int)(N*(1.0 - a - x - EPSILONLR));
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = N - jreal;
+ q = jreal/N + x;
+ if (1.0 - q > EPSILON) {
+ term = LogCom + (jreal - 1.0)*Math.log (q) + Njreal*Math.log (1.0 - q);
+ Sum += Math.exp (term);
+ }
+ LogCom += Math.log (Njreal/(jreal + 1.0));
+ }
+ Sum *= x;
+
+ // add the term j = 0
+ if (1.0 - x > EPSILON)
+ Sum += Math.exp (N*Math.log (1.0 - x));
+ return 1.0 - Sum;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to
+\externalclass{umontreal.iro.lecuyer.probdist}{KolmogorovSmirnovPlusDist}
+ but for the case where the distribution function $F$ has a jump of size
+ $a$ at a given point $x_0$, is zero at the left of $x_0$,
+ and is continuous at the right of $x_0$.
+\begin{latexonly}
+ The Kolmogorov-Smirnov statistic is defined in that case as
+ \eq
+ D_N^+(a) = \sup_{a\le u\le 1}
+ \left (\hat{F}_N (F^{-1}(u)) - u\right)
+ = \max_{\rule{0pt}{7pt} \lfloor 1+aN \le j \le N}
+ \left (j/N - F (V_{(j)})\right).
+ \eqlabel{eq:KSPlusJumpOne}
+ \endeq
+\iffalse %%%%%
+ and
+ \eq
+ D_N^-(a) = \sup_{a\le u\le 1} \left (u - \hat F_N (F^{-1}(u))\right)
+ = \max_{\rule{0pt}{7pt} \lfloor 1+aN\rfloor \le j \le N}
+ \left (F (V_{(j)})-(j-1)/N\right),
+ \endeq
+ \pierre {It seems that $D_N^-(a)$ has a {\em different\/} distribution
+ function. }
+\fi %%%%
+ where $V_{(1)},\dots,V_{(N)}$ are the observations sorted by increasing
+ order. The method returns an approximation of
+ $P[D_N^+(a) \le x]$ computed via
+ \begin{eqnarray}
+ P[D_N^+(a) \le x]
+ &=& 1 - x \sum_{i=0}^{\lfloor N (1-a-x)\rfloor}
+ \binom{N}{i}
+ \left (\frac{i}{N} + x \right)^{i-1}
+ \left (1 - \frac{i}{N} - x \right)^{N-i}.
+ \eqlabel{DistKSJ1} \\[6pt]
+ &=& x \sum_{j=0}^{\lfloor N (a+x) \rfloor}
+ \binom{N}{j}
+ \left (\frac{j}{N} - x \right)^j
+ \left (1 - \frac{j}{N} + x \right)^{N-j-1}.
+ \eqlabel{DistKSJ2}
+ \end{eqnarray}
+% La fonction de r\'epartition est la m\^eme pour ---> WRONG!
+% \eq
+% D_N^-(a) = \sup_{a\le u\le 1} (u - \hat F (F^{-1}(u)))
+% = \max_{\lfloor 1+aN\rfloor \le j \le N} (F (T_{(j)})-(j-1)/N),
+% \endeq
+% et aussi lorsque le supremum est sur $0\le u \le 1-a$ au lieu de
+% $a\le u\le 1$.
+ \hpierre{R\'ef\'erences pour ces formules?}
+ The current implementation uses formula (\ref{DistKSJ2})
+ when $N (x+a) < 6.5$ and $x+a < 0.5$, and uses (\ref{DistKSJ1})
+ when $Nx \ge 6.5$ or $x+a \ge 0.5$.
+\end{latexonly}
+ Restriction: $0 < a < 1$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{N}{sample size}
+ \param{a}{size of the jump}
+ \param{x}{positive or negative Kolmogorov-Smirnov statistic}
+ \return{the distribution function of the statistic evaluated at \texttt{x}}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection*{Discrete distributions}
+
+\begin{code}
+
+ public static double scan (int N, double d, int m)\begin{hide} {
+ return 1.0 - FBar.scan (N, d, m);
+ }
+}\end{hide}
+\end{code}
+ \begin{tabb} Returns $F (m)$, the distribution function of the scan statistic
+ with parameters $N$ and $d$, evaluated at $m$.
+ For a description of this statistic and its distribution, see
+ \externalmethod{}{FBar}{scan}{int,double,int},
+ which computes its complementary distribution
+ $\bar F (m) = 1 - F (m-1)$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{N}{sample size ($\ge 2$)}
+ \param{d}{length of the test interval ($\in(0,1)$)}
+ \param{m}{scan statistic}
+ \return{the distribution function of the statistic evaluated at \texttt{m}}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/gof/GofFormat.java b/source/umontreal/iro/lecuyer/gof/GofFormat.java
new file mode 100644
index 0000000..4667cb3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/GofFormat.java
@@ -0,0 +1,1170 @@
+
+
+/*
+ * Class: GofFormat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.gof;
+ import cern.colt.list.*;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.probdist.*;
+import java.io.PrintWriter;
+
+/**
+ * This class contains methods used to format results of GOF
+ * test statistics, or to apply a series of tests
+ * simultaneously and format the results.
+ * It is in fact a translation from C to Java of a set of functions that
+ * were specially written for the implementation of TestU01, a software
+ * package for testing uniform random number generators.
+ *
+ * <P>
+ * Strictly speaking, applying several tests simultaneously makes the
+ * <SPAN CLASS="MATH"><I>p</I></SPAN>-values ``invalid'' in the sense that the probability of having
+ * <EM>at least one</EM> <SPAN CLASS="MATH"><I>p</I></SPAN>-value less than 0.01, say, is larger than 0.01.
+ * One must therefore be careful with the interpretation of these
+ * <SPAN CLASS="MATH"><I>p</I></SPAN>-values (one could use, e.g., the Bonferroni inequality).
+ * Applying simultaneous tests is convenient in some situations, such as in
+ * screening experiments for detecting statistical deficiencies
+ * in random number generators. In that context, rejection of the null
+ * hypothesis typically occurs with extremely small <SPAN CLASS="MATH"><I>p</I></SPAN>-values (e.g., less
+ * than <SPAN CLASS="MATH">10<SUP>-15</SUP></SPAN>), and the interpretation is quite obvious in this case.
+ *
+ * <P>
+ * The class also provides tools to plot an empirical or
+ * theoretical distribution function, by creating a data file that
+ * contains a graphic plot in a format compatible with the software
+ * specified by the environment variable {@link #graphSoft graphSoft}.
+ * NOTE: see also the more recent package
+ * {@link umontreal.iro.lecuyer.charts charts}.
+ *
+ * <P>
+ * Note: This class uses the Colt library.
+ *
+ */
+public class GofFormat {
+ private GofFormat() {}
+
+
+ /**
+ * Data file format used for plotting functions with Gnuplot.
+ *
+ */
+ public static final int GNUPLOT = 0;
+
+
+ /**
+ * Data file format used for creating graphics with Mathematica.
+ *
+ */
+ public static final int MATHEMATICA = 1;
+
+
+ /**
+ * Environment variable that selects the type of software to be
+ * used for plotting the graphs of functions.
+ * The data files produced by {@link #graphFunc graphFunc} and
+ * {@link #graphDistUnif graphDistUnif} will be in a format suitable
+ * for this selected software.
+ * The default value is <TT>GNUPLOT</TT>.
+ * To display a graphic in file <TT>f</TT> using <TT>gnuplot</TT>, for example,
+ * one can use the command ``<TT>plot f with steps, x with lines</TT>''
+ * in <TT>gnuplot</TT>.
+ * <TT>graphSoft</TT> can take the values {@link #GNUPLOT GNUPLOT} or {@link #MATHEMATICA MATHEMATICA}.
+ *
+ */
+ public static int graphSoft = GNUPLOT;
+
+
+ private static String formatMath2 (double x, double y) {
+ // Writes the pair (x, y) in file f, in a format understood
+ // by Mathematica
+ StringBuffer sb = new StringBuffer();
+ String S;
+
+ sb.append (" { ");
+ if ((x != 0.0) && (x < 0.1 || x > 1.0)) {
+ S = PrintfFormat.E (16, 7, x);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, x);
+
+ sb.append (S + ", ");
+
+ if (y != 0.0 && (y < 0.1 || y > 1.0)) {
+ S = PrintfFormat.E (16, 7, y);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, y);
+
+ sb.append (S + " }");
+ return sb.toString();
+ }
+
+
+ private static String graphFunc (ContinuousDistribution dist, double a,
+ double b, int m, int mono, String desc) {
+// Renommer drawCDF en fixant mono = 1 et éliminant mono.
+ int i;
+ double yprec, y, x, h;
+ StringBuffer sb = new StringBuffer();
+ String openComment = "";
+ String closeComment = "";
+ String openGraph = "";
+ String closeGraph = "";
+ if (mono != 1 && mono != -1)
+ throw new IllegalArgumentException ("mono must be 1 or -1");
+ switch (graphSoft) {
+ case GNUPLOT:
+ openComment = "# ";
+ closeComment = "";
+ openGraph = "";
+ closeGraph = PrintfFormat.NEWLINE;
+ break;
+ case MATHEMATICA:
+ openComment = "(* ";
+ closeComment = " *)";
+ openGraph = "points = { " + PrintfFormat.NEWLINE;
+ closeGraph = "}" + PrintfFormat.NEWLINE;
+ break;
+ }
+
+ sb.append (openComment + "----------------------------------" +
+ closeComment + PrintfFormat.NEWLINE);
+ sb.append (openComment + PrintfFormat.s (-70, desc)
+ + closeComment + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ sb.append (openGraph);
+ h = (b - a) / m;
+ if (mono == 1)
+ yprec = -Double.MAX_VALUE;
+ else if (mono == -1)
+ yprec = Double.MAX_VALUE;
+ else
+ yprec = 0.0;
+
+ for (i = 0; i <= m; i++) {
+ x = a + i*h;
+ y = mono == 1 ? dist.cdf (x) : dist.barF (x);
+ switch (graphSoft) {
+ case MATHEMATICA:
+ sb.append (formatMath2 (x, y));
+ if (i < m)
+ sb.append (',');
+ break;
+ default: // Default and GNUPLOT
+ sb.append (PrintfFormat.g (20, 14, x) + " " +
+ PrintfFormat.g (20, 14, y));
+ }
+
+ switch (mono) {
+ case 1:
+ if (y < yprec)
+ sb.append (" " + openComment +
+ " DECREASING" + closeComment);
+ break;
+ case -1:
+ if (y > yprec)
+ sb.append (" " + openComment +
+ " INCREASING" + closeComment);
+ break;
+ default:
+ break;
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ yprec = y;
+ }
+ sb.append (closeGraph);
+ return sb.toString();
+ }
+
+
+ /**
+ * Formats data to plot the graph of the distribution function <SPAN CLASS="MATH"><I>F</I></SPAN> over the
+ * interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>, and returns the result as a {@link String}.
+ * The method <TT>dist.cdf(x)</TT> returns the value of <SPAN CLASS="MATH"><I>F</I></SPAN> at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ * The {@link String} <TT>desc</TT> gives a short caption for the graphic plot.
+ * The method computes the <SPAN CLASS="MATH"><I>m</I> + 1</SPAN> points
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>F</I>(<I>x</I><SUB>i</SUB>))</SPAN>,
+ * where
+ * <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB> = <I>a</I> + <I>i</I>(<I>b</I> - <I>a</I>)/<I>m</I></SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I></SPAN>, and formats these points
+ * into a <TT>String</TT> in a format suitable for the
+ * software specified by {@link #graphSoft graphSoft}.
+ * NOTE: see also the more recent class
+ * {@link umontreal.iro.lecuyer.charts.ContinuousDistChart ContinuousDistChart}.
+ *
+ * @param dist continuous distribution function to plot
+ *
+ * @param a lower bound of the interval to plot
+ *
+ * @param b upper bound of the interval to plot
+ *
+ * @param m number of points in the plot minus one
+ *
+ * @param desc short caption describing the plot
+ *
+ * @return a string representation of the plot data
+ *
+ */
+ public static String drawCdf (ContinuousDistribution dist, double a,
+ double b, int m, String desc) {
+ return graphFunc (dist, a, b, m, 1, desc);
+ }
+
+
+ /**
+ * Formats data to plot the graph of the density <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>,
+ * and returns the result as a {@link String}. The method
+ * <TT>dist.density(x)</TT> returns the value of <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ * The {@link String} <TT>desc</TT> gives a short caption for the graphic
+ * plot. The method computes the <SPAN CLASS="MATH"><I>m</I> + 1</SPAN> points
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>i</SUB>, <I>f</I> (<I>x</I><SUB>i</SUB>))</SPAN>,
+ * where
+ * <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB> = <I>a</I> + <I>i</I>(<I>b</I> - <I>a</I>)/<I>m</I></SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I></SPAN>, and formats these points
+ * into a <TT>String</TT> in a format suitable for the
+ * software specified by {@link #graphSoft graphSoft}.
+ * NOTE: see also the more recent class
+ * {@link umontreal.iro.lecuyer.charts.ContinuousDistChart ContinuousDistChart}.
+ *
+ * @param dist continuous density function to plot
+ *
+ * @param a lower bound of the interval to plot
+ *
+ * @param b upper bound of the interval to plot
+ *
+ * @param m number of points in the plot minus one
+ *
+ * @param desc short caption describing the plot
+ *
+ * @return a string representation of the plot data
+ *
+ */
+ public static String drawDensity (ContinuousDistribution dist, double a,
+ double b, int m, String desc) {
+ int i;
+ double y, x, h;
+ StringBuffer sb = new StringBuffer();
+ String openComment = "";
+ String closeComment = "";
+ String openGraph = "";
+ String closeGraph = "";
+
+ switch (graphSoft) {
+ case GNUPLOT:
+ openComment = "# ";
+ closeComment = "";
+ openGraph = "";
+ closeGraph = PrintfFormat.NEWLINE;
+ break;
+ case MATHEMATICA:
+ openComment = "(* ";
+ closeComment = " *)";
+ openGraph = "points = { " + PrintfFormat.NEWLINE;
+ closeGraph = "}" + PrintfFormat.NEWLINE;
+ break;
+ }
+
+ sb.append (openComment + "----------------------------------" +
+ closeComment + PrintfFormat.NEWLINE);
+ sb.append (openComment + PrintfFormat.s (-70, desc)
+ + closeComment + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ sb.append (openGraph);
+ h = (b - a) / m;
+
+ for (i = 0; i <= m; i++) {
+ x = a + i*h;
+ y = dist.density (x);
+
+ switch (graphSoft) {
+ case MATHEMATICA:
+ sb.append (formatMath2 (x, y));
+ if (i < m)
+ sb.append (',');
+ break;
+ default: // Default and GNUPLOT
+ sb.append (PrintfFormat.g (16, 8, x) + " " +
+ PrintfFormat.g (16, 8, y));
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append (closeGraph);
+ return sb.toString();
+ }
+
+
+ /**
+ * Formats data to plot the empirical distribution of
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(1)</SUB>,..., <I>U</I><SUB>(N)</SUB></SPAN>, which are assumed to be in <TT>data[0...N-1]</TT>,
+ * and to compare it with the uniform distribution. The <SPAN CLASS="MATH"><I>U</I><SUB>(i)</SUB></SPAN> must be sorted.
+ * The two endpoints <SPAN CLASS="MATH">(0, 0)</SPAN> and <SPAN CLASS="MATH">(1, 1)</SPAN> are always included in the plot.
+ * The string <TT>desc</TT> gives a short caption for the graphic plot.
+ * The data is printed in a format suitable for the
+ * software specified by {@link #graphSoft graphSoft}.
+ * NOTE: see also the more recent class
+ * {@link umontreal.iro.lecuyer.charts.EmpiricalChart EmpiricalChart}.
+ *
+ * @param data array of observations to plot
+ *
+ * @param desc short caption describing the plot
+ *
+ * @return a string representation of the plot data
+ *
+ */
+ public static String graphDistUnif (DoubleArrayList data, String desc) {
+ double[] u = data.elements();
+ int n = data.size();
+ int i;
+ double unSurN = 1.0/n;
+ StringBuffer sb = new StringBuffer();
+
+ switch (graphSoft) {
+ case GNUPLOT:
+ sb.append ("#----------------------------------" +
+ PrintfFormat.NEWLINE);
+ sb.append ("# " + PrintfFormat.s (-70, desc) +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ sb.append (PrintfFormat.g (16, 8, 0.0) + " " +
+ PrintfFormat.g (16, 8, 0.0) + PrintfFormat.NEWLINE);
+ for (i = 0; i < n; i++)
+ sb.append (PrintfFormat.g (16, 8, u[i]) + " " +
+ PrintfFormat.g (16, 8, (i + 1)*unSurN) +
+ PrintfFormat.NEWLINE);
+
+ sb.append (PrintfFormat.g (16, 8, 1.0) + " " +
+ PrintfFormat.g (16, 8, 1.0) + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+ break;
+ case MATHEMATICA:
+ sb.append ("(*----------------------------------*)" +
+ PrintfFormat.NEWLINE);
+ sb.append ("(* " + PrintfFormat.s (-70, desc) +
+ PrintfFormat.NEWLINE + " *)" +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE +
+ "points = { " + PrintfFormat.NEWLINE);
+
+ sb.append (formatMath2 (0.0, 0.0) + "," + PrintfFormat.NEWLINE);
+ for (i = 0; i < n; i++)
+ sb.append (formatMath2 (u[i], (i + 1)*unSurN) + "," +
+ PrintfFormat.NEWLINE);
+ sb.append (formatMath2 (1.0, 1.0) + PrintfFormat.NEWLINE);
+ break;
+ default:
+ throw new IllegalArgumentException ("graphSoft unknown");
+ }
+ return sb.toString();
+ }
+
+
+
+ /**
+ * Environment variable used in {@link #formatp0 formatp0} to determine
+ * which <SPAN CLASS="MATH"><I>p</I></SPAN>-values are too close to 0 or 1 to be printed explicitly.
+ * If <TT>EPSILONP</TT>
+ * <SPAN CLASS="MATH">= <I>ε</I></SPAN>, then any <SPAN CLASS="MATH"><I>p</I></SPAN>-value
+ * less than <SPAN CLASS="MATH"><I>ε</I></SPAN> or larger than
+ *
+ * <SPAN CLASS="MATH">1 - <I>ε</I></SPAN> is <EM>not</EM> written explicitly;
+ * the program simply writes ``<TT>eps</TT>'' or ``<TT>1-eps</TT>''.
+ * The default value is <SPAN CLASS="MATH">10<SUP>-15</SUP></SPAN>.
+ *
+ */
+ public static double EPSILONP = 1.0E-15;
+
+
+ /**
+ * Environment variable used in {@link #formatp1 formatp1} to determine
+ * which <SPAN CLASS="MATH"><I>p</I></SPAN>-values should be marked as suspect when printing test results.
+ * If <TT>SUSPECTP</TT> <SPAN CLASS="MATH">= <I>α</I></SPAN>, then any <SPAN CLASS="MATH"><I>p</I></SPAN>-value
+ * less than <SPAN CLASS="MATH"><I>α</I></SPAN> or larger than
+ * <SPAN CLASS="MATH">1 - <I>α</I></SPAN> is considered suspect and is
+ * ``singled out'' by <TT>formatp1</TT>.
+ * The default value is 0.01.
+ *
+ */
+ public static double SUSPECTP = 0.01;
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>p</I></SPAN>-value <SPAN CLASS="MATH"><I>p</I></SPAN> of a test,
+ * in the format ``<SPAN CLASS="MATH">1 - <I>p</I></SPAN>'' if <SPAN CLASS="MATH"><I>p</I></SPAN> is close to 1, and <SPAN CLASS="MATH"><I>p</I></SPAN> otherwise.
+ * Uses the environment variable {@link #EPSILONP EPSILONP} and replaces <SPAN CLASS="MATH"><I>p</I></SPAN>
+ * by <SPAN CLASS="MATH"><I>ε</I></SPAN> when it is too small.
+ *
+ * @param p the <SPAN CLASS="MATH"><I>p</I></SPAN>-value to be formated
+ *
+ * @return the string representation of the <SPAN CLASS="MATH"><I>p</I></SPAN>-value
+ *
+ */
+ public static String formatp0 (double p) {
+ // Formats the p-value of a test, without a descriptor
+ if ((p >= 0.01) && (p <= 0.99))
+ return PrintfFormat.format (8, 2, 1, p);
+ else if (p < EPSILONP)
+ return " eps ";
+ else if (p < 0.01)
+ return PrintfFormat.format (8, 2, 2, p);
+ else if (p >= 1.0 - EPSILONP)
+ return " 1 - eps ";
+ else
+ return " 1 - " + PrintfFormat.g (8, 2, 1.0 - p);
+ }
+
+
+ /**
+ * Returns the string ``<TT>p-value of test : </TT>'',
+ * then calls {@link #formatp0 formatp0} to print <SPAN CLASS="MATH"><I>p</I></SPAN>, and adds
+ * the marker ``<TT>****</TT>'' if <SPAN CLASS="MATH"><I>p</I></SPAN> is considered suspect
+ * (uses the environment variable <TT>SUSPECTP</TT> for this).
+ *
+ * @param p the <SPAN CLASS="MATH"><I>p</I></SPAN>-value to be formated
+ *
+ * @return the string representation of the p-value of test
+ *
+ */
+ public static String formatp1 (double p) {
+ // Prints the p-value of a test, with a descriptor.
+ StringBuffer sb = new StringBuffer();
+ sb.append ("p-value of test :" + formatp0 (p));
+ if (p < SUSPECTP || p > 1.0 - SUSPECTP)
+ sb.append (" *****");
+
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+
+ /**
+ * Returns <TT>x</TT> on a single line, then go to the next line
+ * and calls {@link #formatp1 formatp1}.
+ *
+ * @param x value of the statistic for which the p-value is formated
+ *
+ * @param p the <SPAN CLASS="MATH"><I>p</I></SPAN>-value to be formated
+ *
+ * @return the string representation of the p-value of test
+ *
+ */
+ public static String formatp2 (double x, double p) {
+ // Prints the statistic x and its p-value p.
+ return PrintfFormat.format (8, 2, 1, x) + PrintfFormat.NEWLINE +
+ formatp1 (p);
+ }
+
+
+ /**
+ * Formats the test statistic <TT>x</TT> for a test named <TT>testName</TT>
+ * with <SPAN CLASS="MATH"><I>p</I></SPAN>-value <TT>p</TT>. The first line of the returned string contains
+ * the name of the test and the statistic whereas the second line contains
+ * its p-value. The formated values of <TT>x</TT> and <TT>p</TT> are
+ * aligned.
+ *
+ * @param testName name of the test that was performed
+ *
+ * @param x value of the test statistic
+ *
+ * @param p <SPAN CLASS="MATH"><I>p</I></SPAN>-value of the test
+ *
+ * @return the string representation of the test result
+ *
+ */
+ public static String formatp3 (String testName, double x, double p) {
+ final String SLT = "p-value of test";
+ int l = Math.max (SLT.length(), testName.length());
+ PrintfFormat pf = new PrintfFormat();
+ pf.append (-l, testName).append (" : ").append (8, 2, 1, x).append
+ (PrintfFormat.NEWLINE);
+ pf.append (-l, SLT).append (" : ").append (formatp0 (p));
+ if (p < SUSPECTP || p > 1.0 - SUSPECTP)
+ pf.append (" *****");
+ pf.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return pf.toString();
+ }
+
+
+ /**
+ * Computes the <SPAN CLASS="MATH"><I>p</I></SPAN>-value of the chi-square statistic
+ * <TT>chi2</TT> for a test with <TT>k</TT> intervals. Uses <SPAN CLASS="MATH"><I>d</I></SPAN> decimal digits
+ * of precision in the calculations. The result of the
+ * test is returned as a string. The <SPAN CLASS="MATH"><I>p</I></SPAN>-value is computed using
+ * {@link GofStat#pDisc pDisc}.
+ *
+ * @param k number of subintervals for the chi-square test
+ *
+ * @param chi2 chi-square statistic
+ *
+ * @return the string representation of the test result and <SPAN CLASS="MATH"><I>p</I></SPAN>-value
+ *
+ */
+ public static String formatChi2 (int k, int d, double chi2) {
+ StringBuffer sb = new StringBuffer();
+ sb.append ("Chi2 statistic : " +
+ PrintfFormat.format (8, 2, 1, chi2));
+ sb.append (PrintfFormat.NEWLINE +
+ "p-value : " +
+ formatp0 (GofStat.pDisc
+ (ChiSquareDist.cdf (k - 1, d, chi2),
+ ChiSquareDist.barF (k - 1, d, chi2))));
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+
+ /**
+ * Computes the <SPAN CLASS="MATH"><I>p</I></SPAN>-values of the three Kolmogorov-Smirnov statistics
+ * <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>-</SUP></SPAN>, and <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB></SPAN>, whose values are in <TT>dp, dm, d</TT>,
+ * respectively, assuming a sample of size <TT>n</TT>.
+ * Then formats these statistics and their <SPAN CLASS="MATH"><I>p</I></SPAN>-values
+ * using {@link #formatp2 formatp2} for each one.
+ *
+ * @param n sample size
+ *
+ * @param dp value of the <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>+</SUP></SPAN> statistic
+ *
+ * @param dm value of the <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>-</SUP></SPAN> statistic
+ *
+ * @param d value of the <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB></SPAN> statistic
+ *
+ * @return the string representation of the Kolmogorov-Smirnov statistics and their
+ * p-values
+ *
+ */
+ public static String formatKS (int n, double dp,
+ double dm, double d) {
+ // Prints the results of a Kolmogorov-Smirnov test
+ return "Kolmogorov-Smirnov+ statistic = D+ :" +
+ formatp2 (dp, KolmogorovSmirnovPlusDist.barF (n, dp)) +
+ "Kolmogorov-Smirnov- statistic = D- :" +
+ formatp2 (dm, KolmogorovSmirnovPlusDist.barF (n, dm)) +
+ "Kolmogorov-Smirnov statistic = D :" +
+ formatp2 (d, KolmogorovSmirnovDistQuick.barF (n, d)) +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE;
+ }
+
+
+ /**
+ * Computes the KS test statistics to compare the
+ * empirical distribution of the observations in <TT>data</TT>
+ * with the theoretical distribution <TT>dist</TT> and
+ * formats the results. See also method
+ * {@link umontreal.iro.lecuyer.gof.GofStat#kolmogorovSmirnov GofStat.kolmogorovSmirnov}<TT>(double[],ContinuousDistribution,double[],double[])</TT>.
+ *
+ * @param data array of observations to be tested
+ *
+ * @param dist assumed distribution of the observations
+ *
+ * @return the string representation of the Kolmogorov-Smirnov statistics and their
+ * p-values
+ *
+ */
+ public static String formatKS (DoubleArrayList data,
+ ContinuousDistribution dist) {
+
+ double[] v = data.elements();
+ int n = data.size();
+
+ DoubleArrayList dataUnif = GofStat.unifTransform (data, dist);
+ dataUnif.quickSortFromTo (0, dataUnif.size() - 1);
+ double[] ret = GofStat.kolmogorovSmirnov (dataUnif);
+ return formatKS (n, ret[0], ret[1], ret[2]);
+ }
+
+
+ /**
+ * Similar to {@link #formatKS(int,double,double,double) formatKS},
+ * but for the KS statistic <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>+</SUP>(<I>a</I>)</SPAN>.
+ * Writes a header,
+ * computes the <SPAN CLASS="MATH"><I>p</I></SPAN>-value and calls {@link #formatp2 formatp2}.
+ *
+ * @param n sample size
+ *
+ * @param a size of the jump
+ *
+ * @param dp value of <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>+</SUP>(<I>a</I>)</SPAN>
+ *
+ * @return the string representation of the Kolmogorov-Smirnov statistic and its p-value
+ *
+ */
+ public static String formatKSJumpOne (int n, double a, double dp) {
+ double d = 1.0 - FDist.kolmogorovSmirnovPlusJumpOne (n, a, dp);
+
+ return PrintfFormat.NEWLINE +
+ "Kolmogorov-Smirnov+ statistic = D+ : " +
+ PrintfFormat.g (8, 2, dp) + PrintfFormat.NEWLINE +
+ formatp1 (d) + PrintfFormat.NEWLINE;
+ }
+
+
+ /**
+ * Similar to {@link #formatKS(DoubleArrayList,ContinuousDistribution) formatKS},
+ * but for <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>+</SUP>(<I>a</I>)</SPAN>.
+ *
+ * @param data array of observations to be tested
+ *
+ * @param dist assumed distribution of the data
+ *
+ * @param a size of the jump
+ *
+ * @return string representation of the Kolmogorov-Smirnov statistic and its p-value
+ *
+ */
+ public static String formatKSJumpOne (DoubleArrayList data,
+ ContinuousDistribution dist,
+ double a) {
+
+ double[] v = data.elements();
+ int n = data.size();
+ DoubleArrayList dataUnif = GofStat.unifTransform (data, dist);
+ dataUnif.quickSortFromTo (0, dataUnif.size() - 1);
+ double[] ret = GofStat.kolmogorovSmirnovJumpOne (dataUnif, a);
+ return formatKSJumpOne (n, a, ret[0]);
+ }
+
+
+ /**
+ * Kolmogorov-Smirnov+ test
+ *
+ */
+ public static final int KSP = 0;
+
+
+ /**
+ * Kolmogorov-Smirnov<SPAN CLASS="MATH">-</SPAN> test
+ *
+ */
+ public static final int KSM = 1;
+
+
+ /**
+ * Kolmogorov-Smirnov test
+ *
+ */
+ public static final int KS = 2;
+
+
+ /**
+ * Anderson-Darling test
+ *
+ */
+ public static final int AD = 3;
+
+
+ /**
+ * Cramér-von Mises test
+ *
+ */
+ public static final int CM = 4;
+
+
+ /**
+ * Watson G test
+ *
+ */
+ public static final int WG = 5;
+
+
+ /**
+ * Watson U test
+ *
+ */
+ public static final int WU = 6;
+
+
+ /**
+ * Mean
+ *
+ */
+ public static final int MEAN = 7;
+
+
+ /**
+ * Correlation
+ *
+ */
+ public static final int COR = 8;
+
+
+ /**
+ * Total number of test types
+ *
+ */
+ public static final int NTESTTYPES = 9;
+
+
+ /**
+ * Name of each <TT>testType</TT> test.
+ * Could be used for printing the test results, for example.
+ *
+ */
+ public static final String[] TESTNAMES = {
+ "KolmogorovSmirnovPlus", "KolmogorovSmirnovMinus",
+ "KolmogorovSmirnov", "Anderson-Darling",
+ "CramerVon-Mises", "Watson G", "Watson U",
+ "Mean", "Correlation"
+ };
+
+
+ /**
+ * The set of EDF tests that are to be performed when calling
+ * the methods {@link #activeTests activeTests}, {@link #formatActiveTests formatActiveTests}, etc.
+ * By default, this set contains <TT>KSP</TT>, <TT>KSM</TT>,
+ * and <TT>AD</TT>. Note: <TT>MEAN</TT> and <TT>COR</TT> are <EM>always excluded</EM>
+ * from this set of active tests.
+ * The valid indices for this array are {@link #KSP KSP}, {@link #KSM KSM},
+ * {@link #KS KS}, {@link #AD AD}, {@link #CM CM}, {@link #WG WG},
+ * {@link #WU WU}, {@link #MEAN MEAN}, and {@link #COR COR}.
+ *
+ */
+ public static boolean[] activeTests = null;
+ private static void initActiveTests() {
+ activeTests = new boolean[NTESTTYPES];
+ for (int i = 0; i < activeTests.length; i++)
+ activeTests[i] = false;
+ activeTests[KSP] = activeTests[KSM] = true;
+ activeTests[AD] = activeTests[MEAN] = activeTests[COR] = true;
+ }
+ static {
+ initActiveTests();
+ }
+
+
+ /**
+ * Computes all EDF test statistics
+ * to compare the empirical
+ * distribution of
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(N-1)</SUB></SPAN> with the uniform distribution,
+ * assuming that these sorted observations are in <TT>sortedData</TT>.
+ * If <SPAN CLASS="MATH"><I>N</I> > 1</SPAN>, returns <TT>sVal</TT> with the values of the KS
+ * statistics <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB><SUP>-</SUP></SPAN> and <SPAN CLASS="MATH"><I>D</I><SUB>N</SUB></SPAN>, of the Cramér-von Mises
+ * statistic <SPAN CLASS="MATH"><I>W</I><SUB>N</SUB><SUP>2</SUP></SPAN>, Watson's <SPAN CLASS="MATH"><I>G</I><SUB>N</SUB></SPAN> and <SPAN CLASS="MATH"><I>U</I><SUB>N</SUB><SUP>2</SUP></SPAN>, Anderson-Darling's
+ * <SPAN CLASS="MATH"><I>A</I><SUB>N</SUB><SUP>2</SUP></SPAN>, and the average of the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN>'s, respectively.
+ * If <SPAN CLASS="MATH"><I>N</I> = 1</SPAN>, only puts <SPAN CLASS="MATH">1 -</SPAN><TT>sortedData.get (0)</TT> in <TT>sVal[KSP]</TT>.
+ * Calling this method is more efficient than computing these statistics
+ * separately by calling the corresponding methods in {@link GofStat}.
+ *
+ * @param sortedData array of sorted observations
+ *
+ * @param sVal array that will be filled with the results of the tests
+ *
+ *
+ */
+ public static void tests (DoubleArrayList sortedData, double[] sVal) {
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ int i;
+ double a2 = 0.0, w2, dm = 0.0, dp = 0.0, w;
+ double u1, ui, d2, d1;
+ double sumZ;
+ double unSurN;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (sVal.length != NTESTTYPES)
+ throw new IllegalArgumentException ("sVal must " +
+ "be of size NTESTTYPES.");
+
+ // We assume that u is already sorted.
+ if (n == 1) {
+ sVal[KSP] = 1.0 - u[0];
+ sVal[MEAN] = u[0];
+ return;
+ }
+ unSurN = 1.0 / n;
+ w2 = unSurN / 12.0;
+ sumZ = 0.0;
+ for (i = 0; i < n; i++) {
+ // Statistics KS
+ d1 = u[i] - i*unSurN;
+ d2 = (i + 1)*unSurN - u[i];
+ if (d1 > dm)
+ dm = d1;
+ if (d2 > dp)
+ dp = d2;
+ // Watson U and G
+ sumZ += u[i];
+ w = u[i] - (i + 0.5)*unSurN;
+ w2 += w*w;
+ // Anderson-Darling
+ ui = u[i];
+ u1 = 1.0 - ui;
+ if (ui < GofStat.EPSILONAD)
+ ui = GofStat.EPSILONAD;
+ else if (u1 < GofStat.EPSILONAD)
+ u1 = GofStat.EPSILONAD;
+ a2 += (2*i + 1) * Math.log (ui) + (1 + 2*(n - i - 1))*Math.log (u1);
+ }
+ if (dm > dp)
+ sVal[KS] = dm;
+ else
+ sVal[KS] = dp;
+ sVal[KSM] = dm;
+ sVal[KSP] = dp;
+ sumZ = sumZ * unSurN - 0.5;
+ sVal[CM] = w2;
+ sVal[WG] = Math.sqrt ((double) n) * (dp + sumZ);
+ sVal[WU] = w2 - sumZ * sumZ * n;
+ sVal[AD] = -n - a2 * unSurN;
+ sVal[MEAN] = sumZ + 0.5; // Nouveau ...
+ }
+
+
+ /**
+ * The observations <SPAN CLASS="MATH"><I>V</I></SPAN> are in <TT>data</TT>,
+ * not necessarily sorted, and their empirical
+ * distribution is compared with the continuous distribution <TT>dist</TT>.
+ *
+ * If <SPAN CLASS="MATH"><I>N</I> = 1</SPAN>, only puts <TT>data.get (0)</TT> in <TT>sVal[MEAN]</TT>,
+ * and <SPAN CLASS="MATH">1 -</SPAN><TT>dist.cdf (data.get (0))</TT> in <TT>sVal[KSP]</TT>.
+ *
+ * @param data array of observations to test
+ *
+ * @param dist assumed distribution of the observations
+ *
+ * @param sVal array that will be filled with the results of the tests
+ *
+ *
+ */
+ public static void tests (DoubleArrayList data,
+ ContinuousDistribution dist, double[] sVal) {
+
+ double[] v = data.elements();
+ int n = data.size();
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ DoubleArrayList sortedData = GofStat.unifTransform (data, dist);
+ sortedData.quickSortFromTo (0, sortedData.size()-1);
+ tests (sortedData, sVal);
+ if (n == 1)
+ sVal[MEAN] = v[0]; // On veut v[0], pas u[0].
+ }
+
+
+ /**
+ * Computes the EDF test statistics by calling
+ * {@link #tests(DoubleArrayList,double[]) tests}, then computes the <SPAN CLASS="MATH"><I>p</I></SPAN>-values of those
+ * that currently belong to <TT>activeTests</TT>,
+ * and return these quantities in <TT>sVal</TT> and <TT>pVal</TT>, respectively.
+ * Assumes that
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(N-1)</SUB></SPAN> are in <TT>sortedData</TT>
+ * and that we want to compare their empirical distribution
+ * with the uniform distribution.
+ * If <SPAN CLASS="MATH"><I>N</I> = 1</SPAN>, only puts <SPAN CLASS="MATH">1 -</SPAN><TT>sortedData.get (0)</TT> in
+ * <TT>sVal[KSP], pVal[KSP]</TT>, and <TT>pVal[MEAN]</TT>.
+ *
+ * @param sortedData array of sorted observations
+ *
+ * @param sVal array that will be filled with the results of the tests
+ *
+ * @param pVal array that will be filled with the <SPAN CLASS="MATH"><I>p</I></SPAN>-values
+ *
+ *
+ */
+ public static void activeTests (DoubleArrayList sortedData,
+ double[] sVal, double[] pVal) {
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ if (sVal.length != NTESTTYPES || pVal.length != NTESTTYPES)
+ throw new IllegalArgumentException ("sVal and pVal must " +
+ "be of length NTESTTYPES.");
+
+ if (n == 1) {
+ sVal[KSP] = 1.0 - u[0];
+ pVal[KSP] = 1.0 - u[0];
+ pVal[MEAN] = pVal[KSP];
+ return;
+ }
+ // We assume that u is already sorted.
+ tests (sortedData, sVal);
+
+ if (activeTests.length != NTESTTYPES) {
+ initActiveTests();
+ System.err.println ("activeTests was invalid, it was reinitialized.");
+ }
+
+ if (activeTests[KSP])
+ pVal[KSP] = KolmogorovSmirnovPlusDist.barF (n, sVal[KSP]);
+
+ if (activeTests[KSM])
+ pVal[KSM] = KolmogorovSmirnovPlusDist.barF (n, sVal[KSM]);
+
+ if (activeTests[KS])
+ pVal[KS] = KolmogorovSmirnovDistQuick.barF (n, sVal[KS]);
+
+ if (activeTests[AD])
+ pVal[AD] = AndersonDarlingDistQuick.barF (n, sVal[AD]);
+
+ if (activeTests[CM])
+ pVal[CM] = CramerVonMisesDist.barF (n, sVal[CM]);
+
+ if (activeTests[WG])
+ pVal[WG] = WatsonGDist.barF (n, sVal[WG]);
+
+ if (activeTests[WU])
+ pVal[WU] = WatsonUDist.barF (n, sVal[WU]);
+ }
+
+
+ /**
+ * The observations are in <TT>data</TT>,
+ * not necessarily sorted, and we want to compare their empirical
+ * distribution with the distribution <TT>dist</TT>.
+ * If <SPAN CLASS="MATH"><I>N</I> = 1</SPAN>, only puts <TT>data.get(0)</TT> in <TT>sVal[MEAN]</TT>,
+ * and <SPAN CLASS="MATH">1 -</SPAN><TT>dist.cdf (data.get (0))</TT> in <TT>sVal[KSP], pVal[KSP]</TT>,
+ * and <TT>pVal[MEAN]</TT>.
+ *
+ * @param data array of observations to test
+ *
+ * @param dist assumed distribution of the observations
+ *
+ * @param sVal array that will be filled with the results of the tests
+ *
+ * @param pVal array that will be filled with the <SPAN CLASS="MATH"><I>p</I></SPAN>-values
+ *
+ *
+ */
+ public static void activeTests (DoubleArrayList data,
+ ContinuousDistribution dist,
+ double[] sVal, double[] pVal) {
+ double[] v = data.elements();
+ int n = data.size();
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ DoubleArrayList sortedData = GofStat.unifTransform (data, dist);
+ sortedData.quickSortFromTo (0, sortedData.size() - 1);
+
+ activeTests (sortedData, sVal, pVal);
+ if (n == 1)
+ sVal[MEAN] = v[0];
+ }
+
+
+ /**
+ * Gets the <SPAN CLASS="MATH"><I>p</I></SPAN>-values of the <EM>active</EM> EDF test statistics,
+ * which are in <TT>activeTests</TT>. It is assumed that the values
+ * of these statistics and their <SPAN CLASS="MATH"><I>p</I></SPAN>-values are <EM>already computed</EM>,
+ * in <TT>sVal</TT> and <TT>pVal</TT>, and that the sample size is <TT>n</TT>.
+ * These statistics and <SPAN CLASS="MATH"><I>p</I></SPAN>-values are formated
+ * using {@link #formatp2 formatp2} for each one.
+ * If <TT>n=1</TT>, prints only <TT>pVal[KSP]</TT> using {@link #formatp1 formatp1}.
+ *
+ * @param n sample size
+ *
+ * @param sVal array containing the results of the tests
+ *
+ * @param pVal array containing the <SPAN CLASS="MATH"><I>p</I></SPAN>-values
+ *
+ * @return the results formated as a string
+ *
+ */
+ public static String formatActiveTests (int n, double[] sVal,
+ double[] pVal) {
+
+ if (activeTests.length != NTESTTYPES) {
+ initActiveTests();
+ System.err.println ("activeTests was invalid, it was reinitialized.");
+ }
+ if (sVal.length != NTESTTYPES || pVal.length != NTESTTYPES)
+ throw new IllegalArgumentException ("The length of " +
+ "sVal and pVal must be NTESTTYPES.");
+ if (n == 1)
+ return formatp1 (pVal[KSP]);
+
+ StringBuffer sb = new StringBuffer (PrintfFormat.NEWLINE);
+ if (activeTests[KSP])
+ sb.append ("Kolmogorov-Smirnov+ statistic = D+ :" +
+ formatp2 (sVal[KSP], pVal[KSP]));
+ if (activeTests[KSM])
+ sb.append ("Kolmogorov-Smirnov- statistic = D- :" +
+ formatp2 (sVal[KSM], pVal[KSM]));
+ if (activeTests[KS])
+ sb.append ("Kolmogorov-Smirnov statistic = D :" +
+ formatp2 (sVal[KS], pVal[KS]));
+ if (activeTests[AD])
+ sb.append ("Anderson-Darling statistic = A2 :" +
+ formatp2 (sVal[AD], pVal[AD]));
+ if (activeTests[CM])
+ sb.append ("Cramer-von Mises statistic = W2 :" +
+ formatp2 (sVal[CM], pVal[CM]));
+ if (activeTests[WG])
+ sb.append ("Watson statistic = G :" +
+ formatp2 (sVal[WG], pVal[WG]));
+ if (activeTests[WU])
+ sb.append ("Watson statistic = U2 :" +
+ formatp2 (sVal[WU], pVal[WU]));
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+
+ /**
+ * Repeats the following <TT>k</TT> times:
+ * Applies the {@link GofStat#iterateSpacings GofStat.iterateSpacings}
+ * transformation to the
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(N-1)</SUB></SPAN>, assuming that these observations are in
+ * <TT>sortedData</TT>, then computes the EDF test statistics and calls
+ * {@link #activeTests(DoubleArrayList,double[],double[]) activeTests} after each transformation.
+ * The function returns the <EM>original</EM> array <TT>sortedData</TT> (the
+ * transformations are applied on a copy of <TT>sortedData</TT>).
+ * If <TT>printval = true</TT>, stores all the values into the returned
+ * {@link String} after each iteration.
+ * If <TT>graph = true</TT>, calls {@link #graphDistUnif graphDistUnif} after each iteration
+ * to print to stream <TT>f</TT> the data for plotting the distribution
+ * function of the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN>.
+ *
+ * @param sortedData array containing the sorted observations
+ *
+ * @param k number of times the tests are applied
+ *
+ * @param printval if <TT>true</TT>, stores all the values of the observations at each iteration
+ *
+ * @param graph if <TT>true</TT>, the distribution of the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> will be plotted after each
+ * iteration
+ *
+ * @param f stream where the plots are written to
+ *
+ * @return a string representation of the test results
+ *
+ */
+ public static String iterSpacingsTests (DoubleArrayList sortedData, int k,
+ boolean printval, boolean graph,
+ PrintWriter f) {
+
+ int n = sortedData.size();
+
+ DoubleArrayList sortedDataCopy = (DoubleArrayList)sortedData.clone();
+ DoubleArrayList diffArrayList = new DoubleArrayList(sortedData.size()+2);
+
+ int j;
+ int i;
+ double[] sVal = new double[NTESTTYPES], pVal = new double[NTESTTYPES];
+
+ StringBuffer sb = new StringBuffer (PrintfFormat.NEWLINE);
+
+ for (j = 1; j <= k; j++) {
+ sb.append ("-----------------------------------" +
+ PrintfFormat.NEWLINE +
+ "EDF Tests after \"iterateSpacings\", level : " +
+ PrintfFormat.d (2, j) + PrintfFormat.NEWLINE);
+
+ GofStat.diff (sortedDataCopy, diffArrayList, 0, n - 1, 0.0, 1.0);
+ GofStat.iterateSpacings (sortedDataCopy, diffArrayList);
+ sortedDataCopy.quickSortFromTo (0, sortedDataCopy.size() - 1);
+ activeTests (sortedDataCopy, sVal, pVal);
+
+ sb.append (formatActiveTests (n, sVal, pVal));
+ String desc = "Values of Uniforms after iterateSpacings, level " +
+ PrintfFormat.d (2, j);
+ if (printval) {
+ sb.append (desc + PrintfFormat.NEWLINE +
+ "------------------------" + PrintfFormat.NEWLINE);
+ sb.append (sortedDataCopy + PrintfFormat.NEWLINE);
+ }
+ if (graph && f != null)
+ f.print (graphDistUnif (sortedDataCopy, desc));
+ else if (graph && f == null)
+ sb.append (graphDistUnif (sortedDataCopy, desc));
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Similar to {@link #iterSpacingsTests iterSpacingsTests}, but with the
+ * {@link GofStat#powerRatios GofStat.powerRatios} transformation.
+ *
+ * @param sortedData array containing the sorted observations
+ *
+ * @param k number of times the tests are applied
+ *
+ * @param printval if <TT>true</TT>, stores all the values of the observations at each iteration
+ *
+ * @param graph if <TT>true</TT>, the distribution of the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> will be plotted after each
+ * iteration
+ *
+ * @param f stream where the plots are written to
+ *
+ * @return a string representation of the test results
+ */
+ public static String iterPowRatioTests (DoubleArrayList sortedData, int k,
+ boolean printval, boolean graph,
+ PrintWriter f) {
+
+ int n = sortedData.size();
+ DoubleArrayList sortedDataCopy = (DoubleArrayList)sortedData.clone();
+
+ int i;
+ int j;
+ double[] sVal = new double[NTESTTYPES], pVal = new double[NTESTTYPES];
+
+ StringBuffer sb = new StringBuffer (PrintfFormat.NEWLINE);
+
+ for (i = 1; i <= k; i++) {
+ GofStat.powerRatios (sortedDataCopy);
+ sb.append ("-----------------------------------" +
+ PrintfFormat.NEWLINE +
+ "EDF Tests after \"powerRatios\", level : " +
+ PrintfFormat.d (2, i) + PrintfFormat.NEWLINE);
+
+ sortedDataCopy.quickSortFromTo (0, sortedDataCopy.size() - 1);
+
+ activeTests (sortedDataCopy, sVal, pVal);
+ sb.append (formatActiveTests (n, sVal, pVal));
+ String desc = "Values of Uniforms after PowerRatios, level " +
+ PrintfFormat.d (2, i);
+ if (printval) {
+ sb.append (desc + PrintfFormat.NEWLINE +
+ "--------------------------" + PrintfFormat.NEWLINE);
+ sb.append (sortedDataCopy + PrintfFormat.NEWLINE);
+ }
+ if (graph && f != null)
+ f.print (graphDistUnif (sortedDataCopy, desc));
+ else if (graph && f == null)
+ sb.append (graphDistUnif (sortedDataCopy, desc));
+ }
+ return sb.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/gof/GofFormat.tex b/source/umontreal/iro/lecuyer/gof/GofFormat.tex
new file mode 100644
index 0000000..dea9938
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/GofFormat.tex
@@ -0,0 +1,1195 @@
+\defmodule {GofFormat}
+
+% \textbf{THIS CLASS WILL BE REWRITTEN AND SIMPLIFIED IN THE FUTURE.}
+This class contains methods used to format results of GOF
+test statistics, or to apply a series of tests
+simultaneously and format the results.
+It is in fact a translation from C to Java of a set of functions that
+were specially written for the implementation of TestU01, a software
+package for testing uniform random number generators \cite{iLEC01t}.
+
+Strictly speaking, applying several tests simultaneously makes the
+$p$-values ``invalid'' in the sense that the probability of having
+{\em at least one\/} $p$-value less than 0.01, say, is larger than 0.01.
+One must therefore be careful with the interpretation of these
+$p$-values (one could use, e.g., the Bonferroni inequality \cite{sLAW00a}).
+Applying simultaneous tests is convenient in some situations, such as in
+screening experiments for detecting statistical deficiencies
+in random number generators. In that context, rejection of the null
+hypothesis typically occurs with extremely small $p$-values (e.g., less
+than $10^{-15}$), and the interpretation is quite obvious in this case.
+
+The class also provides tools to plot an empirical or
+theoretical distribution function, by creating a data file that
+contains a graphic plot in a format compatible with the software
+specified by the environment variable \method{graphSoft}{}.
+ NOTE: see also the more recent package
+ \externalclass{umontreal.iro.lecuyer}{charts}.
+
+Note: This class uses the Colt library.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: GofFormat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.gof;
+ import cern.colt.list.*;
+\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.probdist.*;
+import java.io.PrintWriter;\end{hide}
+
+public class GofFormat\begin{hide} {
+ private GofFormat() {} \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Plotting distribution functions}
+
+\begin{code}
+
+ public static final int GNUPLOT\begin{hide} = 0; \end{hide}
+\end{code}
+ \begin{tabb} Data file format used for plotting functions with Gnuplot.
+ \end{tabb}
+\begin{code}
+
+ public static final int MATHEMATICA\begin{hide} = 1; \end{hide}
+\end{code}
+ \begin{tabb} Data file format used for creating graphics with Mathematica.
+ \end{tabb}
+\begin{code}
+
+ public static int graphSoft = GNUPLOT;
+\end{code}
+ \begin{tabb} Environment variable that selects the type of software to be
+ used for plotting the graphs of functions.
+ The data files produced by \method{graphFunc}{} and
+ \method{graphDistUnif}{} will be in a format suitable
+ for this selected software.
+ The default value is \texttt{GNUPLOT}.
+ To display a graphic in file \texttt{f} using \texttt{gnuplot}, for example,
+ one can use the command ``\texttt{plot f with steps, x with lines}''
+ in \texttt{gnuplot}.
+\begin{htmlonly}
+ \texttt{graphSoft} can take the values \method{GNUPLOT}{} or \method{MATHEMATICA}{}.
+\end{htmlonly}
+ \end{tabb}
+\begin{code}
+\begin{hide}
+ private static String formatMath2 (double x, double y) {
+ // Writes the pair (x, y) in file f, in a format understood
+ // by Mathematica
+ StringBuffer sb = new StringBuffer();
+ String S;
+
+ sb.append (" { ");
+ if ((x != 0.0) && (x < 0.1 || x > 1.0)) {
+ S = PrintfFormat.E (16, 7, x);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, x);
+
+ sb.append (S + ", ");
+
+ if (y != 0.0 && (y < 0.1 || y > 1.0)) {
+ S = PrintfFormat.E (16, 7, y);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.g (16, 8, y);
+
+ sb.append (S + " }");
+ return sb.toString();
+ }
+
+
+ private static String graphFunc (ContinuousDistribution dist, double a,
+ double b, int m, int mono, String desc) {
+// Renommer drawCDF en fixant mono = 1 et éliminant mono.
+ int i;
+ double yprec, y, x, h;
+ StringBuffer sb = new StringBuffer();
+ String openComment = "";
+ String closeComment = "";
+ String openGraph = "";
+ String closeGraph = "";
+ if (mono != 1 && mono != -1)
+ throw new IllegalArgumentException ("mono must be 1 or -1");
+ switch (graphSoft) {
+ case GNUPLOT:
+ openComment = "# ";
+ closeComment = "";
+ openGraph = "";
+ closeGraph = PrintfFormat.NEWLINE;
+ break;
+ case MATHEMATICA:
+ openComment = "(* ";
+ closeComment = " *)";
+ openGraph = "points = { " + PrintfFormat.NEWLINE;
+ closeGraph = "}" + PrintfFormat.NEWLINE;
+ break;
+ }
+
+ sb.append (openComment + "----------------------------------" +
+ closeComment + PrintfFormat.NEWLINE);
+ sb.append (openComment + PrintfFormat.s (-70, desc)
+ + closeComment + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ sb.append (openGraph);
+ h = (b - a) / m;
+ if (mono == 1)
+ yprec = -Double.MAX_VALUE;
+ else if (mono == -1)
+ yprec = Double.MAX_VALUE;
+ else
+ yprec = 0.0;
+
+ for (i = 0; i <= m; i++) {
+ x = a + i*h;
+ y = mono == 1 ? dist.cdf (x) : dist.barF (x);
+ switch (graphSoft) {
+ case MATHEMATICA:
+ sb.append (formatMath2 (x, y));
+ if (i < m)
+ sb.append (',');
+ break;
+ default: // Default and GNUPLOT
+ sb.append (PrintfFormat.g (20, 14, x) + " " +
+ PrintfFormat.g (20, 14, y));
+ }
+
+ switch (mono) {
+ case 1:
+ if (y < yprec)
+ sb.append (" " + openComment +
+ " DECREASING" + closeComment);
+ break;
+ case -1:
+ if (y > yprec)
+ sb.append (" " + openComment +
+ " INCREASING" + closeComment);
+ break;
+ default:
+ break;
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ yprec = y;
+ }
+ sb.append (closeGraph);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+% \begin{tabb}
+% Use \method{drawCdf}{} instead.
+% Formats data to plot the graph of the distribution function $F$ (or $\bar F$)
+% over the interval $[a,b]$, and returns the result as a \class{String}.
+% \texttt{dist.cdf(x)} (or \texttt{dist.barF(x)}) returns the value of $F$
+% (or $\bar F$) at $x$,
+% and that $F$ is either non-decreasing or non-increasing.
+% If \texttt{mono} = 1, the method will verify that $F$ is non-decreasing;
+% if \texttt{mono} = $-1$, it will verify that $\bar{F}$ is non-increasing.
+% (This is useful to verify if $F$ is effectively a sensible
+% approximation to a distribution function or its complementary
+% in the given interval.)
+% The \class{String} \texttt{desc} gives a short caption for the graphic plot.
+% The method computes the $m+1$ points $(x_i,\, F (x_i))$,
+% where $x_i = a + i (b-a)/m$ for $i=0,1,\ldots,m$, and formats these points
+% into a \texttt{String} in a format suitable for the
+% software specified by \method{graphSoft}{}.
+% \end{tabb}
+% \begin{htmlonly}
+% \param{dist}{continuous distribution function to plot}
+% \param{a}{lower bound of the interval to plot}
+% \param{b}{upper bound of the interval to plot}
+% \param{m}{number of points in the plot minus one}
+% \param{mono}{1 for plotting a distribution function, -1 if for a complementary
+% distribution function}
+% \param{desc}{short caption describing the plot}
+% \return{a string representation of the plot data}
+% \end{htmlonly}
+\begin{code}
+
+ public static String drawCdf (ContinuousDistribution dist, double a,
+ double b, int m, String desc)\begin{hide} {
+ return graphFunc (dist, a, b, m, 1, desc);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Formats data to plot the graph of the distribution function $F$ over the
+ interval $[a,b]$, and returns the result as a \class{String}.
+ The method \texttt{dist.cdf(x)} returns the value of $F$ at $x$.
+% Checks that $F$ is non-decreasing.
+% If \texttt{mono} = 1, the method will verify that $F$ is non-decreasing;
+% (This is useful to verify if $F$ is effectively a sensible
+% approximation to a distribution function in the given interval.)
+ The \class{String} \texttt{desc} gives a short caption for the graphic plot.
+ The method computes the $m+1$ points $(x_i,\, F (x_i))$,
+ where $x_i = a + i (b-a)/m$ for $i=0,1,\ldots,m$, and formats these points
+ into a \texttt{String} in a format suitable for the
+ software specified by \method{graphSoft}{}.
+ NOTE: see also the more recent class
+ \externalclass{umontreal.iro.lecuyer.charts}{ContinuousDistChart}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{dist}{continuous distribution function to plot}
+ \param{a}{lower bound of the interval to plot}
+ \param{b}{upper bound of the interval to plot}
+ \param{m}{number of points in the plot minus one}
+ \param{desc}{short caption describing the plot}
+ \return{a string representation of the plot data}
+\end{htmlonly}
+\begin{code}
+
+ public static String drawDensity (ContinuousDistribution dist, double a,
+ double b, int m, String desc)\begin{hide} {
+ int i;
+ double y, x, h;
+ StringBuffer sb = new StringBuffer();
+ String openComment = "";
+ String closeComment = "";
+ String openGraph = "";
+ String closeGraph = "";
+
+ switch (graphSoft) {
+ case GNUPLOT:
+ openComment = "# ";
+ closeComment = "";
+ openGraph = "";
+ closeGraph = PrintfFormat.NEWLINE;
+ break;
+ case MATHEMATICA:
+ openComment = "(* ";
+ closeComment = " *)";
+ openGraph = "points = { " + PrintfFormat.NEWLINE;
+ closeGraph = "}" + PrintfFormat.NEWLINE;
+ break;
+ }
+
+ sb.append (openComment + "----------------------------------" +
+ closeComment + PrintfFormat.NEWLINE);
+ sb.append (openComment + PrintfFormat.s (-70, desc)
+ + closeComment + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ sb.append (openGraph);
+ h = (b - a) / m;
+
+ for (i = 0; i <= m; i++) {
+ x = a + i*h;
+ y = dist.density (x);
+
+ switch (graphSoft) {
+ case MATHEMATICA:
+ sb.append (formatMath2 (x, y));
+ if (i < m)
+ sb.append (',');
+ break;
+ default: // Default and GNUPLOT
+ sb.append (PrintfFormat.g (16, 8, x) + " " +
+ PrintfFormat.g (16, 8, y));
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append (closeGraph);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Formats data to plot the graph of the density $f(x)$ over the interval $[a,b]$,
+ and returns the result as a \class{String}. The method
+ \texttt{dist.density(x)} returns the value of $f(x)$ at $x$.
+ The \class{String} \texttt{desc} gives a short caption for the graphic
+ plot. The method computes the $m+1$ points $(x_i,\, f(x_i))$,
+ where $x_i = a + i (b-a)/m$ for $i=0,1,\ldots,m$, and formats these points
+ into a \texttt{String} in a format suitable for the
+ software specified by \method{graphSoft}{}.
+ NOTE: see also the more recent class
+ \externalclass{umontreal.iro.lecuyer.charts}{ContinuousDistChart}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{dist}{continuous density function to plot}
+ \param{a}{lower bound of the interval to plot}
+ \param{b}{upper bound of the interval to plot}
+ \param{m}{number of points in the plot minus one}
+ \param{desc}{short caption describing the plot}
+ \return{a string representation of the plot data}
+\end{htmlonly}
+\begin{code}
+
+ public static String graphDistUnif (DoubleArrayList data, String desc)\begin{hide} {
+ double[] u = data.elements();
+ int n = data.size();
+ int i;
+ double unSurN = 1.0/n;
+ StringBuffer sb = new StringBuffer();
+
+ switch (graphSoft) {
+ case GNUPLOT:
+ sb.append ("#----------------------------------" +
+ PrintfFormat.NEWLINE);
+ sb.append ("# " + PrintfFormat.s (-70, desc) +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ sb.append (PrintfFormat.g (16, 8, 0.0) + " " +
+ PrintfFormat.g (16, 8, 0.0) + PrintfFormat.NEWLINE);
+ for (i = 0; i < n; i++)
+ sb.append (PrintfFormat.g (16, 8, u[i]) + " " +
+ PrintfFormat.g (16, 8, (i + 1)*unSurN) +
+ PrintfFormat.NEWLINE);
+
+ sb.append (PrintfFormat.g (16, 8, 1.0) + " " +
+ PrintfFormat.g (16, 8, 1.0) + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+ break;
+ case MATHEMATICA:
+ sb.append ("(*----------------------------------*)" +
+ PrintfFormat.NEWLINE);
+ sb.append ("(* " + PrintfFormat.s (-70, desc) +
+ PrintfFormat.NEWLINE + " *)" +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE +
+ "points = { " + PrintfFormat.NEWLINE);
+
+ sb.append (formatMath2 (0.0, 0.0) + "," + PrintfFormat.NEWLINE);
+ for (i = 0; i < n; i++)
+ sb.append (formatMath2 (u[i], (i + 1)*unSurN) + "," +
+ PrintfFormat.NEWLINE);
+ sb.append (formatMath2 (1.0, 1.0) + PrintfFormat.NEWLINE);
+ break;
+ default:
+ throw new IllegalArgumentException ("graphSoft unknown");
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Formats data to plot the empirical distribution of
+ $U_{(1)},\dots,U_{(N)}$, which are assumed to be in \texttt{data[0...N-1]},
+ and to compare it with the uniform distribution. The $U_{(i)}$ must be sorted.
+ The two endpoints $(0, 0)$ and $(1, 1)$ are always included in the plot.
+ The string \texttt{desc} gives a short caption for the graphic plot.
+ The data is printed in a format suitable for the
+ software specified by \method{graphSoft}{}.
+ NOTE: see also the more recent class
+ \externalclass{umontreal.iro.lecuyer.charts}{EmpiricalChart}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to plot}
+ \param{desc}{short caption describing the plot}
+ \return{a string representation of the plot data}
+\end{htmlonly}
+\begin{code}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Computing and printing $p$-values for EDF test statistics}
+
+\begin{code}
+
+ public static double EPSILONP = 1.0E-15;
+\end{code}
+ \begin{tabb} Environment variable used in \method{formatp0}{} to determine
+ which $p$-values are too close to 0 or 1 to be printed explicitly.
+ If \texttt{EPSILONP} $= \epsilon$, then any $p$-value
+ less than $\epsilon$ or larger than
+ $1-\epsilon$ is {\em not\/} written explicitly;
+ the program simply writes ``\texttt{eps}'' or ``\texttt{1-eps}''.
+ The default value is $10^{-15}$.
+ \hpierre{Nouveau. Utilis\'e seulement dans \texttt{formatp0}.}
+ \end{tabb}
+\begin{code}
+
+ public static double SUSPECTP = 0.01;
+\end{code}
+ \begin{tabb} Environment variable used in \method{formatp1}{} to determine
+ which $p$-values should be marked as suspect when printing test results.
+ If \texttt{SUSPECTP} $= \alpha$, then any $p$-value
+ less than $\alpha$ or larger than
+ $1-\alpha$ is considered suspect and is
+ ``singled out'' by \texttt{formatp1}.
+ The default value is 0.01.
+ \hpierre{Utilis\'e seulement dans \texttt{formatp1}, mais
+ on ne veut pas la mettre en parametre car utilise souvent et ne
+ change presque jamais. }
+ \end{tabb}
+\begin{code}
+
+ public static String formatp0 (double p)\begin{hide} {
+ // Formats the p-value of a test, without a descriptor
+ if ((p >= 0.01) && (p <= 0.99))
+ return PrintfFormat.format (8, 2, 1, p);
+ else if (p < EPSILONP)
+ return " eps ";
+ else if (p < 0.01)
+ return PrintfFormat.format (8, 2, 2, p);
+ else if (p >= 1.0 - EPSILONP)
+ return " 1 - eps ";
+ else
+ return " 1 - " + PrintfFormat.g (8, 2, 1.0 - p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $p$-value $p$ of a test,
+ in the format ``$1-p$'' if $p$ is close to 1, and $p$ otherwise.
+ Uses the environment variable \method{EPSILONP}{} and replaces $p$
+ by $\epsilon$ when it is too small.
+ \hrichard {Pourquoi ne pas la cacher dans \tt gofw.c?}
+ \hpierre {Quelqu'un pourrait vouloir utiliser un autre format que
+ celui de \texttt{formatp1}. }
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the $p$-value to be formated}
+ \return{the string representation of the $p$-value}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatp1 (double p)\begin{hide} {
+ // Prints the p-value of a test, with a descriptor.
+ StringBuffer sb = new StringBuffer();
+ sb.append ("p-value of test :" + formatp0 (p));
+ if (p < SUSPECTP || p > 1.0 - SUSPECTP)
+ sb.append (" *****");
+
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the string ``\texttt{p-value of test : }'',
+ then calls \method{formatp0}{} to print $p$, and adds
+ the marker ``\texttt{****}'' if $p$ is considered suspect
+ (uses the environment variable \texttt{SUSPECTP} for this).
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the $p$-value to be formated}
+ \return{the string representation of the p-value of test}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatp2 (double x, double p)\begin{hide} {
+ // Prints the statistic x and its p-value p.
+ return PrintfFormat.format (8, 2, 1, x) + PrintfFormat.NEWLINE +
+ formatp1 (p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns \texttt{x} on a single line, then go to the next line
+ and calls \method{formatp1}{}.
+ \hpierre {Peut \^etre \`a cacher?}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value of the statistic for which the p-value is formated}
+ \param{p}{the $p$-value to be formated}
+ \return{the string representation of the p-value of test}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatp3 (String testName, double x, double p)\begin{hide} {
+ final String SLT = "p-value of test";
+ int l = Math.max (SLT.length(), testName.length());
+ PrintfFormat pf = new PrintfFormat();
+ pf.append (-l, testName).append (" : ").append (8, 2, 1, x).append
+ (PrintfFormat.NEWLINE);
+ pf.append (-l, SLT).append (" : ").append (formatp0 (p));
+ if (p < SUSPECTP || p > 1.0 - SUSPECTP)
+ pf.append (" *****");
+ pf.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return pf.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats the test statistic \texttt{x} for a test named \texttt{testName}
+ with $p$-value \texttt{p}. The first line of the returned string contains
+ the name of the test and the statistic whereas the second line contains
+ its p-value. The formated values of \texttt{x} and \texttt{p} are
+ aligned.
+\end{tabb}
+\begin{htmlonly}
+ \param{testName}{name of the test that was performed}
+ \param{x}{value of the test statistic}
+ \param{p}{$p$-value of the test}
+ \return{the string representation of the test result}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatChi2 (int k, int d, double chi2)\begin{hide} {
+ StringBuffer sb = new StringBuffer();
+ sb.append ("Chi2 statistic : " +
+ PrintfFormat.format (8, 2, 1, chi2));
+ sb.append (PrintfFormat.NEWLINE +
+ "p-value : " +
+ formatp0 (GofStat.pDisc
+ (ChiSquareDist.cdf (k - 1, d, chi2),
+ ChiSquareDist.barF (k - 1, d, chi2))));
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the $p$-value of the chi-square statistic
+ \texttt{chi2} for a test with \texttt{k} intervals. Uses $d$ decimal digits
+ of precision in the calculations. The result of the
+ test is returned as a string. The $p$-value is computed using
+ \externalmethod{}{GofStat}{pDisc}{}.
+\end{tabb}
+\begin{htmlonly}
+ \param{k}{number of subintervals for the chi-square test}
+ \param{chi2}{chi-square statistic}
+ \return{the string representation of the test result and $p$-value}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatKS (int n, double dp,
+ double dm, double d)\begin{hide} {
+ // Prints the results of a Kolmogorov-Smirnov test
+ return "Kolmogorov-Smirnov+ statistic = D+ :" +
+ formatp2 (dp, KolmogorovSmirnovPlusDist.barF (n, dp)) +
+ "Kolmogorov-Smirnov- statistic = D- :" +
+ formatp2 (dm, KolmogorovSmirnovPlusDist.barF (n, dm)) +
+ "Kolmogorov-Smirnov statistic = D :" +
+ formatp2 (d, KolmogorovSmirnovDistQuick.barF (n, d)) +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the $p$-values of the three Kolmogorov-Smirnov statistics
+ $D_N^+$, $D_N^-$, and $D_N$, whose values are in \texttt{dp, dm, d},
+ respectively, assuming a sample of size \texttt{n}.
+ Then formats these statistics and their $p$-values
+ using \method{formatp2}{} for each one.
+ \hrichard {Devrait \^etre \'elimin\'e. Cas particulier de EDF avec les
+ autres CM, AD, Wat, .... Pourquoi traitement de faveur.}
+ \hpierre {Je pr\'ef\`ere laisser ceci pour les gens qui ne veulent pas
+ prendre le temps de comprendre l'id\'ee des ``active tests'', que
+ j'ai d'ailleurs repouss\'es compl\`etement \`a la fin.
+ Les tests de KS sont de loin les plus utilis\'es par les gens
+ pour les lois continues.}
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{sample size}
+ \param{dp}{value of the $D_N^+$ statistic}
+ \param{dm}{value of the $D_N^-$ statistic}
+ \param{d}{value of the $D_N$ statistic}
+ \return{the string representation of the Kolmogorov-Smirnov statistics and their
+ p-values}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatKS (DoubleArrayList data,
+ ContinuousDistribution dist)\begin{hide} {
+
+ double[] v = data.elements();
+ int n = data.size();
+
+ DoubleArrayList dataUnif = GofStat.unifTransform (data, dist);
+ dataUnif.quickSortFromTo (0, dataUnif.size() - 1);
+ double[] ret = GofStat.kolmogorovSmirnov (dataUnif);
+ return formatKS (n, ret[0], ret[1], ret[2]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the KS test statistics to compare the
+ empirical distribution of the observations in \texttt{data}
+ with the theoretical distribution \texttt{dist} and
+ formats the results. See also method
+\clsexternalmethod{umontreal.iro.lecuyer.gof}{GofStat}{kolmogorovSmirnov}{}\texttt{(double[],ContinuousDistribution,double[],double[])}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to be tested}
+ \param{dist}{assumed distribution of the observations}
+ \return{the string representation of the Kolmogorov-Smirnov statistics and their
+ p-values}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatKSJumpOne (int n, double a, double dp)\begin{hide} {
+ double d = 1.0 - FDist.kolmogorovSmirnovPlusJumpOne (n, a, dp);
+
+ return PrintfFormat.NEWLINE +
+ "Kolmogorov-Smirnov+ statistic = D+ : " +
+ PrintfFormat.g (8, 2, dp) + PrintfFormat.NEWLINE +
+ formatp1 (d) + PrintfFormat.NEWLINE;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{formatKS}{int,double,double,double},
+ but for the KS statistic $D_N^+(a)$\latex{ defined in (\ref{eq:KSPlusJumpOne})}.
+ Writes a header,
+ computes the $p$-value and calls \method{formatp2}{}.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{sample size}
+ \param{a}{size of the jump}
+ \param{dp}{value of $D_N^+(a)$}
+ \return{the string representation of the Kolmogorov-Smirnov statistic and its p-value}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatKSJumpOne (DoubleArrayList data,
+ ContinuousDistribution dist,
+ double a)\begin{hide} {
+
+ double[] v = data.elements();
+ int n = data.size();
+ DoubleArrayList dataUnif = GofStat.unifTransform (data, dist);
+ dataUnif.quickSortFromTo (0, dataUnif.size() - 1);
+ double[] ret = GofStat.kolmogorovSmirnovJumpOne (dataUnif, a);
+ return formatKSJumpOne (n, a, ret[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{formatKS}{DoubleArrayList,ContinuousDistribution},
+ but for $D_N^+(a)$\latex{ defined in (\ref{eq:KSPlusJumpOne})}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to be tested}
+ \param{dist}{assumed distribution of the data}
+ \param{a}{size of the jump}
+ \return{string representation of the Kolmogorov-Smirnov statistic and its p-value}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Applying several tests at once and printing results}
+
+Higher-level tools for applying several EDF goodness-of-fit tests
+simultaneously are offered here.
+The environment variable \texttt{activeTests} specifies which
+tests in this list are to be performed when asking for several
+simultaneous tests via the functions \texttt{activeTests},
+\texttt{formatActiveTests}, etc.
+
+\begin{code}
+
+ public static final int KSP = 0;
+\end{code}
+\begin{tabb} Kolmogorov-Smirnov+ test \end{tabb}
+\begin{code}
+
+ public static final int KSM = 1;
+\end{code}
+\begin{tabb} Kolmogorov-Smirnov$-$ test \end{tabb}
+\begin{code}
+
+ public static final int KS = 2;
+\end{code}
+\begin{tabb} Kolmogorov-Smirnov test \end{tabb}
+\begin{code}
+
+ public static final int AD = 3;
+\end{code}
+\begin{tabb} Anderson-Darling test \end{tabb}
+\begin{code}
+
+ public static final int CM = 4;
+\end{code}
+\begin{tabb} Cram\'er-von Mises test \end{tabb}
+\begin{code}
+
+ public static final int WG = 5;
+\end{code}
+\begin{tabb} Watson G test \end{tabb}
+\begin{code}
+
+ public static final int WU = 6;
+\end{code}
+\begin{tabb} Watson U test \end{tabb}
+\begin{code}
+
+ public static final int MEAN = 7;
+\end{code}
+\begin{tabb} Mean \end{tabb}
+\begin{code}
+
+ public static final int COR = 8;
+\end{code}
+\begin{tabb} Correlation \end{tabb}
+\begin{code}
+
+ public static final int NTESTTYPES = 9;
+\end{code}
+\begin{tabb} Total number of test types \end{tabb}
+
+\iffalse
+ \begin{tabb} Types of EDF tests supported by the present modules.
+ Here, \texttt{MEAN} and \texttt{COR} usually
+ represent tests based on the mean of the observations and on the
+ correlation between pairs of successive observations.
+ \hpierre {Est-ce utile que garder ces deux-l\`a dans ce type enum?}
+ \hrichard {N\'ecessaire si l'on veut que les tableaux sVal, pVal,
+ r\'eservent aussi une place pour ces 2. Sinon, il faudrait introduire
+ au moins 4 nouvelles variables globales pour chaque module.}
+% {\bf Remark:} if \texttt{testType} is modified in any way, then the
+% variable \texttt{TESTNAMES} must be updated correspondingly.
+ \end{tabb}
+\fi
+\begin{code}
+
+ public static final String[] TESTNAMES\begin{hide} = {
+ "KolmogorovSmirnovPlus", "KolmogorovSmirnovMinus",
+ "KolmogorovSmirnov", "Anderson-Darling",
+ "CramerVon-Mises", "Watson G", "Watson U",
+ "Mean", "Correlation"
+ };\end{hide}
+\end{code}
+ \begin{tabb} Name of each \texttt{testType} test.
+ Could be used for printing the test results, for example.
+% {\bf Remark:} If the type \texttt{testType}
+% is modified in any way, then \texttt{TESTNAMES} must be updated
+% correspondingly.
+ \hpierre{Il me semble que cette liste des noms devrait \^etre juste \`a
+ cot\'e de la liste \texttt{testType}. Est-ce OK comme ceci? }
+ \hrichard {On ne peut pas initialiser les variables extern dans *.h.}
+ \end{tabb}
+\begin{code}
+
+ public static boolean[] activeTests\begin{hide} = null;
+ private static void initActiveTests() {
+ activeTests = new boolean[NTESTTYPES];
+ for (int i = 0; i < activeTests.length; i++)
+ activeTests[i] = false;
+ activeTests[KSP] = activeTests[KSM] = true;
+ activeTests[AD] = activeTests[MEAN] = activeTests[COR] = true;
+ }
+ static {
+ initActiveTests();
+ }\end{hide}
+\end{code}
+ \begin{tabb} The set of EDF tests that are to be performed when calling
+ the methods \method{activeTests}{}, \method{formatActiveTests}{}, etc.
+ By default, this set contains \texttt{KSP}, \texttt{KSM},
+ and \texttt{AD}. %, but it can be changed.
+ Note: \texttt{MEAN} and \texttt{COR} are {\em always excluded\/}
+ from this set of active tests.
+\begin{htmlonly}
+% These constants are not regrouped under an identifying label in Javadoc
+ The valid indices for this array are \method{KSP}{}, \method{KSM}{},
+ \method{KS}{}, \method{AD}{}, \method{CM}{}, \method{WG}{},
+ \method{WU}{}, \method{MEAN}{}, and \method{COR}{}.
+\end{htmlonly}
+ \end{tabb}
+\begin{code}
+
+ public static void tests (DoubleArrayList sortedData, double[] sVal)\begin{hide} {
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ int i;
+ double a2 = 0.0, w2, dm = 0.0, dp = 0.0, w;
+ double u1, ui, d2, d1;
+ double sumZ;
+ double unSurN;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (sVal.length != NTESTTYPES)
+ throw new IllegalArgumentException ("sVal must " +
+ "be of size NTESTTYPES.");
+
+ // We assume that u is already sorted.
+ if (n == 1) {
+ sVal[KSP] = 1.0 - u[0];
+ sVal[MEAN] = u[0];
+ return;
+ }
+ unSurN = 1.0 / n;
+ w2 = unSurN / 12.0;
+ sumZ = 0.0;
+ for (i = 0; i < n; i++) {
+ // Statistics KS
+ d1 = u[i] - i*unSurN;
+ d2 = (i + 1)*unSurN - u[i];
+ if (d1 > dm)
+ dm = d1;
+ if (d2 > dp)
+ dp = d2;
+ // Watson U and G
+ sumZ += u[i];
+ w = u[i] - (i + 0.5)*unSurN;
+ w2 += w*w;
+ // Anderson-Darling
+ ui = u[i];
+ u1 = 1.0 - ui;
+ if (ui < GofStat.EPSILONAD)
+ ui = GofStat.EPSILONAD;
+ else if (u1 < GofStat.EPSILONAD)
+ u1 = GofStat.EPSILONAD;
+ a2 += (2*i + 1) * Math.log (ui) + (1 + 2*(n - i - 1))*Math.log (u1);
+ }
+ if (dm > dp)
+ sVal[KS] = dm;
+ else
+ sVal[KS] = dp;
+ sVal[KSM] = dm;
+ sVal[KSP] = dp;
+ sumZ = sumZ * unSurN - 0.5;
+ sVal[CM] = w2;
+ sVal[WG] = Math.sqrt ((double) n) * (dp + sumZ);
+ sVal[WU] = w2 - sumZ * sumZ * n;
+ sVal[AD] = -n - a2 * unSurN;
+ sVal[MEAN] = sumZ + 0.5; // Nouveau ...
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes all EDF test statistics\latex{ enumerated above
+ (except \texttt{COR})}
+ to compare the empirical
+ distribution of $U_{(0)},\dots,U_{(N-1)}$ with the uniform distribution,
+ assuming that these sorted observations are in \texttt{sortedData}.
+ If $N > 1$, returns \texttt{sVal} with the values of the KS
+ statistics $D_N^+$, $D_N^-$ and $D_N$, of the Cram\'er-von Mises
+ statistic $W_N^2$, Watson's $G_N$ and $U_N^2$, Anderson-Darling's
+ $A_N^2$, and the average of the $U_i$'s, respectively.
+ If $N = 1$, only puts $1 - {}$\texttt{sortedData.get (0)} in \texttt{sVal[KSP]}.
+ Calling this method is more efficient than computing these statistics
+ separately by calling the corresponding methods in \class{GofStat}.
+ \hrichard{\'Eliminer, garder seulement Active. On devrait \'eliminer
+ les fonctions qui ont une version active, et enlever le mot Active dans
+ leur nom. Les actives sont d\'etermin\'ees
+ par une variable d'environnement; donc toujours sous-entendu actives.
+ Les non-actives ne sont jamais utilis\'ees.}
+ \hpierre {On ne peut pas les \'eliminer car les \texttt{Active...} appellent
+ celles-ci pour calculer toutes les stats d'un seul coup.
+ Si on trouve qu'elles genent, on peut les cacher, mais je ne crois pas.
+ Devrait peut-etre aller dans gofs, mais je l'ai mis
+ ici pour eviter de definir \texttt{testArray} dans GofStat. }
+\end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted observations}
+ \param{sVal}{array that will be filled with the results of the tests}
+\end{htmlonly}
+\begin{code}
+
+ public static void tests (DoubleArrayList data,
+ ContinuousDistribution dist, double[] sVal)\begin{hide} {
+
+ double[] v = data.elements();
+ int n = data.size();
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ DoubleArrayList sortedData = GofStat.unifTransform (data, dist);
+ sortedData.quickSortFromTo (0, sortedData.size()-1);
+ tests (sortedData, sVal);
+ if (n == 1)
+ sVal[MEAN] = v[0]; // On veut v[0], pas u[0].
+ }\end{hide}
+\end{code}
+\begin{tabb} The observations $V$ are in \texttt{data},
+ not necessarily sorted, and their empirical
+ distribution is compared with the continuous distribution \texttt{dist}.
+ \hpierre{ Note: if $N > 1$, the value returned in \texttt{sVal[MEAN]}
+ is the average of the \texttt{U[i] = dist.cdf (V[i])}, not
+ the average of the \texttt{V[i]}. }
+% The EDF tests (p-values) are valid only if \texttt{F} is continuous.
+ If $N = 1$, only puts \texttt{data.get (0)} in \texttt{sVal[MEAN]},
+ and $1 - {}$\texttt{dist.cdf (data.get (0))} in \texttt{sVal[KSP]}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to test}
+ \param{dist}{assumed distribution of the observations}
+ \param{sVal}{array that will be filled with the results of the tests}
+\end{htmlonly}
+\begin{code}
+
+ public static void activeTests (DoubleArrayList sortedData,
+ double[] sVal, double[] pVal)\begin{hide} {
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ if (sVal.length != NTESTTYPES || pVal.length != NTESTTYPES)
+ throw new IllegalArgumentException ("sVal and pVal must " +
+ "be of length NTESTTYPES.");
+
+ if (n == 1) {
+ sVal[KSP] = 1.0 - u[0];
+ pVal[KSP] = 1.0 - u[0];
+ pVal[MEAN] = pVal[KSP];
+ return;
+ }
+ // We assume that u is already sorted.
+ tests (sortedData, sVal);
+
+ if (activeTests.length != NTESTTYPES) {
+ initActiveTests();
+ System.err.println ("activeTests was invalid, it was reinitialized.");
+ }
+
+ if (activeTests[KSP])
+ pVal[KSP] = KolmogorovSmirnovPlusDist.barF (n, sVal[KSP]);
+
+ if (activeTests[KSM])
+ pVal[KSM] = KolmogorovSmirnovPlusDist.barF (n, sVal[KSM]);
+
+ if (activeTests[KS])
+ pVal[KS] = KolmogorovSmirnovDistQuick.barF (n, sVal[KS]);
+
+ if (activeTests[AD])
+ pVal[AD] = AndersonDarlingDistQuick.barF (n, sVal[AD]);
+
+ if (activeTests[CM])
+ pVal[CM] = CramerVonMisesDist.barF (n, sVal[CM]);
+
+ if (activeTests[WG])
+ pVal[WG] = WatsonGDist.barF (n, sVal[WG]);
+
+ if (activeTests[WU])
+ pVal[WU] = WatsonUDist.barF (n, sVal[WU]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the EDF test statistics by calling
+ \method{tests}{DoubleArrayList,double[]}, then computes the $p$-values of those
+ that currently belong to \texttt{activeTests},
+% \{\texttt{KSP, KSM, KS0, CM,
+% WG, WU, AD}\}.
+ and return these quantities in \texttt{sVal} and \texttt{pVal}, respectively.
+ Assumes that $U_{(0)},\dots,U_{(N-1)}$ are in \texttt{sortedData}
+ and that we want to compare their empirical distribution
+ with the uniform distribution.
+ If $N = 1$, only puts $1 - {}$\texttt{sortedData.get (0)} in
+ \texttt{sVal[KSP], pVal[KSP]}, and \texttt{pVal[MEAN]}.
+\end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted observations}
+ \param{sVal}{array that will be filled with the results of the tests}
+ \param{pVal}{array that will be filled with the $p$-values}
+\end{htmlonly}
+\begin{code}
+
+ public static void activeTests (DoubleArrayList data,
+ ContinuousDistribution dist,
+ double[] sVal, double[] pVal)\begin{hide} {
+ double[] v = data.elements();
+ int n = data.size();
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ DoubleArrayList sortedData = GofStat.unifTransform (data, dist);
+ sortedData.quickSortFromTo (0, sortedData.size() - 1);
+
+ activeTests (sortedData, sVal, pVal);
+ if (n == 1)
+ sVal[MEAN] = v[0];
+ }\end{hide}
+\end{code}
+\begin{tabb} The observations are in \texttt{data},
+ not necessarily sorted, and we want to compare their empirical
+ distribution with the distribution \texttt{dist}.
+ If $N = 1$, only puts \texttt{data.get(0)} in \texttt{sVal[MEAN]},
+ and $1 - {}$\texttt{dist.cdf (data.get (0))} in \texttt{sVal[KSP], pVal[KSP]},
+ and \texttt{pVal[MEAN]}.
+ \hrichard {Utilis\'e dans tous les tests de testu01.}
+ \end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to test}
+ \param{dist}{assumed distribution of the observations}
+ \param{sVal}{array that will be filled with the results of the tests}
+ \param{pVal}{array that will be filled with the $p$-values}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatActiveTests (int n, double[] sVal,
+ double[] pVal)\begin{hide} {
+
+ if (activeTests.length != NTESTTYPES) {
+ initActiveTests();
+ System.err.println ("activeTests was invalid, it was reinitialized.");
+ }
+ if (sVal.length != NTESTTYPES || pVal.length != NTESTTYPES)
+ throw new IllegalArgumentException ("The length of " +
+ "sVal and pVal must be NTESTTYPES.");
+ if (n == 1)
+ return formatp1 (pVal[KSP]);
+
+ StringBuffer sb = new StringBuffer (PrintfFormat.NEWLINE);
+ if (activeTests[KSP])
+ sb.append ("Kolmogorov-Smirnov+ statistic = D+ :" +
+ formatp2 (sVal[KSP], pVal[KSP]));
+ if (activeTests[KSM])
+ sb.append ("Kolmogorov-Smirnov- statistic = D- :" +
+ formatp2 (sVal[KSM], pVal[KSM]));
+ if (activeTests[KS])
+ sb.append ("Kolmogorov-Smirnov statistic = D :" +
+ formatp2 (sVal[KS], pVal[KS]));
+ if (activeTests[AD])
+ sb.append ("Anderson-Darling statistic = A2 :" +
+ formatp2 (sVal[AD], pVal[AD]));
+ if (activeTests[CM])
+ sb.append ("Cramer-von Mises statistic = W2 :" +
+ formatp2 (sVal[CM], pVal[CM]));
+ if (activeTests[WG])
+ sb.append ("Watson statistic = G :" +
+ formatp2 (sVal[WG], pVal[WG]));
+ if (activeTests[WU])
+ sb.append ("Watson statistic = U2 :" +
+ formatp2 (sVal[WU], pVal[WU]));
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Gets the $p$-values of the {\em active\/} EDF test statistics,
+ which are in \texttt{activeTests}. It is assumed that the values
+ of these statistics and their $p$-values are {\em already computed},
+ in \texttt{sVal} and \texttt{pVal}, and that the sample size is \texttt{n}.
+ These statistics and $p$-values are formated
+ using \method{formatp2}{} for each one.
+ If \texttt{n=1}, prints only \texttt{pVal[KSP]} using \method{formatp1}{}.
+ \hrichard{Utilis\'e souvent dans testu01. ..Test1 jamais utilis\'e.}
+ \hpierre{Je pr\'ef\`ere conserver ...Tests quand meme.}
+% \{\texttt{StatCalc.KSP, StatCalc.KSM, StatCalc.KS,
+% StatCalc.watsonG, StatCalc.watsonU, Statcalc.cramerVonMises,
+% StatCalc.andDar}\} and \texttt{StatCalc.testActives}
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{sample size}
+ \param{sVal}{array containing the results of the tests}
+ \param{pVal}{array containing the $p$-values}
+ \return{the results formated as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String iterSpacingsTests (DoubleArrayList sortedData, int k,
+ boolean printval, boolean graph,
+ PrintWriter f)\begin{hide} {
+
+ int n = sortedData.size();
+
+ DoubleArrayList sortedDataCopy = (DoubleArrayList)sortedData.clone();
+ DoubleArrayList diffArrayList = new DoubleArrayList(sortedData.size()+2);
+
+ int j;
+ int i;
+ double[] sVal = new double[NTESTTYPES], pVal = new double[NTESTTYPES];
+
+ StringBuffer sb = new StringBuffer (PrintfFormat.NEWLINE);
+
+ for (j = 1; j <= k; j++) {
+ sb.append ("-----------------------------------" +
+ PrintfFormat.NEWLINE +
+ "EDF Tests after \"iterateSpacings\", level : " +
+ PrintfFormat.d (2, j) + PrintfFormat.NEWLINE);
+
+ GofStat.diff (sortedDataCopy, diffArrayList, 0, n - 1, 0.0, 1.0);
+ GofStat.iterateSpacings (sortedDataCopy, diffArrayList);
+ sortedDataCopy.quickSortFromTo (0, sortedDataCopy.size() - 1);
+ activeTests (sortedDataCopy, sVal, pVal);
+
+ sb.append (formatActiveTests (n, sVal, pVal));
+ String desc = "Values of Uniforms after iterateSpacings, level " +
+ PrintfFormat.d (2, j);
+ if (printval) {
+ sb.append (desc + PrintfFormat.NEWLINE +
+ "------------------------" + PrintfFormat.NEWLINE);
+ sb.append (sortedDataCopy + PrintfFormat.NEWLINE);
+ }
+ if (graph && f != null)
+ f.print (graphDistUnif (sortedDataCopy, desc));
+ else if (graph && f == null)
+ sb.append (graphDistUnif (sortedDataCopy, desc));
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Repeats the following \texttt{k} times:
+ Applies the \clsexternalmethod{}{GofStat}{iterateSpacings}{}
+ transformation to the
+ $U_{(0)},\dots,U_{(N-1)}$, assuming that these observations are in
+ \texttt{sortedData}, then computes the EDF test statistics and calls
+ \method{activeTests}{DoubleArrayList,double[],double[]} after each transformation.
+ The function returns the {\em original\/} array \texttt{sortedData} (the
+ transformations are applied on a copy of \texttt{sortedData}).
+% If \texttt{statsDetail = true}, prints all the values.
+% If \texttt{graphicsDetail = true}, prints to \texttt{PrintWriter f}
+% the values appropriate for drawing graphics.
+ If \texttt{printval = true}, stores all the values into the returned
+ \class{String} after each iteration.
+ If \texttt{graph = true}, calls \method{graphDistUnif}{} after each iteration
+ to print to stream \texttt{f} the data for plotting the distribution
+ function of the $U_i$.
+\end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array containing the sorted observations}
+ \param{k}{number of times the tests are applied}
+ \param{printval}{if \texttt{true}, stores all the values of the observations at each iteration}
+ \param{graph}{if \texttt{true}, the distribution of the $U_i$ will be plotted after each
+ iteration}
+ \param{f}{stream where the plots are written to}
+ \return{a string representation of the test results}
+\end{htmlonly}
+\begin{code}
+
+ public static String iterPowRatioTests (DoubleArrayList sortedData, int k,
+ boolean printval, boolean graph,
+ PrintWriter f)\begin{hide} {
+
+ int n = sortedData.size();
+ DoubleArrayList sortedDataCopy = (DoubleArrayList)sortedData.clone();
+
+ int i;
+ int j;
+ double[] sVal = new double[NTESTTYPES], pVal = new double[NTESTTYPES];
+
+ StringBuffer sb = new StringBuffer (PrintfFormat.NEWLINE);
+
+ for (i = 1; i <= k; i++) {
+ GofStat.powerRatios (sortedDataCopy);
+ sb.append ("-----------------------------------" +
+ PrintfFormat.NEWLINE +
+ "EDF Tests after \"powerRatios\", level : " +
+ PrintfFormat.d (2, i) + PrintfFormat.NEWLINE);
+
+ sortedDataCopy.quickSortFromTo (0, sortedDataCopy.size() - 1);
+
+ activeTests (sortedDataCopy, sVal, pVal);
+ sb.append (formatActiveTests (n, sVal, pVal));
+ String desc = "Values of Uniforms after PowerRatios, level " +
+ PrintfFormat.d (2, i);
+ if (printval) {
+ sb.append (desc + PrintfFormat.NEWLINE +
+ "--------------------------" + PrintfFormat.NEWLINE);
+ sb.append (sortedDataCopy + PrintfFormat.NEWLINE);
+ }
+ if (graph && f != null)
+ f.print (graphDistUnif (sortedDataCopy, desc));
+ else if (graph && f == null)
+ sb.append (graphDistUnif (sortedDataCopy, desc));
+ }
+ return sb.toString();
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{iterSpacingsTests}{}, but with the
+ \clsexternalmethod{}{GofStat}{powerRatios}{} transformation.
+\end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array containing the sorted observations}
+ \param{k}{number of times the tests are applied}
+ \param{printval}{if \texttt{true}, stores all the values of the observations at each iteration}
+ \param{graph}{if \texttt{true}, the distribution of the $U_i$ will be plotted after each
+ iteration}
+ \param{f}{stream where the plots are written to}
+ \return{a string representation of the test results}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/gof/GofStat.java b/source/umontreal/iro/lecuyer/gof/GofStat.java
new file mode 100644
index 0000000..1fe9a04
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/GofStat.java
@@ -0,0 +1,1429 @@
+
+
+/*
+ * Class: GofStat
+ * Description: Goodness-of-fit test statistics
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.gof;
+ import cern.colt.list.*;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+import java.util.Arrays;
+
+/**
+ * This class provides methods to compute several types of EDF goodness-of-fit
+ * test statistics and to apply certain transformations to a set of
+ * observations. This includes the probability integral transformation
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB> = <I>F</I>(<I>X</I><SUB>i</SUB>)</SPAN>, as well as the power ratio and iterated spacings
+ * transformations. Here,
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(n-1)</SUB></SPAN> stand
+ * for <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>U</I><SUB>0</SUB>,..., <I>U</I><SUB>n-1</SUB></SPAN> sorted by increasing order, where
+ *
+ * <SPAN CLASS="MATH">0 <= <I>U</I><SUB>i</SUB> <= 1</SPAN>.
+ *
+ * <P>
+ * Note: This class uses the Colt library.
+ *
+ */
+public class GofStat {
+ private GofStat() {}
+
+
+ // Used in discontinuous distributions
+ private static double EPSILOND = 1.0E-15;
+
+
+ /**
+ * Applies the probability integral transformation
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB> = <I>F</I>(<I>V</I><SUB>i</SUB>)</SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>,
+ * where <SPAN CLASS="MATH"><I>F</I></SPAN> is a <EM>continuous</EM> distribution function,
+ * and returns the result as an array of length <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * <SPAN CLASS="MATH"><I>V</I></SPAN> represents the <SPAN CLASS="MATH"><I>n</I></SPAN> observations contained in <TT>data</TT>,
+ * and <SPAN CLASS="MATH"><I>U</I></SPAN>, the returned transformed observations.
+ * If <TT>data</TT> contains random variables from the distribution function
+ * <TT>dist</TT>, then the result will contain uniform random variables
+ * over <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ * @param data array of observations to be transformed
+ *
+ * @param dist assumed distribution of the observations
+ *
+ * @return the array of transformed observations
+ *
+ */
+ public static DoubleArrayList unifTransform (DoubleArrayList data,
+ ContinuousDistribution dist) {
+ double[] v = data.elements();
+ int n = data.size();
+
+ double[] u = new double[n];
+ for (int i = 0; i < n; i++)
+ u[i] = dist.cdf (v[i]);
+ return new DoubleArrayList(u);
+ }
+
+
+ /**
+ * Applies the transformation
+ * <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB> = <I>F</I>(<I>V</I><SUB>i</SUB>)</SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>,
+ * where <SPAN CLASS="MATH"><I>F</I></SPAN> is a <EM>discrete</EM> distribution function,
+ * and returns the result as an array of length <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * <SPAN CLASS="MATH"><I>V</I></SPAN> represents the <SPAN CLASS="MATH"><I>n</I></SPAN> observations contained in <TT>data</TT>,
+ * and <SPAN CLASS="MATH"><I>U</I></SPAN>, the returned transformed observations.
+ *
+ * <P>
+ * Note: If <SPAN CLASS="MATH"><I>V</I></SPAN> are the values of random variables with
+ * distribution function <TT>dist</TT>, then the result will contain
+ * the values of <EM>discrete</EM> random variables distributed over the
+ * set of values taken by <TT>dist</TT>,
+ * not uniform random variables over <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ * @param data array of observations to be transformed
+ *
+ * @param dist assumed distribution of the observations
+ *
+ * @return the array of transformed observations
+ *
+ */
+ public static DoubleArrayList unifTransform (DoubleArrayList data,
+ DiscreteDistribution dist) {
+ double[] v = data.elements();
+ int n = data.size();
+
+ double[] u = new double[n];
+ for (int i = 0; i < n; i++)
+ u[i] = dist.cdf ((int)v[i]);
+ return new DoubleArrayList (u);
+ }
+
+
+ /**
+ * Assumes that the real-valued observations
+ * <SPAN CLASS="MATH"><I>U</I><SUB>0</SUB>,..., <I>U</I><SUB>n-1</SUB></SPAN>
+ * contained in <TT>sortedData</TT>
+ * are already sorted in increasing order and computes the differences
+ * between the successive observations. Let <SPAN CLASS="MATH"><I>D</I></SPAN> be the differences
+ * returned in <TT>spacings</TT>.
+ * The difference
+ * <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB> - <I>U</I><SUB>i-1</SUB></SPAN> is put in <SPAN CLASS="MATH"><I>D</I><SUB>i</SUB></SPAN> for
+ * <TT>n1 < i <= n2</TT>, whereas
+ * <SPAN CLASS="MATH"><I>U</I><SUB>n1</SUB> - <I>a</I></SPAN> is put into <SPAN CLASS="MATH"><I>D</I><SUB>n1</SUB></SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>b</I> - <I>U</I><SUB>n2</SUB></SPAN> is put into <SPAN CLASS="MATH"><I>D</I><SUB>n2+1</SUB></SPAN>.
+ * The number of observations must be greater or equal than <TT>n2</TT>, we
+ * must have
+ * <TT>n1 < n2</TT>, and <TT>n1</TT> and <TT>n2</TT> are greater than 0.
+ * The size of <TT>spacings</TT> will be at least <SPAN CLASS="MATH"><I>n</I> + 1</SPAN> after
+ * the call returns.
+ *
+ * @param sortedData array of sorted observations
+ *
+ * @param spacings pointer to an array object that will be filled with spacings
+ *
+ * @param n1 starting index, in <TT>sortedData</TT>, of the processed observations
+ *
+ * @param n2 ending index, in <TT>sortedData</TT> of the processed observations
+ *
+ * @param a minimum value of the observations
+ *
+ * @param b maximum value of the observations
+ *
+ *
+ */
+ public static void diff (IntArrayList sortedData, IntArrayList spacings,
+ int n1, int n2, int a, int b) {
+ if (n1 < 0 || n2 < 0 || n1 >= n2 || n2 >= sortedData.size())
+ throw new IllegalArgumentException ("n1 and n2 not valid.");
+ int[] u = sortedData.elements();
+ int n = sortedData.size();
+ if (spacings.size() <= (n2 + 2))
+ spacings.setSize (n2 + 2);
+ int[] d = spacings.elements();
+
+ d[n1] = u[n1] - a;
+ for (int i = n1 + 1; i <= n2; i++)
+ d[i] = u[i] - u[i - 1];
+ d[n2+1] = b - u[n2];
+ }
+
+
+ /**
+ * Same as method
+ * {@link #diff diff}<TT>(IntArrayList,IntArrayList,int,int,int,int)</TT>,
+ * but for the continuous case.
+ *
+ * @param sortedData array of sorted observations
+ *
+ * @param spacings pointer to an array object that will be filled with spacings
+ *
+ * @param n1 starting index of the processed observations in <TT>sortedData</TT>
+ *
+ * @param n2 ending index, in <TT>sortedData</TT> of the processed observations
+ *
+ * @param a minimum value of the observations
+ *
+ * @param b maximum value of the observations
+ *
+ *
+ */
+ public static void diff (DoubleArrayList sortedData,
+ DoubleArrayList spacings,
+ int n1, int n2, double a, double b) {
+
+ if (n1 < 0 || n2 < 0 || n1 >= n2 || n2 >= sortedData.size())
+ throw new IllegalArgumentException ("n1 and n2 not valid.");
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ if (spacings.size() <= (n2 + 2))
+ spacings.setSize (n2 + 2);
+ double[] d = spacings.elements();
+
+ d[n1] = u[n1] - a;
+ for (int i = n1 + 1; i <= n2; i++)
+ d[i] = u[i] - u[i - 1];
+ d[n2+1] = b - u[n2];
+ }
+
+
+ /**
+ * Applies one iteration of the <EM>iterated spacings</EM>
+ * transformation.
+ * Let <SPAN CLASS="MATH"><I>U</I></SPAN> be the <SPAN CLASS="MATH"><I>n</I></SPAN> observations contained into <TT>data</TT>,
+ * and let <SPAN CLASS="MATH"><I>S</I></SPAN> be the spacings contained into <TT>spacings</TT>,
+ * Assumes that <SPAN CLASS="MATH"><I>S</I>[0..<I>n</I>]</SPAN> contains the <EM>spacings</EM>
+ * between <SPAN CLASS="MATH"><I>n</I></SPAN> real numbers
+ * <SPAN CLASS="MATH"><I>U</I><SUB>0</SUB>,..., <I>U</I><SUB>n-1</SUB></SPAN> in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ * These spacings are defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>S</I><SUB>i</SUB> = <I>U</I><SUB>(i)</SUB> - <I>U</I><SUB>(i-1)</SUB>, 1 <= <I>i</I> < <I>n</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB> = 0</SPAN>,
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(n-1)</SUB> = 1</SPAN>, and
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(n-1)</SUB></SPAN>, are the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> sorted in increasing order.
+ * These spacings may have been obtained by calling
+ * {@link #diff(DoubleArrayList,DoubleArrayList,int,int,double,double) diff}.
+ * This method transforms the spacings into new
+ * spacings:
+ * it sorts
+ * <SPAN CLASS="MATH"><I>S</I><SUB>0</SUB>,..., <I>S</I><SUB>n</SUB></SPAN> to obtain
+ *
+ * <SPAN CLASS="MATH"><I>S</I><SUB>(0)</SUB> <= <I>S</I><SUB>(1)</SUB> <= <I>S</I><SUB>(2)</SUB> <= <SUP> ... </SUP> <= <I>S</I><SUB>(n)</SUB></SPAN>,
+ * computes the weighted differences
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>S</I><SUB>0</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>(<I>n</I> + 1)<I>S</I><SUB>(0)</SUB>,</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>S</I><SUB>1</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>n</I>(<I>S</I><SUB>(1)</SUB> - <I>S</I><SUB>(0)</SUB>),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>S</I><SUB>2</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>(<I>n</I> - 1)(<I>S</I><SUB>(2)</SUB> - <I>S</I><SUB>(1)</SUB>),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"> </TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>...</TD>
+ * <TD> </TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>S</I><SUB>n</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>S</I><SUB>(n)</SUB> - <I>S</I><SUB>(n-1)</SUB>,</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * and computes
+ * <SPAN CLASS="MATH"><I>V</I><SUB>i</SUB> = <I>S</I><SUB>0</SUB> + <I>S</I><SUB>1</SUB> + <SUP> ... </SUP> + <I>S</I><SUB>i</SUB></SPAN> for
+ * <SPAN CLASS="MATH">0 <= <I>i</I> < <I>n</I></SPAN>.
+ * It then returns
+ * <SPAN CLASS="MATH"><I>S</I><SUB>0</SUB>,..., <I>S</I><SUB>n</SUB></SPAN> in <TT>S[0..n]</TT> and
+ *
+ * <SPAN CLASS="MATH"><I>V</I><SUB>1</SUB>,..., <I>V</I><SUB>n</SUB></SPAN> in <TT>V[1..n]</TT>.
+ *
+ * <P>
+ * Under the assumption that the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> are i.i.d. <SPAN CLASS="MATH"><I>U</I>(0, 1)</SPAN>, the new
+ * <SPAN CLASS="MATH"><I>S</I><SUB>i</SUB></SPAN> can be considered as a new set of spacings having the same
+ * distribution as the original spacings, and the <SPAN CLASS="MATH"><I>V</I><SUB>i</SUB></SPAN> are a new sample
+ * of i.i.d. <SPAN CLASS="MATH"><I>U</I>(0, 1)</SPAN> random variables, sorted by increasing order.
+ *
+ * <P>
+ * This transformation is useful to detect <EM>clustering</EM> in a data
+ * set: A pair of observations that are close to each other is transformed
+ * into an observation close to zero. A data set with unusually clustered
+ * observations is thus transformed to a data set with an
+ * accumulation of observations near zero, which is easily detected by
+ * the Anderson-Darling GOF test.
+ *
+ * @param data array of observations
+ *
+ * @param spacings spacings between the observations, will be filled with the new spacings
+ *
+ *
+ */
+ public static void iterateSpacings (DoubleArrayList data,
+ DoubleArrayList spacings) {
+ if (spacings.size() < (data.size()+1))
+ throw new IllegalArgumentException ("Invalid array sizes.");
+ double[] v = data.elements();
+ spacings.quickSortFromTo (0, data.size());
+ double[] s = spacings.elements();
+ int n = data.size();
+
+ for (int i = 0; i < n; i++)
+ s[n - i] = (i + 1) * (s[n - i] - s[n - i - 1]);
+ s[0] = (n + 1) * s[0];
+ v[0] = s[0];
+ for (int i = 1; i < n; i++)
+ v[i] = v[i - 1] + s[i];
+ }
+
+
+ /**
+ * Applies the <EM>power ratios</EM> transformation <SPAN CLASS="MATH"><I>W</I></SPAN>.
+ * Let <SPAN CLASS="MATH"><I>U</I></SPAN> be the <SPAN CLASS="MATH"><I>n</I></SPAN> observations contained into <TT>sortedData</TT>.
+ * Assumes that <SPAN CLASS="MATH"><I>U</I></SPAN> contains <SPAN CLASS="MATH"><I>n</I></SPAN> real numbers
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(n-1)</SUB></SPAN> from the interval <SPAN CLASS="MATH">[0, 1]</SPAN>,
+ * already sorted in increasing order, and computes the transformations:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>U'</I><SUB>i</SUB> = (<I>U</I><SUB>(i)</SUB>/<I>U</I><SUB>(i+1)</SUB>)<SUP>i+1</SUP>, <I>i</I> = 0,..., <I>n</I> - 1,
+ * </DIV><P></P>
+ * with
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(n)</SUB> = 1</SPAN>.
+ * These <SPAN CLASS="MATH"><I>U'</I><SUB>i</SUB></SPAN> are sorted in increasing order and put back in
+ * <TT>U[1...n]</TT>.
+ * If the <SPAN CLASS="MATH"><I>U</I><SUB>(i)</SUB></SPAN> are i.i.d. <SPAN CLASS="MATH"><I>U</I>(0, 1)</SPAN> sorted by increasing order,
+ * then the <SPAN CLASS="MATH"><I>U'</I><SUB>i</SUB></SPAN> are also i.i.d. <SPAN CLASS="MATH"><I>U</I>(0, 1)</SPAN>.
+ *
+ * <P>
+ * This transformation is useful to detect clustering, as explained in
+ * {@link #iterateSpacings(DoubleArrayList,DoubleArrayList) iterateSpacings},
+ * except that here a pair of
+ * observations close to each other is transformed
+ * into an observation close to 1.
+ * An accumulation of observations near 1 is also easily detected by
+ * the Anderson-Darling GOF test.
+ *
+ * @param sortedData sorted array of real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ * that will be overwritten with the transformed observations
+ *
+ */
+ public static void powerRatios (DoubleArrayList sortedData) {
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ for (int i = 0; i < (n-1); i++) {
+ if (u[i + 1] == 0.0 || u[i + 1] == -0.0)
+ u[i] = 1.0;
+ else
+ u[i] = Math.pow (u[i] / u[i + 1], (double) i + 1);
+ }
+
+ u[n-1] = Math.pow (u[n-1], (double) n);
+ sortedData.quickSortFromTo (0, sortedData.size() - 1);
+ }
+
+
+ /**
+ * This class helps managing the partitions of possible outcomes
+ * into categories for applying chi-square tests.
+ * It permits one to automatically regroup categories to make sure that
+ * the expected number of observations in each category is large enough.
+ * To use this facility, one must first construct an
+ * <TT>OutcomeCategoriesChi2</TT> object by passing to the constructor
+ * the expected number of observations for each original category.
+ * Then, calling the method {@link #regroupCategories regroupCategories} will regroup
+ * categories in a way that the expected number of observations in each
+ * category reaches a given threshold <TT>minExp</TT>.
+ * Experts in statistics recommend that <TT>minExp</TT> be always larger
+ * than or equal to 5 for the chi-square test to be valid. Thus,
+ * <TT>minExp</TT> = 10 is a safe value to use.
+ * After the call, <TT>nbExp</TT> gives the expected numbers in the new
+ * categories and <TT>loc[i]</TT> gives the relocation of category <SPAN CLASS="MATH"><I>i</I></SPAN>,
+ * for each <SPAN CLASS="MATH"><I>i</I></SPAN>. That is, <TT>loc[i] = j</TT> means that category <SPAN CLASS="MATH"><I>i</I></SPAN> has
+ * been merged with category <SPAN CLASS="MATH"><I>j</I></SPAN> because its original expected number was
+ * too small, and <TT>nbExp[i]</TT> has been added to <TT>nbExp[j]</TT>
+ * and then set to zero.
+ * In this case, all observations that previously belonged
+ * to category <SPAN CLASS="MATH"><I>i</I></SPAN> are redirected to category <SPAN CLASS="MATH"><I>j</I></SPAN>.
+ * The variable <TT>nbCategories</TT> gives the final number of categories,
+ * <TT>smin</TT> contains the new index of the lowest category,
+ * and <TT>smax</TT> the new index of the highest category.
+ *
+ */
+ public static class OutcomeCategoriesChi2 {
+
+
+ /**
+ * Total number of categories.
+ *
+ */
+ public int nbCategories;
+
+
+ /**
+ * Minimum index for valid expected numbers
+ * in the array <TT>nbExp</TT>.
+ *
+ */
+ public int smin;
+
+
+ /**
+ * Maximum index for valid expected numbers
+ * in the array <TT>nbExp</TT>.
+ *
+ */
+ public int smax;
+
+
+ /**
+ * Expected number of observations for each category.
+ *
+ */
+ public double[] nbExp;
+
+
+ /**
+ * <TT>loc[i]</TT> gives the relocation of the category <TT>i</TT> in
+ * the <TT>nbExp</TT> array.
+ *
+ */
+ public int[] loc;
+
+
+ /**
+ * Constructs an <TT>OutcomeCategoriesChi2</TT> object
+ * using the array <TT>nbExp</TT> for the number of expected observations in
+ * each category. The <TT>smin</TT> and <TT>smax</TT> fields are set to 0 and
+ * <SPAN CLASS="MATH">(<I>n</I> - 1)</SPAN> respectively, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the length of array <TT>nbExp</TT>.
+ * The <TT>loc</TT> field is set such that <TT>loc[i]=i</TT> for each <TT>i</TT>.
+ * The field <TT>nbCategories</TT> is set to <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @param nbExp array of expected observations for each category
+ *
+ *
+ */
+ public OutcomeCategoriesChi2 (double[] nbExp) {
+ this.nbExp = nbExp;
+ smin = 0;
+ smax = nbExp.length - 1;
+ nbCategories = nbExp.length;
+ loc = new int[nbExp.length];
+ for (int i = 0; i < nbExp.length; i++)
+ loc[i] = i;
+ }
+
+
+ /**
+ * Constructs an <TT>OutcomeCategoriesChi2</TT> object using the
+ * given <TT>nbExp</TT> expected observations array. Only the expected
+ * numbers from the <TT>smin</TT> to <TT>smax</TT> (inclusive) indices will be
+ * considered valid. The <TT>loc</TT> field is set such that <TT>loc[i]=i</TT>
+ * for each <TT>i</TT> in the interval <TT>[smin, smax]</TT>. All <TT>loc[i]</TT>
+ * for <TT>i <SPAN CLASS="MATH"> <= </SPAN> smin</TT> are set to <TT>smin</TT>, and all <TT>loc[i]</TT> for
+ * <TT>i <SPAN CLASS="MATH"> >= </SPAN> smax</TT> are set to <TT>smax</TT>.
+ * The field <TT>nbCategories</TT> is set to (<TT>smax - smin + 1</TT>).
+ *
+ * @param nbExp array of expected observations for each category
+ *
+ * @param smin Minimum index for valid expected number of observations
+ *
+ * @param smax Maximum index for valid expected number of observations
+ *
+ *
+ */
+ public OutcomeCategoriesChi2 (double[] nbExp, int smin, int smax) {
+ this.nbExp = nbExp;
+ this.smin = smin;
+ this.smax = smax;
+ nbCategories = smax - smin + 1;
+ loc = new int[nbExp.length];
+ for (int i = 0; i < smin; i++)
+ loc[i] = smin;
+ for (int i = smin; i < smax; i++)
+ loc[i] = i;
+ for (int i = smax; i < nbExp.length; i++)
+ loc[i] = smax;
+ }
+
+
+ /**
+ * Constructs an <TT>OutcomeCategoriesChi2</TT> object.
+ * The field <TT>nbCategories</TT> is set to <TT>nbCat</TT>.
+ *
+ * @param nbExp array of expected observations for each category
+ *
+ * @param smin Minimum index for valid expected number of observations
+ *
+ * @param smax Maximum index for valid expected number of observations
+ *
+ * @param loc array for which <TT>loc[i]</TT> gives the relocation of the category <TT>i</TT>
+ *
+ *
+ */
+ public OutcomeCategoriesChi2 (double[] nbExp, int[] loc,
+ int smin, int smax, int nbCat) {
+ this.nbExp = nbExp;
+ this.smin = smin;
+ this.smax = smax;
+ this.nbCategories = nbCat;
+ this.loc = loc;
+ }
+
+
+ /**
+ * Regroup categories as explained earlier, so that the expected
+ * number of observations in each category is at least <TT>minExp</TT>.
+ * We usually choose <TT>minExp</TT> = 10.
+ *
+ * @param minExp mininum number of expected observations in each category
+ *
+ *
+ */
+ public void regroupCategories (double minExp) {
+ int s0 = 0, j;
+ double somme;
+
+ nbCategories = 0;
+ int s = smin;
+ while (s <= smax) {
+ /* Merge categories to ensure that the number expected
+ in each category is >= minExp. */
+ if (nbExp[s] < minExp) {
+ s0 = s;
+ somme = nbExp[s];
+ while (somme < minExp && s < smax) {
+ nbExp[s] = 0.0;
+ ++s;
+ somme += nbExp[s];
+ }
+ nbExp[s] = somme;
+ for (j = s0; j <= s; j++)
+ loc[j] = s;
+
+ } else
+ loc[s] = s;
+
+ ++nbCategories;
+ ++s;
+ }
+ smin = loc[smin];
+
+ // Special case: the last category, if nbExp < minExp
+ if (nbExp[smax] < minExp) {
+ if (s0 > smin)
+ --s0;
+ nbExp[s0] += nbExp[smax];
+ nbExp[smax] = 0.0;
+ --nbCategories;
+ for (j = s0 + 1; j <= smax; j++)
+ loc[j] = s0;
+ smax = s0;
+ }
+ if (nbCategories <= 1)
+ throw new IllegalStateException ("nbCategories < 2");
+ }
+
+
+ /**
+ * Provides a report on the categories.
+ *
+ * @return the categories represented as a string
+ *
+ */
+ public String toString() {
+ int s, s0;
+ double somme;
+ final double EPSILON = 5.0E-16;
+ StringBuffer sb = new StringBuffer();
+ sb.append ("-----------------------------------------------" +
+ PrintfFormat.NEWLINE);
+ if (nbExp[smin] < EPSILON)
+ sb.append ("Only expected numbers larger than " +
+ PrintfFormat.g (6, 1, EPSILON) + " are printed" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Number of categories: " +
+ PrintfFormat.d (4, nbCategories) + PrintfFormat.NEWLINE +
+ "Expected numbers per category:" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE + "Category s nbExp[s]" +
+ PrintfFormat.NEWLINE);
+
+ // Do not print values < EPSILON
+ s = smin;
+ while (nbExp[s] < EPSILON)
+ s++;
+ int s1 = s;
+
+ s = smax;
+ while (nbExp[s] < EPSILON)
+ s--;
+ int s2 = s;
+
+ somme = 0.0;
+ for (s = s1 ; s <= s2; s++)
+ if (loc[s] == s) {
+ somme += nbExp[s];
+ sb.append (PrintfFormat.d (4, s) + " " +
+ PrintfFormat.f (18, 4, nbExp[s]) +
+ PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE + "Total expected number = " +
+ PrintfFormat.f (18, 2, somme) + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE +
+ "The groupings:" + PrintfFormat.NEWLINE +
+ " Category s loc[s]" + PrintfFormat.NEWLINE);
+ for (s = smin; s <= smax; s++) {
+ if ((s == smin) && (s > 0))
+ sb.append ("<= ");
+ else if ((s == smax) && (s < loc.length - 1))
+ sb.append (">= ");
+ else
+ sb.append (" ");
+ sb.append (PrintfFormat.d (4, s) + " " +
+ PrintfFormat.d (12, loc[s]) + PrintfFormat.NEWLINE);
+ }
+
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+ }
+
+
+ /**
+ * Computes and returns the chi-square statistic for the
+ * observations <SPAN CLASS="MATH"><I>o</I><SUB>i</SUB></SPAN> in <TT>count[smin...smax]</TT>, for which the
+ * corresponding expected values <SPAN CLASS="MATH"><I>e</I><SUB>i</SUB></SPAN> are in <TT>nbExp[smin...smax]</TT>.
+ * Assuming that <SPAN CLASS="MATH"><I>i</I></SPAN> goes from 1 to <SPAN CLASS="MATH"><I>k</I></SPAN>, where <SPAN CLASS="MATH"><I>k</I> =</SPAN> <TT>smax-smin+1</TT>
+ * is the number of categories, the chi-square statistic is defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>X</I><SUP>2</SUP> = ∑<SUB>i=1</SUB><SUP>k</SUP>(<I>o</I><SUB>i</SUB> - <I>e</I><SUB>i</SUB>)<SUP>2</SUP>/<I>e</I><SUB>i</SUB>.<P>
+ * </DIV><P></P>
+ * Under the hypothesis that the <SPAN CLASS="MATH"><I>e</I><SUB>i</SUB></SPAN> are the correct expectations and
+ * if these <SPAN CLASS="MATH"><I>e</I><SUB>i</SUB></SPAN> are large enough, <SPAN CLASS="MATH"><I>X</I><SUP>2</SUP></SPAN> follows approximately the
+ * chi-square distribution with <SPAN CLASS="MATH"><I>k</I> - 1</SPAN> degrees of freedom.
+ * If some of the <SPAN CLASS="MATH"><I>e</I><SUB>i</SUB></SPAN> are too small, one can use
+ * <TT>OutcomeCategoriesChi2</TT> to regroup categories.
+ *
+ * @param nbExp numbers expected in each category
+ *
+ * @param count numbers observed in each category
+ *
+ * @param smin index of the first valid data in <TT>count</TT> and <TT>nbExp</TT>
+ *
+ * @param smax index of the last valid data in <TT>count</TT> and <TT>nbExp</TT>
+ *
+ * @return the <SPAN CLASS="MATH"><I>X</I><SUP>2</SUP></SPAN> statistic
+ *
+ */
+ public static double chi2 (double[] nbExp, int[] count,
+ int smin, int smax) {
+ double diff, khi = 0.0;
+
+ for (int s = smin; s <= smax; s++) {
+ if (nbExp[s] <= 0.0) {
+ if (count[s] != 0)
+ throw new IllegalArgumentException (
+ "nbExp[s] = 0 and count[s] > 0");
+ }
+ else {
+ diff = count[s] - nbExp[s];
+ khi += diff * diff / nbExp[s];
+ }
+ }
+ return khi;
+ }
+
+
+ /**
+ * Computes and returns the chi-square statistic for the
+ * observations <SPAN CLASS="MATH"><I>o</I><SUB>i</SUB></SPAN> in <TT>count</TT>, for which the
+ * corresponding expected values <SPAN CLASS="MATH"><I>e</I><SUB>i</SUB></SPAN> are in <TT>cat</TT>.
+ * This assumes that <TT>cat.regroupCategories</TT> has been called before
+ * to regroup categories in order to make sure that the expected numbers in each
+ * category are large enough for the chi-square test.
+ *
+ * @param cat numbers expected in each category
+ *
+ * @param count numbers observed in each category
+ *
+ * @return the <SPAN CLASS="MATH"><I>X</I><SUP>2</SUP></SPAN> statistic
+ *
+ */
+ public static double chi2 (OutcomeCategoriesChi2 cat, int[] count) {
+ int[] newcount = new int[1 + cat.smax];
+ for (int s = cat.smin; s <= cat.smax; s++) {
+ newcount[cat.loc[s]] += count[s];
+ }
+
+ double diff, khi = 0.0;
+
+ for (int s = cat.smin; s <= cat.smax; s++) {
+ if (cat.nbExp[s] > 0.0) {
+ diff = newcount[s] - cat.nbExp[s];
+ khi += diff * diff / cat.nbExp[s];
+ }
+ }
+ newcount = null;
+ return khi;
+ }
+
+
+ /**
+ * Computes and returns the chi-square statistic for the
+ * observations stored in <TT>data</TT>, assuming that these observations follow
+ * the discrete distribution <TT>dist</TT>. For <TT>dist</TT>, we assume that
+ * there is one set
+ * <SPAN CLASS="MATH"><I>S</I> = {<I>a</I>, <I>a</I> + 1,..., <I>b</I> - 1, <I>b</I>}</SPAN>, where <SPAN CLASS="MATH"><I>a</I> < <I>b</I></SPAN> and <SPAN CLASS="MATH"><I>a</I> >= 0</SPAN>,
+ * for which <SPAN CLASS="MATH"><I>p</I>(<I>s</I>) > 0</SPAN> if <SPAN CLASS="MATH"><I>s</I>∈<I>S</I></SPAN> and <SPAN CLASS="MATH"><I>p</I>(<I>s</I>) = 0</SPAN> otherwise.
+ *
+ * <P>
+ * Generally, it is not possible to divide the integers in intervals satisfying
+ *
+ * <SPAN CLASS="MATH"><I>nP</I>(<I>a</I><SUB>0</SUB> <= <I>s</I> < <I>a</I><SUB>1</SUB>) = <I>nP</I>(<I>a</I><SUB>1</SUB> <= <I>s</I> < <I>a</I><SUB>2</SUB>) = <SUP> ... </SUP> = <I>nP</I>(<I>a</I><SUB>j-1</SUB> <= <I>s</I> < <I>a</I><SUB>j</SUB>)</SPAN>
+ * for a discrete distribution, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the sample size, i.e.,
+ * the number of
+ * observations stored into <TT>data</TT>.
+ * To perform a general chi-square test, the method starts
+ * from <TT>smin</TT> and finds the first non-negligible
+ * probability
+ * <SPAN CLASS="MATH"><I>p</I>(<I>s</I>) >= <I>ε</I></SPAN>, where
+ * <SPAN CLASS="MATH"><I>ε</I> =</SPAN> {@link DiscreteDistributionInt#EPSILON DiscreteDistributionInt.EPSILON}.
+ * It uses <TT>smax</TT> to allocate an array storing the
+ * number of expected observations (<SPAN CLASS="MATH"><I>np</I>(<I>s</I>)</SPAN>) for each <SPAN CLASS="MATH"><I>s</I> >= </SPAN> <TT>smin</TT>.
+ * Starting from <SPAN CLASS="MATH"><I>s</I> =</SPAN> <TT>smin</TT>, the <SPAN CLASS="MATH"><I>np</I>(<I>s</I>)</SPAN> terms are computed and
+ * the allocated array grows if required until a negligible probability
+ * term is found.
+ * This gives the number of expected elements for
+ * each category, where an outcome category corresponds here to
+ * an interval in which sample observations could lie.
+ * The categories are regrouped to have at least
+ * <TT>minExp</TT> observations per category.
+ * The method then counts the number of samples in each categories and calls
+ * {@link #chi2(double[],int[],int,int) chi2} to get the chi-square test
+ * statistic. If <TT>numCat</TT> is not
+ * <TT>null</TT>, the number of categories after regrouping is returned
+ * in <TT>numCat[0]</TT>. The number of degrees of freedom is equal to
+ * <TT>numCat[0]-1</TT>. We usually choose <TT>minExp</TT> = 10.
+ *
+ * @param data observations, not necessarily sorted
+ *
+ * @param dist assumed probability distribution
+ *
+ * @param smin estimated minimum value of <SPAN CLASS="MATH"><I>s</I></SPAN> for which <SPAN CLASS="MATH"><I>p</I>(<I>s</I>) > 0</SPAN>
+ *
+ * @param smax estimated maximum value of <SPAN CLASS="MATH"><I>s</I></SPAN> for which <SPAN CLASS="MATH"><I>p</I>(<I>s</I>) > 0</SPAN>
+ *
+ * @param minExp minimum number of expected observations in each
+ * interval
+ *
+ * @param numCat one-element array that will be filled with the number of
+ * categories after regrouping
+ *
+ * @return the chi-square statistic for a discrete distribution
+ *
+ */
+ public static double chi2 (IntArrayList data, DiscreteDistributionInt dist,
+ int smin, int smax, double minExp, int[] numCat) {
+ int i;
+ int n = data.size();
+
+ // Find the first non-negligible probability term and fix
+ // the real smin. The linear search starts from the given smin.
+ i = smin;
+ while (dist.prob (i)*n <= DiscreteDistributionInt.EPSILON)
+ i++;
+ smin = i--;
+
+ // smax > smin is required
+ while (smax <= smin)
+ smax = 2*smax + 1;
+
+ // Allocate and fill the array of expected observations
+ // Each category s corresponds to a value s for which p(s)>0.
+ double[] nbExp = new double[smax+1];
+ do {
+ i++;
+ if (i > smax) {
+ smax *= 2;
+ double[] newNbExp = new double[smax + 1];
+ System.arraycopy (nbExp, smin, newNbExp, smin, nbExp.length - smin);
+ nbExp = newNbExp;
+ }
+ nbExp[i] = dist.prob (i)*n;
+ }
+ while (nbExp[i] > DiscreteDistributionInt.EPSILON);
+ smax = i - 1;
+
+ // Regroup the expected observations intervals
+ // satisfying np(s)>=minExp
+ OutcomeCategoriesChi2 cat = new OutcomeCategoriesChi2
+ (nbExp, smin, smax);
+ cat.regroupCategories (minExp);
+ if (numCat != null)
+ numCat[0] = cat.nbCategories;
+
+ // Count the number of observations in each categories.
+ int[] count = new int[cat.smax+1];
+ for (i = 0; i < count.length; i++)
+ count[i] = 0;
+ for (i = 0; i < n; i++) {
+ int s = data.get (i);
+ while (cat.loc[s] != s)
+ s = cat.loc[s];
+ count[s]++;
+ }
+
+ // Perform the chi-square test
+ return chi2 (cat.nbExp, count, cat.smin, cat.smax);
+ }
+
+
+ /**
+ * Similar to {@link #chi2(double[],int[],int,int) chi2},
+ * except that the expected
+ * number of observations per category is assumed to be the same for
+ * all categories, and equal to <TT>nbExp</TT>.
+ *
+ * @param nbExp number of expected observations in each category (or interval)
+ *
+ * @param count number of counted observations in each category
+ *
+ * @param smin index of the first valid data in <TT>count</TT> and <TT>nbExp</TT>
+ *
+ * @param smax index of the last valid data in <TT>count</TT> and <TT>nbExp</TT>
+ *
+ * @return the <SPAN CLASS="MATH"><I>X</I><SUP>2</SUP></SPAN> statistic
+ *
+ */
+ public static double chi2Equal (double nbExp, int[] count,
+ int smin, int smax) {
+
+ double diff, khi = 0.0;
+ for (int s = smin; s <= smax; s++) {
+ diff = count[s] - nbExp;
+ khi += diff * diff;
+ }
+ return khi / nbExp;
+ }
+
+
+ /**
+ * Computes the chi-square statistic for a continuous distribution.
+ * Here, the equiprobable case can be used. Assuming that <TT>data</TT> contains
+ * observations coming from the uniform distribution, the <SPAN CLASS="MATH">[0, 1]</SPAN> interval
+ * is divided into <SPAN CLASS="MATH">1/<I>p</I></SPAN> subintervals, where <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>minExp</TT><SPAN CLASS="MATH">/<I>n</I></SPAN>, <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * being the sample size, i.e., the number of observations stored in
+ * <TT>data</TT>. For each subinterval, the method counts the number of
+ * contained observations and the chi-square statistic is computed
+ * using {@link #chi2Equal(double,int[],int,int) chi2Equal}.
+ * We usually choose <TT>minExp</TT> = 10.
+ *
+ * @param data array of observations in <SPAN CLASS="MATH">[0, 1)</SPAN>
+ *
+ * @param minExp minimum number of expected observations in each subintervals
+ *
+ * @return the chi-square statistic for a continuous distribution
+ *
+ */
+ public static double chi2Equal (DoubleArrayList data, double minExp) {
+ int n = data.size();
+ if (n < (int)Math.ceil (minExp))
+ throw new IllegalArgumentException ("Not enough observations");
+ double p = minExp/n;
+ int m = (int)Math.ceil (1.0/p);
+ // to avoid an exception when data[i] = 1/p, reserve one element more
+ int[] count = new int[m + 1];
+ for (int i = 0; i < n; i++) {
+ int j = (int)Math.floor (data.get (i)/p);
+ count[j]++;
+ }
+ // put the elements in count[m] where they belong: in count[m-1]
+ count[m - 1] += count[m];
+ return chi2Equal (minExp, count, 0, m - 1);
+ }
+
+
+ /**
+ * Equivalent to <TT>chi2Equal (data, 10)</TT>.
+ *
+ * @param data array of observations in <SPAN CLASS="MATH">[0, 1)</SPAN>
+ *
+ * @return the chi-square statistic for a continuous distribution
+ *
+ */
+ public static double chi2Equal (DoubleArrayList data) {
+ return chi2Equal (data, 10.0);
+}
+
+
+ /**
+ * Computes and returns the scan statistic <SPAN CLASS="MATH"><I>S</I><SUB>n</SUB>(<I>d</I> )</SPAN>,
+ * defined in {@link FBar#scan FBar.scan}.
+ * Let <SPAN CLASS="MATH"><I>U</I></SPAN> be the <SPAN CLASS="MATH"><I>n</I></SPAN> observations contained into <TT>sortedData</TT>.
+ * The <SPAN CLASS="MATH"><I>n</I></SPAN> observations in <SPAN CLASS="MATH"><I>U</I>[0..<I>n</I> - 1]</SPAN> must be real numbers
+ * in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>, sorted in increasing order.
+ * (See {@link FBar#scan FBar.scan} for the distribution function of <SPAN CLASS="MATH"><I>S</I><SUB>n</SUB>(<I>d</I> )</SPAN>).
+ *
+ * @param sortedData sorted array of real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @param d length of the test interval (<SPAN CLASS="MATH">∈(0, 1)</SPAN>)
+ *
+ * @return the scan statistic
+ *
+ */
+ public static int scan (DoubleArrayList sortedData, double d) {
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ int m = 1, j = 0, i = -1;
+ double High = 0.0;
+
+ while (j < (n-1) && High < 1.0) {
+ ++i;
+
+ High = u[i] + d;
+ while (j < n && u[j] < High)
+ ++j;
+ // j is now the index of the first obs. to the right of High.
+ if (j - i > m)
+ m = j - i;
+ }
+ return m;
+ }
+
+
+ /**
+ * Computes and returns the Cramér-von Mises statistic <SPAN CLASS="MATH"><I>W</I><SUB>n</SUB><SUP>2</SUP></SPAN>. It is
+ * defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>W</I><SUB>n</SUB><SUP>2</SUP> = 1/(12<I>n</I>) + ∑<SUB>j=0</SUB><SUP>n-1</SUP>(<I>U</I><SUB>(j)</SUB> - (<I>j</I> + 0.5)/<I>n</I>)<SUP>2</SUP>,<P>
+ * </DIV><P></P>
+ * assuming that <TT>sortedData</TT> contains
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(n-1)</SUB></SPAN>
+ * sorted in increasing order.
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @return the Cramér-von Mises statistic
+ *
+ */
+ public static double cramerVonMises (DoubleArrayList sortedData) {
+ double w, w2;
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ if (n <= 0) {
+ System.err.println ("cramerVonMises: n <= 0");
+ return 0.0;
+ }
+
+ w2 = 1.0 / (12 * n);
+ for (int i = 0; i < n; i++) {
+ w = u[i] - (i + 0.5) / n;
+ w2 += w * w;
+ }
+ return w2;
+ }
+
+
+ /**
+ * Computes and returns the Watson statistic <SPAN CLASS="MATH"><I>G</I><SUB>n</SUB></SPAN>. It is
+ * defined by
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>G</I><SUB>n</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>(n)<SUP>1/2</SUP>max<SUB>0 <= j <= n-1</SUB>{(<I>j</I> + 1)/<I>n</I> - <I>U</I><SUB>(j)</SUB> + bar(U)<SUB>n</SUB> -1/2}</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"> </TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>(n)<SUP>1/2</SUP>(<I>D</I><SUB>n</SUB><SUP>+</SUP> + bar(U)<SUB>n</SUB> - 1/2),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * where
+ * <SPAN CLASS="MATH">bar(U)<SUB>n</SUB></SPAN> is the average of the observations <SPAN CLASS="MATH"><I>U</I><SUB>(j)</SUB></SPAN>,
+ * assuming that <TT>sortedData</TT> contains the sorted
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(n-1)</SUB></SPAN>.
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @return the Watson statistic <SPAN CLASS="MATH"><I>G</I><SUB>n</SUB></SPAN>
+ *
+ */
+ public static double watsonG (DoubleArrayList sortedData) {
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ double sumZ;
+ double d2;
+ double dp, g;
+ double unSurN = 1.0 / n;
+
+ if (n <= 0) {
+ System.err.println ("watsonG: n <= 0");
+ return 0.0;
+ }
+
+ // degenerate case n = 1
+ if (n == 1)
+ return 0.0;
+
+ // We assume that u is already sorted.
+ dp = sumZ = 0.0;
+ for (int i = 0; i < n; i++) {
+ d2 = (i + 1) * unSurN - u[i];
+ if (d2 > dp)
+ dp = d2;
+ sumZ += u[i];
+ }
+ sumZ = sumZ * unSurN - 0.5;
+ g = Math.sqrt ((double) n) * (dp + sumZ);
+ return g;
+ }
+
+
+ /**
+ * Computes and returns the Watson statistic <SPAN CLASS="MATH"><I>U</I><SUB>n</SUB><SUP>2</SUP></SPAN>. It is
+ * defined by
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>W</I><SUB>n</SUB><SUP>2</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>1/(12<I>n</I>) + ∑<SUB>j=0</SUB><SUP>n-1</SUP>{<I>U</I><SUB>(j)</SUB> - (<I>j</I> + 0.5)/<I>n</I>}<SUP>2</SUP>,</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>U</I><SUB>n</SUB><SUP>2</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>W</I><SUB>n</SUB><SUP>2</SUP> - <I>n</I>(bar(U)<SUB>n</SUB> -1/2)<SUP>2</SUP>.<P></TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * where
+ * <SPAN CLASS="MATH">bar(U)<SUB>n</SUB></SPAN> is the average of the observations <SPAN CLASS="MATH"><I>U</I><SUB>(j)</SUB></SPAN>,
+ * assuming that <TT>sortedData</TT> contains the sorted
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(n-1)</SUB></SPAN>.
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @return the Watson statistic <SPAN CLASS="MATH"><I>U</I><SUB>n</SUB><SUP>2</SUP></SPAN>
+ *
+ *
+ */
+ public static double watsonU (DoubleArrayList sortedData) {
+ double sumZ, w, w2, u2;
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ if (n <= 0) {
+ System.err.println ("watsonU: n <= 0");
+ return 0.0;
+ }
+
+ // degenerate case n = 1
+ if (n == 1)
+ return 1.0 / 12.0;
+
+ sumZ = 0.0;
+ w2 = 1.0 / (12 * n);
+ for (int i = 0; i < n; i++) {
+ sumZ += u[i];
+ w = u[i] - (i + 0.5) / n;
+ w2 += w * w;
+ }
+ sumZ = sumZ / n - 0.5;
+ u2 = w2 - sumZ * sumZ * n;
+ return u2;
+ }
+
+
+
+ public static double EPSILONAD = Num.DBL_EPSILON/2;
+
+
+ /**
+ * Computes and returns the Anderson-Darling statistic <SPAN CLASS="MATH"><I>A</I><SUB>n</SUB><SUP>2</SUP></SPAN>
+ * (see method {@link #andersonDarling(double[]) andersonDarling}).
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @return the Anderson-Darling statistic
+ *
+ */
+ public static double andersonDarling (DoubleArrayList sortedData) {
+ double[] v = sortedData.elements();
+ return andersonDarling (v);
+ }
+
+
+ /**
+ * Computes and returns the Anderson-Darling statistic <SPAN CLASS="MATH"><I>A</I><SUB>n</SUB><SUP>2</SUP></SPAN>.
+ * It is
+ * defined by
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>A</I><SUB>n</SUB><SUP>2</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>- <I>n</I> - 1/<I>n</I> ∑<SUB>j=0</SUB><SUP>n-1</SUP>{(2<I>j</I> + 1)ln(<I>U</I><SUB>(j)</SUB>) + (2<I>n</I> - 1 - 2<I>j</I>)ln(1 - <I>U</I><SUB>(j)</SUB>)},</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * assuming that <TT>sortedData</TT> contains
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(0)</SUB>,..., <I>U</I><SUB>(n-1)</SUB></SPAN>
+ * sorted in increasing order.
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @return the Anderson-Darling statistic
+ *
+ */
+ public static double andersonDarling (double[] sortedData) {
+ double u1;
+ double u, a2;
+ int n = sortedData.length;
+
+ if (n <= 0) {
+ System.err.println ("andersonDarling: n <= 0");
+ return 0.0;
+ }
+
+ a2 = 0.0;
+ for (int i = 0; i < n; i++) {
+ u = sortedData[i];
+ u1 = 1.0 - u;
+ if (u < EPSILONAD)
+ u = EPSILONAD;
+ else if (u1 < EPSILONAD)
+ u1 = EPSILONAD;
+ a2 += (2*i + 1)*Math.log (u) + (1 + 2*(n - i - 1))*
+ Math.log (u1);
+ }
+ a2 = -n - a2 / n;
+ return a2;
+ }
+
+
+ /**
+ * Computes the Anderson-Darling statistic <SPAN CLASS="MATH"><I>A</I><SUB>n</SUB><SUP>2</SUP></SPAN>
+ * and the corresponding <SPAN CLASS="MATH"><I>p</I></SPAN>-value <SPAN CLASS="MATH"><I>p</I></SPAN>. The <SPAN CLASS="MATH"><I>n</I></SPAN> (unsorted) observations in <TT>data</TT>
+ * are assumed to be independent and to come from the continuous
+ * distribution <TT>dist</TT>.
+ * Returns the 2-elements array [<SPAN CLASS="MATH"><I>A</I><SUB>n</SUB><SUP>2</SUP></SPAN>, <SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ * @param data array of observations
+ *
+ * @param dist assumed distribution of the observations
+ *
+ * @return the array <SPAN CLASS="MATH">[<I>A</I><SUB>n</SUB><SUP>2</SUP></SPAN>, <SPAN CLASS="MATH"><I>p</I>]</SPAN>.
+ *
+ */
+ public static double[] andersonDarling (double[] data,
+ ContinuousDistribution dist)
+ {
+ int n = data.length;
+ double[] U = new double[n];
+ for (int i = 0; i < n; i++) {
+ U[i] = dist.cdf(data[i]);
+ }
+
+ Arrays.sort(U);
+ double x = GofStat.andersonDarling(U);
+ double v = AndersonDarlingDistQuick.barF(n, x);
+ double[] res = {x, v};
+ return res;
+ }
+
+
+ /**
+ * Computes the Kolmogorov-Smirnov (KS) test statistics
+ * <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>, and <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB></SPAN> (see method
+ * {@link #kolmogorovSmirnov(DoubleArrayList) kolmogorovSmirnov}). Returns the array [<SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB></SPAN>].
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @return the array [<SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB></SPAN>]
+ *
+ */
+ public static double[] kolmogorovSmirnov (double[] sortedData) {
+ DoubleArrayList v = new DoubleArrayList(sortedData);
+ return kolmogorovSmirnov (v);
+ }
+
+
+ /**
+ * Computes the Kolmogorov-Smirnov (KS) test statistics
+ * <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>, and <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB></SPAN>. It is
+ * defined by
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>D</I><SUB>n</SUB><SUP>+</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>max<SUB>0 <= j <= n-1</SUB>((<I>j</I> + 1)/<I>n</I> - <I>U</I><SUB>(j)</SUB>),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>D</I><SUB>n</SUB><SUP>-</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>max<SUB>0 <= j <= n-1</SUB>(<I>U</I><SUB>(j)</SUB> - <I>j</I>/<I>n</I>),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>D</I><SUB>n</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>max (<I>D</I><SUB>n</SUB><SUP>+</SUP>, <I>D</I><SUB>n</SUB><SUP>-</SUP>).</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * and returns an array of length 3 that contains [<SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB></SPAN>].
+ * These statistics compare the empirical distribution of
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(1)</SUB>,..., <I>U</I><SUB>(n)</SUB></SPAN>, which are assumed to be in <TT>sortedData</TT>,
+ * with the uniform distribution over <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @return the array [<SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB></SPAN>]
+ *
+ */
+ public static double[] kolmogorovSmirnov (DoubleArrayList sortedData) {
+ double[] ret = new double[3];
+ int n = sortedData.size();
+
+ if (n <= 0) {
+ ret[0] = ret[1] = ret[2] = 0.0;
+ System.err.println ("kolmogorovSmirnov: n <= 0");
+ return ret;
+ }
+
+ double[] retjo = kolmogorovSmirnovJumpOne (sortedData, 0.0);
+ ret[0] = retjo[0];
+ ret[1] = retjo[1];
+ if (ret[1] > ret[0])
+ ret[2] = ret[1];
+ else
+ ret[2] = ret[0];
+
+ return ret;
+ }
+
+
+ /**
+ * Computes the KolmogorovSmirnov (KS) test statistics and their <SPAN CLASS="MATH"><I>p</I></SPAN>-values.
+ * This is to compare the empirical distribution of the (unsorted) observations
+ * in <TT>data</TT>
+ * with the theoretical distribution <TT>dist</TT>. The KS statistics
+ * <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN> and <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB></SPAN> are returned in <TT>sval[0]</TT>, <TT>sval[1]</TT>,
+ * and <TT>sval[2]</TT> respectively, and their corresponding <SPAN CLASS="MATH"><I>p</I></SPAN>-values
+ * are returned in <TT>pval[0]</TT>, <TT>pval[1]</TT>, and <TT>pval[2]</TT>.
+ *
+ * @param data array of observations to be tested
+ *
+ * @param dist assumed distribution of the observations
+ *
+ * @param sval values of the 3 KS statistics
+ *
+ * @param pval <SPAN CLASS="MATH"><I>p</I></SPAN>-values for the 3 KS statistics
+ *
+ *
+ */
+ public static void kolmogorovSmirnov (double[] data,
+ ContinuousDistribution dist,
+ double[] sval,
+ double[] pval) {
+ int n = data.length;
+ double[] T = new double[n];
+ for (int i = 0; i < n; i++) {
+ T[i] = dist.cdf (data[i]);
+ }
+
+ Arrays.sort (T);
+ double[] statks = GofStat.kolmogorovSmirnov (T);
+ for (int i = 0; i < 3; i++) {
+ sval[i] = statks[i];
+ }
+ pval[2] = KolmogorovSmirnovDistQuick.barF (n, sval[2]);
+ pval[1] = KolmogorovSmirnovPlusDist.barF (n, sval[1]);
+ pval[0] = KolmogorovSmirnovPlusDist.barF (n, sval[0]);
+ }
+
+
+ /**
+ * Compute the KS statistics <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP>(<I>a</I>)</SPAN> and <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP>(<I>a</I>)</SPAN> defined in
+ * the description of the method
+ * {@link FDist#kolmogorovSmirnovPlusJumpOne FDist.kolmogorovSmirnovPlusJumpOne}, assuming that <SPAN CLASS="MATH"><I>F</I></SPAN> is the
+ * uniform distribution over <SPAN CLASS="MATH">[0, 1]</SPAN> and that
+ *
+ * <SPAN CLASS="MATH"><I>U</I><SUB>(1)</SUB>,..., <I>U</I><SUB>(n)</SUB></SPAN> are in <TT>sortedData</TT>.
+ * Returns the array [<SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>].
+ *
+ * @param sortedData array of sorted real-valued observations in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ * @param a size of the jump
+ *
+ * @return the array [<SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN>, <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>]
+ *
+ */
+ public static double[] kolmogorovSmirnovJumpOne (DoubleArrayList sortedData,
+ double a) {
+ /* Statistics KS+ and KS-. Case with 1 jump at a, near the lower tail of
+ the distribution. */
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ int j, i;
+ double d2, d1, unSurN;
+ double[] ret = new double[2];
+
+ if (n <= 0) {
+ ret[0] = ret[1] = 0.0;
+ System.err.println ("kolmogorovSmirnovJumpOne: n <= 0");
+ return ret;
+ }
+
+ ret[0] = 0.0;
+ ret[1] = 0.0;
+ unSurN = 1.0 / n;
+ j = 0;
+
+ while (j < n && u[j] <= a + EPSILOND) ++j;
+
+ for (i = j - 1; i < n; i++) {
+ if (i >= 0) {
+ d1 = (i + 1) * unSurN - u[i];
+ if (d1 > ret[0])
+ ret[0] = d1;
+ }
+ if (i >= j) {
+ d2 = u[i] - i * unSurN;
+ if (d2 > ret[1])
+ ret[1] = d2;
+ }
+ }
+ return ret;
+ }
+
+
+ /**
+ * Computes a variant of the <SPAN CLASS="MATH"><I>p</I></SPAN>-value <SPAN CLASS="MATH"><I>p</I></SPAN> whenever a test statistic
+ * has a <EM>discrete</EM> probability distribution.
+ * This <SPAN CLASS="MATH"><I>p</I></SPAN>-value is defined as follows:
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>p</I><SUB>L</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>P</I>[<I>Y</I> <= <I>y</I>]</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>p</I><SUB>R</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>P</I>[<I>Y</I> >= <I>y</I>]</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>p</I> =</TD>
+ * <TD ALIGN="LEFT"><I>p</I><SUB>R</SUB>,</TD>
+ * <TD ALIGN="LEFT"> if <I>p</I><SUB>R</SUB> < <I>p</I><SUB>L</SUB>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>p</I> =</TD>
+ * <TD ALIGN="LEFT">1 - <I>p</I><SUB>L</SUB>,</TD>
+ * <TD ALIGN="LEFT"> if <I>p</I><SUB>R</SUB> >= <I>p</I><SUB>L</SUB> and <I>p</I><SUB>L</SUB> < 0.5,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>p</I> =</TD>
+ * <TD ALIGN="LEFT">0.5</TD>
+ * <TD ALIGN="LEFT"> otherwise.</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ * The function takes <SPAN CLASS="MATH"><I>p</I><SUB>L</SUB></SPAN> and <SPAN CLASS="MATH"><I>p</I><SUB>R</SUB></SPAN> as input and returns <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @param pL left <SPAN CLASS="MATH"><I>p</I></SPAN>-value
+ *
+ * @param pR right <SPAN CLASS="MATH"><I>p</I></SPAN>-value
+ *
+ * @return the <SPAN CLASS="MATH"><I>p</I></SPAN>-value for a test on a discrete distribution
+ */
+ public static double pDisc (double pL, double pR) {
+ double p;
+
+ if (pR < pL)
+ p = pR;
+ else if (pL > 0.5)
+ p = 0.5;
+ else
+ p = 1.0 - pL;
+ // Note: si p est tres proche de 1, on perd toute la precision ici!
+ // Note2: je ne pense pas que cela puisse se produire a cause des if (RS)
+ return p;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/gof/GofStat.tex b/source/umontreal/iro/lecuyer/gof/GofStat.tex
new file mode 100644
index 0000000..6fd0059
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/GofStat.tex
@@ -0,0 +1,1284 @@
+\defmodule {GofStat}
+
+This class provides methods to compute several types of EDF goodness-of-fit
+test statistics and to apply certain transformations to a set of
+observations. This includes the probability integral transformation
+$U_i = F(X_i)$, as well as the power ratio and iterated spacings
+transformations \cite{tSTE86a}. Here, $U_{(0)}, \dots, U_{(n-1)}$ stand
+for $n$ observations $U_0,\dots,U_{n-1}$ sorted by increasing order, where
+$0\le U_i\le 1$.
+
+Note: This class uses the Colt library.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GofStat
+ * Description: Goodness-of-fit test statistics
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.gof;
+ import cern.colt.list.*;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+import java.util.Arrays;\end{hide}
+
+public class GofStat\begin{hide} {
+ private GofStat() {}
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Transforming the observations}
+
+\begin{code}\begin{hide}
+ // Used in discontinuous distributions
+ private static double EPSILOND = 1.0E-15;
+\end{hide}
+
+ public static DoubleArrayList unifTransform (DoubleArrayList data,
+ ContinuousDistribution dist)\begin{hide} {
+ double[] v = data.elements();
+ int n = data.size();
+
+ double[] u = new double[n];
+ for (int i = 0; i < n; i++)
+ u[i] = dist.cdf (v[i]);
+ return new DoubleArrayList(u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Applies the probability integral transformation
+ $U_i = F (V_i)$ for $i = 0, 1, \ldots, n-1$,
+ where $F$ is a {\em continuous\/} distribution function,
+ and returns the result as an array of length $n$.
+ $V$ represents the $n$ observations contained in \texttt{data},
+ and $U$, the returned transformed observations.
+ If \texttt{data} contains random variables from the distribution function
+ \texttt{dist}, then the result will contain uniform random variables
+ over $[0,1]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to be transformed}
+ \param{dist}{assumed distribution of the observations}
+ \return{the array of transformed observations}
+\end{htmlonly}
+\begin{code}
+
+ public static DoubleArrayList unifTransform (DoubleArrayList data,
+ DiscreteDistribution dist)\begin{hide} {
+ double[] v = data.elements();
+ int n = data.size();
+
+ double[] u = new double[n];
+ for (int i = 0; i < n; i++)
+ u[i] = dist.cdf ((int)v[i]);
+ return new DoubleArrayList (u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Applies the transformation $U_i = F (V_i)$ for $i = 0, 1, \ldots, n-1$,
+ where $F$ is a {\em discrete\/} distribution function,
+ and returns the result as an array of length $n$.
+ $V$ represents the $n$ observations contained in \texttt{data},
+ and $U$, the returned transformed observations.
+
+ Note: If $V$ are the values of random variables with
+ distribution function \texttt{dist}, then the result will contain
+ the values of {\em discrete\/} random variables distributed over the
+ set of values taken by \texttt{dist},
+ not uniform random variables over $[0,1]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to be transformed}
+ \param{dist}{assumed distribution of the observations}
+ \return{the array of transformed observations}
+\end{htmlonly}
+\begin{code}
+
+ public static void diff (IntArrayList sortedData, IntArrayList spacings,
+ int n1, int n2, int a, int b)\begin{hide} {
+ if (n1 < 0 || n2 < 0 || n1 >= n2 || n2 >= sortedData.size())
+ throw new IllegalArgumentException ("n1 and n2 not valid.");
+ int[] u = sortedData.elements();
+ int n = sortedData.size();
+ if (spacings.size() <= (n2 + 2))
+ spacings.setSize (n2 + 2);
+ int[] d = spacings.elements();
+
+ d[n1] = u[n1] - a;
+ for (int i = n1 + 1; i <= n2; i++)
+ d[i] = u[i] - u[i - 1];
+ d[n2+1] = b - u[n2];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Assumes that the real-valued observations $U_0,\dots,U_{n-1}$
+ contained in \texttt{sortedData}
+ are already sorted in increasing order and computes the differences
+ between the successive observations. Let $D$ be the differences
+ returned in \texttt{spacings}.
+ The difference $U_i - U_{i-1}$ is put in $D_i$ for
+ \texttt{n1 < i <= n2}, whereas $U_{n1} - a$ is put into $D_{n1}$
+ and $b - U_{n2}$ is put into $D_{n2+1}$.
+%
+ The number of observations must be greater or equal than \texttt{n2}, we
+ must have
+ \texttt{n1 < n2}, and \texttt{n1} and \texttt{n2} are greater than 0.
+ The size of \texttt{spacings} will be at least $n+1$ after
+ the call returns.
+\hpierre {ATTENTION: J'ai chang\'e cette proc\'edure et la suivante
+ pour les rendre plus g\'en\'erales et surtout plus {\em semblables}.
+ Un appel \`a l'ancien \texttt{DiffD (U, D, n)} doit se traduire par
+ \texttt{DiffD (U, D, 1, n, 0.0, 1.0)}, tandis qu'un appel
+ \`a l'ancien \texttt{DiffL (U, D, n1, n2, L)} doit se traduire par
+ \texttt{DiffD (U, D, n1, n2, 0, L+U[n1])}. }
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted observations}
+ \param{spacings}{pointer to an array object that will be filled with spacings}
+ \param{n1}{starting index, in \texttt{sortedData}, of the processed observations}
+ \param{n2}{ending index, in \texttt{sortedData} of the processed observations}
+ \param{a}{minimum value of the observations}
+ \param{b}{maximum value of the observations}
+\end{htmlonly}
+\begin{code}
+
+ public static void diff (DoubleArrayList sortedData,
+ DoubleArrayList spacings,
+ int n1, int n2, double a, double b)\begin{hide} {
+
+ if (n1 < 0 || n2 < 0 || n1 >= n2 || n2 >= sortedData.size())
+ throw new IllegalArgumentException ("n1 and n2 not valid.");
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ if (spacings.size() <= (n2 + 2))
+ spacings.setSize (n2 + 2);
+ double[] d = spacings.elements();
+
+ d[n1] = u[n1] - a;
+ for (int i = n1 + 1; i <= n2; i++)
+ d[i] = u[i] - u[i - 1];
+ d[n2+1] = b - u[n2];
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as method
+ \method{diff}{}{\texttt{(IntArrayList,IntArrayList,int,int,int,int)}}{},
+ but for the continuous case.
+\end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted observations}
+ \param{spacings}{pointer to an array object that will be filled with spacings}
+ \param{n1}{starting index of the processed observations in \texttt{sortedData}}
+ \param{n2}{ending index, in \texttt{sortedData} of the processed observations}
+ \param{a}{minimum value of the observations}
+ \param{b}{maximum value of the observations}
+\end{htmlonly}
+\begin{code}
+
+ public static void iterateSpacings (DoubleArrayList data,
+ DoubleArrayList spacings)\begin{hide} {
+ if (spacings.size() < (data.size()+1))
+ throw new IllegalArgumentException ("Invalid array sizes.");
+ double[] v = data.elements();
+ spacings.quickSortFromTo (0, data.size());
+ double[] s = spacings.elements();
+ int n = data.size();
+
+ for (int i = 0; i < n; i++)
+ s[n - i] = (i + 1) * (s[n - i] - s[n - i - 1]);
+ s[0] = (n + 1) * s[0];
+ v[0] = s[0];
+ for (int i = 1; i < n; i++)
+ v[i] = v[i - 1] + s[i];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Applies one iteration of the {\em iterated spacings\/}
+ transformation \cite{rKNU98a,tSTE86a}.
+ Let $U$ be the $n$ observations contained into \texttt{data},
+ and let $S$ be the spacings contained into \texttt{spacings},
+ Assumes that $S[0..n]$ contains the {\em spacings\/}
+ between $n$ real numbers $U_0,\dots,U_{n-1}$ in the interval $[0,1]$.
+ These spacings are defined by
+ $$ S_i = U_{(i)} - U_{(i-1)}, \qquad 1\le i < n, $$
+ where $U_{(0)}=0$, $U_{(n-1)}=1$, and
+ $U_{(0)},\dots,U_{(n-1)}$, are the $U_i$ sorted in increasing order.
+% These $U_i$ do not need to be in the array \texttt{V}.
+ These spacings may have been obtained by calling
+ \method{diff}{DoubleArrayList,DoubleArrayList,int,int,double,double}.
+ This method transforms the spacings into new
+ spacings\latex{, by a variant of the method described
+ in section 11 of \cite {rMAR85a} and also by Stephens \cite{tSTE86a}}:
+% See also Knuth (1998), 3th edition.
+ it sorts $S_0,\dots,S_n$ to obtain
+ $S_{(0)} \le S_{(1)} \le S_{(2)} \le \cdots \le S_{(n)}$,
+ computes the weighted differences
+ \begin {eqnarray*}
+ S_{0} &=& (n+1) S_{(0)}, \\
+ S_{1} &=& n (S_{(1)}-S_{(0)}), \\
+ S_{2} &=& (n-1) (S_{(2)}-S_{(1)}),\\
+ & \latex{\vdots}\html{...}& \\
+ S_{n} &=& S_{(n)}-S_{(n-1)},
+ \end {eqnarray*}
+ and computes $V_i = S_0 + S_1 + \cdots + S_i$ for $0\le i < n$.
+ It then returns $S_0,\dots,S_n$ in \texttt{S[0..n]} and
+ $V_1,\dots,V_n$ in \texttt{V[1..n]}.
+
+ Under the assumption that the $U_i$ are i.i.d.\ $U (0,1)$, the new
+ $S_i$ can be considered as a new set of spacings having the same
+ distribution as the original spacings, and the $V_i$ are a new sample
+ of i.i.d.\ $U (0,1)$ random variables, sorted by increasing order.
+
+ This transformation is useful to detect {\em clustering\/} in a data
+ set: A pair of observations that are close to each other is transformed
+ into an observation close to zero. A data set with unusually clustered
+ observations is thus transformed to a data set with an
+ accumulation of observations near zero, which is easily detected by
+ the Anderson-Darling GOF test.
+ \end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations}
+ \param{spacings}{spacings between the observations, will be filled with the new spacings}
+\end{htmlonly}
+\begin{code}
+
+ public static void powerRatios (DoubleArrayList sortedData)\begin{hide} {
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ for (int i = 0; i < (n-1); i++) {
+ if (u[i + 1] == 0.0 || u[i + 1] == -0.0)
+ u[i] = 1.0;
+ else
+ u[i] = Math.pow (u[i] / u[i + 1], (double) i + 1);
+ }
+
+ u[n-1] = Math.pow (u[n-1], (double) n);
+ sortedData.quickSortFromTo (0, sortedData.size() - 1);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Applies the {\em power ratios\/} transformation $W$\latex{ described
+ in section 8.4 of Stephens \cite{tSTE86a}}.
+ Let $U$ be the $n$ observations contained into \texttt{sortedData}.
+ Assumes that $U$ contains $n$ real numbers
+ $U_{(0)},\dots,U_{(n-1)}$ from the interval $[0,1]$,
+ already sorted in increasing order, and computes the transformations:
+ $$ U'_i = (U_{(i)} / U_{(i+1)})^{i+1}, \qquad i=0,\dots,n-1,$$
+ with $U_{(n)} = 1$.
+ These $U'_i$ are sorted in increasing order and put back in
+ \texttt{U[1...n]}.
+ If the $U_{(i)}$ are i.i.d.\ $U (0,1)$ sorted by increasing order,
+ then the $U'_i$ are also i.i.d.\ $U (0,1)$.
+
+ This transformation is useful to detect clustering, as explained in
+ \method{iterateSpacings}{DoubleArrayList,DoubleArrayList},
+ except that here a pair of
+ observations close to each other is transformed
+ into an observation close to 1.
+ An accumulation of observations near 1 is also easily detected by
+ the Anderson-Darling GOF test.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{sorted array of real-valued observations in the interval $[0,1]$
+ that will be overwritten with the transformed observations}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Partitions for the chi-square tests}
+
+\begin{code}
+
+ public static class OutcomeCategoriesChi2\begin{hide} {\end{hide}
+\end{code}
+\begin{tabb}
+This class helps managing the partitions of possible outcomes
+into categories for applying chi-square tests.
+It permits one to automatically regroup categories to make sure that
+the expected number of observations in each category is large enough.
+% namely larger or equal to \texttt{MINEXPECTED}.
+To use this facility, one must first construct an
+\texttt{OutcomeCategoriesChi2} object by passing to the constructor
+the expected number of observations for each original category.
+Then, calling the method \method{regroupCategories}{} will regroup
+categories in a way that the expected number of observations in each
+category reaches a given threshold \texttt{minExp}.
+Experts in statistics recommend that \texttt{minExp} be always larger
+than or equal to 5 for the chi-square test to be valid. Thus,
+\texttt{minExp} = 10 is a safe value to use.
+After the call, \texttt{nbExp} gives the expected numbers in the new
+categories and \texttt{loc[i]} gives the relocation of category $i$,
+for each $i$. That is, \texttt{loc[i] = j} means that category $i$ has
+been merged with category $j$ because its original expected number was
+too small, and \texttt{nbExp[i]} has been added to \texttt{nbExp[j]}
+and then set to zero.
+In this case, all observations that previously belonged
+to category $i$ are redirected to category $j$.
+% i.e. considered as if they belong to category $j$,
+The variable \texttt{nbCategories} gives the final number of categories,
+\texttt{smin} contains the new index of the lowest category,
+and \texttt{smax} the new index of the highest category.
+\end{tabb}
+\begin{code}
+
+ public int nbCategories;
+\end{code}
+\begin{tabbb} Total number of categories. \end{tabbb}
+\begin{code}
+
+ public int smin;
+\end{code}
+\begin{tabbb} Minimum index for valid expected numbers
+ in the array \texttt{nbExp}.
+\end{tabbb}
+\begin{code}
+
+ public int smax;
+\end{code}
+\begin{tabbb} Maximum index for valid expected numbers
+ in the array \texttt{nbExp}.
+\end{tabbb}
+\begin{code}
+
+ public double[] nbExp;
+\end{code}
+\begin{tabbb} Expected number of observations for each category. \end{tabbb}
+\begin{code}
+
+ public int[] loc;
+\end{code}
+\begin{tabbb} \texttt{loc[i]} gives the relocation of the category \texttt{i} in
+ the \texttt{nbExp} array. \end{tabbb}
+\begin{code}
+
+ public OutcomeCategoriesChi2 (double[] nbExp)\begin{hide} {
+ this.nbExp = nbExp;
+ smin = 0;
+ smax = nbExp.length - 1;
+ nbCategories = nbExp.length;
+ loc = new int[nbExp.length];
+ for (int i = 0; i < nbExp.length; i++)
+ loc[i] = i;
+ }\end{hide}
+\end{code}
+\begin{tabbb} Constructs an \texttt{OutcomeCategoriesChi2} object
+ using the array \texttt{nbExp} for the number of expected observations in
+ each category. The \texttt{smin} and \texttt{smax} fields are set to 0 and
+ $(n-1)$ respectively, where $n$ is the length of array \texttt{nbExp}.
+ The \texttt{loc} field is set such that \texttt{loc[i]=i} for each \texttt{i}.
+ The field \texttt{nbCategories} is set to $n$.
+\end{tabbb}
+\begin{htmlonly}
+ \param{nbExp}{array of expected observations for each category}
+\end{htmlonly}
+\begin{code}
+
+ public OutcomeCategoriesChi2 (double[] nbExp, int smin, int smax)\begin{hide} {
+ this.nbExp = nbExp;
+ this.smin = smin;
+ this.smax = smax;
+ nbCategories = smax - smin + 1;
+ loc = new int[nbExp.length];
+ for (int i = 0; i < smin; i++)
+ loc[i] = smin;
+ for (int i = smin; i < smax; i++)
+ loc[i] = i;
+ for (int i = smax; i < nbExp.length; i++)
+ loc[i] = smax;
+ }\end{hide}
+\end{code}
+\begin{tabbb} Constructs an \texttt{OutcomeCategoriesChi2} object using the
+ given \texttt{nbExp} expected observations array. Only the expected
+ numbers from the \texttt{smin} to \texttt{smax} (inclusive) indices will be
+ considered valid. The \texttt{loc} field is set such that \texttt{loc[i]=i}
+ for each \texttt{i} in the interval \texttt{[smin, smax]}. All \texttt{loc[i]}
+ for \texttt{i $\le$ smin} are set to \texttt{smin}, and all \texttt{loc[i]} for
+ \texttt{i $\ge$ smax} are set to \texttt{smax}.
+ The field \texttt{nbCategories} is set to (\texttt{smax - smin + 1}).
+\end{tabbb}
+\begin{htmlonly}
+ \param{nbExp}{array of expected observations for each category}
+ \param{smin}{Minimum index for valid expected number of observations}
+ \param{smax}{Maximum index for valid expected number of observations}
+\end{htmlonly}
+\begin{code}
+
+ public OutcomeCategoriesChi2 (double[] nbExp, int[] loc,
+ int smin, int smax, int nbCat)\begin{hide} {
+ this.nbExp = nbExp;
+ this.smin = smin;
+ this.smax = smax;
+ this.nbCategories = nbCat;
+ this.loc = loc;
+ }\end{hide}
+\end{code}
+ \begin{tabbb} Constructs an \texttt{OutcomeCategoriesChi2} object.
+ The field \texttt{nbCategories} is set to \texttt{nbCat}.
+ \end{tabbb}
+\begin{htmlonly}
+ \param{nbExp}{array of expected observations for each category}
+ \param{smin}{Minimum index for valid expected number of observations}
+ \param{smax}{Maximum index for valid expected number of observations}
+ \param{loc}{array for which \texttt{loc[i]} gives the relocation of the category \texttt{i}}
+\end{htmlonly}
+ \begin{code}
+
+ public void regroupCategories (double minExp)\begin{hide} {
+ int s0 = 0, j;
+ double somme;
+
+ nbCategories = 0;
+ int s = smin;
+ while (s <= smax) {
+ /* Merge categories to ensure that the number expected
+ in each category is >= minExp. */
+ if (nbExp[s] < minExp) {
+ s0 = s;
+ somme = nbExp[s];
+ while (somme < minExp && s < smax) {
+ nbExp[s] = 0.0;
+ ++s;
+ somme += nbExp[s];
+ }
+ nbExp[s] = somme;
+ for (j = s0; j <= s; j++)
+ loc[j] = s;
+
+ } else
+ loc[s] = s;
+
+ ++nbCategories;
+ ++s;
+ }
+ smin = loc[smin];
+
+ // Special case: the last category, if nbExp < minExp
+ if (nbExp[smax] < minExp) {
+ if (s0 > smin)
+ --s0;
+ nbExp[s0] += nbExp[smax];
+ nbExp[smax] = 0.0;
+ --nbCategories;
+ for (j = s0 + 1; j <= smax; j++)
+ loc[j] = s0;
+ smax = s0;
+ }
+ if (nbCategories <= 1)
+ throw new IllegalStateException ("nbCategories < 2");
+ }\end{hide}
+\end{code}
+ \begin{tabbb} Regroup categories as explained earlier, so that the expected
+ number of observations in each category is at least \texttt{minExp}.
+ We usually choose \texttt{minExp} = 10.
+ \end{tabbb}
+\begin{htmlonly}
+ \param{minExp}{mininum number of expected observations in each category}
+\end{htmlonly}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ int s, s0;
+ double somme;
+ final double EPSILON = 5.0E-16;
+ StringBuffer sb = new StringBuffer();
+ sb.append ("-----------------------------------------------" +
+ PrintfFormat.NEWLINE);
+ if (nbExp[smin] < EPSILON)
+ sb.append ("Only expected numbers larger than " +
+ PrintfFormat.g (6, 1, EPSILON) + " are printed" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Number of categories: " +
+ PrintfFormat.d (4, nbCategories) + PrintfFormat.NEWLINE +
+ "Expected numbers per category:" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE + "Category s nbExp[s]" +
+ PrintfFormat.NEWLINE);
+
+ // Do not print values < EPSILON
+ s = smin;
+ while (nbExp[s] < EPSILON)
+ s++;
+ int s1 = s;
+
+ s = smax;
+ while (nbExp[s] < EPSILON)
+ s--;
+ int s2 = s;
+
+ somme = 0.0;
+ for (s = s1 ; s <= s2; s++)
+ if (loc[s] == s) {
+ somme += nbExp[s];
+ sb.append (PrintfFormat.d (4, s) + " " +
+ PrintfFormat.f (18, 4, nbExp[s]) +
+ PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE + "Total expected number = " +
+ PrintfFormat.f (18, 2, somme) + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE +
+ "The groupings:" + PrintfFormat.NEWLINE +
+ " Category s loc[s]" + PrintfFormat.NEWLINE);
+ for (s = smin; s <= smax; s++) {
+ if ((s == smin) && (s > 0))
+ sb.append ("<= ");
+ else if ((s == smax) && (s < loc.length - 1))
+ sb.append (">= ");
+ else
+ sb.append (" ");
+ sb.append (PrintfFormat.d (4, s) + " " +
+ PrintfFormat.d (12, loc[s]) + PrintfFormat.NEWLINE);
+ }
+
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabbb} Provides a report on the categories.
+\hpierre{Ceci me semble un peu \'etrange. On devrait faire abstraction du
+ fait qu'il y a eu regroupement ou pas. Si pas encore de regroupement,
+ on devrait avoir loc[i]=i et nbCategories = au nombre original de categories. }
+\hrichard{J'ai compl\`etement r\'e\'ecrit cette fonction.}
+ \end{tabbb}
+\begin{htmlonly}
+ \return{the categories represented as a string}
+\end{htmlonly}
+ \begin{code}
+ \begin{hide} }\end{hide}
+ \end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Computing EDF test statistics}
+
+\begin{code}
+
+ public static double chi2 (double[] nbExp, int[] count,
+ int smin, int smax)\begin{hide} {
+ double diff, khi = 0.0;
+
+ for (int s = smin; s <= smax; s++) {
+ if (nbExp[s] <= 0.0) {
+ if (count[s] != 0)
+ throw new IllegalArgumentException (
+ "nbExp[s] = 0 and count[s] > 0");
+ }
+ else {
+ diff = count[s] - nbExp[s];
+ khi += diff * diff / nbExp[s];
+ }
+ }
+ return khi;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the chi-square statistic for the
+ observations $o_i$ in \texttt{count[smin...smax]}, for which the
+ corresponding expected values $e_i$ are in \texttt{nbExp[smin...smax]}.
+ Assuming that $i$ goes from 1 to $k$, where $k =$ \texttt{smax-smin+1}
+ is the number of categories, the chi-square statistic is defined as
+ \eq
+ X^2 = \sum_{i=1}^k \latex{\frac{(o_i - e_i)^2}{e_i}}\html{(o_i - e_i)^2/e_i}.
+ \latex{\eqlabel{eq:chi-square}}
+ \endeq
+ Under the hypothesis that the $e_i$ are the correct expectations and
+ if these $e_i$ are large enough, $X^2$ follows approximately the
+ chi-square distribution with $k-1$ degrees of freedom.
+ If some of the $e_i$ are too small, one can use
+ \texttt{OutcomeCategoriesChi2} to regroup categories.
+\end{tabb}
+\begin{htmlonly}
+ \param{nbExp}{numbers expected in each category}
+ \param{count}{numbers observed in each category}
+ \param{smin}{index of the first valid data in \texttt{count} and \texttt{nbExp}}
+ \param{smax}{index of the last valid data in \texttt{count} and \texttt{nbExp}}
+ \return{the $X^2$ statistic}
+\end{htmlonly}
+\begin{code}
+
+ public static double chi2 (OutcomeCategoriesChi2 cat, int[] count)\begin{hide} {
+ int[] newcount = new int[1 + cat.smax];
+ for (int s = cat.smin; s <= cat.smax; s++) {
+ newcount[cat.loc[s]] += count[s];
+ }
+
+ double diff, khi = 0.0;
+
+ for (int s = cat.smin; s <= cat.smax; s++) {
+ if (cat.nbExp[s] > 0.0) {
+ diff = newcount[s] - cat.nbExp[s];
+ khi += diff * diff / cat.nbExp[s];
+ }
+ }
+ newcount = null;
+ return khi;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the chi-square statistic for the
+ observations $o_i$ in \texttt{count}, for which the
+ corresponding expected values $e_i$ are in \texttt{cat}.
+ This assumes that \texttt{cat.regroupCategories} has been called before
+ to regroup categories in order to make sure that the expected numbers in each
+ category are large enough for the chi-square test.
+\end{tabb}
+\begin{htmlonly}
+ \param{cat}{numbers expected in each category}
+ \param{count}{numbers observed in each category}
+ \return{the $X^2$ statistic}
+\end{htmlonly}
+\begin{code}
+
+ public static double chi2 (IntArrayList data, DiscreteDistributionInt dist,
+ int smin, int smax, double minExp, int[] numCat)\begin{hide} {
+ int i;
+ int n = data.size();
+
+ // Find the first non-negligible probability term and fix
+ // the real smin. The linear search starts from the given smin.
+ i = smin;
+ while (dist.prob (i)*n <= DiscreteDistributionInt.EPSILON)
+ i++;
+ smin = i--;
+
+ // smax > smin is required
+ while (smax <= smin)
+ smax = 2*smax + 1;
+
+ // Allocate and fill the array of expected observations
+ // Each category s corresponds to a value s for which p(s)>0.
+ double[] nbExp = new double[smax+1];
+ do {
+ i++;
+ if (i > smax) {
+ smax *= 2;
+ double[] newNbExp = new double[smax + 1];
+ System.arraycopy (nbExp, smin, newNbExp, smin, nbExp.length - smin);
+ nbExp = newNbExp;
+ }
+ nbExp[i] = dist.prob (i)*n;
+ }
+ while (nbExp[i] > DiscreteDistributionInt.EPSILON);
+ smax = i - 1;
+
+ // Regroup the expected observations intervals
+ // satisfying np(s)>=minExp
+ OutcomeCategoriesChi2 cat = new OutcomeCategoriesChi2
+ (nbExp, smin, smax);
+ cat.regroupCategories (minExp);
+ if (numCat != null)
+ numCat[0] = cat.nbCategories;
+
+ // Count the number of observations in each categories.
+ int[] count = new int[cat.smax+1];
+ for (i = 0; i < count.length; i++)
+ count[i] = 0;
+ for (i = 0; i < n; i++) {
+ int s = data.get (i);
+ while (cat.loc[s] != s)
+ s = cat.loc[s];
+ count[s]++;
+ }
+
+ // Perform the chi-square test
+ return chi2 (cat.nbExp, count, cat.smin, cat.smax);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the chi-square statistic for the
+ observations stored in \texttt{data}, assuming that these observations follow
+ the discrete distribution \texttt{dist}. For \texttt{dist}, we assume that
+ there is one set $S=\{a, a+1,\dots, b-1, b\}$, where $a<b$ and $a\ge 0$,
+ for which $p(s)>0$ if $s\in S$ and $p(s)=0$ otherwise.
+
+ Generally, it is not possible to divide the integers in intervals satisfying
+ $nP(a_0\le s< a_1)=nP(a_1\le s< a_2)=\cdots=nP(a_{j-1}\le s< a_j)$
+ for a discrete distribution, where $n$ is the sample size, i.e.,
+ the number of
+ observations stored into \texttt{data}.
+ To perform a general chi-square test, the method starts
+ from \texttt{smin} and finds the first non-negligible
+ probability $p(s)\ge\epsilon$, where
+ $\epsilon=$ \clsexternalmethod{}{DiscreteDistributionInt}{EPSILON}{}.
+ It uses \texttt{smax} to allocate an array storing the
+ number of expected observations ($np(s)$) for each $s\ge$ \texttt{smin}.
+ Starting from $s=$ \texttt{smin}, the $np(s)$ terms are computed and
+ the allocated array grows if required until a negligible probability
+ term is found.
+ This gives the number of expected elements for
+ each category, where an outcome category corresponds here to
+ an interval in which sample observations could lie.
+ The categories are regrouped to have at least
+ \texttt{minExp} observations per category.
+ The method then counts the number of samples in each categories and calls
+ \method{chi2}{double[],int[],int,int} to get the chi-square test
+ statistic. If \texttt{numCat} is not
+ \texttt{null}, the number of categories after regrouping is returned
+ in \texttt{numCat[0]}. The number of degrees of freedom is equal to
+ \texttt{numCat[0]-1}. We usually choose \texttt{minExp} = 10.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{observations, not necessarily sorted}
+ \param{dist}{assumed probability distribution}
+ \param{smin}{estimated minimum value of $s$ for which $p(s)>0$}
+ \param{smax}{estimated maximum value of $s$ for which $p(s)>0$}
+ \param{minExp}{minimum number of expected observations in each
+ interval}
+ \param{numCat}{one-element array that will be filled with the number of
+ categories after regrouping}
+ \return{the chi-square statistic for a discrete distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double chi2Equal (double nbExp, int[] count,
+ int smin, int smax)\begin{hide} {
+
+ double diff, khi = 0.0;
+ for (int s = smin; s <= smax; s++) {
+ diff = count[s] - nbExp;
+ khi += diff * diff;
+ }
+ return khi / nbExp;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{chi2}{double[],int[],int,int},
+ except that the expected
+ number of observations per category is assumed to be the same for
+ all categories, and equal to \texttt{nbExp}.
+\end{tabb}
+\begin{htmlonly}
+ \param{nbExp}{number of expected observations in each category (or interval)}
+ \param{count}{number of counted observations in each category}
+ \param{smin}{index of the first valid data in \texttt{count} and \texttt{nbExp}}
+ \param{smax}{index of the last valid data in \texttt{count} and \texttt{nbExp}}
+ \return{the $X^2$ statistic}
+\end{htmlonly}
+\begin{code}
+
+ public static double chi2Equal (DoubleArrayList data, double minExp)\begin{hide} {
+ int n = data.size();
+ if (n < (int)Math.ceil (minExp))
+ throw new IllegalArgumentException ("Not enough observations");
+ double p = minExp/n;
+ int m = (int)Math.ceil (1.0/p);
+ // to avoid an exception when data[i] = 1/p, reserve one element more
+ int[] count = new int[m + 1];
+ for (int i = 0; i < n; i++) {
+ int j = (int)Math.floor (data.get (i)/p);
+ count[j]++;
+ }
+ // put the elements in count[m] where they belong: in count[m-1]
+ count[m - 1] += count[m];
+ return chi2Equal (minExp, count, 0, m - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the chi-square statistic for a continuous distribution.
+ Here, the equiprobable case can be used. Assuming that \texttt{data} contains
+ observations coming from the uniform distribution, the $[0,1]$ interval
+ is divided into $1/p$ subintervals, where $p=$ \texttt{minExp}$/n$, $n$
+ being the sample size, i.e., the number of observations stored in
+ \texttt{data}. For each subinterval, the method counts the number of
+ contained observations and the chi-square statistic is computed
+ using \method{chi2Equal}{double,int[],int,int}.
+ We usually choose \texttt{minExp} = 10.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations in $[0,1)$}
+ \param{minExp}{minimum number of expected observations in each subintervals}
+ \return{the chi-square statistic for a continuous distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double chi2Equal (DoubleArrayList data)\begin{hide} {
+ return chi2Equal (data, 10.0);
+}\end{hide}
+\end{code}
+\begin{tabb} Equivalent to \texttt{chi2Equal (data, 10)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations in $[0,1)$}
+ \return{the chi-square statistic for a continuous distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static int scan (DoubleArrayList sortedData, double d)\begin{hide} {
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ int m = 1, j = 0, i = -1;
+ double High = 0.0;
+
+ while (j < (n-1) && High < 1.0) {
+ ++i;
+
+ High = u[i] + d;
+ while (j < n && u[j] < High)
+ ++j;
+ // j is now the index of the first obs. to the right of High.
+ if (j - i > m)
+ m = j - i;
+ }
+ return m;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the scan statistic $S_n (d)$,
+ defined in \latex{(\ref{eq:scan})}\html{\clsexternalmethod{}{FBar}{scan}{}}.
+ Let $U$ be the $n$ observations contained into \texttt{sortedData}.
+ The $n$ observations in $U[0..n-1]$ must be real numbers
+ in the interval $[0,1]$, sorted in increasing order.
+ (See \clsexternalmethod{}{FBar}{scan}{} for the distribution function of $S_n (d)$).
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{sorted array of real-valued observations in the interval $[0,1]$}
+ \param{d}{length of the test interval ($\in(0,1)$)}
+ \return{the scan statistic}
+\end{htmlonly}
+\begin{code}
+
+ public static double cramerVonMises (DoubleArrayList sortedData)\begin{hide} {
+ double w, w2;
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ if (n <= 0) {
+ System.err.println ("cramerVonMises: n <= 0");
+ return 0.0;
+ }
+
+ w2 = 1.0 / (12 * n);
+ for (int i = 0; i < n; i++) {
+ w = u[i] - (i + 0.5) / n;
+ w2 += w * w;
+ }
+ return w2;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the Cram\'er-von Mises statistic $W_n^2$\html{. It is}
+ \latex{(see \cite{tDUR73a,tSTE70a,tSTE86b}),} defined by
+ \begin {equation}
+ W_n^2 = \latex{\frac{1}{ 12n}}\html{1/(12n)} +
+ \sum_{j=0}^{n-1} \left (U_{(j)} - \latex{\frac{(j+0.5) }{ n}}\html{(j+0.5)/n}\right)^2,
+ \latex{\eqlabel {eq:CraMis}}
+ \end {equation}
+ assuming that \texttt{sortedData} contains $U_{(0)},\dots,U_{(n-1)}$
+ sorted in increasing order.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \return{the Cram\'er-von Mises statistic}
+\end{htmlonly}
+\begin{code}
+
+ public static double watsonG (DoubleArrayList sortedData)\begin{hide} {
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ double sumZ;
+ double d2;
+ double dp, g;
+ double unSurN = 1.0 / n;
+
+ if (n <= 0) {
+ System.err.println ("watsonG: n <= 0");
+ return 0.0;
+ }
+
+ // degenerate case n = 1
+ if (n == 1)
+ return 0.0;
+
+ // We assume that u is already sorted.
+ dp = sumZ = 0.0;
+ for (int i = 0; i < n; i++) {
+ d2 = (i + 1) * unSurN - u[i];
+ if (d2 > dp)
+ dp = d2;
+ sumZ += u[i];
+ }
+ sumZ = sumZ * unSurN - 0.5;
+ g = Math.sqrt ((double) n) * (dp + sumZ);
+ return g;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the Watson statistic $G_n$\html{. It is}
+ \latex{(see \cite{tWAT76a,tDAR83a}),} defined by
+ \begin {eqnarray}
+ G_n &=& \sqrt{n} \max_{\latex{\rule{0pt}{7pt}} 0\le j \le n-1}
+ \left\{ (j+1)/n -
+ U_{(j)} + \overline{U}_n - 1/2 \right\}
+ \latex{\eqlabel {eq:WatsonG}} \\[6pt]
+ &=& \sqrt{n}\left (D_n^+ + \overline{U}_n - 1/2\right), \nonumber
+ \end {eqnarray}
+ where $\overline{U}_n$ is the average of the observations $U_{(j)}$,
+ assuming that \texttt{sortedData} contains the sorted $U_{(0)},\dots,U_{(n-1)}$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \return{the Watson statistic $G_n$}
+\end{htmlonly}
+\begin{code}
+
+ public static double watsonU (DoubleArrayList sortedData)\begin{hide} {
+ double sumZ, w, w2, u2;
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+
+ if (n <= 0) {
+ System.err.println ("watsonU: n <= 0");
+ return 0.0;
+ }
+
+ // degenerate case n = 1
+ if (n == 1)
+ return 1.0 / 12.0;
+
+ sumZ = 0.0;
+ w2 = 1.0 / (12 * n);
+ for (int i = 0; i < n; i++) {
+ sumZ += u[i];
+ w = u[i] - (i + 0.5) / n;
+ w2 += w * w;
+ }
+ sumZ = sumZ / n - 0.5;
+ u2 = w2 - sumZ * sumZ * n;
+ return u2;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the Watson statistic $U_n^2$\html{. It is}
+ \latex{(see \cite{tDUR73a,tSTE70a,tSTE86b}),} defined by
+ \begin {eqnarray}
+ W_n^2 &=& \latex{\frac{1}{ 12n}}\html{1/(12n)} +
+ \sum_{j=0}^{n-1} \left\{U_{(j)} - \latex{\frac{(j + 0.5)}{ n}\right}
+ \html{(j + 0.5)/n}\}^2, \\
+ U_n^2 &=& W_n^2 - n\left (\overline {U}_n - 1/2\right)^2.
+ \latex{\eqlabel {eq:WatsonU}}
+ \end {eqnarray}
+ where $\overline {U}_n$ is the average of the observations $U_{(j)}$,
+ assuming that \texttt{sortedData} contains the sorted
+ $U_{(0)},\dots,U_{(n-1)}$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \return{the Watson statistic $U_n^2$}
+\end{htmlonly}
+\begin{detailed}
+\begin{code}
+
+
+ public static double EPSILONAD = Num.DBL_EPSILON/2;
+\end{code}
+\begin{tabb} Used by \method{andersonDarling}{DoubleArrayList}.
+\texttt{Num.DBL\_EPSILON} is usually $2^{-52}$.
+\end{tabb}
+\end{detailed}
+\begin{code}
+
+ public static double andersonDarling (DoubleArrayList sortedData)\begin{hide} {
+ double[] v = sortedData.elements();
+ return andersonDarling (v);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the Anderson-Darling statistic $A_n^2$
+(see method \method{andersonDarling}{double[]}).
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \return{the Anderson-Darling statistic}
+\end{htmlonly}
+\begin{code}
+
+ public static double andersonDarling (double[] sortedData)\begin{hide} {
+ double u1;
+ double u, a2;
+ int n = sortedData.length;
+
+ if (n <= 0) {
+ System.err.println ("andersonDarling: n <= 0");
+ return 0.0;
+ }
+
+ a2 = 0.0;
+ for (int i = 0; i < n; i++) {
+ u = sortedData[i];
+ u1 = 1.0 - u;
+ if (u < EPSILONAD)
+ u = EPSILONAD;
+ else if (u1 < EPSILONAD)
+ u1 = EPSILONAD;
+ a2 += (2*i + 1)*Math.log (u) + (1 + 2*(n - i - 1))*
+ Math.log (u1);
+ }
+ a2 = -n - a2 / n;
+ return a2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the Anderson-Darling statistic $A_n^2$\html{.
+ It is}
+ \latex{(see \cite{tLEW61a,tSTE86b,tAND52a}),} defined by
+ \begin {eqnarray*}
+ A_n^2 &=& -n -\latex{\frac{1}{ n}}\html{1/n\quad} \sum_{j=0}^{n-1}
+ \left\{ (2j+1)\ln (U_{(j)})
+ + (2n-1-2j) \ln (1-U_{(j)}) \right\}, \eqlabel {eq:Andar}
+ \end {eqnarray*}
+ assuming that \texttt{sortedData} contains $U_{(0)},\dots,U_{(n-1)}$
+ sorted in increasing order.
+ \begin{detailed}
+ When computing $A_n^2$,
+ all observations $U_i$ are projected on the interval
+ $[\epsilon,\,1-\epsilon]$ for some $\epsilon > 0$, in order to
+ avoid numerical overflow when taking the logarithm of $U_i$ or
+ $1-U_i$. The variable \texttt{EPSILONAD} gives the value of $\epsilon$.
+ \hpierre {Autre choix possible: cacher tout cela.
+ Mais il ne semble pas y avoir d'avantage \`a faire cela,
+ tandis que le laisser ici peut permettre aux ``experts'' de faire
+ \'eventuellement des exp\'eriences avec le choix de $\epsilon$. }
+ \end{detailed}
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \return{the Anderson-Darling statistic}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] andersonDarling (double[] data,
+ ContinuousDistribution dist)
+ \begin{hide} {
+ int n = data.length;
+ double[] U = new double[n];
+ for (int i = 0; i < n; i++) {
+ U[i] = dist.cdf(data[i]);
+ }
+
+ Arrays.sort(U);
+ double x = GofStat.andersonDarling(U);
+ double v = AndersonDarlingDistQuick.barF(n, x);
+ double[] res = {x, v};
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the Anderson-Darling statistic $A_n^2$
+and the corresponding $p$-value $p$. The $n$ (unsorted) observations in \texttt{data}
+are assumed to be independent and to come from the continuous
+distribution \texttt{dist}.
+Returns the 2-elements array [$A_n^2$, $p$].
+ \end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations}
+ \param{dist}{assumed distribution of the observations}
+ \return{the array $[A_n^2$, $p]$.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] kolmogorovSmirnov (double[] sortedData)\begin{hide} {
+ DoubleArrayList v = new DoubleArrayList(sortedData);
+ return kolmogorovSmirnov (v);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the Kolmogorov-Smirnov (KS) test statistics
+ $D_n^+$, $D_n^-$, and $D_n$ (see method
+ \method{kolmogorovSmirnov}{DoubleArrayList}). Returns the array [$D_n^+$, $D_n^-$, $D_n$].
+\end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \return{the array [$D_n^+$, $D_n^-$, $D_n$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] kolmogorovSmirnov (DoubleArrayList sortedData)\begin{hide} {
+ double[] ret = new double[3];
+ int n = sortedData.size();
+
+ if (n <= 0) {
+ ret[0] = ret[1] = ret[2] = 0.0;
+ System.err.println ("kolmogorovSmirnov: n <= 0");
+ return ret;
+ }
+
+ double[] retjo = kolmogorovSmirnovJumpOne (sortedData, 0.0);
+ ret[0] = retjo[0];
+ ret[1] = retjo[1];
+ if (ret[1] > ret[0])
+ ret[2] = ret[1];
+ else
+ ret[2] = ret[0];
+
+ return ret;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the Kolmogorov-Smirnov (KS) test statistics
+ $D_n^+$, $D_n^-$, and $D_n$\html{. It is}
+ defined by
+ \begin {eqnarray}
+ D_n^+ &=& \max_{0\le j\le n-1} \left ((j+1)/n - U_{(j)}\right),
+ \eqlabel{eq:DNp} \\
+ D_n^- &=& \max_{0\le j\le n-1} \left (U_{(j)} - j/n\right),
+ \eqlabel{eq:DNm} \\
+ D_n &=& \max\ (D_n^+, D_n^-). \eqlabel{eq:DN}
+ \end {eqnarray}
+ and returns an array of length 3 that contains [$D_n^+$, $D_n^-$, $D_n$].
+ These statistics compare the empirical distribution of
+ $U_{(1)},\dots,U_{(n)}$, which are assumed to be in \texttt{sortedData},
+ with the uniform distribution over $[0,1]$.
+\hrichard {Pourquoi avoir enlev\'e les calculs des EDF de ce fichier et
+ l'avoir mis dans gofw? On calcule d\'ej\`a toutes les stats EDF
+ explicitement.}
+\hpierre {Simplement pour \'eviter d'introduire \texttt{TestType},
+ \texttt{TestArray}, etc. dans ce module, et pouvoir tout cacher
+ cela ensemble \`a la fin de \texttt{gofw}. Ces choses sont commodes
+ pour Testu01, mais trop sp\'ecialis\'ees et pas trop int\'eressantes
+ pour la plupart des gens. }
+\end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \return{the array [$D_n^+$, $D_n^-$, $D_n$]}
+\end{htmlonly}
+\begin{code}
+
+ public static void kolmogorovSmirnov (double[] data,
+ ContinuousDistribution dist,
+ double[] sval,
+ double[] pval)\begin{hide} {
+ int n = data.length;
+ double[] T = new double[n];
+ for (int i = 0; i < n; i++) {
+ T[i] = dist.cdf (data[i]);
+ }
+
+ Arrays.sort (T);
+ double[] statks = GofStat.kolmogorovSmirnov (T);
+ for (int i = 0; i < 3; i++) {
+ sval[i] = statks[i];
+ }
+ pval[2] = KolmogorovSmirnovDistQuick.barF (n, sval[2]);
+ pval[1] = KolmogorovSmirnovPlusDist.barF (n, sval[1]);
+ pval[0] = KolmogorovSmirnovPlusDist.barF (n, sval[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the KolmogorovSmirnov (KS) test statistics and their $p$-values.
+ This is to compare the empirical distribution of the (unsorted) observations
+ in \texttt{data}
+ with the theoretical distribution \texttt{dist}. The KS statistics
+ $D_n^+$, $D_n^-$ and $D_n$ are returned in \texttt{sval[0]}, \texttt{sval[1]},
+ and \texttt{sval[2]} respectively, and their corresponding $p$-values
+ are returned in \texttt{pval[0]}, \texttt{pval[1]}, and \texttt{pval[2]}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{array of observations to be tested}
+ \param{dist}{assumed distribution of the observations}
+ \param{sval}{values of the 3 KS statistics}
+ \param{pval}{$p$-values for the 3 KS statistics}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] kolmogorovSmirnovJumpOne (DoubleArrayList sortedData,
+ double a)\begin{hide} {
+ /* Statistics KS+ and KS-. Case with 1 jump at a, near the lower tail of
+ the distribution. */
+
+ double[] u = sortedData.elements();
+ int n = sortedData.size();
+ int j, i;
+ double d2, d1, unSurN;
+ double[] ret = new double[2];
+
+ if (n <= 0) {
+ ret[0] = ret[1] = 0.0;
+ System.err.println ("kolmogorovSmirnovJumpOne: n <= 0");
+ return ret;
+ }
+
+ ret[0] = 0.0;
+ ret[1] = 0.0;
+ unSurN = 1.0 / n;
+ j = 0;
+
+ while (j < n && u[j] <= a + EPSILOND) ++j;
+
+ for (i = j - 1; i < n; i++) {
+ if (i >= 0) {
+ d1 = (i + 1) * unSurN - u[i];
+ if (d1 > ret[0])
+ ret[0] = d1;
+ }
+ if (i >= j) {
+ d2 = u[i] - i * unSurN;
+ if (d2 > ret[1])
+ ret[1] = d2;
+ }
+ }
+ return ret;
+ }\end{hide}
+\end{code}
+\begin{tabb} Compute the KS statistics $D_n^+(a)$ and $D_n^-(a)$ defined in
+ the description of the method
+ \clsexternalmethod{}{FDist}{kolmogorovSmirnovPlusJumpOne}{}, assuming that $F$ is the
+ uniform distribution over $[0,1]$ and that
+ $U_{(1)},\dots,U_{(n)}$ are in \texttt{sortedData}.
+ Returns the array [$D_n^+$, $D_n^-$].
+ \end{tabb}
+\begin{htmlonly}
+ \param{sortedData}{array of sorted real-valued observations in the interval $[0,1]$}
+ \param{a}{size of the jump}
+ \return{the array [$D_n^+$, $D_n^-$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double pDisc (double pL, double pR)\begin{hide} {
+ double p;
+
+ if (pR < pL)
+ p = pR;
+ else if (pL > 0.5)
+ p = 0.5;
+ else
+ p = 1.0 - pL;
+ // Note: si p est tres proche de 1, on perd toute la precision ici!
+ // Note2: je ne pense pas que cela puisse se produire a cause des if (RS)
+ return p;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Computes a variant of the $p$-value $p$ whenever a test statistic
+ has a {\em discrete\/} probability distribution.
+ This $p$-value is defined as follows:
+ \begin{eqnarray*}
+ p_L & = & P[Y \le y] \\
+ p_R & = & P[Y \ge y] \\[6pt]
+%begin{latexonly}
+ p & = & \left\{ \begin{array}{l@{\qquad}l}
+ p_R, & \mbox{if } p_R < p_L \\[6pt]
+ 1 - p_L, & \mbox{if }
+ p_R \ge p_L \mbox{ and } p_L < 0.5 \\[6pt]
+ 0.5 & \mbox{otherwise.}
+ \end{array} \right.
+%end{latexonly}
+ \end{eqnarray*}
+\begin{htmlonly}
+ \[\begin{array}{rll}
+ p =& p_R, &\qquad\mbox{if } p_R < p_L, \\
+ p =& 1 - p_L, &\qquad\mbox{if } p_R \ge p_L \mbox{ and } p_L < 0.5, \\
+ p =& 0.5 &\qquad\mbox{otherwise.}
+ \end{array} \]
+\end{htmlonly}
+ The function takes $p_L$ and $p_R$ as input and returns $p$.
+\end{tabb}
+\begin{htmlonly}
+ \param{pL}{left $p$-value}
+ \param{pR}{right $p$-value}
+ \return{the $p$-value for a test on a discrete distribution}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/gof/KernelDensity.java b/source/umontreal/iro/lecuyer/gof/KernelDensity.java
new file mode 100644
index 0000000..bf73324
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/KernelDensity.java
@@ -0,0 +1,101 @@
+
+
+/*
+ * Class: KernelDensity
+ * Description: Kernel density estimators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.gof;
+ import umontreal.iro.lecuyer.probdist.*;
+
+import umontreal.iro.lecuyer.randvar.KernelDensityGen;
+
+
+/**
+ * This class provides methods to compute a kernel density estimator from a set
+ * of <SPAN CLASS="MATH"><I>n</I></SPAN> individual observations
+ * <SPAN CLASS="MATH"><I>x</I><SUB>0</SUB>,…, <I>x</I><SUB>n-1</SUB></SPAN>, and returns its value
+ * at <SPAN CLASS="MATH"><I>m</I></SPAN> selected points. For details on how the kernel density is defined,
+ * and how to select the kernel and the bandwidth <SPAN CLASS="MATH"><I>h</I></SPAN>,
+ * see the documentation of class
+ * {@link umontreal.iro.lecuyer.randvar.KernelDensityGen KernelDensityGen}
+ * in package <TT>randvar</TT>.
+ *
+ */
+public class KernelDensity {
+
+ private static double estimate (EmpiricalDist dist,
+ ContinuousDistribution kern,
+ double h, double y) {
+ // Computes and returns the kernel density estimate at $y$, where the
+ // kernel is the density kern.density(x), and the bandwidth is $h$.
+ double z;
+ double a = kern.getXinf(); // lower limit of density
+ double b = kern.getXsup(); // upper limit of density
+ double sum = 0;
+ int n = dist.getN();
+ for (int i = 0; i < n; i++) {
+ z = (y - dist.getObs(i))/h;
+ if ((z >= a) && (z <= b))
+ sum += kern.density(z);
+ }
+
+ sum /= (h*n);
+ return sum;
+ }
+
+
+ /**
+ * Given the empirical distribution <TT>dist</TT>, this method computes the
+ * kernel density estimate at each of the <SPAN CLASS="MATH"><I>m</I></SPAN> points <TT>Y[<SPAN CLASS="MATH"><I>j</I></SPAN>]</TT>,
+ *
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…,(<I>m</I> - 1)</SPAN>, where <SPAN CLASS="MATH"><I>m</I></SPAN> is the length of <TT>Y</TT>, the kernel
+ * is <TT>kern.density(x)</TT>,
+ * and the bandwidth is <SPAN CLASS="MATH"><I>h</I></SPAN>. Returns the estimates as an array of <SPAN CLASS="MATH"><I>m</I></SPAN> values.
+ *
+ */
+ public static double[] computeDensity (EmpiricalDist dist,
+ ContinuousDistribution kern,
+ double h, double[] Y) {
+ int m = Y.length;
+ double[] u = new double[m];
+ for (int j = 0; j < m; j++)
+ u[j] = estimate (dist, kern, h, Y[j]);
+ return u;
+ }
+
+
+ /**
+ * Similar to method
+ * {@link #computeDensity((EmpiricalDist,ContinuousDistribution,double,double[])) computeDensity} ,
+ * but the bandwidth <SPAN CLASS="MATH"><I>h</I></SPAN> is obtained from the method
+ * {@link umontreal.iro.lecuyer.randvar.KernelDensityGen#getBaseBandwidth(EmpiricalDist) KernelDensityGen.getBaseBandwidth}<TT>(dist)</TT> in package <TT>randvar</TT>.
+ *
+ */
+ public static double[] computeDensity (EmpiricalDist dist,
+ ContinuousDistribution kern,
+ double[] Y) {
+ double h = KernelDensityGen.getBaseBandwidth(dist);
+ return computeDensity (dist, kern, h, Y);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/gof/KernelDensity.tex b/source/umontreal/iro/lecuyer/gof/KernelDensity.tex
new file mode 100644
index 0000000..8ed747c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/KernelDensity.tex
@@ -0,0 +1,112 @@
+\defmodule {KernelDensity}
+
+This class provides methods to compute a kernel density estimator from a set
+of $n$ individual observations $x_0, \ldots, x_{n-1}$, and returns its value
+at $m$ selected points. For details on how the kernel density is defined,
+and how to select the kernel and the bandwidth $h$,
+see the documentation of class
+\externalclass{umontreal.iro.lecuyer.randvar}{KernelDensityGen}
+in package \texttt{randvar}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: KernelDensity
+ * Description: Kernel density estimators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.gof;
+ import umontreal.iro.lecuyer.probdist.*;
+\begin{hide}
+import umontreal.iro.lecuyer.randvar.KernelDensityGen;
+\end{hide}
+
+public class KernelDensity \begin{hide} {
+
+ private static double estimate (EmpiricalDist dist,
+ ContinuousDistribution kern,
+ double h, double y) {
+ // Computes and returns the kernel density estimate at $y$, where the
+ // kernel is the density kern.density(x), and the bandwidth is $h$.
+ double z;
+ double a = kern.getXinf(); // lower limit of density
+ double b = kern.getXsup(); // upper limit of density
+ double sum = 0;
+ int n = dist.getN();
+ for (int i = 0; i < n; i++) {
+ z = (y - dist.getObs(i))/h;
+ if ((z >= a) && (z <= b))
+ sum += kern.density(z);
+ }
+
+ sum /= (h*n);
+ return sum;
+ }\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static double[] computeDensity (EmpiricalDist dist,
+ ContinuousDistribution kern,
+ double h, double[] Y)\begin{hide} {
+ int m = Y.length;
+ double[] u = new double[m];
+ for (int j = 0; j < m; j++)
+ u[j] = estimate (dist, kern, h, Y[j]);
+ return u;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+Given the empirical distribution \texttt{dist}, this method computes the
+kernel density estimate at each of the $m$ points \texttt{Y[$j$]},
+ $j= 0, 1, \ldots, (m-1)$, where $m$ is the length of \texttt{Y}, the kernel
+ is \texttt{kern.density(x)},
+ and the bandwidth is $h$. Returns the estimates as an array of $m$ values.
+\end{tabb}
+\begin{code}
+
+ public static double[] computeDensity (EmpiricalDist dist,
+ ContinuousDistribution kern,
+ double[] Y)\begin{hide} {
+ double h = KernelDensityGen.getBaseBandwidth(dist);
+ return computeDensity (dist, kern, h, Y);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+Similar to method
+\method{computeDensity}{(EmpiricalDist,ContinuousDistribution,double,double[])}
+\begin{latexonly}{ above}\end{latexonly},
+but the bandwidth $h$ is obtained from the method
+\clsexternalmethod{umontreal.iro.lecuyer.randvar}{KernelDensityGen}{getBaseBandwidth}{EmpiricalDist}\texttt{(dist)} in package \texttt{randvar}.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/gof/guidegof.bbl b/source/umontreal/iro/lecuyer/gof/guidegof.bbl
new file mode 100644
index 0000000..c57c069
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/guidegof.bbl
@@ -0,0 +1,104 @@
+\begin{thebibliography}{10}
+
+\bibitem{tAND95b}
+N.~H. Anderson and D.~M. Titterington.
+\newblock A comparison of two statistics for detecting clustering in one
+ dimension.
+\newblock {\em Journal of Statistical Computation and Simulation}, 53:103--125,
+ 1995.
+
+\bibitem{tAND52a}
+T.~W. Anderson and D.~A. Darling.
+\newblock Asymptotic theory of certain goodness of fit criteria based on
+ stochastic processes.
+\newblock {\em Annals of Mathematical Statistics}, 23:193--212, 1952.
+
+\bibitem{tDAR83a}
+D.~A. Darling.
+\newblock On the asymptotic distribution of {W}atson's statistic.
+\newblock {\em The Annals of Statistics}, 11(4):1263--1266, 1983.
+
+\bibitem{tDUR73a}
+J.~Durbin.
+\newblock {\em Distribution Theory for Tests Based on the Sample Distribution
+ Function}.
+\newblock SIAM CBMS-NSF Regional Conference Series in Applied Mathematics.
+ SIAM, Philadelphia, PA, 1973.
+
+\bibitem{tGLA89a}
+J.~Glaz.
+\newblock Approximations and bounds for the distribution of the scan statistic.
+\newblock {\em Journal of the American Statistical Association}, 84:560--566,
+ 1989.
+
+\bibitem{tGLA01a}
+J.~Glaz, J.~Naus, and S.~Wallenstein.
+\newblock {\em Scan statistics}.
+\newblock Springer Series in Statistics. Springer, New York, NY, 2001.
+
+\bibitem{rKNU98a}
+D.~E. Knuth.
+\newblock {\em The Art of Computer Programming, Volume 2: Seminumerical
+ Algorithms}.
+\newblock Addison-Wesley, Reading, MA, third edition, 1998.
+
+\bibitem{sLAW00a}
+A.~M. Law and W.~D. Kelton.
+\newblock {\em Simulation Modeling and Analysis}.
+\newblock McGraw-Hill, New York, NY, third edition, 2000.
+
+\bibitem{iLEC01t}
+P.~L'Ecuyer and R.~Simard.
+\newblock {\em {TestU01}: A Software Library in {ANSI C} for Empirical Testing
+ of Random Number Generators}, 2002.
+\newblock Software {user's} guide. Available at
+ \url{http://www.iro.umontreal.ca/~lecuyer}.
+
+\bibitem{tLEW61a}
+P.~A.~W. Lewis.
+\newblock Distribution of the {A}nderson-{D}arling statistic.
+\newblock {\em Annals of Mathematical Statistics}, 32:1118--1124, 1961.
+
+\bibitem{rMAR85a}
+G.~Marsaglia.
+\newblock A current view of random number generators.
+\newblock In L.~Billard, editor, {\em Computer Science and Statistics,
+ Sixteenth Symposium on the Interface}, pages 3--10, North-Holland, Amsterdam,
+ 1985. Elsevier Science Publishers.
+
+\bibitem{tREA88a}
+T.~R.~C. Read and N.~A.~C. Cressie.
+\newblock {\em Goodness-of-Fit Statistics for Discrete Multivariate Data}.
+\newblock Springer Series in Statistics. Springer-Verlag, New York, NY, 1988.
+
+\bibitem{tSTE70a}
+M.~A. Stephens.
+\newblock Use of the {K}olmogorov-{S}mirnov, {C}ram{\'e}r-{V}on {M}ises and
+ related statistics without extensive tables.
+\newblock {\em Journal of the Royal Statistical Society, Series B},
+ 33(1):115--122, 1970.
+
+\bibitem{tSTE86b}
+M.~S. Stephens.
+\newblock Tests based on {EDF} statistics.
+\newblock In R.~B. D'Agostino and M.~S. Stephens, editors, {\em Goodness-of-Fit
+ Techniques}. Marcel Dekker, New York and Basel, 1986.
+
+\bibitem{tSTE86a}
+M.~S. Stephens.
+\newblock Tests for the uniform distribution.
+\newblock In R.~B. D'Agostino and M.~S. Stephens, editors, {\em Goodness-of-Fit
+ Techniques}, pages 331--366. Marcel Dekker, New York and Basel, 1986.
+
+\bibitem{tWAL87a}
+S.~R. Wallenstein and N.~Neff.
+\newblock An approximation for the distribution of the scan statistic.
+\newblock {\em Statistics in Medicine}, 6:197--207, 1987.
+
+\bibitem{tWAT76a}
+G.~S. Watson.
+\newblock Optimal invariant tests for uniformity.
+\newblock In {\em Studies in Probability and Statistics}, pages 121--127. North
+ Holland, Amsterdam, 1976.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/gof/guidegof.tex b/source/umontreal/iro/lecuyer/gof/guidegof.tex
new file mode 100644
index 0000000..4b3e39d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/guidegof.tex
@@ -0,0 +1,44 @@
+\documentclass[12pt]{article}
+
+\usepackage{amsmath}
+\usepackage{amssymb}
+\usepackage{alltt}
+\usepackage{html}
+\usepackage{ssj}
+
+\mytwoheads\dateheadtrue
+
+\newcommand{\cH}{\latex{\mathcal{H}}\html{H}}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+\title{gof}{Goodness-of-fit test Statistics}
+
+This package provides facilities for performing and
+reporting different types of univariate goodness-of-fit statistical tests.
+
+\vfill
+\end{titlepage}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+
+\include{overview}
+
+\include{FDist}
+\include{FBar}
+\include{KernelDensity}
+
+\include{GofStat}
+\include{GofFormat}
+
+\bibliographystyle{plain}
+\bibliography{stat,random,simul,math,ift}
+
+\end{document}
+
diff --git a/source/umontreal/iro/lecuyer/gof/overview.tex b/source/umontreal/iro/lecuyer/gof/overview.tex
new file mode 100644
index 0000000..a8e441a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/gof/overview.tex
@@ -0,0 +1,122 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+
+This package contains tools for performing
+univariate \emph{goodness-of-fit} (GOF) statistical tests.
+% and collect statistics.
+Methods for computing (or approximating) the distribution
+function $F(x)$ of certain GOF test statistics, as well as their
+complementary distribution function $\bar F(x) = 1 - F(x)$, are
+implemented in classes of package
+\externalclass{umontreal.iro.lecuyer}{probdist}.
+Tools for computing the GOF test statistics and the corresponding
+$p$-values, and for formating the results, are provided in classes
+\externalclass{umontreal.iro.lecuyer.gof}{GofStat} and
+\externalclass{umontreal.iro.lecuyer.gof}{GofFormat}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsection* {Goodness-of-fit tests}
+
+% The \texttt{GofStat} class provides tools for computing {\em goodness-of-fit\/}
+We are concerned here with GOF test statistics for testing the hypothesis
+$\cH_0$ that a sample of $N$ observations
+$X_1,\dots,X_N$ comes from a
+given univariate probability distribution $F$.
+We consider tests such as those of Kolmogorov-Smirnov, Anderson-Darling,
+Cr\'amer-von Mises, etc.
+These test statistics generally measure, in different ways, the
+distance between a {\em continuous\/} distribution function $F$ and
+the {\em empirical distribution function\/}
+(EDF) $\hat F_N$ of $X_1,\dots,X_N$.
+They are also called EDF test statistics.
+The observations $X_i$ are usually transformed into $U_i = F (X_i)$,
+which satisfy $0\le U_i\le 1$ and which
+follow the $U(0,1)$ distribution under $\cH_0$.
+(This is called the {\em probability integral transformation}.)
+Methods for applying this transformation, as well as other types of
+transformations, to the observations $X_i$ or $U_i$
+are provided in \externalclass{umontreal.iro.lecuyer.gof}{GofStat}.
+
+Then the GOF tests are applied to the $U_i$ sorted by increasing order.
+The corresponding $p$-values are easily computed by calling the appropriate
+methods in the classes of package \externalclass{umontreal.iro.lecuyer}{probdist}.
+If a GOF test statistic $Y$ has a continuous distribution under
+$\cH_0$ and takes the value $y$, its (right) $p$-value is defined as
+$p = P[Y \ge y \mid \cH_0]$. The test usually rejects
+$\cH_0$ if $p$
+is deemed too close to 0 (for a one-sided test) or too close to 0 or 1
+(for a two-sided test).
+
+In the case where $Y$ has a {\em discrete distribution\/}
+under $\cH_0$, we distinguish the {\em right $p$-value\/}
+$p_R = P[Y \ge y \mid \cH_0]$ and the {\em left $p$-value\/}
+$p_L = P[Y \le y \mid \cH_0]$.
+We then define the $p$-value for a two-sided test as
+\begin{htmlonly}
+\[\begin{array}{rll}
+ p=&p_R&\qquad\mbox{if } p_R < p_L,\\
+ p=&1 - p_L&\qquad\mbox{if } p_R \ge p_L\mbox{ and }p_L < 0.5,\\
+ p=&0.5 &\qquad\mbox{otherwise.}
+\end{array}\]
+\end{htmlonly}
+\begin{latexonly}
+\begin{eqnarray}
+ p & = & \left\{
+ \begin{array}{l@{\qquad}l}
+ p_R, & \mbox{if } p_R < p_L \\[6pt]
+ 1 - p_L, & \mbox{if } p_R \ge p_L \mbox{ and } p_L < 0.5 \\[6pt]
+ 0.5 & \mbox{otherwise.}
+ \end{array}\right. \label{eq:pdisc}
+\end{eqnarray}
+\end{latexonly}
+Why such a definition?
+Consider for example a Poisson random variable $Y$ with mean 1
+under $\cH_0$.
+If $Y$ takes the value 0, the right $p$-value is
+$p_R = P[Y \ge 0 \mid \cH_0] = 1$. In the uniform case, this would
+obviously lead to rejecting $\cH_0$
+on the basis that the $p$-value is too close to 1.
+However, $P[Y = 0 \mid \cH_0] = 1/e
+\approx 0.368$, so it does not
+really make sense to reject $\cH_0$ in this case.
+In fact, the left $p$-value here is $p_L = 0.368$, and the $p$-value
+computed with the above definition is $p = 1 - p_L \approx 0.632$.
+Note that if $p_L$ is very small, in this definition, $p$ becomes
+close to 1. If the left $p$-value was defined as
+$p_L = 1 - p_R = P[Y < y \mid \cH_0]$, this would also lead to problems.
+In the example, one would have $p_L = 0$ in that case.
+
+A very common type of test in the discrete case is the
+{\em chi-square\/} test, which applies when the possible outcomes are
+partitioned into a finite number of categories.
+Suppose there are $k$ categories and that each observation belongs
+to category $i$ with probability $p_i$, for $0\le i < k$.
+If there are $n$ independent observations, the expected number of
+observations in category $i$ is $e_i = n p_i$, and the chi-square
+test statistic is defined as
+\eq
+ X^2 = \sum_{i=0}^{k-1} \latex{\frac{(o_i - e_i)^2}{e_i} \label{eq:chi-square0}}
+\html{(o_i - e_i)^2/e_i}
+\endeq
+where $o_i$ is the actual number of observations in category $i$.
+Assuming that all $e_i$'s are large enough (a popular rule of thumb
+asks for $e_i \ge 5$ for each $i$), $X^2$ follows approximately the
+chi-square distribution with $k-1$ degrees of freedom \cite{tREA88a}.
+The class
+\externalclass{umontreal.iro.lecuyer.gof}{GofStat.OutcomeCategoriesChi2},
+a nested class defined inside the
+ \externalclass{umontreal.iro.lecuyer.gof}{GofStat} class, provides tools to automatically
+regroup categories in the cases where some $e_i$'s are too small.
+
+The class \externalclass{umontreal.iro.lecuyer.gof}{GofFormat}
+contains methods used to format results of GOF
+test statistics, or to apply several such tests simultaneously to a
+given data set and format the results to produce a report that also
+contains the $p$-values of all these tests.
+A C version of this class is actually used extensively in the package
+TestU01, which applies statistical tests to random number generators
+\cite{iLEC01t}.
+The class also provides tools to plot an empirical or
+theoretical distribution function, by creating a data file that
+contains a graphic plot in a format compatible with a given software.
+
+
diff --git a/source/umontreal/iro/lecuyer/hups/AntitheticPointSet.java b/source/umontreal/iro/lecuyer/hups/AntitheticPointSet.java
new file mode 100644
index 0000000..6f82235
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/AntitheticPointSet.java
@@ -0,0 +1,101 @@
+
+
+/*
+ * Class: AntitheticPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This container class provides antithetic points.
+ * That is,
+ * <SPAN CLASS="MATH">1 - <I>u</I><SUB>i, j</SUB></SPAN> is returned in place of coordinate <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN>.
+ * To generate regular and antithetic variates with a point
+ * set <TT>p</TT>, e.g., for variance reduction, one can define an
+ * {@link AntitheticPointSet} object <TT>pa</TT> that contains <TT>p</TT>,
+ * and then generate the regular variates with <TT>p</TT> and the
+ * antithetic variates with <TT>pa</TT>.
+ *
+ */
+public class AntitheticPointSet extends ContainerPointSet {
+
+
+
+ /**
+ * Constructs an antithetic point set from the given point set <TT>P</TT>.
+ *
+ * @param P point set for which we want antithetic version
+ *
+ *
+ */
+ public AntitheticPointSet (PointSet P) {
+ init (P);
+ }
+
+
+
+ public double getCoordinate (int i, int j) {
+ return 1.0 - P.getCoordinate (i, j);
+ }
+
+ public PointSetIterator iterator(){
+ return new AntitheticPointSetIterator();
+ }
+
+ public String toString() {
+ return "Antithetic point set of: {" + PrintfFormat.NEWLINE +
+ P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+
+ // ***************************************************************
+
+ protected class AntitheticPointSetIterator
+ extends ContainerPointSetIterator {
+
+ public double nextCoordinate() {
+ return 1.0 - innerIterator.nextCoordinate();
+ }
+
+ public double nextDouble() {
+ return 1.0 - innerIterator.nextCoordinate();
+ }
+
+ public void nextCoordinates (double p[], int d) {
+ innerIterator.nextCoordinates (p, d);
+ for (int j = 0; j < d; j++)
+ p[j] = 1.0 - p[j];
+ }
+
+ public int nextPoint (double p[], int d) {
+ innerIterator.nextPoint (p, d);
+ for (int j = 0; j < d; j++)
+ p[j] = 1.0 - p[j];
+ return getCurPointIndex();
+ }
+
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/AntitheticPointSet.tex b/source/umontreal/iro/lecuyer/hups/AntitheticPointSet.tex
new file mode 100644
index 0000000..ff8942d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/AntitheticPointSet.tex
@@ -0,0 +1,112 @@
+\defmodule{AntitheticPointSet}
+
+This container class provides antithetic points.
+That is, $1 - u_{i,j}$ is returned in place of coordinate $u_{i,j}$.
+To generate regular and antithetic variates with a point
+set \texttt{p}, e.g., for variance reduction, one can define an
+\class{AntitheticPointSet} object \texttt{pa} that contains \texttt{p},
+and then generate the regular variates with \texttt{p} and the
+antithetic variates with \texttt{pa}.
+
+
+\hpierre{Perhaps we should have a container of a stream rather
+ than a container of a point set.}
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: AntitheticPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class AntitheticPointSet extends ContainerPointSet \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+\begin{code}
+
+ public AntitheticPointSet (PointSet P) \begin{hide} {
+ init (P);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs an antithetic point set from the given point set \texttt{P}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set for which we want antithetic version}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ return 1.0 - P.getCoordinate (i, j);
+ }
+
+ public PointSetIterator iterator(){
+ return new AntitheticPointSetIterator();
+ }
+
+ public String toString() {
+ return "Antithetic point set of: {" + PrintfFormat.NEWLINE +
+ P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+
+ // ***************************************************************
+
+ protected class AntitheticPointSetIterator
+ extends ContainerPointSetIterator {
+
+ public double nextCoordinate() {
+ return 1.0 - innerIterator.nextCoordinate();
+ }
+
+ public double nextDouble() {
+ return 1.0 - innerIterator.nextCoordinate();
+ }
+
+ public void nextCoordinates (double p[], int d) {
+ innerIterator.nextCoordinates (p, d);
+ for (int j = 0; j < d; j++)
+ p[j] = 1.0 - p[j];
+ }
+
+ public int nextPoint (double p[], int d) {
+ innerIterator.nextPoint (p, d);
+ for (int j = 0; j < d; j++)
+ p[j] = 1.0 - p[j];
+ return getCurPointIndex();
+ }
+
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/BakerTransformedPointSet.java b/source/umontreal/iro/lecuyer/hups/BakerTransformedPointSet.java
new file mode 100644
index 0000000..e623c73
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/BakerTransformedPointSet.java
@@ -0,0 +1,117 @@
+
+
+/*
+ * Class: BakerTransformedPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This container class embodies a point set to which a
+ * <SPAN CLASS="textit">Baker transformation</SPAN> is applied.
+ * It transforms each coordinate <SPAN CLASS="MATH"><I>u</I></SPAN> into <SPAN CLASS="MATH">2<I>u</I></SPAN> if <SPAN CLASS="MATH"><I>u</I> <= 1/2</SPAN>
+ * and <SPAN CLASS="MATH">2(1 - <I>u</I>)</SPAN> if <SPAN CLASS="MATH"><I>u</I> > 1/2</SPAN>.
+ *
+ */
+public class BakerTransformedPointSet extends ContainerPointSet {
+
+
+
+ /**
+ * Constructs a Baker-transformed point set from the given point set <TT>P</TT>.
+ *
+ * @param P point set for which we want a Baker-transfomed version
+ *
+ *
+ */
+ public BakerTransformedPointSet (PointSet P) {
+ init (P);
+ }
+
+
+
+ public double getCoordinate (int i, int j) {
+ double u = P.getCoordinate (i, j);
+ if (u < 0.5) return 2.0 * u;
+ else return 2.0 * (1 - u);
+ }
+
+ public PointSetIterator iterator(){
+ return new BakerTransformedPointSetIterator();
+ }
+
+ public String toString() {
+ return "Baker transformed point set of: {" + PrintfFormat.NEWLINE
+ + P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+/*
+ public String formatPoints() {
+ try {
+ return super.formatPoints();
+ }
+ catch (UnsupportedOperationException e) {
+ return "The values are Baker transformed for each coordinate:" +
+ PrintfFormat.NEWLINE + " {" +
+ P.formatPoints() + PrintfFormat.NEWLINE + "}";
+ }
+ }
+*/
+ // ***************************************************************
+
+ protected class BakerTransformedPointSetIterator
+ extends ContainerPointSetIterator {
+
+ public double nextCoordinate() {
+ double u = innerIterator.nextCoordinate();
+ if (u < 0.5) return 2.0 * u;
+ else return 2.0 * (1.0 - u);
+ }
+
+ // Same as nextCoordinate.
+ public double nextDouble() {
+ double u = innerIterator.nextCoordinate();
+ if (u < 0.5) return 2.0 * u;
+ else return 2.0 * (1.0 - u);
+ }
+
+ public void nextCoordinates (double p[], int d) {
+ innerIterator.nextCoordinates (p, d);
+ for (int j = 0; j < d; j++)
+ if (p[j] < 0.5) p[j] *= 2.0;
+ else p[j] = 2.0 * (1.0 - p[j]);
+ }
+
+ public int nextPoint (double p[], int d) {
+ innerIterator.nextPoint (p, d);
+ for (int j = 0; j < d; j++)
+ if (p[j] < 0.5) p[j] *= 2.0;
+ else p[j] = 2.0 * (1.0 - p[j]);
+ return getCurPointIndex();
+ }
+
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/BakerTransformedPointSet.tex b/source/umontreal/iro/lecuyer/hups/BakerTransformedPointSet.tex
new file mode 100644
index 0000000..1b89df1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/BakerTransformedPointSet.tex
@@ -0,0 +1,127 @@
+\defmodule{BakerTransformedPointSet}
+
+This container class embodies a point set to which a
+\emph{Baker transformation} is applied\latex{ (see, e.g., \cite{vHIC02a})}.
+It transforms each coordinate $u$ into $2u$ if $u \le 1/2$
+and $2(1-u)$ if $u > 1/2$.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BakerTransformedPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class BakerTransformedPointSet extends ContainerPointSet \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+\begin{code}
+
+ public BakerTransformedPointSet (PointSet P) \begin{hide} {
+ init (P);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a Baker-transformed point set from the given point set \texttt{P}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set for which we want a Baker-transfomed version}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ double u = P.getCoordinate (i, j);
+ if (u < 0.5) return 2.0 * u;
+ else return 2.0 * (1 - u);
+ }
+
+ public PointSetIterator iterator(){
+ return new BakerTransformedPointSetIterator();
+ }
+
+ public String toString() {
+ return "Baker transformed point set of: {" + PrintfFormat.NEWLINE
+ + P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+/*
+ public String formatPoints() {
+ try {
+ return super.formatPoints();
+ }
+ catch (UnsupportedOperationException e) {
+ return "The values are Baker transformed for each coordinate:" +
+ PrintfFormat.NEWLINE + " {" +
+ P.formatPoints() + PrintfFormat.NEWLINE + "}";
+ }
+ }
+*/
+ // ***************************************************************
+
+ protected class BakerTransformedPointSetIterator
+ extends ContainerPointSetIterator {
+
+ public double nextCoordinate() {
+ double u = innerIterator.nextCoordinate();
+ if (u < 0.5) return 2.0 * u;
+ else return 2.0 * (1.0 - u);
+ }
+
+ // Same as nextCoordinate.
+ public double nextDouble() {
+ double u = innerIterator.nextCoordinate();
+ if (u < 0.5) return 2.0 * u;
+ else return 2.0 * (1.0 - u);
+ }
+
+ public void nextCoordinates (double p[], int d) {
+ innerIterator.nextCoordinates (p, d);
+ for (int j = 0; j < d; j++)
+ if (p[j] < 0.5) p[j] *= 2.0;
+ else p[j] = 2.0 * (1.0 - p[j]);
+ }
+
+ public int nextPoint (double p[], int d) {
+ innerIterator.nextPoint (p, d);
+ for (int j = 0; j < d; j++)
+ if (p[j] < 0.5) p[j] *= 2.0;
+ else p[j] = 2.0 * (1.0 - p[j]);
+ return getCurPointIndex();
+ }
+
+ }
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/hups/CachedPointSet.java b/source/umontreal/iro/lecuyer/hups/CachedPointSet.java
new file mode 100644
index 0000000..b196bef
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/CachedPointSet.java
@@ -0,0 +1,143 @@
+
+
+/*
+ * Class: CachedPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+ import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This container class caches a point set by precomputing
+ * and storing its points locally in an array.
+ * This can be used to speed up computations when using
+ * a small low-dimensional point set more than once.
+ *
+ */
+public class CachedPointSet extends PointSet {
+ protected PointSet P; // Original PointSet which is cached here.
+ protected double x[][]; // Cached points.
+ protected CachedPointSet() {}
+
+
+
+ /**
+ * Creates a new <TT>PointSet</TT> object that contains an array storing
+ * the first <TT>dim</TT> coordinates of the first <TT>n</TT> points of <TT>P</TT>.
+ * The original point set <TT>P</TT> itself is not modified.
+ *
+ * @param P point set to be cached
+ *
+ * @param n number of points
+ *
+ * @param dim number of dimensions of the points
+ *
+ *
+ */
+ public CachedPointSet (PointSet P, int n, int dim) {
+ if (P.getNumPoints() < n)
+ throw new IllegalArgumentException(
+ "Cannot cache more points than in point set P.");
+ if (P.getDimension() < dim)
+ throw new IllegalArgumentException(
+ "Cannot cache points with more coordinates than the dimension.");
+ numPoints = n;
+ this.dim = dim;
+ this.P = P;
+ init ();
+ }
+
+ protected void init () {
+ PointSetIterator itr = P.iterator();
+ x = new double[numPoints][dim];
+ for (int i = 0; i < numPoints; i++)
+ itr.nextPoint (x[i], dim);
+ }
+
+
+ /**
+ * Creates a new <TT>PointSet</TT> object that contains an array storing
+ * the points of <TT>P</TT>.
+ * The number of points and their dimension are the same as in the
+ * original point set. Both must be finite.
+ *
+ * @param P point set to be cached
+ *
+ */
+ public CachedPointSet (PointSet P) {
+ numPoints = P.getNumPoints();
+ dim = P.getDimension();
+ if (numPoints == Integer.MAX_VALUE)
+ throw new IllegalArgumentException(
+ "Cannot cache infinite number of points");
+ if (dim == Integer.MAX_VALUE)
+ throw new IllegalArgumentException(
+ "Cannot cache infinite dimensional points");
+ this.P = P;
+ init ();
+ }
+
+
+ /**
+ * Add the shift to the contained point set and recaches the points. See the doc of the
+ * overridden method {@link umontreal.iro.lecuyer.hups.PointSet#addRandomShift((int, int, RandomStream)) addRandomShift}<TT>(d1, d2, stream)</TT> in {@link PointSet}.
+ *
+ */
+ public void addRandomShift(int d1, int d2, RandomStream stream) {
+ P.addRandomShift(d1, d2, stream);
+ init();
+ }
+
+
+ /**
+ * Randomizes the underlying point set using <TT>rand</TT> and
+ * recaches the points.
+ *
+ */
+ public void randomize (PointSetRandomization rand) {
+ P.randomize(rand);
+ init();
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Cached point set" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ sb.append (PrintfFormat.NEWLINE + "Cached point set information {"
+ + PrintfFormat.NEWLINE);
+ sb.append (P.toString());
+ sb.append (PrintfFormat.NEWLINE + "}");
+ return sb.toString();
+ }
+
+ public double getCoordinate (int i, int j) {
+ return x[i][j];
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/CachedPointSet.tex b/source/umontreal/iro/lecuyer/hups/CachedPointSet.tex
new file mode 100644
index 0000000..a8f5771
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/CachedPointSet.tex
@@ -0,0 +1,173 @@
+\defmodule{CachedPointSet}
+
+This container class caches a point set by precomputing
+and storing its points locally in an array.
+This can be used to speed up computations when using
+a small low-dimensional point set more than once.
+
+\begin{detailed} %%
+After the points are stored in the array, this class uses
+the default methods and the default iterator type provided by
+the base class \class{PointSet}{}.
+This is one of the rare cases where direct use of the
+\method{getCoordinate}{} method is efficient.
+\pierre {We could also implement an iterator that directly returns
+ \texttt{x[i][j]} instead of calling \texttt{getCoordinate}, for slightly
+ better efficiency. On the other hand, even better efficiency can
+ be achieved by getting an entire point at a time in an array.
+} However, it might require too much memory for a large point set.
+\end{detailed} %%
+
+% No array index range check is performed in this class,
+% neither for the dimension nor for the number of points.
+% However, this is done by Java.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: CachedPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+ import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+public class CachedPointSet extends PointSet \begin{hide} {
+ protected PointSet P; // Original PointSet which is cached here.
+ protected double x[][]; // Cached points.
+ protected CachedPointSet() {}
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public CachedPointSet (PointSet P, int n, int dim) \begin{hide} {
+ if (P.getNumPoints() < n)
+ throw new IllegalArgumentException(
+ "Cannot cache more points than in point set P.");
+ if (P.getDimension() < dim)
+ throw new IllegalArgumentException(
+ "Cannot cache points with more coordinates than the dimension.");
+ numPoints = n;
+ this.dim = dim;
+ this.P = P;
+ init ();
+ }
+
+ protected void init () {
+ PointSetIterator itr = P.iterator();
+ x = new double[numPoints][dim];
+ for (int i = 0; i < numPoints; i++)
+ itr.nextPoint (x[i], dim);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates a new \texttt{PointSet} object that contains an array storing
+ the first \texttt{dim} coordinates of the first \texttt{n} points of \texttt{P}.
+ The original point set \texttt{P} itself is not modified.
+%, except for its point and coordinate iterators.
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set to be cached}
+ \param{n}{number of points}
+ \param{dim}{number of dimensions of the points}
+\end{htmlonly}
+\begin{code}
+
+ public CachedPointSet (PointSet P) \begin{hide} {
+ numPoints = P.getNumPoints();
+ dim = P.getDimension();
+ if (numPoints == Integer.MAX_VALUE)
+ throw new IllegalArgumentException(
+ "Cannot cache infinite number of points");
+ if (dim == Integer.MAX_VALUE)
+ throw new IllegalArgumentException(
+ "Cannot cache infinite dimensional points");
+ this.P = P;
+ init ();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates a new \texttt{PointSet} object that contains an array storing
+ the points of \texttt{P}.
+ The number of points and their dimension are the same as in the
+ original point set. Both must be finite.
+% The point set \texttt{P} itself is not modified.
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set to be cached}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void addRandomShift(int d1, int d2, RandomStream stream)\begin{hide} {
+ P.addRandomShift(d1, d2, stream);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Add the shift to the contained point set and recaches the points. See the doc of the
+overridden method \externalmethod{umontreal.iro.lecuyer.hups}{PointSet}{addRandomShift}{(int, int, RandomStream)}\texttt{(d1, d2, stream)} in \class{PointSet}.
+\end{tabb}
+\begin{code}
+
+ public void randomize (PointSetRandomization rand)\begin{hide} {
+ P.randomize(rand);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Randomizes the underlying point set using \texttt{rand} and
+recaches the points.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Cached point set" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ sb.append (PrintfFormat.NEWLINE + "Cached point set information {"
+ + PrintfFormat.NEWLINE);
+ sb.append (P.toString());
+ sb.append (PrintfFormat.NEWLINE + "}");
+ return sb.toString();
+ }
+
+ public double getCoordinate (int i, int j) {
+ return x[i][j];
+ }
+
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/ContainerPointSet.java b/source/umontreal/iro/lecuyer/hups/ContainerPointSet.java
new file mode 100644
index 0000000..7170106
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/ContainerPointSet.java
@@ -0,0 +1,205 @@
+
+
+/*
+ * Class: ContainerPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This acts as a generic base class for all <SPAN CLASS="textit">container
+ * classes</SPAN> that contain a point set and apply some kind of
+ * transformation to the coordinates to define a new point set.
+ * One example of such transformation is the <SPAN CLASS="textit">antithetic</SPAN> map,
+ * applied by the container class {@link AntitheticPointSet},
+ * where each output coordinate <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> is transformed into <SPAN CLASS="MATH">1 - <I>u</I><SUB>i, j</SUB></SPAN>.
+ * Another example is {@link RandShiftedPointSet}.
+ *
+ * <P>
+ * The class implements a specialized type of iterator for container
+ * point sets. This type of iterator contains itself an iterator for
+ * the contained point set and uses it to access the points and coordinates
+ * internally, instead of maintaining itself indices for the current point
+ * and current coordinate.
+ *
+ */
+public abstract class ContainerPointSet extends PointSet {
+ protected PointSet P; // contained point set
+
+
+
+ /**
+ * Initializes the container point set which will contain point set <TT>P0</TT>.
+ * This method must be called by the constructor of any class inheriting from
+ * {@link ContainerPointSet}.
+ *
+ * @param P0 contained point set
+ *
+ *
+ */
+ protected void init (PointSet P0) {
+ P = P0;
+// this.dim = P.getDimension();
+// this.numPoints = P.getNumPoints();
+ }
+
+
+ /**
+ * Returns the (untransformed) point set inside this container.
+ *
+ * @return the point set inside this container
+ *
+ */
+ public PointSet getOriginalPointSet() {
+ return P;
+ }
+
+
+ /**
+ * Returns the dimension of the contained point set.
+ *
+ * @return the dimension of the contained point set
+ *
+ */
+ public int getDimension() {
+ return P.getDimension();
+ }
+
+
+ /**
+ * Returns the number of points of the contained point set.
+ *
+ * @return the number of points of the contained point set
+ *
+ */
+ public int getNumPoints() {
+ return P.getNumPoints();
+ }
+
+
+ public double getCoordinate(int i, int j) {
+ return P.getCoordinate (i, j);
+ }
+
+ public PointSetIterator iterator(){
+ return new ContainerPointSetIterator();
+ }
+
+ /**
+ * Randomizes the contained point set using <TT>rand</TT>.
+ *
+ * @param rand {@link PointSetRandomization} to use
+ *
+ *
+ */
+ public void randomize (PointSetRandomization rand) {
+ P.randomize(rand);
+ }
+
+
+ /**
+ * Calls <TT>addRandomShift(d1, d2, stream)</TT> of the contained point set.
+ *
+ * @param d1 lower dimension of the random shift
+ *
+ * @param d2 upper dimension of the random shift
+ *
+ * @param stream the random stream
+ *
+ *
+ */
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ P.addRandomShift (d1, d2, stream);
+ }
+
+
+ /**
+ * Calls <TT>addRandomShift(stream)</TT> of the contained point set.
+ *
+ * @param stream the random stream
+ *
+ *
+ */
+ public void addRandomShift (RandomStream stream) {
+ P.addRandomShift (stream);
+ }
+
+
+ /**
+ * Calls <TT>clearRandomShift()</TT> of the contained point set.
+ *
+ */
+ public void clearRandomShift() {
+ P.clearRandomShift ();
+ }
+
+
+ public String toString() {
+ return "Container point set of: {" + PrintfFormat.NEWLINE
+ + P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+
+ // ********************************************************
+ protected class ContainerPointSetIterator extends DefaultPointSetIterator {
+
+ protected PointSetIterator innerIterator = P.iterator();
+
+ public void setCurCoordIndex (int j) {
+ innerIterator.setCurCoordIndex (j);
+ }
+
+ public void resetCurCoordIndex() {
+ innerIterator.resetCurCoordIndex();
+ }
+
+ public boolean hasNextCoordinate() {
+ return innerIterator.hasNextCoordinate();
+ }
+
+ public double nextCoordinate() {
+ return innerIterator.nextCoordinate();
+ }
+
+ public void setCurPointIndex (int i) {
+ innerIterator.setCurPointIndex(i);
+ }
+
+ public void resetCurPointIndex() {
+ innerIterator.resetCurPointIndex();
+ }
+
+ public int resetToNextPoint() {
+ return innerIterator.resetToNextPoint();
+ }
+
+ public boolean hasNextPoint() {
+ return innerIterator.hasNextPoint();
+ }
+
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/ContainerPointSet.tex b/source/umontreal/iro/lecuyer/hups/ContainerPointSet.tex
new file mode 100644
index 0000000..d31a6fa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/ContainerPointSet.tex
@@ -0,0 +1,216 @@
+\defmodule{ContainerPointSet}
+
+This acts as a generic base class for all \emph{container
+classes} that contain a point set and apply some kind of
+transformation to the coordinates to define a new point set.
+One example of such transformation is the \emph{antithetic} map,
+applied by the container class \class{AntitheticPointSet},
+where each output coordinate $u_{i,j}$ is transformed into $1-u_{i,j}$.
+Another example is \class{RandShiftedPointSet}.
+
+The class implements a specialized type of iterator for container
+point sets. This type of iterator contains itself an iterator for
+the contained point set and uses it to access the points and coordinates
+internally, instead of maintaining itself indices for the current point
+and current coordinate.
+
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ContainerPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+public abstract class ContainerPointSet extends PointSet \begin{hide} {
+ protected PointSet P; // contained point set
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection*{Constructor}
+\begin{code}
+
+ protected void init (PointSet P0) \begin{hide} {
+ P = P0;
+// this.dim = P.getDimension();
+// this.numPoints = P.getNumPoints();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Initializes the container point set which will contain point set \texttt{P0}.
+ This method must be called by the constructor of any class inheriting from
+ \class{ContainerPointSet}.
+ \hpierre{Since this is an abstract class, this constructor should be
+ replaced by a method \texttt{init()}, if really needed.}
+ \end{tabb}
+\begin{htmlonly}
+ \param{P0}{contained point set}
+\end{htmlonly}
+\begin{code}
+
+ public PointSet getOriginalPointSet() \begin{hide} {
+ return P;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the (untransformed) point set inside this container.
+\end{tabb}
+\begin{htmlonly}
+ \return{the point set inside this container}
+\end{htmlonly}
+\begin{code}
+
+ public int getDimension() \begin{hide} {
+ return P.getDimension();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dimension of the contained point set.
+\end{tabb}
+\begin{htmlonly}
+ \return{the dimension of the contained point set}
+\end{htmlonly}
+\begin{code}
+
+ public int getNumPoints()\begin{hide} {
+ return P.getNumPoints();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the number of points of the contained point set.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of points of the contained point set}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double getCoordinate(int i, int j) {
+ return P.getCoordinate (i, j);
+ }
+
+ public PointSetIterator iterator(){
+ return new ContainerPointSetIterator();
+ }\end{hide}
+
+ public void randomize (PointSetRandomization rand) \begin{hide} {
+ P.randomize(rand);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Randomizes the contained point set using \texttt{rand}.
+\end{tabb}
+\begin{htmlonly}
+ \param{rand}{\class{PointSetRandomization} to use}
+\end{htmlonly}
+\begin{code}
+
+ public void addRandomShift (int d1, int d2, RandomStream stream)\begin{hide} {
+ P.addRandomShift (d1, d2, stream);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Calls \texttt{addRandomShift(d1, d2, stream)} of the contained point set.
+\end{tabb}
+\begin{htmlonly}
+ \param{d1}{lower dimension of the random shift}
+ \param{d2}{upper dimension of the random shift}
+ \param{stream}{the random stream}
+\end{htmlonly}
+\begin{code}
+
+ public void addRandomShift (RandomStream stream)\begin{hide} {
+ P.addRandomShift (stream);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Calls \texttt{addRandomShift(stream)} of the contained point set.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the random stream}
+\end{htmlonly}
+\begin{code}
+
+ public void clearRandomShift()\begin{hide} {
+ P.clearRandomShift ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Calls \texttt{clearRandomShift()} of the contained point set.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ return "Container point set of: {" + PrintfFormat.NEWLINE
+ + P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+
+ // ********************************************************
+ protected class ContainerPointSetIterator extends DefaultPointSetIterator {
+
+ protected PointSetIterator innerIterator = P.iterator();
+
+ public void setCurCoordIndex (int j) {
+ innerIterator.setCurCoordIndex (j);
+ }
+
+ public void resetCurCoordIndex() {
+ innerIterator.resetCurCoordIndex();
+ }
+
+ public boolean hasNextCoordinate() {
+ return innerIterator.hasNextCoordinate();
+ }
+
+ public double nextCoordinate() {
+ return innerIterator.nextCoordinate();
+ }
+
+ public void setCurPointIndex (int i) {
+ innerIterator.setCurPointIndex(i);
+ }
+
+ public void resetCurPointIndex() {
+ innerIterator.resetCurPointIndex();
+ }
+
+ public int resetToNextPoint() {
+ return innerIterator.resetToNextPoint();
+ }
+
+ public boolean hasNextPoint() {
+ return innerIterator.hasNextPoint();
+ }
+
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/CycleBasedPointSet.java b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSet.java
new file mode 100644
index 0000000..578c5f0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSet.java
@@ -0,0 +1,316 @@
+
+
+/*
+ * Class: CycleBasedPointSet
+ * Description: provides the basic structures for storing and manipulating
+ a highly uniform point set defined by a set of cycles
+ defined by a set of cycles.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.list.*;
+
+
+/**
+ * This abstract class provides the basic structures for
+ * storing and manipulating a <SPAN CLASS="textit">highly uniform point set</SPAN>
+ * defined by a set of cycles.
+ * The <SPAN CLASS="MATH"><I>s</I></SPAN>-dimensional points are all the vectors of <SPAN CLASS="MATH"><I>s</I></SPAN> successive
+ * values found in any of the cycles, from any starting point.
+ * Since this is defined for any positive integer <SPAN CLASS="MATH"><I>s</I></SPAN>, the points
+ * effectively have an infinite number of dimensions.
+ * The number of points, <SPAN CLASS="MATH"><I>n</I></SPAN>, is the sum of lengths of all the cycles.
+ * The cycles of the point set are simply stored as a list of arrays,
+ * where each array contains the successive values for a given cycle.
+ * By default, the values are stored in <TT>double</TT>.
+ *
+ * <P>
+ * This structure is convenient for implementing recurrence-based point
+ * sets, where the point set in <SPAN CLASS="MATH"><I>s</I></SPAN> dimensions is defined as
+ * the set of all vectors of <SPAN CLASS="MATH"><I>s</I></SPAN> successive values of a periodic recurrence,
+ * from all its possible initial states.
+ *
+ */
+public abstract class CycleBasedPointSet extends PointSet {
+
+ protected int numCycles = 0; // Total number of cycles.
+ // dim = Integer.MAX_VALUE; // Dimension is infinite.
+ private double[] shift; // Random shift, initially null.
+ // Entry j is for dimension j.
+ protected ObjectArrayList cycles = new ObjectArrayList(); // List of cycles.
+
+
+
+
+ public double getCoordinate (int i, int j) {
+ // Find cycle that contains point i, then index in cycle.
+ int l = 0; // Length of next cycle.
+ int n = 0; // Total length of cycles added so far.
+ int k;
+ for (k = 0; n <= i; k++)
+ n += l = ((AbstractList) cycles.get (k)).size();
+ AbstractList curCycle = (AbstractList) cycles.get (k-1);
+ int coordinate = (i - n + l + j) % curCycle.size();
+// double[] curCycleD = ((DoubleArrayList) curCycle).elements();
+// return curCycleD[coordinate];
+ double x = ((DoubleArrayList) curCycle).get (coordinate);
+ return x;
+ }
+
+ /**
+ * Adds a random shift to all the points
+ * of the point set, using stream <TT>stream</TT> to generate the random numbers,
+ * for coordinates <TT>d1</TT> to <TT>d2 - 1</TT>. This applies an addition modulo 1
+ * of a single random point to all the points.
+ *
+ * @param stream Stream used to generate random numbers
+ *
+ *
+ */
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (dim, 1);
+ if (shift == null) {
+ shift = new double[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ double[] temp = new double[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = shift[i];
+ shift = temp;
+ }
+ dimShift = d2;
+ for (int i = d1; i < d2; i++)
+ shift[i] = stream.nextDouble ();
+ shiftStream = stream;
+ }
+
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ shift = null;
+ }
+
+
+ /**
+ * Adds the cycle <TT>c</TT> to the list of all cycles.
+ * This method is used by subclass constructors to fill up the list of cycles.
+ *
+ */
+ protected void addCycle (AbstractList c) {
+ // Adds the cycle \texttt{c} to the list of all cycles.
+ // Used by subclass constructors to fill up the list of cycles.
+ cycles.add (c);
+ numCycles++;
+ numPoints += c.size();
+ }
+
+
+ public int getDimension() {
+ return Integer.MAX_VALUE;
+ }
+
+ public PointSetIterator iterator(){
+ return new CycleBasedPointSetIterator();
+ }
+
+ public String toString() {
+ String s = super.toString();
+ return s + PrintfFormat.NEWLINE + "Number of cycles: " + numCycles;
+ }
+
+ public String formatPoints() {
+ StringBuffer sb = new StringBuffer (toString());
+ for (int c = 0; c < numCycles; c++) {
+ AbstractList curCycle = (AbstractList)cycles.get (c);
+ double[] cycle = ((DoubleArrayList)curCycle).elements();
+ sb.append (PrintfFormat.NEWLINE + "Cycle " + c + ": (");
+ boolean first = true;
+ for (int e = 0; e < curCycle.size(); e++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (cycle[e]);
+ }
+ sb.append (")");
+ }
+ return sb.toString();
+ }
+
+ // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ public class CycleBasedPointSetIterator extends DefaultPointSetIterator {
+
+ protected int startPointInCycle = 0; // Index where the current point
+ // starts in the current cycle.
+ protected int curCoordInCycle = 0; // Index of the current coordinate
+ // in the current cycle.
+ protected int curCycleIndex = 0; // Index of the current cycle.
+ protected AbstractList curCycle; // The current cycle.
+ protected double[] curCycleD; // The array for current cycle
+
+
+ public CycleBasedPointSetIterator () {
+ init();
+ }
+
+ protected void init () {
+ resetCurCycle(0);
+ }
+
+ public void resetCurCycle (int index) {
+ curCycleIndex = index;
+ curCycle = (AbstractList) cycles.get (index);
+ curCycleD = ((DoubleArrayList) curCycle).elements();
+ }
+
+ public void setCurCoordIndex (int i) {
+ curCoordIndex = i;
+ curCoordInCycle = (i + startPointInCycle) % curCycle.size();
+ }
+
+ public void resetCurCoordIndex() {
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ }
+
+ public boolean hasNextCoordinate() {
+ return true;
+ }
+
+ // We want to avoid generating 0 or 1
+ public double nextDouble() {
+ return nextCoordinate() + EpsilonHalf;
+ }
+
+ public double nextCoordinate() {
+ // First, verify if there are still points....
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ double x = curCycleD [curCoordInCycle];
+ if (shift != null) {
+ if (curCoordIndex >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, curCoordIndex + 1, shiftStream);
+ x += shift[curCoordIndex];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ curCoordIndex++;
+ curCoordInCycle++;
+ if (curCoordInCycle >= curCycle.size())
+ curCoordInCycle = 0;
+ return x;
+ }
+
+ public void nextCoordinates (double p[], int dim) {
+ // First, verify if there are still points....
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ if (curCoordIndex + dim >= dimShift)
+ addRandomShift (dimShift, curCoordIndex + dim + 1, shiftStream);
+ int j = curCoordInCycle;
+ int maxj = curCycle.size();
+ double x;
+ for (int i = 0; i < dim; i++) {
+ x = curCycleD [curCoordInCycle++];
+ if (curCoordInCycle >= maxj) curCoordInCycle = 0;
+ if (shift != null) {
+ x += shift[curCoordIndex + i];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ p[i] = x;
+ }
+ curCoordIndex += dim;
+ }
+
+ public void setCurPointIndex (int i) {
+ int l = 0;
+ int n = 0;
+ int j ;
+ for (j=0; n <= i; j++)
+ n += l = ((AbstractList) cycles.get (j)).size();
+ resetCurCycle (j-1);
+ startPointInCycle = i - n + l;
+ curPointIndex = i;
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ }
+
+ public void resetCurPointIndex() {
+ resetCurCycle (0);
+ startPointInCycle = 0;
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ curCoordInCycle = 0;
+ }
+
+ public int resetToNextPoint() {
+ curPointIndex++;
+ startPointInCycle++;
+ if (startPointInCycle >= curCycle.size()) {
+ startPointInCycle = 0;
+ if (curCycleIndex < (numCycles - 1))
+ resetCurCycle (curCycleIndex + 1);
+ }
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ return curPointIndex;
+ }
+
+ public int nextPoint (double p[], int dim) {
+ // First, verify if there are still points....
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ int j = startPointInCycle;
+ int maxj = curCycle.size() - 1;
+ for (int i = 0; i < dim; i++) {
+ p[i] = curCycleD [j];
+ if (j < maxj) j++; else j = 0;
+ }
+ resetToNextPoint();
+ return curPointIndex;
+ }
+
+ public String formatState() {
+ return super.formatState() + PrintfFormat.NEWLINE +
+ "Current cycle: " + curCycleIndex;
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/CycleBasedPointSet.tex b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSet.tex
new file mode 100644
index 0000000..fbb11ab
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSet.tex
@@ -0,0 +1,329 @@
+\defmodule {CycleBasedPointSet}
+
+This abstract class provides the basic structures for
+storing and manipulating a \emph{highly uniform point set}
+ defined by a set of cycles.
+The $s$-dimensional points are all the vectors of $s$ successive
+values found in any of the cycles, from any starting point.
+Since this is defined for any positive integer $s$, the points
+effectively have an infinite number of dimensions.
+The number of points, $n$, is the sum of lengths of all the cycles.
+The cycles of the point set are simply stored as a list of arrays,
+where each array contains the successive values for a given cycle.
+By default, the values are stored in \texttt{double}.
+%In subclass \texttt{CycleBasedPointSetInt}, they are stored as integers.
+
+This structure is convenient for implementing recurrence-based point
+sets, where the point set in $s$ dimensions is defined as
+the set of all vectors of $s$ successive values of a periodic recurrence,
+from all its possible initial states.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: CycleBasedPointSet
+ * Description: provides the basic structures for storing and manipulating
+ a highly uniform point set defined by a set of cycles
+ defined by a set of cycles.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.list.*;
+\end{hide}
+
+public abstract class CycleBasedPointSet extends PointSet \begin{hide} {
+
+ protected int numCycles = 0; // Total number of cycles.
+ // dim = Integer.MAX_VALUE; // Dimension is infinite.
+ private double[] shift; // Random shift, initially null.
+ // Entry j is for dimension j.
+ protected ObjectArrayList cycles = new ObjectArrayList(); // List of cycles.
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ // Find cycle that contains point i, then index in cycle.
+ int l = 0; // Length of next cycle.
+ int n = 0; // Total length of cycles added so far.
+ int k;
+ for (k = 0; n <= i; k++)
+ n += l = ((AbstractList) cycles.get (k)).size();
+ AbstractList curCycle = (AbstractList) cycles.get (k-1);
+ int coordinate = (i - n + l + j) % curCycle.size();
+// double[] curCycleD = ((DoubleArrayList) curCycle).elements();
+// return curCycleD[coordinate];
+ double x = ((DoubleArrayList) curCycle).get (coordinate);
+ return x;
+ }\end{hide}
+
+ public void addRandomShift (int d1, int d2, RandomStream stream)\begin{hide} {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (dim, 1);
+ if (shift == null) {
+ shift = new double[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ double[] temp = new double[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = shift[i];
+ shift = temp;
+ }
+ dimShift = d2;
+ for (int i = d1; i < d2; i++)
+ shift[i] = stream.nextDouble ();
+ shiftStream = stream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds a random shift to all the points
+ of the point set, using stream \texttt{stream} to generate the random numbers,
+ for coordinates \texttt{d1} to \texttt{d2 - 1}. This applies an addition modulo 1
+ of a single random point to all the points.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{Stream used to generate random numbers}
+\end{htmlonly}
+\begin{hide}\begin{code}
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ shift = null;
+ }
+\end{code}
+\begin{tabb}
+ Erases the current random shift, if any.
+\end{tabb}
+\end{hide}
+\begin{code}
+
+ protected void addCycle (AbstractList c) \begin{hide} {
+ // Adds the cycle \texttt{c} to the list of all cycles.
+ // Used by subclass constructors to fill up the list of cycles.
+ cycles.add (c);
+ numCycles++;
+ numPoints += c.size();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Adds the cycle \texttt{c} to the list of all cycles.
+ This method is used by subclass constructors to fill up the list of cycles.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public int getDimension() {
+ return Integer.MAX_VALUE;
+ }
+
+ public PointSetIterator iterator(){
+ return new CycleBasedPointSetIterator();
+ }
+
+ public String toString() {
+ String s = super.toString();
+ return s + PrintfFormat.NEWLINE + "Number of cycles: " + numCycles;
+ }
+
+ public String formatPoints() {
+ StringBuffer sb = new StringBuffer (toString());
+ for (int c = 0; c < numCycles; c++) {
+ AbstractList curCycle = (AbstractList)cycles.get (c);
+ double[] cycle = ((DoubleArrayList)curCycle).elements();
+ sb.append (PrintfFormat.NEWLINE + "Cycle " + c + ": (");
+ boolean first = true;
+ for (int e = 0; e < curCycle.size(); e++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (cycle[e]);
+ }
+ sb.append (")");
+ }
+ return sb.toString();
+ }
+
+ // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ public class CycleBasedPointSetIterator extends DefaultPointSetIterator {
+
+ protected int startPointInCycle = 0; // Index where the current point
+ // starts in the current cycle.
+ protected int curCoordInCycle = 0; // Index of the current coordinate
+ // in the current cycle.
+ protected int curCycleIndex = 0; // Index of the current cycle.
+ protected AbstractList curCycle; // The current cycle.
+ protected double[] curCycleD; // The array for current cycle
+
+
+ public CycleBasedPointSetIterator () {
+ init();
+ }
+
+ protected void init () {
+ resetCurCycle(0);
+ }
+
+ public void resetCurCycle (int index) {
+ curCycleIndex = index;
+ curCycle = (AbstractList) cycles.get (index);
+ curCycleD = ((DoubleArrayList) curCycle).elements();
+ }
+
+ public void setCurCoordIndex (int i) {
+ curCoordIndex = i;
+ curCoordInCycle = (i + startPointInCycle) % curCycle.size();
+ }
+
+ public void resetCurCoordIndex() {
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ }
+
+ public boolean hasNextCoordinate() {
+ return true;
+ }
+
+ // We want to avoid generating 0 or 1
+ public double nextDouble() {
+ return nextCoordinate() + EpsilonHalf;
+ }
+
+ public double nextCoordinate() {
+ // First, verify if there are still points....
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ double x = curCycleD [curCoordInCycle];
+ if (shift != null) {
+ if (curCoordIndex >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, curCoordIndex + 1, shiftStream);
+ x += shift[curCoordIndex];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ curCoordIndex++;
+ curCoordInCycle++;
+ if (curCoordInCycle >= curCycle.size())
+ curCoordInCycle = 0;
+ return x;
+ }
+
+ public void nextCoordinates (double p[], int dim) {
+ // First, verify if there are still points....
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ if (curCoordIndex + dim >= dimShift)
+ addRandomShift (dimShift, curCoordIndex + dim + 1, shiftStream);
+ int j = curCoordInCycle;
+ int maxj = curCycle.size();
+ double x;
+ for (int i = 0; i < dim; i++) {
+ x = curCycleD [curCoordInCycle++];
+ if (curCoordInCycle >= maxj) curCoordInCycle = 0;
+ if (shift != null) {
+ x += shift[curCoordIndex + i];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ p[i] = x;
+ }
+ curCoordIndex += dim;
+ }
+
+ public void setCurPointIndex (int i) {
+ int l = 0;
+ int n = 0;
+ int j ;
+ for (j=0; n <= i; j++)
+ n += l = ((AbstractList) cycles.get (j)).size();
+ resetCurCycle (j-1);
+ startPointInCycle = i - n + l;
+ curPointIndex = i;
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ }
+
+ public void resetCurPointIndex() {
+ resetCurCycle (0);
+ startPointInCycle = 0;
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ curCoordInCycle = 0;
+ }
+
+ public int resetToNextPoint() {
+ curPointIndex++;
+ startPointInCycle++;
+ if (startPointInCycle >= curCycle.size()) {
+ startPointInCycle = 0;
+ if (curCycleIndex < (numCycles - 1))
+ resetCurCycle (curCycleIndex + 1);
+ }
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ return curPointIndex;
+ }
+
+ public int nextPoint (double p[], int dim) {
+ // First, verify if there are still points....
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ int j = startPointInCycle;
+ int maxj = curCycle.size() - 1;
+ for (int i = 0; i < dim; i++) {
+ p[i] = curCycleD [j];
+ if (j < maxj) j++; else j = 0;
+ }
+ resetToNextPoint();
+ return curPointIndex;
+ }
+
+ public String formatState() {
+ return super.formatState() + PrintfFormat.NEWLINE +
+ "Current cycle: " + curCycleIndex;
+ }
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/CycleBasedPointSetBase2.java b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSetBase2.java
new file mode 100644
index 0000000..e182c54
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSetBase2.java
@@ -0,0 +1,229 @@
+
+
+/*
+ * Class: CycleBasedPointSetBase2
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.list.*;
+
+
+/**
+ * Similar to {@link CycleBasedPointSet}, except that the successive
+ * values in the cycles are stored as integers in the range
+ *
+ * <SPAN CLASS="MATH">{0,..., 2<SUP>k</SUP> -1}</SPAN>, where
+ * <SPAN CLASS="MATH">1 <= <I>k</I> <= 31</SPAN>.
+ * The output values <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> are obtained by dividing these integer
+ * values by <SPAN CLASS="MATH">2<SUP>k</SUP></SPAN>. Point sets where the successive coordinates of each
+ * point are obtained via linear recurrences modulo 2 (e.g., linear feedback
+ * shift registers or Korobov-type polynomial lattice rules)
+ * are naturally expressed in this form.
+ * Storing the integers
+ * <SPAN CLASS="MATH">2<SUP>k</SUP><I>u</I><SUB>i, j</SUB></SPAN> instead of the <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> themselves
+ * makes it easier to apply randomizations such as digital random shifts
+ * in base 2, which are applied to the bits <SPAN CLASS="textit">before</SPAN> transforming
+ * the value to a real number <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN>. When a random digital shift is
+ * performed, it applies a bitwise exclusive-or of all the points with a single
+ * random point.
+ *
+ */
+public abstract class CycleBasedPointSetBase2 extends CycleBasedPointSet {
+
+// dim = Integer.MAX_VALUE; // Dimension is infinite.
+ private int[] digitalShift; // Digital shift, initially zero (null).
+ // Entry j is for dimension j.
+ protected int numBits; // Number of bits in stored values.
+ protected double normFactor; // To convert output to (0,1); 1/2^numBits.
+
+
+
+
+
+ public double getCoordinate (int i, int j) {
+ // Find cycle that contains point i, then index in cycle.
+ int l = 0; // Length of next cycle.
+ int n = 0; // Total length of cycles added so far.
+ int k;
+ for (k = 0; n <= i; k++)
+ n += l = ((AbstractList) cycles.get (k)).size();
+ AbstractList curCycle = (AbstractList) cycles.get (k-1);
+ int[] curCycleI = ((IntArrayList) curCycle).elements();
+ int coordinate = (i - n + l + j) % curCycle.size();
+ int shift = 0;
+ if (digitalShift != null) {
+ shift = digitalShift[j];
+ return (shift ^ curCycleI[coordinate]) * normFactor + EpsilonHalf;
+ } else
+ return (shift ^ curCycleI[coordinate]) * normFactor;
+ }
+
+ public PointSetIterator iterator() {
+ return new CycleBasedPointSetBase2Iterator ();
+ }
+
+
+ /**
+ * Adds a random digital shift in base 2 to all the points
+ * of the point set, using stream <TT>stream</TT> to generate the random numbers,
+ * for coordinates <TT>d1</TT> to <TT>d2 - 1</TT>.
+ * This applies a bitwise exclusive-or of all the points with a single
+ * random point.
+ *
+ *
+ */
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (digitalShift == null) {
+ digitalShift = new int[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ int[] temp = new int[d3];
+ capacityShift = d3;
+ for (int i = 0; i < dimShift; i++)
+ temp[i] = digitalShift[i];
+ digitalShift = temp;
+ }
+ dimShift = d2;
+ int maxj;
+ if (numBits < 31) {
+ maxj = (1 << numBits) - 1;
+ } else {
+ maxj = 2147483647;
+ }
+ for (int i = d1; i < d2; i++)
+ digitalShift[i] = stream.nextInt (0, maxj);
+ shiftStream = stream;
+
+ }
+
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ digitalShift = null;
+ }
+
+
+
+ public String formatPoints() {
+ StringBuffer sb = new StringBuffer (toString());
+ for (int c = 0; c < numCycles; c++) {
+ AbstractList curCycle = (AbstractList)cycles.get (c);
+ int[] cycle = ((IntArrayList)curCycle).elements();
+ sb.append (PrintfFormat.NEWLINE + "Cycle " + c + ": (");
+ boolean first = true;
+ for (int e = 0; e < curCycle.size(); e++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (cycle[e]);
+ }
+ sb.append (")");
+ }
+ return sb.toString();
+ }
+
+ // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ public class CycleBasedPointSetBase2Iterator
+ extends CycleBasedPointSetIterator {
+
+ protected int[] curCycleI; // The array for current cycle
+
+ public CycleBasedPointSetBase2Iterator () {
+ super ();
+ resetCurCycle (0);
+ }
+
+ protected void init() { }
+
+ public void resetCurCycle (int index) {
+ curCycleIndex = index;
+ curCycle = (AbstractList) cycles.get (index);
+ curCycleI = ((IntArrayList) curCycle).elements();
+ }
+
+ public double nextCoordinate() {
+ // First, verify if there are still points....
+ if (curPointIndex >= numPoints)
+ outOfBounds();
+ int x = curCycleI [curCoordInCycle];
+ if (digitalShift != null) {
+ if (curCoordIndex >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, curCoordIndex + 1, shiftStream);
+ x ^= digitalShift[curCoordIndex];
+ }
+ curCoordIndex++;
+ curCoordInCycle++;
+ if (curCoordInCycle >= curCycle.size())
+ curCoordInCycle = 0;
+ if (digitalShift == null)
+ return x * normFactor;
+ else
+ return x * normFactor + EpsilonHalf;
+ }
+
+ public void nextCoordinates (double p[], int dim) {
+ // First, verify if there are still points....
+ if (curPointIndex >= numPoints)
+ outOfBounds();
+ if (curCoordIndex + dim >= dimShift)
+ addRandomShift (dimShift, curCoordIndex + dim + 1, shiftStream);
+ int j = curCoordInCycle;
+ int maxj = curCycle.size();
+ int x;
+ for (int i = 0; i < dim; i++) {
+ x = curCycleI [curCoordInCycle++];
+ if (curCoordInCycle >= maxj) curCoordInCycle = 0;
+ if (digitalShift == null)
+ p[i] = x * normFactor;
+ else
+ p[i] = (digitalShift[curCoordIndex + i] ^ x) * normFactor + EpsilonHalf;
+ }
+ curCoordIndex += dim;
+ }
+
+ public int nextPoint (double p[], int dim) {
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ nextCoordinates (p, dim);
+ resetToNextPoint();
+ return curPointIndex;
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/CycleBasedPointSetBase2.tex b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSetBase2.tex
new file mode 100644
index 0000000..312cee4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/CycleBasedPointSetBase2.tex
@@ -0,0 +1,238 @@
+\defmodule {CycleBasedPointSetBase2}
+
+Similar to \class{CycleBasedPointSet}, except that the successive
+values in the cycles are stored as integers in the range
+$\{0,\dots,2^k-1\}$, where $1\le k \le 31$.
+The output values $u_{i,j}$ are obtained by dividing these integer
+values by $2^k$. Point sets where the successive coordinates of each
+point are obtained via linear recurrences modulo 2 (e.g., linear feedback
+shift registers or Korobov-type polynomial lattice rules)
+are naturally expressed in this form.
+Storing the integers $2^k u_{i,j}$ instead of the $u_{i,j}$ themselves
+makes it easier to apply randomizations such as digital random shifts
+in base 2, which are applied to the bits \emph{before} transforming
+the value to a real number $u_{i,j}$. When a random digital shift is
+performed, it applies a bitwise exclusive-or of all the points with a single
+ random point.
+% \richard{Ne faudrait-il pas un constructeur explicite pour cette classe?}
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: CycleBasedPointSetBase2
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.list.*;
+\end{hide}
+
+public abstract class CycleBasedPointSetBase2 extends CycleBasedPointSet\begin{hide} {
+
+// dim = Integer.MAX_VALUE; // Dimension is infinite.
+ private int[] digitalShift; // Digital shift, initially zero (null).
+ // Entry j is for dimension j.
+ protected int numBits; // Number of bits in stored values.
+ protected double normFactor; // To convert output to (0,1); 1/2^numBits.
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection* {Methods}
+
+\begin{code}
+\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ // Find cycle that contains point i, then index in cycle.
+ int l = 0; // Length of next cycle.
+ int n = 0; // Total length of cycles added so far.
+ int k;
+ for (k = 0; n <= i; k++)
+ n += l = ((AbstractList) cycles.get (k)).size();
+ AbstractList curCycle = (AbstractList) cycles.get (k-1);
+ int[] curCycleI = ((IntArrayList) curCycle).elements();
+ int coordinate = (i - n + l + j) % curCycle.size();
+ int shift = 0;
+ if (digitalShift != null) {
+ shift = digitalShift[j];
+ return (shift ^ curCycleI[coordinate]) * normFactor + EpsilonHalf;
+ } else
+ return (shift ^ curCycleI[coordinate]) * normFactor;
+ }
+
+ public PointSetIterator iterator() {
+ return new CycleBasedPointSetBase2Iterator ();
+ }
+\end{hide}
+
+ public void addRandomShift (int d1, int d2, RandomStream stream) \begin{hide} {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (digitalShift == null) {
+ digitalShift = new int[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ int[] temp = new int[d3];
+ capacityShift = d3;
+ for (int i = 0; i < dimShift; i++)
+ temp[i] = digitalShift[i];
+ digitalShift = temp;
+ }
+ dimShift = d2;
+ int maxj;
+ if (numBits < 31) {
+ maxj = (1 << numBits) - 1;
+ } else {
+ maxj = 2147483647;
+ }
+ for (int i = d1; i < d2; i++)
+ digitalShift[i] = stream.nextInt (0, maxj);
+ shiftStream = stream;
+
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds a random digital shift in base 2 to all the points
+ of the point set, using stream \texttt{stream} to generate the random numbers,
+ for coordinates \texttt{d1} to \texttt{d2 - 1}.
+ This applies a bitwise exclusive-or of all the points with a single
+ random point.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ digitalShift = null;
+ }
+\end{code}
+\begin{tabb}
+ Erases the current digital random shift, if any.
+\end{tabb}\end{hide}
+\begin{code}
+\begin{hide}
+
+ public String formatPoints() {
+ StringBuffer sb = new StringBuffer (toString());
+ for (int c = 0; c < numCycles; c++) {
+ AbstractList curCycle = (AbstractList)cycles.get (c);
+ int[] cycle = ((IntArrayList)curCycle).elements();
+ sb.append (PrintfFormat.NEWLINE + "Cycle " + c + ": (");
+ boolean first = true;
+ for (int e = 0; e < curCycle.size(); e++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (cycle[e]);
+ }
+ sb.append (")");
+ }
+ return sb.toString();
+ }
+
+ // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ public class CycleBasedPointSetBase2Iterator
+ extends CycleBasedPointSetIterator {
+
+ protected int[] curCycleI; // The array for current cycle
+
+ public CycleBasedPointSetBase2Iterator () {
+ super ();
+ resetCurCycle (0);
+ }
+
+ protected void init() { }
+
+ public void resetCurCycle (int index) {
+ curCycleIndex = index;
+ curCycle = (AbstractList) cycles.get (index);
+ curCycleI = ((IntArrayList) curCycle).elements();
+ }
+
+ public double nextCoordinate() {
+ // First, verify if there are still points....
+ if (curPointIndex >= numPoints)
+ outOfBounds();
+ int x = curCycleI [curCoordInCycle];
+ if (digitalShift != null) {
+ if (curCoordIndex >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, curCoordIndex + 1, shiftStream);
+ x ^= digitalShift[curCoordIndex];
+ }
+ curCoordIndex++;
+ curCoordInCycle++;
+ if (curCoordInCycle >= curCycle.size())
+ curCoordInCycle = 0;
+ if (digitalShift == null)
+ return x * normFactor;
+ else
+ return x * normFactor + EpsilonHalf;
+ }
+
+ public void nextCoordinates (double p[], int dim) {
+ // First, verify if there are still points....
+ if (curPointIndex >= numPoints)
+ outOfBounds();
+ if (curCoordIndex + dim >= dimShift)
+ addRandomShift (dimShift, curCoordIndex + dim + 1, shiftStream);
+ int j = curCoordInCycle;
+ int maxj = curCycle.size();
+ int x;
+ for (int i = 0; i < dim; i++) {
+ x = curCycleI [curCoordInCycle++];
+ if (curCoordInCycle >= maxj) curCoordInCycle = 0;
+ if (digitalShift == null)
+ p[i] = x * normFactor;
+ else
+ p[i] = (digitalShift[curCoordIndex + i] ^ x) * normFactor + EpsilonHalf;
+ }
+ curCoordIndex += dim;
+ }
+
+ public int nextPoint (double p[], int dim) {
+ if (getCurPointIndex() >= getNumPoints ())
+ outOfBounds();
+ curCoordIndex = 0;
+ curCoordInCycle = startPointInCycle;
+ nextCoordinates (p, dim);
+ resetToNextPoint();
+ return curPointIndex;
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNet.java b/source/umontreal/iro/lecuyer/hups/DigitalNet.java
new file mode 100644
index 0000000..4a55aa7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNet.java
@@ -0,0 +1,1291 @@
+
+
+/*
+ * Class: DigitalNet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.*;
+
+
+/**
+ * This class provides the basic structures for storing and
+ * manipulating <SPAN CLASS="textit">linear digital nets in base <SPAN CLASS="MATH"><I>b</I></SPAN></SPAN>, for an arbitrary
+ * base <SPAN CLASS="MATH"><I>b</I> >= 2</SPAN>. We recall that a net contains <SPAN CLASS="MATH"><I>n</I> = <I>b</I><SUP>k</SUP></SPAN> points in
+ * <SPAN CLASS="MATH"><I>s</I></SPAN> dimensions, where the <SPAN CLASS="MATH"><I>i</I></SPAN>th point
+ * <SPAN CLASS="MATH"><B>u</B><SUB>i</SUB></SPAN>,
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,..., <I>b</I><SUP>k</SUP> - 1</SPAN>, is defined as follows:
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>i</I></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP> = </TD>
+ * <TD ALIGN="LEFT" NOWRAP>∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>i, r</SUB><I>b</I><SUP>r</SUP>,</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT">(<I>u</I><SUB>i, j, 1</SUB> <I>u</I><SUB>i, j, 2</SUB> …)<SUP>T</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP> = </TD>
+ * <TD ALIGN="LEFT" NOWRAP><B>C</B><SUB>j</SUB> (<I>a</I><SUB>i, 0</SUB> <I>a</I><SUB>i, 1</SUB> … <I>a</I><SUB>i, k-1</SUB>)<SUP>T</SUP>,</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>u</I><SUB>i, j</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP> = </TD>
+ * <TD ALIGN="LEFT" NOWRAP>∑<SUB>r=1</SUB><SUP>∞</SUP><I>u</I><SUB>i, j, r</SUB><I>b</I><SUP>-r</SUP>,</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><B>u</B><SUB>i</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP> = </TD>
+ * <TD ALIGN="LEFT" NOWRAP>(<I>u</I><SUB>i, 0</SUB>, …, <I>u</I><SUB>i, s-1</SUB>).</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * In our implementation, the matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> are <SPAN CLASS="MATH"><I>r</I>×<I>k</I></SPAN>,
+ * so the expansion of <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> is truncated to its first <SPAN CLASS="MATH"><I>r</I></SPAN> terms.
+ * The points are stored implicitly by storing the generator matrices
+ *
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> in a large two-dimensional array of integers,
+ * with <SPAN CLASS="MATH"><I>srk</I></SPAN> elements.
+ *
+ * <P>
+ * The points
+ * <SPAN CLASS="MATH"><B>u</B><SUB>i</SUB></SPAN> are enumerated using the Gray code
+ * technique.
+ * With this technique, the <SPAN CLASS="MATH"><I>b</I></SPAN>-ary representation of <SPAN CLASS="MATH"><I>i</I></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><B>a</B><SUB>i</SUB> = (<I>a</I><SUB>i, 0</SUB>,..., <I>a</I><SUB>i, k-1</SUB>)</SPAN>, is replaced by a Gray code representation of <SPAN CLASS="MATH"><I>i</I></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><B>g</B><SUB>i</SUB> = (<I>g</I><SUB>i, 0</SUB>,..., <I>g</I><SUB>i, k-1</SUB>)</SPAN>. The Gray code
+ * <SPAN CLASS="MATH"><B>g</B><SUB>i</SUB></SPAN>
+ * used here is defined by
+ * <SPAN CLASS="MATH"><I>g</I><SUB>i, k-1</SUB> = <I>a</I><SUB>i, k-1</SUB></SPAN> and
+ *
+ *
+ * <SPAN CLASS="MATH"><I>g</I><SUB>i, <I>ν</I></SUB> = (<I>a</I><SUB>i, <I>ν</I></SUB> - <I>a</I><SUB>i, <I>ν</I>+1</SUB>)mod <I>b</I></SPAN> for
+ * <SPAN CLASS="MATH"><I>ν</I> = 0,..., <I>k</I> - 2</SPAN>.
+ * It has the property that
+ *
+ * <SPAN CLASS="MATH"><B>g</B><SUB>i</SUB> = (<I>g</I><SUB>i, 0</SUB>,..., <I>g</I><SUB>i, k-1</SUB>)</SPAN> and
+ *
+ * <SPAN CLASS="MATH"><B>g</B><SUB>i+1</SUB> = (<I>g</I><SUB>i+1, 0</SUB>,..., <I>g</I><SUB>i+1, k-1</SUB>)</SPAN>
+ * differ only in the position of the smallest index
+ * <SPAN CLASS="MATH"><I>ν</I></SPAN> such that
+ *
+ * <SPAN CLASS="MATH"><I>a</I><SUB>i, <I>ν</I></SUB> < <I>b</I> - 1</SPAN>, and we have
+ * <SPAN CLASS="MATH"><I>g</I><SUB>i+1, <I>ν</I></SUB> = (<I>g</I><SUB>i, <I>ν</I></SUB> +1)mod <I>b</I></SPAN>
+ * in that position.
+ *
+ * <P>
+ * This Gray code representation permits a more efficient enumeration
+ * of the points by the iterators.
+ * It changes the order in which the points
+ * <SPAN CLASS="MATH"><B>u</B><SUB>i</SUB></SPAN> are enumerated,
+ * but the first <SPAN CLASS="MATH"><I>b</I><SUP>m</SUP></SPAN> points remain the same for every integer <SPAN CLASS="MATH"><I>m</I></SPAN>.
+ * The <SPAN CLASS="MATH"><I>i</I></SPAN>th point of the sequence with the Gray enumeration
+ * is the <SPAN CLASS="MATH"><I>i'</I></SPAN>th point of the original enumeration, where <SPAN CLASS="MATH"><I>i'</I></SPAN> is the
+ * integer whose <SPAN CLASS="MATH"><I>b</I></SPAN>-ary representation
+ * <SPAN CLASS="MATH"><B>a</B><SUB>i'</SUB></SPAN> is given by the Gray
+ * code
+ * <SPAN CLASS="MATH"><B>g</B><SUB>i</SUB></SPAN>.
+ * To enumerate all the points successively, we never need to compute
+ * the Gray codes explicitly.
+ * It suffices to know the position <SPAN CLASS="MATH"><I>ν</I></SPAN> of the Gray code digit
+ * that changes at each step, and this can be found quickly from the
+ * <SPAN CLASS="MATH"><I>b</I></SPAN>-ary representation
+ * <SPAN CLASS="MATH"><B>a</B><SUB>i</SUB></SPAN>.
+ * The digits of each coordinate <SPAN CLASS="MATH"><I>j</I></SPAN> of the current point can be updated by
+ * adding column <SPAN CLASS="MATH"><I>ν</I></SPAN> of the generator matrix
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> to the old digits,
+ * modulo <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * <P>
+ * One should avoid using the method {@link #getCoordinate getCoordinate}(i, j)
+ * for arbitrary values of <TT>i</TT> and <TT>j</TT>, because this is much
+ * slower than using an iterator to access successive coordinates.
+ *
+ * <P>
+ * Digital nets can be randomized in various ways.
+ * Several types of randomizations specialized for nets are implemented
+ * directly in this class.
+ *
+ * <P>
+ * A simple but important randomization is the <SPAN CLASS="textit">random digital shift</SPAN>
+ * in base <SPAN CLASS="MATH"><I>b</I></SPAN>, defined as follows: replace each digit
+ * <SPAN CLASS="MATH"><I>u</I><SUB>i, j, <I>ν</I></SUB></SPAN> in the third equation above
+ * by
+ * <SPAN CLASS="MATH">(<I>u</I><SUB>i, j, <I>ν</I></SUB> + <I>d</I><SUB>j, <I>ν</I></SUB>)mod <I>b</I></SPAN>,
+ * where the <SPAN CLASS="MATH"><I>d</I><SUB>j, <I>ν</I></SUB></SPAN>'s are i.i.d. uniform over
+ * <SPAN CLASS="MATH">{0,..., <I>b</I> - 1}</SPAN>.
+ * This is equivalent to applying a single random shift to all the points
+ * in a formal series representation of their coordinates.
+ * In practice, the digital shift is truncated to <SPAN CLASS="MATH"><I>w</I></SPAN> digits,
+ * for some integer <SPAN CLASS="MATH"><I>w</I> >= <I>r</I></SPAN>.
+ * Applying a digital shift does not change the equidistribution
+ * and <SPAN CLASS="MATH">(<I>t</I>, <I>m</I>, <I>s</I>)</SPAN>-net properties of a point set.
+ * Moreover, with the random shift, each point is uniformly distributed over
+ * the unit hypercube (but the points are not independent, of course).
+ *
+ * <P>
+ * A second class of randomizations specialized for digital nets
+ * are the <SPAN CLASS="textit">linear matrix scrambles</SPAN>, which multiply the matrices
+ *
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>
+ * by a random invertible matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN>, modulo <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * There are several variants, depending on how
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> is generated, and on
+ * whether
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> is multiplied on the left or on the right.
+ * In our implementation, the linear matrix scrambles are incorporated directly
+ * into the matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>, so they
+ * do not slow down the enumeration of points.
+ * Methods are available for applying
+ * linear matrix scrambles and for removing these randomizations.
+ * These methods generate the appropriate random numbers and make
+ * the corresponding changes to the
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>'s.
+ * A copy of the original
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>'s is maintained, so the point
+ * set can be returned to its original unscrambled state at any time.
+ * When a new linear matrix scramble is applied, it is always applied to
+ * the <SPAN CLASS="textit">original</SPAN> generator matrices.
+ * The method {@link #resetGeneratorMatrices resetGeneratorMatrices} removes the current matrix
+ * scramble by resetting the generator matrices to their original state.
+ * On the other hand, the method {@link #eraseOriginalGeneratorMatrices eraseOriginalGeneratorMatrices}
+ * replaces the original generator matrices by the current
+ * ones, making the changes permanent.
+ * This is useful if one wishes to apply two or more linear matrix
+ * scrambles on top of each other.
+ *
+ * <P>
+ * Linear matrix scrambles are usually combined with a random digital shift;
+ * this combination is called an <SPAN CLASS="textit">affine matrix scramble</SPAN>.
+ * These two randomizations are applied via separate methods.
+ * The linear matrix scrambles are incorporated into the matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>
+ * whereas the digital random shift is stored and applied separately,
+ * independently of the other scramblings.
+ *
+ * <P>
+ * Applying a digital shift or a linear matrix scramble to a digital net
+ * invalidates all iterators for that randomized point,
+ * because each iterator uses a
+ * <SPAN CLASS="textit">cached</SPAN> copy of the current point, which is updated only when
+ * the current point index of that iterator changes, and the update also
+ * depends on the cached copy of the previous point.
+ * After applying any kind of scrambling, the iterators must be
+ * reinitialized to the <SPAN CLASS="textit">initial point</SPAN> by invoking
+ * {@link PointSetIterator#resetCurPointIndex resetCurPointIndex}
+ * or reinstantiated by the {@link #iterator iterator} method
+ * (this is not done automatically).
+ *
+ */
+public class DigitalNet extends PointSet {
+
+ // Variables to be initialized by the constructor subclasses.
+ protected int b = 0; // Base.
+ protected int numCols = 0; // The number of columns in each C_j. (= k)
+ protected int numRows = 0; // The number of rows in each C_j. (= r)
+ protected int outDigits = 0; // Number of output digits (= w)
+ private int[][] originalMat; // Original gen. matrices without randomizat.
+ protected int[][] genMat; // The current generator matrices.
+ // genMat[j*numCols+c][l] contains column c
+ // and row l of C_j.
+ protected int[][] digitalShift; // The digital shift, initially zero (null).
+ // Entry [j][l] is for dimension j, digit l,
+ // for 0 <= l < outDigits.
+ protected double normFactor; // To convert output to (0,1); 1/b^outDigits.
+ protected double[] factor; // Lookup table in ascending order: factor[i]
+ // = 1/b^{i+1} for 0 <= i < outDigits.
+
+ // primes gives the first index in array FaureFactor
+ // for the prime p. If primes[i] = p, then
+ // FaureFactor[p][j] contains the Faure ordered factors of base p.
+ private int[] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
+ 43, 47, 53, 59, 61, 67};
+
+ // Factors on the diagonal corresponding to base b = prime[i] ordered by
+ // increasing Bounds.
+ private int[][] FaureFactor = {{1}, {1, 2}, {2, 3, 1, 4},
+ { 2, 3, 4, 5, 1, 6}, {3, 4, 7, 8, 2, 5, 6, 9, 1, 10},
+ { 5, 8, 3, 4, 9, 10, 2, 6, 7, 11, 1, 12},
+ { 5, 7, 10, 12, 3, 6, 11, 14, 4, 13, 2, 8, 9, 15, 1, 16},
+ { 7, 8, 11, 12, 4, 5, 14, 15, 3, 6, 13, 16, 2, 9, 10, 17, 1, 18},
+ { 5, 9, 14, 18, 7, 10, 13, 16, 4, 6, 17, 19, 3, 8, 15, 20, 2, 11, 12,
+ 21, 1, 22},
+ { 8, 11, 18, 21, 12, 17, 9, 13, 16, 20, 5, 6, 23, 24, 4, 7, 22, 25, 3,
+ 10, 19, 26, 2, 14, 15, 27, 1, 28},
+ { 12, 13, 18, 19, 11, 14, 17, 20, 7, 9, 22, 24, 4, 8, 23, 27, 5, 6, 25,
+ 26, 3, 10, 21, 28, 2, 15, 16, 29, 1, 30},
+ { 8, 14, 23, 29, 10, 11, 26, 27, 13, 17, 20, 24, 7, 16, 21, 30, 5, 15,
+ 22, 32, 6, 31, 4, 9, 28, 33, 3, 12, 25, 34, 2, 18, 19, 35, 1, 36},
+ { 16, 18, 23, 25, 11, 15, 26, 30, 12, 17, 24, 29, 9, 32, 13, 19, 22,
+ 28, 6, 7, 34, 35, 5, 8, 33, 36, 4, 10, 31, 37, 3, 14, 27, 38, 2, 20,
+ 21, 39, 1, 40},
+ { 12, 18, 25, 31, 9, 19, 24, 34, 8, 16, 27, 35, 10, 13, 30, 33, 15, 20,
+ 23, 28, 5, 17, 26, 38, 6, 7, 36, 37, 4, 11, 32, 39, 3, 14, 29, 40, 2,
+ 21, 22, 41, 1, 42},
+ { 13, 18, 29, 34, 11, 17, 30, 36, 10, 14, 33, 37, 7, 20, 27, 40, 9, 21,
+ 26, 38, 15, 22, 25, 32, 6, 8, 39, 41, 5, 19, 28, 42, 4, 12, 35, 43,
+ 3, 16, 31, 44, 2, 23, 24, 45, 1, 46},
+ { 14, 19, 34, 39, 23, 30, 12, 22, 31, 41, 8, 11, 20, 24, 29, 33, 42, 45,
+ 10, 16, 37, 43, 7, 15, 38, 46, 17, 25, 28, 36, 5, 21, 32, 48, 6, 9,
+ 44, 47, 4, 13, 40, 49, 3, 18, 35, 50, 2, 26, 27, 51, 1, 52},
+ { 25, 26, 33, 34, 18, 23, 36, 41, 14, 21, 38, 45, 24, 27, 32, 35, 11,
+ 16, 43, 48, 9, 13, 46, 50, 8, 22, 37, 51, 7, 17, 42, 52, 19, 28, 31,
+ 40, 6, 10, 49, 53, 5, 12, 47, 54, 4, 15, 44, 55, 3, 20, 39, 56, 2,
+ 29, 30, 57, 1, 58},
+ { 22, 25, 36, 39, 17, 18, 43, 44, 24, 28, 33, 37, 13, 14, 47, 48, 16,
+ 19, 42, 45, 9, 27, 34, 52, 8, 23, 38, 53, 11, 50, 7, 26, 35, 54, 21,
+ 29, 32, 40, 6, 10, 51, 55, 5, 12, 49, 56, 4, 15, 46, 57, 3, 20, 41,
+ 58, 2, 30, 31, 59, 1, 60},
+ { 18, 26, 41, 49, 14, 24, 43, 53, 12, 28, 39, 55, 29, 30, 37, 38, 10,
+ 20, 47, 57, 16, 21, 46, 51, 8, 25, 42, 59, 13, 31, 36, 54, 9, 15,
+ 52, 58, 7, 19, 48, 60, 23, 32, 35, 44, 5, 27, 40, 62, 6, 11, 56,
+ 61, 4, 17, 50, 63, 3, 22, 45, 64, 2, 33, 34, 65, 1, 66}
+};
+
+
+ public double getCoordinate (int i, int j) {
+ // convert i to Gray representation, put digits in gdigit[].
+ int l, c, sum;
+ int[] bdigit = new int[numCols];
+ int[] gdigit = new int[numCols];
+ int idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ double result = 0;
+ if (digitalShift != null && dimShift < j)
+ addRandomShift (dimShift, j, shiftStream);
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * gdigit[c];
+ result += (sum % b) * factor[l];
+ }
+ if (digitalShift != null)
+ result += EpsilonHalf;
+ return result;
+ }
+
+ public PointSetIterator iterator() {
+ return new DigitalNetIterator();
+ }
+
+
+ /**
+ * Empty constructor.
+ *
+ */
+ public DigitalNet () {
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN>, the coordinate <SPAN CLASS="MATH"><I>j</I></SPAN> of point <SPAN CLASS="MATH"><I>i</I></SPAN>, the points
+ * being enumerated in the standard order (no Gray code).
+ *
+ * @param i point index
+ *
+ * @param j coordinate index
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN>
+ *
+ */
+ public double getCoordinateNoGray (int i, int j) {
+ // convert i to b-ary representation, put digits in bdigit[].
+ int l, c, sum;
+ int[] bdigit = new int[numCols];
+ int idigits = 0;
+ for (c = 0; i > 0; c++) {
+ idigits++;
+ bdigit[c] = i % b;
+ i = i / b;
+ }
+ if (digitalShift != null && dimShift < j)
+ addRandomShift (dimShift, j, shiftStream);
+ double result = 0;
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * bdigit[c];
+ result += (sum % b) * factor[l];
+ }
+ if (digitalShift != null)
+ result += EpsilonHalf;
+ return result;
+ }
+
+
+ /**
+ * This iterator does not use the Gray code. Thus the points are enumerated
+ * in the order of their first coordinate before randomization.
+ *
+ */
+ public PointSetIterator iteratorNoGray() {
+ return new DigitalNetIteratorNoGray();
+ }
+
+
+ /**
+ * Adds a random digital shift to all the points of the point set,
+ * using stream <TT>stream</TT> to generate the random numbers.
+ * For each coordinate <SPAN CLASS="MATH"><I>j</I></SPAN> from <TT>d1</TT> to <TT>d2-1</TT>,
+ * the shift vector
+ * <SPAN CLASS="MATH">(<I>d</I><SUB>j, 0</SUB>,..., <I>d</I><SUB>j, k-1</SUB>)</SPAN>
+ * is generated uniformly over
+ * <SPAN CLASS="MATH">{0,..., <I>b</I> - 1}<SUP>k</SUP></SPAN> and added modulo <SPAN CLASS="MATH"><I>b</I></SPAN> to
+ * the digits of all the points.
+ * After adding a digital shift, all iterators must be reconstructed or
+ * reset to zero.
+ *
+ * @param stream random number stream used to generate uniforms
+ *
+ *
+ */
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (digitalShift == null) {
+ digitalShift = new int[d2][outDigits];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ int[][] temp = new int[d3][outDigits];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ for (int j = 0; j < outDigits; j++)
+ temp[i][j] = digitalShift[i][j];
+ digitalShift = temp;
+ }
+ for (int i = d1; i < d2; i++)
+ for (int j = 0; j < outDigits; j++)
+ digitalShift[i][j] = stream.nextInt (0, b - 1);
+ dimShift = d2;
+ shiftStream = stream;
+ }
+
+
+ /**
+ * Same as {@link #addRandomShift addRandomShift}<TT>(0, dim, stream)</TT>,
+ * where <TT>dim</TT> is the dimension of the digital net.
+ *
+ * @param stream random number stream used to generate uniforms
+ *
+ *
+ */
+ public void addRandomShift (RandomStream stream) {
+ addRandomShift (0, dim, stream);
+ }
+
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ digitalShift = null;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer (100);
+ if (b > 0) {
+ sb.append ("Base = "); sb.append (b);
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append ("Num cols = "); sb.append (numCols);
+ sb.append (PrintfFormat.NEWLINE + "Num rows = ");
+ sb.append (numRows);
+ sb.append (PrintfFormat.NEWLINE + "outDigits = ");
+ sb.append (outDigits);
+ return sb.toString();
+ }
+
+
+ // Print matrices M for dimensions 0 to N-1.
+ private void printMat (int N, int[][][] A, String name) {
+ for (int i = 0; i < N; i++) {
+ System.out.println ("-------------------------------------" +
+ PrintfFormat.NEWLINE + name + " dim = " + i);
+ int l, c; // row l, column c, dimension i for A[i][l][c].
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numCols; c++) {
+ System.out.print (A[i][l][c] + " ");
+ }
+ System.out.println ("");
+ }
+ }
+ System.out.println ("");
+ }
+
+
+ // Print matrix M
+ private void printMat0 (int[][] A, String name) {
+ System.out.println ("-------------------------------------" +
+ PrintfFormat.NEWLINE + name);
+ int l, c; // row l, column c for A[l][c].
+ for (l = 0; l < numCols; l++) {
+ for (c = 0; c < numCols; c++) {
+ System.out.print (A[l][c] + " ");
+ }
+ System.out.println ("");
+ }
+ System.out.println ("");
+ }
+
+
+ // Left-multiplies lower-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // This implementation is safe only if (numRows*(b-1)^2) is a valid int.
+ private void leftMultiplyMat (int j, int[][] Mj) {
+ int l, c, i, sum; // Dimension j, row l, column c for new C_j.
+ for (l = 0; l < numRows ; l++) {
+ for (c = 0; c < numCols; c++) {
+ // Multiply row l of M_j by column c of C_j.
+ sum = 0;
+ for (i = 0; i <= l; i++)
+ sum += Mj[l][i] * originalMat[j*numCols+c][i];
+ genMat[j*numCols+c][l] = sum % b;
+ }
+ }
+ }
+
+
+ // Left-multiplies diagonal matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // This implementation is safe only if (numRows*(b-1)^2) is a valid int.
+ private void leftMultiplyMatDiag (int j, int[][] Mj) {
+ int l, c, sum; // Dimension j, row l, column c for new C_j.
+ for (l = 0; l < numRows ; l++) {
+ for (c = 0; c < numCols; c++) {
+ // Multiply row l of M_j by column c of C_j.
+ sum = Mj[l][l] * originalMat[j*numCols+c][l];
+ genMat[j*numCols+c][l] = sum % b;
+ }
+ }
+ }
+
+
+ // Right-multiplies original C_j by upper-triangular matrix Mj,
+ // where original C_j is in originalMat and result is in genMat.
+ // This implementation is safe only if (numCols*(b-1)^2) is a valid int.
+ private void rightMultiplyMat (int j, int[][] Mj) {
+ int l, c, i, sum; // Dimension j, row l, column c for new C_j.
+ for (l = 0; l < numRows ; l++) {
+ for (c = 0; c < numCols; c++) {
+ // Multiply row l of C_j by column c of M_j.
+ sum = 0;
+ for (i = 0; i <= c; i++)
+ sum += originalMat[j*numCols+i][l] * Mj[i][c];
+ genMat[j*numCols+c][l] = sum % b;
+ }
+ }
+ }
+
+
+ private int getFaureIndex (String method, int sb, int flag) {
+ // Check for errors in ...FaurePermut. Returns the index ib of the
+ // base b in primes, i.e. b = primes[ib].
+ if (sb >= b)
+ throw new IllegalArgumentException (PrintfFormat.NEWLINE +
+ " sb >= base in " + method);
+ if (sb < 1)
+ throw new IllegalArgumentException (PrintfFormat.NEWLINE +
+ " sb = 0 in " + method);
+ if ((flag > 2) || (flag < 0))
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE + " lowerFlag not in {0, 1, 2} in "
+ + method);
+
+ // Find index ib of base in array primes
+ int ib = 0;
+ while ((ib < primes.length) && (primes[ib] < b))
+ ib++;
+ if (ib >= primes.length)
+ throw new IllegalArgumentException ("base too large in " + method);
+ if (b != primes[ib])
+ throw new IllegalArgumentException (
+ "Faure factors are not implemented for this base in " + method);
+ return ib;
+ }
+
+
+ /**
+ * Applies a linear scramble by multiplying each
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> on the left
+ * by a <SPAN CLASS="MATH"><I>w</I>×<I>w</I></SPAN> nonsingular lower-triangular matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN>,
+ * as suggested by Matoušek and implemented
+ * by Hong and Hickernell. The diagonal entries of
+ * each matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> are generated uniformly over
+ *
+ * <SPAN CLASS="MATH">{1,..., <I>b</I> - 1}</SPAN>, the entries below the diagonal are generated uniformly
+ * over
+ * <SPAN CLASS="MATH">{0,..., <I>b</I> - 1}</SPAN>, and all these entries are generated independently.
+ * This means that in base <SPAN CLASS="MATH"><I>b</I> = 2</SPAN>, all diagonal elements are equal to 1.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ *
+ */
+ public void leftMatrixScramble (RandomStream stream) {
+ int j, l, c; // dimension j, row l, column c.
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numRows; c++) {
+ if (c == l) // No zero on the diagonal.
+ scrambleMat[j][l][c] = stream.nextInt(1, b - 1) ;
+ else if (c < l)
+ scrambleMat[j][l][c] = stream.nextInt(0, b - 1);
+ else
+ scrambleMat[j][l][c] = 0; // Zeros above the diagonal;
+ }
+ }
+ }
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+
+ /**
+ * Similar to {@link #leftMatrixScramble leftMatrixScramble} except that all the
+ * off-diagonal elements of the
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> are 0.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ *
+ */
+ public void leftMatrixScrambleDiag (RandomStream stream) {
+ int j, l, c; // dimension j, row l, column c.
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the diagonal scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numRows; c++) {
+ if (c == l) // No zero on the diagonal.
+ scrambleMat[j][l][c] = stream.nextInt(1, b - 1) ;
+ else
+ scrambleMat[j][l][c] = 0; // Diagonal matrix;
+ }
+ }
+ }
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]);
+ }
+
+
+ private void LMSFaurePermut (String method, RandomStream stream, int sb,
+ int lowerFlag) {
+/*
+ If \texttt{lowerFlag = 2}, the off-diagonal elements below the diagonal
+ of $\mathbf{M}_j$ are chosen as in \method{leftMatrixScramble}{}.
+ If \texttt{lowerFlag = 1}, the off-diagonal elements below the diagonal of
+ $\mathbf{M}_j$ are also chosen from the restricted set. If
+ \texttt{lowerFlag = 0}, the off-diagonal elements of $\mathbf{M}_j$ are all 0.
+*/
+ int ib = getFaureIndex (method, sb, lowerFlag);
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int jb;
+ int j, l, c; // dimension j, row l, column c.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numRows; c++) {
+ if (c == l) {
+ jb = stream.nextInt(0, sb - 1);
+ scrambleMat[j][l][c] = FaureFactor[ib][jb];
+ } else if (c < l) {
+ if (lowerFlag == 2) {
+ scrambleMat[j][l][c] = stream.nextInt(0, b - 1);
+ } else if (lowerFlag == 1) {
+ jb = stream.nextInt(0, sb - 1);
+ scrambleMat[j][l][c] = FaureFactor[ib][jb];
+ } else { // lowerFlag == 0
+ scrambleMat[j][l][c] = 0;
+ }
+ } else
+ scrambleMat[j][l][c] = 0; // Zeros above the diagonal;
+ }
+ }
+ }
+ // printMat (dim, scrambleMat, method);
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ if (lowerFlag == 0)
+ for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]);
+ else
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+ /**
+ * Similar to {@link #leftMatrixScramble leftMatrixScramble} except that the diagonal elements
+ * of each matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> are chosen from a restricted set of the best
+ * integers as calculated by Faure. They are generated
+ * uniformly over the first <TT>sb</TT> elements of array <SPAN CLASS="MATH"><I>F</I></SPAN>, where <SPAN CLASS="MATH"><I>F</I></SPAN> is
+ * made up of a permutation of the integers
+ * <SPAN CLASS="MATH">[1..(<I>b</I> - 1)]</SPAN>. These integers are
+ * sorted by increasing order of the upper bounds of the extreme discrepancy
+ * for the given integer.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ * @param sb Only the first <SPAN CLASS="MATH"><I>sb</I></SPAN> elements of <SPAN CLASS="MATH"><I>F</I></SPAN> are used
+ *
+ *
+ */
+ public void leftMatrixScrambleFaurePermut (RandomStream stream, int sb) {
+ LMSFaurePermut ("leftMatrixScrambleFaurePermut", stream, sb, 2);
+ }
+
+
+ /**
+ * Similar to {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut} except that all
+ * off-diagonal elements are 0.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ * @param sb Only the first <SPAN CLASS="MATH"><I>sb</I></SPAN> elements of <SPAN CLASS="MATH"><I>F</I></SPAN> are used
+ *
+ *
+ */
+ public void leftMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb) {
+ LMSFaurePermut ("leftMatrixScrambleFaurePermutDiag",
+ stream, sb, 0);
+ }
+
+
+ /**
+ * Similar to {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut} except that the
+ * elements under the diagonal are also
+ * chosen from the same restricted set as the diagonal elements.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ * @param sb Only the first <SPAN CLASS="MATH"><I>sb</I></SPAN> elements of <SPAN CLASS="MATH"><I>F</I></SPAN> are used
+ *
+ *
+ */
+ public void leftMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ LMSFaurePermut ("leftMatrixScrambleFaurePermutAll",
+ stream, sb, 1);
+ }
+
+
+ /**
+ * Applies the <SPAN CLASS="MATH"><I>i</I></SPAN>-binomial matrix scramble proposed by Tezuka
+ * .
+ * This multiplies each
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> on the left
+ * by a <SPAN CLASS="MATH"><I>w</I>×<I>w</I></SPAN> nonsingular lower-triangular matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> as in
+ * {@link #leftMatrixScramble leftMatrixScramble}, but with the additional constraint that
+ * all entries on any given diagonal or subdiagonal of
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> are identical.
+ *
+ * @param stream random number stream used as generator of the randomness
+ *
+ *
+ */
+ public void iBinomialMatrixScramble (RandomStream stream) {
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ diag = stream.nextInt(1, b - 1);
+ for (l = 0; l < numRows; l++) {
+ // Single random nonzero element on the diagonal.
+ scrambleMat[j][l][l] = diag;
+ for (c = l+1; c < numRows; c++) scrambleMat[j][l][c] = 0;
+ }
+ for (l = 1; l < numRows; l++) {
+ col1 = stream.nextInt(0, b - 1);
+ for (c = 0; l+c < numRows; c++) scrambleMat[j][l+c][c] = col1;
+ }
+ }
+ // printMat (dim, scrambleMat, "iBinomialMatrixScramble");
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+
+ private void iBMSFaurePermut (String method, RandomStream stream,
+ int sb, int lowerFlag) {
+ int ib = getFaureIndex (method, sb, lowerFlag);
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+ int jb;
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ jb = stream.nextInt(0, sb - 1);
+ diag = FaureFactor[ib][jb];
+ for (l = 0; l < numRows; l++) {
+ // Single random nonzero element on the diagonal.
+ scrambleMat[j][l][l] = diag;
+ for (c = l+1; c < numRows; c++) scrambleMat[j][l][c] = 0;
+ }
+ for (l = 1; l < numRows; l++) {
+ if (lowerFlag == 2) {
+ col1 = stream.nextInt(0, b - 1);
+ } else if (lowerFlag == 1) {
+ jb = stream.nextInt(0, sb - 1);
+ col1 = FaureFactor[ib][jb];
+ } else { // lowerFlag == 0
+ col1 = 0;
+ }
+ for (c = 0; l+c < numRows; c++) scrambleMat[j][l+c][c] = col1;
+ }
+ }
+ // printMat (dim, scrambleMat, method);
+
+ if (lowerFlag > 0)
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ else
+ for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]);
+ }
+
+ /**
+ * Similar to {@link #iBinomialMatrixScramble iBinomialMatrixScramble} except that the diagonal
+ * elements of each matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> are chosen as in
+ * {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut}.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ * @param sb Only the first <SPAN CLASS="MATH"><I>sb</I></SPAN> elements of <SPAN CLASS="MATH"><I>F</I></SPAN> are used
+ *
+ *
+ */
+ public void iBinomialMatrixScrambleFaurePermut (RandomStream stream,
+ int sb) {
+ iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermut",
+ stream, sb, 2);
+ }
+
+
+ /**
+ * Similar to {@link #iBinomialMatrixScrambleFaurePermut iBinomialMatrixScrambleFaurePermut} except that all the
+ * off-diagonal elements are 0.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ * @param sb Only the first <SPAN CLASS="MATH"><I>sb</I></SPAN> elements of <SPAN CLASS="MATH"><I>F</I></SPAN> are used
+ *
+ *
+ */
+ public void iBinomialMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb) {
+ iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermutDiag",
+ stream, sb, 0);
+ }
+
+
+ /**
+ * Similar to {@link #iBinomialMatrixScrambleFaurePermut iBinomialMatrixScrambleFaurePermut} except that the
+ * elements under the diagonal are also
+ * chosen from the same restricted set as the diagonal elements.
+ *
+ * @param stream random number stream used to generate the randomness
+ *
+ * @param sb Only the first <SPAN CLASS="MATH"><I>sb</I></SPAN> elements of <SPAN CLASS="MATH"><I>F</I></SPAN> are used
+ *
+ *
+ */
+ public void iBinomialMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermutAll",
+ stream, sb, 1);
+ }
+
+
+ /**
+ * Applies the <SPAN CLASS="textit">striped matrix scramble</SPAN> proposed by Owen.
+ * It multiplies each
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> on the left
+ * by a <SPAN CLASS="MATH"><I>w</I>×<I>w</I></SPAN> nonsingular lower-triangular matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> as in
+ * {@link #leftMatrixScramble leftMatrixScramble}, but with the additional constraint that
+ * in any column, all entries below the diagonal are equal to the
+ * diagonal entry, which is generated randomly over
+ * <SPAN CLASS="MATH">{1,..., <I>b</I> - 1}</SPAN>.
+ * Note that for <SPAN CLASS="MATH"><I>b</I> = 2</SPAN>, the matrices
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> become deterministic, with
+ * all entries on and below the diagonal equal to 1.
+ *
+ * @param stream random number stream used as generator of the randomness
+ *
+ *
+ */
+ public void stripedMatrixScramble (RandomStream stream) {
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+
+ // If genMat contains original gener. matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (c = 0; c < numRows; c++) {
+ diag = stream.nextInt (1, b - 1); // Random entry in this column.
+ for (l = 0; l < c; l++) scrambleMat[j][l][c] = 0;
+ for (l = c; l < numRows; l++) scrambleMat[j][l][c] = diag;
+ }
+ }
+ // printMat (dim, scrambleMat, "stripedMatrixScramble");
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+
+ /**
+ * Similar to {@link #stripedMatrixScramble stripedMatrixScramble} except that the
+ * elements on and under the diagonal of each matrix
+ * <SPAN CLASS="MATH"><B>M</B><SUB>j</SUB></SPAN> are
+ * chosen as in {@link #leftMatrixScrambleFaurePermut leftMatrixScrambleFaurePermut}.
+ *
+ * @param stream random number stream used as generator of the randomness
+ *
+ * @param sb Only the first <SPAN CLASS="MATH"><I>sb</I></SPAN> elements of <SPAN CLASS="MATH"><I>F</I></SPAN> are used
+ *
+ *
+ */
+ public void stripedMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ int ib = getFaureIndex ("stripedMatrixScrambleFaurePermutAll", sb, 1);
+
+ // If genMat contains original gener. matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+ int jb;
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (c = 0; c < numRows; c++) {
+ jb = stream.nextInt(0, sb - 1);
+ diag = FaureFactor[ib][jb]; // Random entry in this column.
+ for (l = 0; l < c; l++) scrambleMat[j][l][c] = 0;
+ for (l = c; l < numRows; l++) scrambleMat[j][l][c] = diag;
+ }
+ }
+ // printMat (dim, scrambleMat, "stripedMatrixScrambleFaurePermutAll");
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+
+ /**
+ * Applies a linear scramble by multiplying each
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> on the right
+ * by a single <SPAN CLASS="MATH"><I>k</I>×<I>k</I></SPAN> nonsingular upper-triangular matrix
+ * <SPAN CLASS="MATH"><B>M</B></SPAN>,
+ * as suggested by Faure and Tezuka.
+ * The diagonal entries of the matrix
+ * <SPAN CLASS="MATH"><B>M</B></SPAN> are generated uniformly over
+ *
+ * <SPAN CLASS="MATH">{1,..., <I>b</I> - 1}</SPAN>, the entries above the diagonal are generated uniformly
+ * over
+ * <SPAN CLASS="MATH">{0,..., <I>b</I> - 1}</SPAN>, and all the entries are generated independently.
+ * The effect of this scramble is only to change the order in which the
+ * points are generated. If one computes the average value of a
+ * function over <SPAN CLASS="textit">all</SPAN> the points of a given digital net, or over
+ * a number of points that is a power of the basis, then this
+ * scramble makes no difference.
+ *
+ * @param stream random number stream used as generator of the randomness
+ *
+ *
+ */
+ public void rightMatrixScramble (RandomStream stream) {
+ int j, c, l, i, sum; // dimension j, row l, column c, of genMat.
+
+ // SaveOriginalMat();
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Generate an upper triangular matrix for Faure-Tezuka right-scramble.
+ // Entry [l][c] is in row l, col c.
+ int[][] scrambleMat = new int[numCols][numCols];
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < c; l++) scrambleMat[l][c] = stream.nextInt (0, b - 1);
+ scrambleMat[c][c] = stream.nextInt (1, b - 1);
+ for (l = c+1; l < numCols; l++) scrambleMat[l][c] = 0;
+ }
+
+ // printMat0 (scrambleMat, "rightMatrixScramble");
+ // Right-multiply the generator matrices by the scrambling matrix.
+ for (j = 0 ; j < dim; j++) rightMultiplyMat (j, scrambleMat);
+ }
+
+
+ public void unrandomize() {
+ resetGeneratorMatrices();
+ digitalShift = null;
+ }
+
+
+ /**
+ * Restores the original generator matrices.
+ * This removes the current linear matrix scrambles.
+ *
+ */
+ public void resetGeneratorMatrices() {
+ if (originalMat != null) {
+ genMat = originalMat;
+ originalMat = null;
+ }
+ }
+
+
+ /**
+ * Erases the original generator matrices and replaces them by
+ * the current ones. The current linear matrix scrambles thus become
+ * <SPAN CLASS="textit">permanent</SPAN>. This is useful if we want to apply several
+ * scrambles in succession to a given digital net.
+ *
+ */
+ public void eraseOriginalGeneratorMatrices() {
+ originalMat = null;
+ }
+
+
+ /**
+ * Prints the generator matrices in standard form for dimensions 1 to <SPAN CLASS="MATH"><I>s</I></SPAN>.
+ *
+ */
+ public void printGeneratorMatrices (int s) {
+ // row l, column c, dimension j.
+ for (int j = 0; j < s; j++) {
+ System.out.println ("dim = " + (j+1) + PrintfFormat.NEWLINE);
+ for (int l = 0; l < numRows; l++) {
+ for (int c = 0; c < numCols; c++)
+ System.out.print (genMat[j*numCols+c][l] + " ");
+ System.out.println ("");
+ }
+ System.out.println ("----------------------------------");
+ }
+ }
+
+
+ // Computes the digital expansion of $i$ in base $b$, and the digits
+ // of the Gray code of $i$.
+ // These digits are placed in arrays \texttt{bary} and \texttt{gray},
+ // respectively, and the method returns the number of digits in the
+ // expansion. The two arrays are assumed to be of sizes larger or
+ // equal to this new number of digits.
+ protected int intToDigitsGray (int b, int i, int numDigits,
+ int[] bary, int[] gray) {
+ if (i == 0)
+ return 0;
+ int idigits = 0; // Num. of digits in b-ary and Gray repres.
+ int c;
+ // convert i to b-ary representation, put digits in bary[].
+ for (c = 0; i > 0; c++) {
+ idigits++;
+ bary[c] = i % b;
+ i = i / b;
+ }
+ // convert b-ary representation to Gray code.
+ gray[idigits-1] = bary[idigits-1];
+ int diff;
+ for (c = 0; c < idigits-1; c++) {
+ diff = bary[c] - bary[c+1];
+ if (diff < 0)
+ gray[c] = diff + b;
+ else
+ gray[c] = diff;
+ }
+ for (c = idigits; c < numDigits; c++) gray[c] = bary[c] = 0;
+ return idigits;
+ }
+
+
+// ************************************************************************
+
+ protected class DigitalNetIterator extends
+ PointSet.DefaultPointSetIterator {
+
+ protected int idigits; // Num. of digits in current code.
+ protected int[] bdigit; // b-ary code of current point index.
+ protected int[] gdigit; // Gray code of current point index.
+ protected int dimS; // = dim except in the shift iterator
+ // children where it is = dim + 1.
+ protected int[] cachedCurPoint; // Digits of coords of the current point,
+ // with digital shift already applied.
+ // Digit l of coord j is at pos. [j*outDigits + l].
+ // Has one more dimension for the shift iterators.
+
+ public DigitalNetIterator() {
+ // EpsilonHalf = 0.5 / Math.pow ((double) b, (double) outDigits);
+ bdigit = new int[numCols];
+ gdigit = new int[numCols];
+ dimS = dim;
+ cachedCurPoint = new int[(dim + 1)* outDigits];
+ init(); // This call is important so that subclasses don't
+ // automatically call 'resetCurPointIndex' at the time
+ // of construction as this may cause a subtle
+ // 'NullPointerException'
+ }
+
+ public void init() { // See constructor
+ resetCurPointIndex();
+ }
+
+ // We want to avoid generating 0 or 1
+ public double nextDouble() {
+ return nextCoordinate() + EpsilonHalf;
+ }
+
+ public double nextCoordinate() {
+ if (curPointIndex >= numPoints || curCoordIndex >= dimS)
+ outOfBounds();
+ int start = outDigits * curCoordIndex++;
+ double sum = 0;
+ // Can always have up to outDigits digits, because of digital shift.
+ for (int k = 0; k < outDigits; k++)
+ sum += cachedCurPoint [start+k] * factor[k];
+ if (digitalShift != null)
+ sum += EpsilonHalf;
+ return sum;
+ }
+
+ public void resetCurPointIndex() {
+ if (digitalShift == null)
+ for (int i = 0; i < cachedCurPoint.length; i++)
+ cachedCurPoint[i] = 0;
+ else {
+ if (dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ for (int j = 0; j < dimS; j++)
+ for (int k = 0; k < outDigits; k++)
+ cachedCurPoint[j*outDigits + k] = digitalShift[j][k];
+ }
+ for (int i = 0; i < numCols; i++) bdigit[i] = 0;
+ for (int i = 0; i < numCols; i++) gdigit[i] = 0;
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ idigits = 0;
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+
+ // Digits of Gray code, used to reconstruct cachedCurPoint.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * gdigit[c];
+ cachedCurPoint [j*outDigits+l] = sum % b;
+ }
+ }
+ }
+
+ public int resetToNextPoint() {
+ // incremental computation.
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Update the digital expansion of i in base b, and find the
+ // position of change in the Gray code. Set all digits == b-1 to 0
+ // and increase the first one after by 1.
+ int pos; // Position of change in the Gray code.
+ for (pos = 0; gdigit[pos] == b-1; pos++)
+ gdigit[pos] = 0;
+ gdigit[pos]++;
+
+ // Update the cachedCurPoint by adding the column of the gener.
+ // matrix that corresponds to the Gray code digit that has changed.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < lsup; l++) {
+ cachedCurPoint[j*outDigits + l] += genMat[j*numCols + pos][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ return curPointIndex;
+ }
+ }
+
+
+// ************************************************************************
+
+ protected class DigitalNetIteratorNoGray extends DigitalNetIterator {
+
+ public DigitalNetIteratorNoGray() {
+ super();
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+ if (dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+
+ // Convert i to b-ary representation, put digits in bdigit.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * bdigit[c];
+ cachedCurPoint [j*outDigits+l] = sum % b;
+ }
+ }
+ }
+
+ public int resetToNextPoint() {
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Find the position of change in the digits of curPointIndex in base
+ // b. Set all digits = b-1 to 0; increase the first one after by 1.
+ int pos;
+ for (pos = 0; bdigit[pos] == b-1; pos++)
+ bdigit[pos] = 0;
+ bdigit[pos]++;
+
+ // Update the digital expansion of curPointIndex in base b.
+ // Update the cachedCurPoint by adding 1 unit at the digit pos.
+ // If pos > 0, remove b-1 units in the positions < pos. Since
+ // calculations are mod b, this is equivalent to adding 1 unit.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < lsup; l++) {
+ for (c = 0; c <= pos; c++)
+ cachedCurPoint[j*outDigits + l] += genMat[j*numCols + c][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ return curPointIndex;
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNet.tex b/source/umontreal/iro/lecuyer/hups/DigitalNet.tex
new file mode 100644
index 0000000..d00f6b3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNet.tex
@@ -0,0 +1,1250 @@
+\defmodule{DigitalNet}
+
+This class provides the basic structures for storing and
+manipulating \emph{linear digital nets in base $b$}, for an arbitrary
+base $b\ge 2$. We recall that a net contains $n = b^k$ points in
+$s$ dimensions, where the $i$th point $\mathbf{u}_i$,
+for $i=0,\dots,b^k-1$, is defined as follows:
+\begin{latexonly}
+\begin{eqnarray*}
+ i &=& \sum_{\ell=0}^{k-1} a_{i,\ell} b^\ell, \\ % \eqlabel{eq:digital-i} \\
+ \pmatrix{u_{i,j,1}\cr u_{i,j,2}\cr \vdots \cr }
+ &=& \bC_j \pmatrix{a_{i,0}\cr a_{i,1}\cr \vdots \cr a_{i,k-1}\cr},
+ \\ % \eqlabel{eq:digital-Cj} \\
+ u_{i,j} &=& \sum_{\ell=1}^\infty u_{i,j,\ell} b^{-\ell},
+ \\[7pt] % \eqlabel{eq:digital-uij} \\
+ \bu_i &=& (u_{i,0},\dots,u_{i,s-1}). \\ % \eqlabel{eq:digital-ui}
+\end{eqnarray*}
+\end{latexonly}
+\begin{htmlonly}
+\begin{eqnarray}
+ i &\quad=\quad& \sum_{r=0}^{k-1} a_{i,r} b^r, \\
+ (u_{i,j,1}\ u_{i,j,2}\ \ldots )^T
+ &\quad=\quad& \mathbf{C}_j\ (a_{i,0}\ a_{i,1}\ \ldots \ a_{i,k-1})^T, \\
+ u_{i,j} &\quad=\quad& \sum_{r=1}^\infty u_{i,j,r} b^{-r}, \\
+ \mathbf{u}_i &\quad=\quad& (u_{i,0},\ \ldots,\ u_{i,s-1}).
+\end{eqnarray}
+\end{htmlonly}
+In our implementation, the matrices $\mathbf{C}_j$ are $r\times k$,
+so the expansion of $u_{i,j}$ is truncated to its first $r$ terms.
+The points are stored implicitly by storing the generator matrices
+$\mathbf{C}_j$ in a large two-dimensional array of integers,
+ with $srk$ elements.
+\begin{detailed}
+For general $b$, the element $(l,c)$ of $\mathbf{C}_j$ (counting elements
+ from 0) is stored at position $[jk+c][l]$ in this array.
+\end{detailed}
+
+The points $\mathbf{u}_i$ are enumerated using the Gray code
+technique\latex{ as proposed in \cite{rANT79a,rTEZ95a}
+(see also \cite{fGLA04a,vHON03a})}.
+With this technique, the $b$-ary representation of $i$,
+$\mathbf{a}_i = (a_{i,0}, \dots, a_{i,k-1})$, is replaced \latex{in Equation
+(\ref{eq:digital-Cj})} by a Gray code representation of $i$,
+$\mathbf{g}_i = (g_{i,0}, \dots, g_{i,k-1})$. The Gray code $\mathbf{g}_i$
+ used here is defined by $g_{i,k-1} = a_{i,k-1}$ and
+\latex{
+$g_{i,\ell} = (a_{i,\ell} - a_{i,\ell+1}) \mod b$ for $\ell = 0,\dots,k-2$.}
+\html{$g_{i,\nu} = (a_{i,\nu} - a_{i,\nu+1}) \mod b$ for $\nu = 0,\dots,k-2$.}
+It has the property that
+$\mathbf{g}_i = (g_{i,0}, \dots, g_{i,k-1})$ and
+$\mathbf{g}_{i+1} = (g_{i+1,0}, \dots, g_{i+1,k-1})$
+differ only in the position of the smallest index
+\latex{$\ell$}\html{$\nu$} such that
+\latex{$a_{i,\ell} < b - 1$, and we have $g_{i+1,\ell} = (g_{i,\ell}+1) \mod b$}\html{$a_{i,\nu} < b - 1$, and we have $g_{i+1,\nu} = (g_{i,\nu}+1) \mod b$}
+in that position.
+
+This Gray code representation permits a more efficient enumeration
+of the points by the iterators.
+It changes the order in which the points $\mathbf{u}_i$ are enumerated,
+but the first $b^m$ points remain the same for every integer $m$.
+The $i$th point of the sequence with the Gray enumeration
+is the $i'$th point of the original enumeration, where $i'$ is the
+integer whose $b$-ary representation $\mathbf{a}_{i'}$ is given by the Gray
+code $\mathbf{g}_i$.
+To enumerate all the points successively, we never need to compute
+the Gray codes explicitly.
+It suffices to know the position \latex{$\ell$}\html{$\nu$} of the Gray code digit
+that changes at each step, and this can be found quickly from the
+$b$-ary representation $\mathbf{a}_i$.
+The digits of each coordinate $j$ of the current point can be updated by
+adding column \latex{$\ell$}\html{$\nu$} of the generator matrix $\mathbf{C}_j$ to the old digits,
+modulo $b$.
+
+One should avoid using the method \method{getCoordinate}{}{(i, j)}
+for arbitrary values of \texttt{i} and \texttt{j}, because this is much
+slower than using an iterator to access successive coordinates.
+
+Digital nets can be randomized in various ways
+\cite{mMAT99a,rFAU02a,vLEC02a,vOWE03a}.
+Several types of randomizations specialized for nets are implemented
+directly in this class.
+
+A simple but important randomization is the \emph{random digital shift}
+in base $b$, defined as follows: replace each digit $u_{i,j,\latex{\ell}\html{\nu}}$ in \html{the third equation above}
+\latex{(\ref{eq:digital-uij})} by $(u_{i,j,\latex{\ell}\html{\nu}} + d_{j,\latex{\ell}\html{\nu}}) \mod b$,
+where the $d_{j,\latex{\ell}\html{\nu}}$'s are i.i.d.\ uniform over $\{0,\dots,b-1\}$.
+This is equivalent to applying a single random shift to all the points
+in a formal series representation of their coordinates \cite{vLEC02a,vLEM03a}.
+In practice, the digital shift is truncated to $w$ digits,
+for some integer $w\ge r$.
+Applying a digital shift does not change the equidistribution
+and $(t,m,s)$-net properties of a point set \cite{vHON03a,vLEC99a,vLEM03a}.
+Moreover, with the random shift, each point is uniformly distributed over
+the unit hypercube (but the points are not independent, of course).
+
+A second class of randomizations specialized for digital nets
+are the \emph{linear matrix scrambles}
+\cite{mMAT99a,rFAU02a,vHON03a,vOWE03a}, which multiply the matrices
+ $\mathbf{C}_j$
+by a random invertible matrix $\mathbf{M}_j$, modulo $b$.
+There are several variants, depending on how $\mathbf{M}_j$ is generated, and on
+whether $\mathbf{C}_j$ is multiplied on the left or on the right.
+In our implementation, the linear matrix scrambles are incorporated directly
+into the matrices $\mathbf{C}_j$\latex{ (as in \cite{vHON03a})}, so they
+ do not slow down the enumeration of points.
+Methods are available for applying
+linear matrix scrambles and for removing these randomizations.
+These methods generate the appropriate random numbers and make
+the corresponding changes to the $\mathbf{C}_j$'s.
+A copy of the original $\mathbf{C}_j$'s is maintained, so the point
+set can be returned to its original unscrambled state at any time.
+When a new linear matrix scramble is applied, it is always applied to
+the \emph{original} generator matrices.
+% (so the new matrix scramble is combined with the previous ones).
+The method \method{resetGeneratorMatrices}{} removes the current matrix
+scramble by resetting the generator matrices to their original state.
+On the other hand, the method \method{eraseOriginalGeneratorMatrices}{}
+replaces the original generator matrices by the current
+ones, making the changes permanent.
+This is useful if one wishes to apply two or more linear matrix
+scrambles on top of each other.
+
+Linear matrix scrambles are usually combined with a random digital shift;
+this combination is called an \emph{affine matrix scramble} \cite{vOWE03a}.
+These two randomizations are applied via separate methods.
+The linear matrix scrambles are incorporated into the matrices $\mathbf{C}_j$
+whereas the digital random shift is stored and applied separately,
+independently of the other scramblings.
+
+Applying a digital shift or a linear matrix scramble to a digital net
+invalidates all iterators for that randomized point,
+because each iterator uses a
+\emph{cached} copy of the current point, which is updated only when
+the current point index of that iterator changes, and the update also
+depends on the cached copy of the previous point.
+After applying any kind of scrambling, the iterators must be
+reinitialized to the \emph{initial point} by invoking
+\externalmethod{}{PointSetIterator}{resetCurPointIndex}{}
+or reinstantiated by the \method{iterator}{} method
+(this is not done automatically).
+
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DigitalNet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.*;
+\end{hide}
+
+public class DigitalNet extends PointSet \begin{hide} {
+
+ // Variables to be initialized by the constructor subclasses.
+ protected int b = 0; // Base.
+ protected int numCols = 0; // The number of columns in each C_j. (= k)
+ protected int numRows = 0; // The number of rows in each C_j. (= r)
+ protected int outDigits = 0; // Number of output digits (= w)
+ private int[][] originalMat; // Original gen. matrices without randomizat.
+ protected int[][] genMat; // The current generator matrices.
+ // genMat[j*numCols+c][l] contains column c
+ // and row l of C_j.
+ protected int[][] digitalShift; // The digital shift, initially zero (null).
+ // Entry [j][l] is for dimension j, digit l,
+ // for 0 <= l < outDigits.
+ protected double normFactor; // To convert output to (0,1); 1/b^outDigits.
+ protected double[] factor; // Lookup table in ascending order: factor[i]
+ // = 1/b^{i+1} for 0 <= i < outDigits.
+
+ // primes gives the first index in array FaureFactor
+ // for the prime p. If primes[i] = p, then
+ // FaureFactor[p][j] contains the Faure ordered factors of base p.
+ private int[] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
+ 43, 47, 53, 59, 61, 67};
+
+ // Factors on the diagonal corresponding to base b = prime[i] ordered by
+ // increasing Bounds.
+ private int[][] FaureFactor = {{1}, {1, 2}, {2, 3, 1, 4},
+ { 2, 3, 4, 5, 1, 6}, {3, 4, 7, 8, 2, 5, 6, 9, 1, 10},
+ { 5, 8, 3, 4, 9, 10, 2, 6, 7, 11, 1, 12},
+ { 5, 7, 10, 12, 3, 6, 11, 14, 4, 13, 2, 8, 9, 15, 1, 16},
+ { 7, 8, 11, 12, 4, 5, 14, 15, 3, 6, 13, 16, 2, 9, 10, 17, 1, 18},
+ { 5, 9, 14, 18, 7, 10, 13, 16, 4, 6, 17, 19, 3, 8, 15, 20, 2, 11, 12,
+ 21, 1, 22},
+ { 8, 11, 18, 21, 12, 17, 9, 13, 16, 20, 5, 6, 23, 24, 4, 7, 22, 25, 3,
+ 10, 19, 26, 2, 14, 15, 27, 1, 28},
+ { 12, 13, 18, 19, 11, 14, 17, 20, 7, 9, 22, 24, 4, 8, 23, 27, 5, 6, 25,
+ 26, 3, 10, 21, 28, 2, 15, 16, 29, 1, 30},
+ { 8, 14, 23, 29, 10, 11, 26, 27, 13, 17, 20, 24, 7, 16, 21, 30, 5, 15,
+ 22, 32, 6, 31, 4, 9, 28, 33, 3, 12, 25, 34, 2, 18, 19, 35, 1, 36},
+ { 16, 18, 23, 25, 11, 15, 26, 30, 12, 17, 24, 29, 9, 32, 13, 19, 22,
+ 28, 6, 7, 34, 35, 5, 8, 33, 36, 4, 10, 31, 37, 3, 14, 27, 38, 2, 20,
+ 21, 39, 1, 40},
+ { 12, 18, 25, 31, 9, 19, 24, 34, 8, 16, 27, 35, 10, 13, 30, 33, 15, 20,
+ 23, 28, 5, 17, 26, 38, 6, 7, 36, 37, 4, 11, 32, 39, 3, 14, 29, 40, 2,
+ 21, 22, 41, 1, 42},
+ { 13, 18, 29, 34, 11, 17, 30, 36, 10, 14, 33, 37, 7, 20, 27, 40, 9, 21,
+ 26, 38, 15, 22, 25, 32, 6, 8, 39, 41, 5, 19, 28, 42, 4, 12, 35, 43,
+ 3, 16, 31, 44, 2, 23, 24, 45, 1, 46},
+ { 14, 19, 34, 39, 23, 30, 12, 22, 31, 41, 8, 11, 20, 24, 29, 33, 42, 45,
+ 10, 16, 37, 43, 7, 15, 38, 46, 17, 25, 28, 36, 5, 21, 32, 48, 6, 9,
+ 44, 47, 4, 13, 40, 49, 3, 18, 35, 50, 2, 26, 27, 51, 1, 52},
+ { 25, 26, 33, 34, 18, 23, 36, 41, 14, 21, 38, 45, 24, 27, 32, 35, 11,
+ 16, 43, 48, 9, 13, 46, 50, 8, 22, 37, 51, 7, 17, 42, 52, 19, 28, 31,
+ 40, 6, 10, 49, 53, 5, 12, 47, 54, 4, 15, 44, 55, 3, 20, 39, 56, 2,
+ 29, 30, 57, 1, 58},
+ { 22, 25, 36, 39, 17, 18, 43, 44, 24, 28, 33, 37, 13, 14, 47, 48, 16,
+ 19, 42, 45, 9, 27, 34, 52, 8, 23, 38, 53, 11, 50, 7, 26, 35, 54, 21,
+ 29, 32, 40, 6, 10, 51, 55, 5, 12, 49, 56, 4, 15, 46, 57, 3, 20, 41,
+ 58, 2, 30, 31, 59, 1, 60},
+ { 18, 26, 41, 49, 14, 24, 43, 53, 12, 28, 39, 55, 29, 30, 37, 38, 10,
+ 20, 47, 57, 16, 21, 46, 51, 8, 25, 42, 59, 13, 31, 36, 54, 9, 15,
+ 52, 58, 7, 19, 48, 60, 23, 32, 35, 44, 5, 27, 40, 62, 6, 11, 56,
+ 61, 4, 17, 50, 63, 3, 22, 45, 64, 2, 33, 34, 65, 1, 66}
+};
+
+
+ public double getCoordinate (int i, int j) {
+ // convert i to Gray representation, put digits in gdigit[].
+ int l, c, sum;
+ int[] bdigit = new int[numCols];
+ int[] gdigit = new int[numCols];
+ int idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ double result = 0;
+ if (digitalShift != null && dimShift < j)
+ addRandomShift (dimShift, j, shiftStream);
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * gdigit[c];
+ result += (sum % b) * factor[l];
+ }
+ if (digitalShift != null)
+ result += EpsilonHalf;
+ return result;
+ }
+
+ public PointSetIterator iterator() {
+ return new DigitalNetIterator();
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public DigitalNet ()\begin{hide} {
+ }\end{hide}
+\end{code}
+\begin{tabb} Empty constructor.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double getCoordinateNoGray (int i, int j)\begin{hide} {
+ // convert i to b-ary representation, put digits in bdigit[].
+ int l, c, sum;
+ int[] bdigit = new int[numCols];
+ int idigits = 0;
+ for (c = 0; i > 0; c++) {
+ idigits++;
+ bdigit[c] = i % b;
+ i = i / b;
+ }
+ if (digitalShift != null && dimShift < j)
+ addRandomShift (dimShift, j, shiftStream);
+ double result = 0;
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * bdigit[c];
+ result += (sum % b) * factor[l];
+ }
+ if (digitalShift != null)
+ result += EpsilonHalf;
+ return result;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns $u_{i,j}$, the coordinate $j$ of point $i$, the points
+ being enumerated in the standard order (no Gray code).
+ \end{tabb}
+\begin{htmlonly}
+ \param{i}{point index}
+ \param{j}{coordinate index}
+ \return{the value of $u_{i,j}$}
+\end{htmlonly}
+\begin{code}
+
+ public PointSetIterator iteratorNoGray()\begin{hide} {
+ return new DigitalNetIteratorNoGray();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ This iterator does not use the Gray code. Thus the points are enumerated
+ in the order of their first coordinate before randomization.
+\end{tabb}
+\begin{code}
+
+ public void addRandomShift (int d1, int d2, RandomStream stream)\begin{hide} {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (digitalShift == null) {
+ digitalShift = new int[d2][outDigits];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ int[][] temp = new int[d3][outDigits];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ for (int j = 0; j < outDigits; j++)
+ temp[i][j] = digitalShift[i][j];
+ digitalShift = temp;
+ }
+ for (int i = d1; i < d2; i++)
+ for (int j = 0; j < outDigits; j++)
+ digitalShift[i][j] = stream.nextInt (0, b - 1);
+ dimShift = d2;
+ shiftStream = stream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds a random digital shift to all the points of the point set,
+ using stream \texttt{stream} to generate the random numbers.
+ For each coordinate $j$ from \texttt{d1} to \texttt{d2-1},
+ the shift vector $(d_{j,0},\dots,d_{j,k-1})$
+ is generated uniformly over $\{0,\dots,b-1\}^k$ and added modulo $b$ to
+ the digits of all the points.
+ After adding a digital shift, all iterators must be reconstructed or
+ reset to zero.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate uniforms}
+\end{htmlonly}
+\begin{code}
+
+ public void addRandomShift (RandomStream stream)\begin{hide} {
+ addRandomShift (0, dim, stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{addRandomShift}{}\texttt{(0, dim, stream)},
+ where \texttt{dim} is the dimension of the digital net.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate uniforms}
+\end{htmlonly}
+\begin{hide}\begin{code}
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ digitalShift = null;
+ }
+\end{code}
+\begin{tabb}
+ Erases the current digital random shift, if any.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer (100);
+ if (b > 0) {
+ sb.append ("Base = "); sb.append (b);
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append ("Num cols = "); sb.append (numCols);
+ sb.append (PrintfFormat.NEWLINE + "Num rows = ");
+ sb.append (numRows);
+ sb.append (PrintfFormat.NEWLINE + "outDigits = ");
+ sb.append (outDigits);
+ return sb.toString();
+ }
+
+
+ // Print matrices M for dimensions 0 to N-1.
+ private void printMat (int N, int[][][] A, String name) {
+ for (int i = 0; i < N; i++) {
+ System.out.println ("-------------------------------------" +
+ PrintfFormat.NEWLINE + name + " dim = " + i);
+ int l, c; // row l, column c, dimension i for A[i][l][c].
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numCols; c++) {
+ System.out.print (A[i][l][c] + " ");
+ }
+ System.out.println ("");
+ }
+ }
+ System.out.println ("");
+ }
+
+
+ // Print matrix M
+ private void printMat0 (int[][] A, String name) {
+ System.out.println ("-------------------------------------" +
+ PrintfFormat.NEWLINE + name);
+ int l, c; // row l, column c for A[l][c].
+ for (l = 0; l < numCols; l++) {
+ for (c = 0; c < numCols; c++) {
+ System.out.print (A[l][c] + " ");
+ }
+ System.out.println ("");
+ }
+ System.out.println ("");
+ }
+
+
+ // Left-multiplies lower-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // This implementation is safe only if (numRows*(b-1)^2) is a valid int.
+ private void leftMultiplyMat (int j, int[][] Mj) {
+ int l, c, i, sum; // Dimension j, row l, column c for new C_j.
+ for (l = 0; l < numRows ; l++) {
+ for (c = 0; c < numCols; c++) {
+ // Multiply row l of M_j by column c of C_j.
+ sum = 0;
+ for (i = 0; i <= l; i++)
+ sum += Mj[l][i] * originalMat[j*numCols+c][i];
+ genMat[j*numCols+c][l] = sum % b;
+ }
+ }
+ }
+
+
+ // Left-multiplies diagonal matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // This implementation is safe only if (numRows*(b-1)^2) is a valid int.
+ private void leftMultiplyMatDiag (int j, int[][] Mj) {
+ int l, c, sum; // Dimension j, row l, column c for new C_j.
+ for (l = 0; l < numRows ; l++) {
+ for (c = 0; c < numCols; c++) {
+ // Multiply row l of M_j by column c of C_j.
+ sum = Mj[l][l] * originalMat[j*numCols+c][l];
+ genMat[j*numCols+c][l] = sum % b;
+ }
+ }
+ }
+
+
+ // Right-multiplies original C_j by upper-triangular matrix Mj,
+ // where original C_j is in originalMat and result is in genMat.
+ // This implementation is safe only if (numCols*(b-1)^2) is a valid int.
+ private void rightMultiplyMat (int j, int[][] Mj) {
+ int l, c, i, sum; // Dimension j, row l, column c for new C_j.
+ for (l = 0; l < numRows ; l++) {
+ for (c = 0; c < numCols; c++) {
+ // Multiply row l of C_j by column c of M_j.
+ sum = 0;
+ for (i = 0; i <= c; i++)
+ sum += originalMat[j*numCols+i][l] * Mj[i][c];
+ genMat[j*numCols+c][l] = sum % b;
+ }
+ }
+ }
+
+
+ private int getFaureIndex (String method, int sb, int flag) {
+ // Check for errors in ...FaurePermut. Returns the index ib of the
+ // base b in primes, i.e. b = primes[ib].
+ if (sb >= b)
+ throw new IllegalArgumentException (PrintfFormat.NEWLINE +
+ " sb >= base in " + method);
+ if (sb < 1)
+ throw new IllegalArgumentException (PrintfFormat.NEWLINE +
+ " sb = 0 in " + method);
+ if ((flag > 2) || (flag < 0))
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE + " lowerFlag not in {0, 1, 2} in "
+ + method);
+
+ // Find index ib of base in array primes
+ int ib = 0;
+ while ((ib < primes.length) && (primes[ib] < b))
+ ib++;
+ if (ib >= primes.length)
+ throw new IllegalArgumentException ("base too large in " + method);
+ if (b != primes[ib])
+ throw new IllegalArgumentException (
+ "Faure factors are not implemented for this base in " + method);
+ return ib;
+ }
+\end{hide}
+
+ public void leftMatrixScramble (RandomStream stream) \begin{hide} {
+ int j, l, c; // dimension j, row l, column c.
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numRows; c++) {
+ if (c == l) // No zero on the diagonal.
+ scrambleMat[j][l][c] = stream.nextInt(1, b - 1) ;
+ else if (c < l)
+ scrambleMat[j][l][c] = stream.nextInt(0, b - 1);
+ else
+ scrambleMat[j][l][c] = 0; // Zeros above the diagonal;
+ }
+ }
+ }
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Applies a linear scramble by multiplying each $\mathbf{C}_j$ on the left
+ by a $w\times w$ nonsingular lower-triangular matrix $\mathbf{M}_j$,
+ as suggested by Matou\v{s}ek\latex{ \cite{mMAT99a}} and implemented
+ by Hong and Hickernell\latex{ \cite{vHON03a}}. The diagonal entries of
+ each matrix $\mathbf{M}_j$ are generated uniformly over
+ $\{1,\dots,b-1\}$, the entries below the diagonal are generated uniformly
+ over $\{0,\dots,b-1\}$, and all these entries are generated independently.
+ This means that in base $b=2$, all diagonal elements are equal to 1.
+\richard{Les matrices de \texttt{leftMatrixScramble} sont carr\'ees et
+ triangulaires inf\'erieures. PL pense qu'il faut consid\'erer la
+ possibilit\'e de rajouter des lignes \`a ces matrices pour pouvoir
+ randomiser plus les derniers chiffres ou les derniers bits.}
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+\end{htmlonly}
+\begin{code}
+
+ public void leftMatrixScrambleDiag (RandomStream stream) \begin{hide} {
+ int j, l, c; // dimension j, row l, column c.
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the diagonal scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numRows; c++) {
+ if (c == l) // No zero on the diagonal.
+ scrambleMat[j][l][c] = stream.nextInt(1, b - 1) ;
+ else
+ scrambleMat[j][l][c] = 0; // Diagonal matrix;
+ }
+ }
+ }
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{leftMatrixScramble}{} except that all the
+ off-diagonal elements of the $\mathbf{M}_j$ are 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private void LMSFaurePermut (String method, RandomStream stream, int sb,
+ int lowerFlag) {
+/*
+ If \texttt{lowerFlag = 2}, the off-diagonal elements below the diagonal
+ of $\mathbf{M}_j$ are chosen as in \method{leftMatrixScramble}{}.
+ If \texttt{lowerFlag = 1}, the off-diagonal elements below the diagonal of
+ $\mathbf{M}_j$ are also chosen from the restricted set. If
+ \texttt{lowerFlag = 0}, the off-diagonal elements of $\mathbf{M}_j$ are all 0.
+*/
+ int ib = getFaureIndex (method, sb, lowerFlag);
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int jb;
+ int j, l, c; // dimension j, row l, column c.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numRows; c++) {
+ if (c == l) {
+ jb = stream.nextInt(0, sb - 1);
+ scrambleMat[j][l][c] = FaureFactor[ib][jb];
+ } else if (c < l) {
+ if (lowerFlag == 2) {
+ scrambleMat[j][l][c] = stream.nextInt(0, b - 1);
+ } else if (lowerFlag == 1) {
+ jb = stream.nextInt(0, sb - 1);
+ scrambleMat[j][l][c] = FaureFactor[ib][jb];
+ } else { // lowerFlag == 0
+ scrambleMat[j][l][c] = 0;
+ }
+ } else
+ scrambleMat[j][l][c] = 0; // Zeros above the diagonal;
+ }
+ }
+ }
+ // printMat (dim, scrambleMat, method);
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ if (lowerFlag == 0)
+ for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]);
+ else
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }\end{hide}
+
+ public void leftMatrixScrambleFaurePermut (RandomStream stream, int sb)\begin{hide} {
+ LMSFaurePermut ("leftMatrixScrambleFaurePermut", stream, sb, 2);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{leftMatrixScramble}{} except that the diagonal elements
+ of each matrix $\mathbf{M}_j$ are chosen from a restricted set of the best
+ integers as calculated by Faure \cite{rFAU02a}. They are generated
+ uniformly over the first \texttt{sb} elements of array $F$, where $F$ is
+ made up of a permutation of the integers $[1..(b-1)]$. These integers are
+ sorted by increasing order of the upper bounds of the extreme discrepancy
+ for the given integer.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+ \param{sb}{Only the first $sb$ elements of $F$ are used}
+\end{htmlonly}
+\begin{code}
+
+ public void leftMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb)\begin{hide} {
+ LMSFaurePermut ("leftMatrixScrambleFaurePermutDiag",
+ stream, sb, 0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{leftMatrixScrambleFaurePermut}{} except that all
+ off-diagonal elements are 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+ \param{sb}{Only the first $sb$ elements of $F$ are used}
+\end{htmlonly}
+\begin{code}
+
+ public void leftMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb)\begin{hide} {
+ LMSFaurePermut ("leftMatrixScrambleFaurePermutAll",
+ stream, sb, 1);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{leftMatrixScrambleFaurePermut}{} except that the
+ elements under the diagonal are also
+ chosen from the same restricted set as the diagonal elements.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+ \param{sb}{Only the first $sb$ elements of $F$ are used}
+\end{htmlonly}
+\begin{code}
+
+ public void iBinomialMatrixScramble (RandomStream stream)\begin{hide} {
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ diag = stream.nextInt(1, b - 1);
+ for (l = 0; l < numRows; l++) {
+ // Single random nonzero element on the diagonal.
+ scrambleMat[j][l][l] = diag;
+ for (c = l+1; c < numRows; c++) scrambleMat[j][l][c] = 0;
+ }
+ for (l = 1; l < numRows; l++) {
+ col1 = stream.nextInt(0, b - 1);
+ for (c = 0; l+c < numRows; c++) scrambleMat[j][l+c][c] = col1;
+ }
+ }
+ // printMat (dim, scrambleMat, "iBinomialMatrixScramble");
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Applies the $i$-binomial matrix scramble proposed by Tezuka \cite{rTEZ02a}
+ \latex{ (see also \cite{vOWE03a})}.
+ This multiplies each $\mathbf{C}_j$ on the left
+ by a $w\times w$ nonsingular lower-triangular matrix $\mathbf{M}_j$ as in
+ \method{leftMatrixScramble}{}, but with the additional constraint that
+ all entries on any given diagonal or subdiagonal of $\mathbf{M}_j$ are identical.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used as generator of the randomness}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private void iBMSFaurePermut (String method, RandomStream stream,
+ int sb, int lowerFlag) {
+ int ib = getFaureIndex (method, sb, lowerFlag);
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+ int jb;
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ jb = stream.nextInt(0, sb - 1);
+ diag = FaureFactor[ib][jb];
+ for (l = 0; l < numRows; l++) {
+ // Single random nonzero element on the diagonal.
+ scrambleMat[j][l][l] = diag;
+ for (c = l+1; c < numRows; c++) scrambleMat[j][l][c] = 0;
+ }
+ for (l = 1; l < numRows; l++) {
+ if (lowerFlag == 2) {
+ col1 = stream.nextInt(0, b - 1);
+ } else if (lowerFlag == 1) {
+ jb = stream.nextInt(0, sb - 1);
+ col1 = FaureFactor[ib][jb];
+ } else { // lowerFlag == 0
+ col1 = 0;
+ }
+ for (c = 0; l+c < numRows; c++) scrambleMat[j][l+c][c] = col1;
+ }
+ }
+ // printMat (dim, scrambleMat, method);
+
+ if (lowerFlag > 0)
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ else
+ for (j = 0 ; j < dim; j++) leftMultiplyMatDiag (j, scrambleMat[j]);
+ }\end{hide}
+
+ public void iBinomialMatrixScrambleFaurePermut (RandomStream stream,
+ int sb)\begin{hide} {
+ iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermut",
+ stream, sb, 2);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{iBinomialMatrixScramble}{} except that the diagonal
+ elements of each matrix $\mathbf{M}_j$ are chosen as in
+ \method{leftMatrixScrambleFaurePermut}{}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+ \param{sb}{Only the first $sb$ elements of $F$ are used}
+\end{htmlonly}
+\begin{code}
+
+ public void iBinomialMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb)\begin{hide} {
+ iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermutDiag",
+ stream, sb, 0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{iBinomialMatrixScrambleFaurePermut}{} except that all the
+ off-diagonal elements are 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+ \param{sb}{Only the first $sb$ elements of $F$ are used}
+\end{htmlonly}
+\begin{code}
+
+ public void iBinomialMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb)\begin{hide} {
+ iBMSFaurePermut ("iBinomialMatrixScrambleFaurePermutAll",
+ stream, sb, 1);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{iBinomialMatrixScrambleFaurePermut}{} except that the
+ elements under the diagonal are also
+ chosen from the same restricted set as the diagonal elements.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used to generate the randomness}
+ \param{sb}{Only the first $sb$ elements of $F$ are used}
+\end{htmlonly}
+\begin{code}
+
+ public void stripedMatrixScramble (RandomStream stream)\begin{hide} {
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+
+ // If genMat contains original gener. matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (c = 0; c < numRows; c++) {
+ diag = stream.nextInt (1, b - 1); // Random entry in this column.
+ for (l = 0; l < c; l++) scrambleMat[j][l][c] = 0;
+ for (l = c; l < numRows; l++) scrambleMat[j][l][c] = diag;
+ }
+ }
+ // printMat (dim, scrambleMat, "stripedMatrixScramble");
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Applies the \emph{striped matrix scramble} proposed by Owen \cite{vOWE03a}.
+ It multiplies each $\mathbf{C}_j$ on the left
+ by a $w\times w$ nonsingular lower-triangular matrix $\mathbf{M}_j$ as in
+ \method{leftMatrixScramble}{}, but with the additional constraint that
+ in any column, all entries below the diagonal are equal to the
+ diagonal entry, which is generated randomly over $\{1,\dots,b-1\}$.
+ Note that for $b=2$, the matrices $\mathbf{M}_j$ become deterministic, with
+ all entries on and below the diagonal equal to 1.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used as generator of the randomness}
+\end{htmlonly}
+\begin{code}
+
+ public void stripedMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb)\begin{hide} {
+ int ib = getFaureIndex ("stripedMatrixScrambleFaurePermutAll", sb, 1);
+
+ // If genMat contains original gener. matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, r by r.
+ int j, l, c; // dimension j, row l, column c.
+ int diag; // random entry on the diagonal;
+ int col1; // random entries below the diagonal;
+ int jb;
+ int[][][] scrambleMat = new int[dim][numRows][numRows];
+ for (j = 0 ; j < dim; j++) {
+ for (c = 0; c < numRows; c++) {
+ jb = stream.nextInt(0, sb - 1);
+ diag = FaureFactor[ib][jb]; // Random entry in this column.
+ for (l = 0; l < c; l++) scrambleMat[j][l][c] = 0;
+ for (l = c; l < numRows; l++) scrambleMat[j][l][c] = diag;
+ }
+ }
+ // printMat (dim, scrambleMat, "stripedMatrixScrambleFaurePermutAll");
+ for (j = 0 ; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Similar to \method{stripedMatrixScramble}{} except that the
+ elements on and under the diagonal of each matrix $\mathbf{M}_j$ are
+ chosen as in \method{leftMatrixScrambleFaurePermut}{}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used as generator of the randomness}
+ \param{sb}{Only the first $sb$ elements of $F$ are used}
+\end{htmlonly}
+\begin{code}
+
+ public void rightMatrixScramble (RandomStream stream) \begin{hide} {
+ int j, c, l, i, sum; // dimension j, row l, column c, of genMat.
+
+ // SaveOriginalMat();
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols][numRows];
+ }
+
+ // Generate an upper triangular matrix for Faure-Tezuka right-scramble.
+ // Entry [l][c] is in row l, col c.
+ int[][] scrambleMat = new int[numCols][numCols];
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < c; l++) scrambleMat[l][c] = stream.nextInt (0, b - 1);
+ scrambleMat[c][c] = stream.nextInt (1, b - 1);
+ for (l = c+1; l < numCols; l++) scrambleMat[l][c] = 0;
+ }
+
+ // printMat0 (scrambleMat, "rightMatrixScramble");
+ // Right-multiply the generator matrices by the scrambling matrix.
+ for (j = 0 ; j < dim; j++) rightMultiplyMat (j, scrambleMat);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Applies a linear scramble by multiplying each $\mathbf{C}_j$ on the right
+ by a single $k\times k$ nonsingular upper-triangular matrix $\mathbf{M}$,
+ as suggested by Faure and Tezuka\latex{ \cite{rFAU02a} (see also
+ \cite{vHON03a})}.
+ The diagonal entries of the matrix $\mathbf{M}$ are generated uniformly over
+ $\{1,\dots,b-1\}$, the entries above the diagonal are generated uniformly
+ over $\{0,\dots,b-1\}$, and all the entries are generated independently.
+ The effect of this scramble is only to change the order in which the
+ points are generated. If one computes the average value of a
+ function over \emph{all} the points of a given digital net, or over
+ a number of points that is a power of the basis, then this
+ scramble makes no difference.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{random number stream used as generator of the randomness}
+\end{htmlonly}
+\begin{hide} %%%%
+\begin{code}
+
+ public void unrandomize() {
+ resetGeneratorMatrices();
+ digitalShift = null;
+ }
+\end{code}
+\begin{tabb}
+ Restores the original generator matrices and
+ removes the random shift.
+\end{tabb}
+\end{hide}
+\begin{code}
+
+ public void resetGeneratorMatrices()\begin{hide} {
+ if (originalMat != null) {
+ genMat = originalMat;
+ originalMat = null;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Restores the original generator matrices.
+ This removes the current linear matrix scrambles.
+\end{tabb}
+\begin{code}
+
+ public void eraseOriginalGeneratorMatrices()\begin{hide} {
+ originalMat = null;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Erases the original generator matrices and replaces them by
+ the current ones. The current linear matrix scrambles thus become
+ \emph{permanent}. This is useful if we want to apply several
+ scrambles in succession to a given digital net.
+\end{tabb}
+\begin{code}
+
+ public void printGeneratorMatrices (int s) \begin{hide} {
+ // row l, column c, dimension j.
+ for (int j = 0; j < s; j++) {
+ System.out.println ("dim = " + (j+1) + PrintfFormat.NEWLINE);
+ for (int l = 0; l < numRows; l++) {
+ for (int c = 0; c < numCols; c++)
+ System.out.print (genMat[j*numCols+c][l] + " ");
+ System.out.println ("");
+ }
+ System.out.println ("----------------------------------");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Prints the generator matrices in standard form for dimensions 1 to $s$.
+\end{tabb}
+
+\begin{code}\begin{hide}
+
+ // Computes the digital expansion of $i$ in base $b$, and the digits
+ // of the Gray code of $i$.
+ // These digits are placed in arrays \texttt{bary} and \texttt{gray},
+ // respectively, and the method returns the number of digits in the
+ // expansion. The two arrays are assumed to be of sizes larger or
+ // equal to this new number of digits.
+ protected int intToDigitsGray (int b, int i, int numDigits,
+ int[] bary, int[] gray) {
+ if (i == 0)
+ return 0;
+ int idigits = 0; // Num. of digits in b-ary and Gray repres.
+ int c;
+ // convert i to b-ary representation, put digits in bary[].
+ for (c = 0; i > 0; c++) {
+ idigits++;
+ bary[c] = i % b;
+ i = i / b;
+ }
+ // convert b-ary representation to Gray code.
+ gray[idigits-1] = bary[idigits-1];
+ int diff;
+ for (c = 0; c < idigits-1; c++) {
+ diff = bary[c] - bary[c+1];
+ if (diff < 0)
+ gray[c] = diff + b;
+ else
+ gray[c] = diff;
+ }
+ for (c = idigits; c < numDigits; c++) gray[c] = bary[c] = 0;
+ return idigits;
+ }
+
+
+// ************************************************************************
+
+ protected class DigitalNetIterator extends
+ PointSet.DefaultPointSetIterator {
+
+ protected int idigits; // Num. of digits in current code.
+ protected int[] bdigit; // b-ary code of current point index.
+ protected int[] gdigit; // Gray code of current point index.
+ protected int dimS; // = dim except in the shift iterator
+ // children where it is = dim + 1.
+ protected int[] cachedCurPoint; // Digits of coords of the current point,
+ // with digital shift already applied.
+ // Digit l of coord j is at pos. [j*outDigits + l].
+ // Has one more dimension for the shift iterators.
+
+ public DigitalNetIterator() {
+ // EpsilonHalf = 0.5 / Math.pow ((double) b, (double) outDigits);
+ bdigit = new int[numCols];
+ gdigit = new int[numCols];
+ dimS = dim;
+ cachedCurPoint = new int[(dim + 1)* outDigits];
+ init(); // This call is important so that subclasses don't
+ // automatically call 'resetCurPointIndex' at the time
+ // of construction as this may cause a subtle
+ // 'NullPointerException'
+ }
+
+ public void init() { // See constructor
+ resetCurPointIndex();
+ }
+
+ // We want to avoid generating 0 or 1
+ public double nextDouble() {
+ return nextCoordinate() + EpsilonHalf;
+ }
+
+ public double nextCoordinate() {
+ if (curPointIndex >= numPoints || curCoordIndex >= dimS)
+ outOfBounds();
+ int start = outDigits * curCoordIndex++;
+ double sum = 0;
+ // Can always have up to outDigits digits, because of digital shift.
+ for (int k = 0; k < outDigits; k++)
+ sum += cachedCurPoint [start+k] * factor[k];
+ if (digitalShift != null)
+ sum += EpsilonHalf;
+ return sum;
+ }
+
+ public void resetCurPointIndex() {
+ if (digitalShift == null)
+ for (int i = 0; i < cachedCurPoint.length; i++)
+ cachedCurPoint[i] = 0;
+ else {
+ if (dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ for (int j = 0; j < dimS; j++)
+ for (int k = 0; k < outDigits; k++)
+ cachedCurPoint[j*outDigits + k] = digitalShift[j][k];
+ }
+ for (int i = 0; i < numCols; i++) bdigit[i] = 0;
+ for (int i = 0; i < numCols; i++) gdigit[i] = 0;
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ idigits = 0;
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+
+ // Digits of Gray code, used to reconstruct cachedCurPoint.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * gdigit[c];
+ cachedCurPoint [j*outDigits+l] = sum % b;
+ }
+ }
+ }
+
+ public int resetToNextPoint() {
+ // incremental computation.
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Update the digital expansion of i in base b, and find the
+ // position of change in the Gray code. Set all digits == b-1 to 0
+ // and increase the first one after by 1.
+ int pos; // Position of change in the Gray code.
+ for (pos = 0; gdigit[pos] == b-1; pos++)
+ gdigit[pos] = 0;
+ gdigit[pos]++;
+
+ // Update the cachedCurPoint by adding the column of the gener.
+ // matrix that corresponds to the Gray code digit that has changed.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < lsup; l++) {
+ cachedCurPoint[j*outDigits + l] += genMat[j*numCols + pos][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ return curPointIndex;
+ }
+ }
+
+
+// ************************************************************************
+
+ protected class DigitalNetIteratorNoGray extends DigitalNetIterator {
+
+ public DigitalNetIteratorNoGray() {
+ super();
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+ if (dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+
+ // Convert i to b-ary representation, put digits in bdigit.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[j*numCols+c][l] * bdigit[c];
+ cachedCurPoint [j*outDigits+l] = sum % b;
+ }
+ }
+ }
+
+ public int resetToNextPoint() {
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Find the position of change in the digits of curPointIndex in base
+ // b. Set all digits = b-1 to 0; increase the first one after by 1.
+ int pos;
+ for (pos = 0; bdigit[pos] == b-1; pos++)
+ bdigit[pos] = 0;
+ bdigit[pos]++;
+
+ // Update the digital expansion of curPointIndex in base b.
+ // Update the cachedCurPoint by adding 1 unit at the digit pos.
+ // If pos > 0, remove b-1 units in the positions < pos. Since
+ // calculations are mod b, this is equivalent to adding 1 unit.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 0; j < dimS; j++) {
+ for (l = 0; l < lsup; l++) {
+ for (c = 0; c <= pos; c++)
+ cachedCurPoint[j*outDigits + l] += genMat[j*numCols + c][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ return curPointIndex;
+ }
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNetBase2.java b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2.java
new file mode 100644
index 0000000..b7a1a2d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2.java
@@ -0,0 +1,674 @@
+
+
+/*
+ * Class: DigitalNetBase2
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.util.*;
+
+
+/**
+ * A special case of {@link DigitalNet} for the base <SPAN CLASS="MATH"><I>b</I> = 2</SPAN>.
+ * The implementation exploit the binary nature of
+ * computers and is much more efficient than for the general case.
+ * Binary expansions are easy to obtain because the computer already
+ * uses them internally.
+ * The generator matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> are stored in a large array of size <SPAN CLASS="MATH"><I>sk</I></SPAN>.
+ * The <SPAN CLASS="MATH"><I>c</I></SPAN>-th column of
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>, for
+ * <SPAN CLASS="MATH"><I>c</I> = 0,..., <I>k</I> - 1</SPAN>,
+ * is stored at position <SPAN CLASS="MATH"><I>jk</I> + <I>c</I></SPAN> of that array, as a 32-bit integer.
+ * For all derived classes, the above 32-bit integer must be of the form
+ *
+ * <SPAN CLASS="MATH">[00<SUP> ... </SUP><I>C</I><SUB>0</SUB><I>C</I><SUB>1</SUB><SUP> ... </SUP><I>C</I><SUB>r-1</SUB>]</SPAN>.
+ * The value of <SPAN CLASS="MATH"><I>k</I></SPAN> cannot exceed 31 (32 is not allowed because Java does
+ * not have 32-bit unsigned integers). The value of <SPAN CLASS="MATH"><I>w</I></SPAN> is always 31.
+ *
+ * <P>
+ * The random digital shift in base 2 corresponds to a random XOR.
+ * It can be applied via the method {@link #addRandomShift addRandomShift}.
+ *
+ */
+public class DigitalNetBase2 extends DigitalNet {
+ private int[] originalMat; // Original matrices, without randomization.
+ protected int[] genMat; // The current generator matrix.
+ protected int[] digitalShift; // Stores the digital shift vector.
+
+
+ /**
+ * Prints the generator matrices as bit matrices in standard form
+ * for dimensions 1 to <SPAN CLASS="MATH"><I>s</I></SPAN>.
+ *
+ */
+ public void printGeneratorMatrices (int s) {
+ int r, c, j; // row r, column c, dimension j
+ int[] mat = new int[numCols]; // Bit matrix
+ int[] matTrans = new int[numRows]; // Transposed bit matrix
+ for (j = 0; j < s; j++) {
+ System.out.println ("dim = " + (j+1) + PrintfFormat.NEWLINE);
+ for (r = 0; r < numRows; r++)
+ matTrans[r] = 0;
+ for (c = 0; c < numCols; c++) {
+ mat[c] = genMat[j*numCols+c];
+ for (r = numRows - 1; r >= 0; r--) {
+ matTrans[r] <<= 1;
+ matTrans[r] |= mat[c] & 1;
+ mat[c] >>= 1;
+ }
+ }
+ for (r = 0; r < numRows; r++) {
+ StringBuffer sb = new StringBuffer();
+ int x = matTrans[r];
+ for (c = 0; c < numCols; c++) {
+ sb.insert(0, x & 1);
+ x >>= 1;
+ }
+ System.out.println (sb);
+ }
+ System.out.println ("----------------------------------");
+ }
+ }
+
+
+ /**
+ * Prints the generator matrices transposed in the form of integers
+ * for dimensions 1 to <SPAN CLASS="MATH"><I>s</I></SPAN>. Each integer corresponds to a column of bits.
+ *
+ */
+ public void printGeneratorMatricesTrans (int s) {
+ // column c, dimension j.
+ for (int j = 0; j < s; j++) {
+ System.out.println ("dim = " + (j+1) + PrintfFormat.NEWLINE);
+ for (int c = 0; c < numCols; c++)
+ System.out.println (genMat[j*numCols+c]);
+ System.out.println ("----------------------------------");
+ }
+ }
+
+
+ public double getCoordinate (int i, int j) {
+ int res;
+ int pos = 0;
+ int grayCode = i ^ (i >> 1);
+
+ if (digitalShift == null)
+ res = 0;
+ else
+ res = digitalShift[j];
+ while ((grayCode >> pos) != 0) {
+ if (((grayCode >> pos) & 1) != 0)
+ res ^= genMat[j*numCols + pos];
+ pos++;
+ }
+ if (digitalShift != null)
+ return res * normFactor + EpsilonHalf;
+ else
+ return res * normFactor;
+ }
+
+ public double getCoordinateNoGray (int i, int j) {
+ int res;
+ if (digitalShift == null)
+ res = 0;
+ else
+ res = digitalShift[j];
+ int pos = 0; // Position of the bit that is examined.
+ while ((i >> pos) != 0) {
+ if ((((i >> pos) & 1) != 0) && (pos < numCols))
+ res ^= genMat[j*numCols + pos];
+ pos++;
+ }
+ if (digitalShift != null)
+ return res * normFactor + EpsilonHalf;
+ else
+ return res * normFactor;
+ }
+
+ public PointSetIterator iterator() {
+ return new DigitalNetBase2Iterator();
+ }
+
+ /**
+ * This iterator does not use the
+ * Gray code. Thus the points are enumerated in the order of their first
+ * coordinate before randomization.
+ *
+ */
+ public PointSetIterator iteratorNoGray() {
+ return new DigitalNetBase2IteratorNoGray();
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("DigitalNetBase2:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ digitalShift = null;
+ }
+
+ public void addRandomShift (RandomStream stream) {
+ addRandomShift (0, dim, stream);
+ }
+
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (digitalShift == null) {
+ digitalShift = new int[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ int[] temp = new int[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = digitalShift[i];
+ digitalShift = temp;
+ }
+ int maxj;
+ if (outDigits < 31)
+ maxj = (1 << outDigits) - 1;
+ else
+ maxj = 2147483647;
+ for (int i = d1; i < d2; i++)
+ digitalShift[i] = stream.nextInt (0, maxj);
+ dimShift = d2;
+ shiftStream = stream;
+ }
+
+
+ // Left-multiplies lower-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // Mj[d] is assumed to contain the d-th subdiagonal of matrix Mj,
+ // for d=0,...,w-1. Each subdiagonal is represented as a
+ // w-bit integer, whose most significant bits are those on the
+ // diagonal. For example, for d=w-3, the subdiagonal has 3 bits,
+ // say b1, b2, b3, and is represented by the integer
+ // Mj[w-3] = b1 * 2^{w-1} + b2 * 2^{w-2} + b3 * b^{w-3}.
+ //
+ private void leftMultiplyMat (int j, int[] Mj) {
+ int c, d, col; // Dimension j, column c for new C_j.
+ for (c = 0; c < numCols; c++) {
+ col = 0;
+ for (d = 0; d < outDigits; d++)
+ // Multiply subdiagonal d of M_j by column c of C_j, and xor.
+ col ^= (Mj[d] & originalMat[j* numCols + c]) >> d;
+ genMat[j * numCols + c] = col;
+ }
+ }
+
+/*
+ // Left-multiplies lower-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ private void leftMultiplyMat (int j, int[] Mj) {
+ int c, l, i, prod; // Dimension j, column c for new C_j.
+ int numOnes;
+ int col; // Will be column c of genMat.
+ for (c = 0; c < numCols; c++) {
+ col = 0;
+ for (l = 0; l < outDigits; l++) {
+ // Multiply row l of M_j by column c of C_j.
+ prod = Mj[l] & originalMat[j* numCols + c];
+ numOnes = 0; // Counts the number of ones in prod, modulo 2.
+ for (i = 0; i < outDigits; i++)
+ numOnes += (1 & prod >> i);
+ // Put a 1 in column c, row l, of C_j if numOnes is odd.
+ col += ((numOnes & 1) << (outDigits-l-1));
+ }
+ genMat[j * numCols + c] = col;
+ }
+ }
+*/
+
+ // Right-multiplies upper-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // Mj[d] is assumed to contain the d-th column of matrix Mj,
+ // for d=0,...,w-1. Each column is represented as a w-bit integer,
+ // whose most significant bits are those at index 0.
+ // For example, for d=2, the column has 3 bits, (the others are 0
+ // since under the diagonal) say b1, b2, b3, and is represented by
+ // the integer Mj[2] = b1 * 2^{w-1} + b2 * 2^{w-2} + b3 * b^{w-3}.
+ //
+ private void rightMultiplyMat (int j, int[] Mj) {
+ int c, r, col; // Dimension j, column c for new C_j.
+ int mask; // Bit of column Mj[c]
+
+ for (c = 0; c < numCols; c++) {
+ mask = 1 << outDigits - 1;
+ col = originalMat[j*numCols + c];
+ for (r = 0; r < c; r++) {
+ // If bit (outDigits - 1 - r) of Mj[c] is 1, add column r
+ if ((Mj[c] & mask) != 0)
+ col ^= originalMat[j*numCols + r];
+ mask >>= 1;
+ }
+ genMat[j * numCols + c] = col;
+ }
+ }
+
+/*
+ // Right-multiplies original C_j by upper-triangular matrix Mj,
+ // where original C_j is in originalMat and result is in genMat.
+ private void rightMultiplyMat (int j, int[] Mj) {
+ int c, l, i, mask; // Dimension j, column c for new C_j.
+ int numOnes;
+ int col; // Will be column c of genMat.
+ boolean bool1, bool2;
+ for (c = 0; c < numCols; c++) {
+ col = 0;
+ for (l = 0; l < outDigits; l++) {
+ // Multiply row l of C_j by column c of Mj.
+ mask = (1 << outDigits-l-1); // ???
+ // xor = originalMat[j* numCols + l] & Mj[c];
+ numOnes = 0; // Counts the number of ones in xor, modulo 2.
+ for (i = 0; i < numCols; i++) {
+ bool1 = (mask & originalMat[j * numCols + i]) != 0;
+ bool2 = ((1 << (outDigits-i-1)) & Mj[i]) != 0;
+ if (bool1 & bool2) numOnes++;
+ }
+ // Put a 1 in column c, row l, of C_j if numOnes is odd.
+ col += ((numOnes & 1) << (outDigits-l-1));
+ }
+ genMat[j * numCols + c] = col;
+ }
+ }
+*/
+
+ public void leftMatrixScramble (RandomStream stream) {
+ int j, d; // dimension j, subdiagonal d.
+ final int allOnes = (1 << outDigits) - 1; // outDigits ones.
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l in a single integer (binary repres.)
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ scrambleMat[j][0] = allOnes;
+ for (d = 1; d < outDigits; d++)
+ scrambleMat[j][d] = (stream.nextInt (0, allOnes >> d)) << d;
+ }
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+
+ public void iBinomialMatrixScramble (RandomStream stream) {
+ int j, d; // Dimension j, subdiagonal d of M_j.
+ final int allOnes = (1 << outDigits) - 1; // outDigits ones.
+ int lastRow; // Last row of M_j: w-1 random bits followed by 1.
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l of M_j.
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ scrambleMat[j][0] = allOnes;
+ lastRow = stream.nextInt (0, allOnes) | 1;
+ for (d = 1; d < outDigits; d++)
+ // Subdiagonal d contains either all ones or all zeros.
+ if (((1 << d) & lastRow) == 0) scrambleMat[j][d] = 0;
+ else scrambleMat[j][d] = (allOnes >> d) << d;
+ }
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ // leftMultiplyMat (scrambleMat);
+ }
+
+ public void stripedMatrixScramble (RandomStream stream) {
+ int j, d; // dimension j, subdiagonal d of M_j.
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+ // Constructs the lower-triangular scrambling matrix M, w by w,
+ // filled with 1's. scrambleMat[d] contains subdiagonal d of M.
+ int[] scrambleMat = new int[outDigits];
+ final int allOnes = (1 << outDigits) - 1; // outDigits ones.
+ for (d = 0; d < outDigits; d++)
+ scrambleMat[d] = (allOnes >> d) << d;
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat);
+ }
+
+
+/*
+ public void leftMatrixScramble (RandomStream stream) {
+ int j, l; // dimension j, row l.
+ int boundInt;
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l in a single integer (binary repres.)
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ boundInt = 0;
+ for (l = 0; l < outDigits; l++) {
+ boundInt += (1 << l); // Integer repres. by string of l+1 ones.
+ scrambleMat[j][l] = (stream.nextInt (0, boundInt) | 1)
+ << (outDigits-l-1);
+ }
+ }
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+ public void iBinomialMatrixScramble (RandomStream stream) {
+ int j, l; // dimension j, row l of M_j.
+ int allOnes;
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l of M_j.
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ allOnes = ~0 >> (32 - outDigits); // outDigits ones.
+ scrambleMat[j][outDigits-1] = stream.nextInt (0, allOnes) | 1;
+ for (l = outDigits - 2; l >= 0; l--)
+ scrambleMat[j][l] = scrambleMat[j][l+1] << 1;
+ }
+
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ // leftMultiplyMat (scrambleMat);
+ }
+
+ public void stripedMatrixScramble (RandomStream stream) {
+ int j, l; // dimension j, row l of M_j.
+ int allOnes;
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrix M, w by w,
+ // filled with 1's. scrambleMat[l] contains row l of M.
+ int[] scrambleMat = new int[outDigits];
+ allOnes = ~0 >> (32 - outDigits); // outDigits ones.
+ for (l = 0; l < outDigits; l++)
+ scrambleMat[l] = (allOnes << (outDigits - 1 - l)) & allOnes;
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat);
+ }
+*/
+
+ public void rightMatrixScramble (RandomStream stream) {
+ int j, c, l, i; // Dimension j, column c for new C_j.
+
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Generate an upper triangular matrix for Faure-Tezuka right-scramble.
+ // scrambleMat[c] contains column c of M.
+ int[] scrambleMat = new int[outDigits];
+ int boundInt = 0;
+ for (c = 0; c < numCols; c++) {
+ boundInt += (1 << c); // Integer repres. by string of c+1 ones.
+ scrambleMat[c] = (1 | stream.nextInt (0, boundInt))
+ << (outDigits-c-1);
+ }
+
+ // Right-multiply the generator matrices by the scrambling matrix.
+ for (j = 0; j < dim; j++)
+ rightMultiplyMat (j, scrambleMat);
+ }
+
+ //-----------------------------------------------------------------------
+ private void ScrambleError (String method) {
+ throw new UnsupportedOperationException
+ (PrintfFormat.NEWLINE + " " + method +
+ " is meaningless for DigitalNetBase2");
+ }
+
+ public void leftMatrixScrambleDiag (RandomStream stream) {
+ ScrambleError ("leftMatrixScrambleDiag");
+ }
+
+ public void leftMatrixScrambleFaurePermut (RandomStream stream, int sb) {
+ ScrambleError ("leftMatrixScrambleFaurePermut");
+ }
+
+ public void leftMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb) {
+ ScrambleError ("leftMatrixScrambleFaurePermutDiag");
+ }
+
+ public void leftMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ ScrambleError ("leftMatrixScrambleFaurePermutAll");
+ }
+
+ public void iBinomialMatrixScrambleFaurePermut (RandomStream stream,
+ int sb) {
+ ScrambleError ("iBinomialMatrixScrambleFaurePermut");
+ }
+
+ public void iBinomialMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb) {
+ ScrambleError ("iBinomialMatrixScrambleFaurePermutDiag");
+ }
+
+ public void iBinomialMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ ScrambleError ("iBinomialMatrixScrambleFaurePermutAll");
+ }
+
+ public void stripedMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ ScrambleError ("stripedMatrixScrambleFaurePermutAll");
+ }
+
+
+ // *******************************************************************
+ protected class DigitalNetBase2Iterator extends DigitalNetIterator {
+
+ // Coordinates of the current point stored (cached) as integers.
+ // Initially contains zeros, because first point is always zero.
+ // Incorporates the random shift, except for the first point.
+ // There is one more dimension for the points because of the
+ // shift iterators children of DigitalNetBase2Iterator.
+ // dimS = dim, except for the shift iterator children where
+ // dimS = dim + 1.
+ protected int dimS;
+
+ public DigitalNetBase2Iterator() {
+ super();
+ EpsilonHalf = 0.5 / Num.TWOEXP[outDigits];
+ cachedCurPoint = new int[dim + 1];
+ dimS = dim;
+ init2();
+ }
+
+ public void init() { // This method is necessary to overload
+ } // the init() of DigitalNetIterator
+
+ public void init2() { // See constructor
+ resetCurPointIndex();
+ }
+
+ // We want to avoid generating 0 or 1
+ public double nextDouble() {
+ return nextCoordinate();
+ }
+
+ public double nextCoordinate() {
+ if (curPointIndex >= numPoints || curCoordIndex >= dimS)
+ outOfBounds();
+ if (digitalShift == null)
+ return cachedCurPoint[curCoordIndex++] * normFactor;
+ else
+ return cachedCurPoint[curCoordIndex++] * normFactor + EpsilonHalf;
+ }
+
+ protected void addShiftToCache () {
+ if (digitalShift == null)
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] = 0;
+ else {
+ if (dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] = digitalShift[j];
+ }
+ }
+
+ public void resetCurPointIndex() {
+ addShiftToCache ();
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex(); return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+
+ int j;
+ int grayCode = i ^ (i >> 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((grayCode >> pos) != 0) {
+ if (((grayCode >> pos) & 1) != 0)
+ for (j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ int pos = 0; // Will be position of change in Gray code,
+ // = pos. of first 0 in binary code of point index.
+ while (((curPointIndex >> pos) & 1) != 0)
+ pos++;
+ if (pos < numCols) {
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ public int nextPoint (double p[], int d){
+ if (curPointIndex >= numPoints || d > dimS)
+ outOfBounds();
+ if (digitalShift == null) {
+ for (int j=0; j < d; j++)
+ p[j] = cachedCurPoint[j] * normFactor;
+ } else {
+ for (int j=0; j < d; j++)
+ p[j] = cachedCurPoint[j] * normFactor + EpsilonHalf;
+ }
+ return resetToNextPoint();
+ }
+ }
+
+
+ // *******************************************************************
+ protected class DigitalNetBase2IteratorNoGray
+ extends DigitalNetBase2Iterator {
+
+ // Same as DigitalNetBase2Iterator,
+ // except that the Gray code is not used.
+
+ public DigitalNetBase2IteratorNoGray() {
+ super();
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+ int pos = 0; // Position of the bit that is examined.
+ while ((i >> pos) != 0) {
+ if ((((i >> pos) & 1) != 0) && (pos < numCols)) {
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ }
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ // Contains the bits of i that changed.
+ if (curPointIndex + 1 >= numPoints)
+ return ++curPointIndex;
+ int diff = curPointIndex ^ (curPointIndex + 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((diff >> pos) != 0) {
+ if ((((diff >> pos) & 1) != 0) && (pos < numCols)) {
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ }
+ pos++;
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNetBase2.tex b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2.tex
new file mode 100644
index 0000000..7f0b921
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2.tex
@@ -0,0 +1,683 @@
+\defmodule{DigitalNetBase2}
+
+A special case of \class{DigitalNet}{} for the base $b=2$.
+The implementation exploit the binary nature of
+computers and is much more efficient than for the general case.
+Binary expansions are easy to obtain because the computer already
+uses them internally.
+The generator matrices $\mathbf{C}_j$ are stored in a large array of size $sk$.
+The $c$-th column of $\mathbf{C}_j$, for $c=0,\dots,k-1$,
+is stored at position $jk + c$ of that array, as a 32-bit integer.
+For all derived classes, the above 32-bit integer must be of the form
+ $ [0 0 \cdots C_0 C_1 \cdots C_{r-1}]$.
+The value of $k$ cannot exceed 31 (32 is not allowed because Java does
+not have 32-bit unsigned integers). The value of $w$ is always 31.
+\pierre{In this implementation, $w$ is always used in place of $r$
+ so the value of $r$ is not used.}
+
+The random digital shift in base 2 corresponds to a random XOR.
+It can be applied via the method \method{addRandomShift}{}.
+% To apply it to a digital net in base 2, it is more efficient to use the
+% method rather than the class \class{RandXoredPointSet}.
+
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DigitalNetBase2
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.util.*;
+\end{hide}
+
+public class DigitalNetBase2 extends DigitalNet \begin{hide} {
+ private int[] originalMat; // Original matrices, without randomization.
+ protected int[] genMat; // The current generator matrix.
+ protected int[] digitalShift; // Stores the digital shift vector.
+\end{hide}
+
+ public void printGeneratorMatrices (int s) \begin{hide} {
+ int r, c, j; // row r, column c, dimension j
+ int[] mat = new int[numCols]; // Bit matrix
+ int[] matTrans = new int[numRows]; // Transposed bit matrix
+ for (j = 0; j < s; j++) {
+ System.out.println ("dim = " + (j+1) + PrintfFormat.NEWLINE);
+ for (r = 0; r < numRows; r++)
+ matTrans[r] = 0;
+ for (c = 0; c < numCols; c++) {
+ mat[c] = genMat[j*numCols+c];
+ for (r = numRows - 1; r >= 0; r--) {
+ matTrans[r] <<= 1;
+ matTrans[r] |= mat[c] & 1;
+ mat[c] >>= 1;
+ }
+ }
+ for (r = 0; r < numRows; r++) {
+ StringBuffer sb = new StringBuffer();
+ int x = matTrans[r];
+ for (c = 0; c < numCols; c++) {
+ sb.insert(0, x & 1);
+ x >>= 1;
+ }
+ System.out.println (sb);
+ }
+ System.out.println ("----------------------------------");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Prints the generator matrices as bit matrices in standard form
+ for dimensions 1 to $s$.
+\end{tabb}
+\begin{code}
+
+ public void printGeneratorMatricesTrans (int s) \begin{hide} {
+ // column c, dimension j.
+ for (int j = 0; j < s; j++) {
+ System.out.println ("dim = " + (j+1) + PrintfFormat.NEWLINE);
+ for (int c = 0; c < numCols; c++)
+ System.out.println (genMat[j*numCols+c]);
+ System.out.println ("----------------------------------");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Prints the generator matrices transposed in the form of integers
+ for dimensions 1 to $s$. Each integer corresponds to a column of bits.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ int res;
+ int pos = 0;
+ int grayCode = i ^ (i >> 1);
+
+ if (digitalShift == null)
+ res = 0;
+ else
+ res = digitalShift[j];
+ while ((grayCode >> pos) != 0) {
+ if (((grayCode >> pos) & 1) != 0)
+ res ^= genMat[j*numCols + pos];
+ pos++;
+ }
+ if (digitalShift != null)
+ return res * normFactor + EpsilonHalf;
+ else
+ return res * normFactor;
+ }
+
+ public double getCoordinateNoGray (int i, int j) {
+ int res;
+ if (digitalShift == null)
+ res = 0;
+ else
+ res = digitalShift[j];
+ int pos = 0; // Position of the bit that is examined.
+ while ((i >> pos) != 0) {
+ if ((((i >> pos) & 1) != 0) && (pos < numCols))
+ res ^= genMat[j*numCols + pos];
+ pos++;
+ }
+ if (digitalShift != null)
+ return res * normFactor + EpsilonHalf;
+ else
+ return res * normFactor;
+ }
+
+ public PointSetIterator iterator() {
+ return new DigitalNetBase2Iterator();
+ }\end{hide}
+
+ public PointSetIterator iteratorNoGray()\begin{hide} {
+ return new DigitalNetBase2IteratorNoGray();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ This iterator does not use the
+ Gray code. Thus the points are enumerated in the order of their first
+ coordinate before randomization.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("DigitalNetBase2:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ digitalShift = null;
+ }
+
+ public void addRandomShift (RandomStream stream) {
+ addRandomShift (0, dim, stream);
+ }
+
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (digitalShift == null) {
+ digitalShift = new int[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ int[] temp = new int[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = digitalShift[i];
+ digitalShift = temp;
+ }
+ int maxj;
+ if (outDigits < 31)
+ maxj = (1 << outDigits) - 1;
+ else
+ maxj = 2147483647;
+ for (int i = d1; i < d2; i++)
+ digitalShift[i] = stream.nextInt (0, maxj);
+ dimShift = d2;
+ shiftStream = stream;
+ }
+
+
+ // Left-multiplies lower-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // Mj[d] is assumed to contain the d-th subdiagonal of matrix Mj,
+ // for d=0,...,w-1. Each subdiagonal is represented as a
+ // w-bit integer, whose most significant bits are those on the
+ // diagonal. For example, for d=w-3, the subdiagonal has 3 bits,
+ // say b1, b2, b3, and is represented by the integer
+ // Mj[w-3] = b1 * 2^{w-1} + b2 * 2^{w-2} + b3 * b^{w-3}.
+ //
+ private void leftMultiplyMat (int j, int[] Mj) {
+ int c, d, col; // Dimension j, column c for new C_j.
+ for (c = 0; c < numCols; c++) {
+ col = 0;
+ for (d = 0; d < outDigits; d++)
+ // Multiply subdiagonal d of M_j by column c of C_j, and xor.
+ col ^= (Mj[d] & originalMat[j* numCols + c]) >> d;
+ genMat[j * numCols + c] = col;
+ }
+ }
+
+/*
+ // Left-multiplies lower-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ private void leftMultiplyMat (int j, int[] Mj) {
+ int c, l, i, prod; // Dimension j, column c for new C_j.
+ int numOnes;
+ int col; // Will be column c of genMat.
+ for (c = 0; c < numCols; c++) {
+ col = 0;
+ for (l = 0; l < outDigits; l++) {
+ // Multiply row l of M_j by column c of C_j.
+ prod = Mj[l] & originalMat[j* numCols + c];
+ numOnes = 0; // Counts the number of ones in prod, modulo 2.
+ for (i = 0; i < outDigits; i++)
+ numOnes += (1 & prod >> i);
+ // Put a 1 in column c, row l, of C_j if numOnes is odd.
+ col += ((numOnes & 1) << (outDigits-l-1));
+ }
+ genMat[j * numCols + c] = col;
+ }
+ }
+*/
+
+ // Right-multiplies upper-triangular matrix Mj by original C_j,
+ // where original C_j is in originalMat and result is in genMat.
+ // Mj[d] is assumed to contain the d-th column of matrix Mj,
+ // for d=0,...,w-1. Each column is represented as a w-bit integer,
+ // whose most significant bits are those at index 0.
+ // For example, for d=2, the column has 3 bits, (the others are 0
+ // since under the diagonal) say b1, b2, b3, and is represented by
+ // the integer Mj[2] = b1 * 2^{w-1} + b2 * 2^{w-2} + b3 * b^{w-3}.
+ //
+ private void rightMultiplyMat (int j, int[] Mj) {
+ int c, r, col; // Dimension j, column c for new C_j.
+ int mask; // Bit of column Mj[c]
+
+ for (c = 0; c < numCols; c++) {
+ mask = 1 << outDigits - 1;
+ col = originalMat[j*numCols + c];
+ for (r = 0; r < c; r++) {
+ // If bit (outDigits - 1 - r) of Mj[c] is 1, add column r
+ if ((Mj[c] & mask) != 0)
+ col ^= originalMat[j*numCols + r];
+ mask >>= 1;
+ }
+ genMat[j * numCols + c] = col;
+ }
+ }
+
+/*
+ // Right-multiplies original C_j by upper-triangular matrix Mj,
+ // where original C_j is in originalMat and result is in genMat.
+ private void rightMultiplyMat (int j, int[] Mj) {
+ int c, l, i, mask; // Dimension j, column c for new C_j.
+ int numOnes;
+ int col; // Will be column c of genMat.
+ boolean bool1, bool2;
+ for (c = 0; c < numCols; c++) {
+ col = 0;
+ for (l = 0; l < outDigits; l++) {
+ // Multiply row l of C_j by column c of Mj.
+ mask = (1 << outDigits-l-1); // ???
+ // xor = originalMat[j* numCols + l] & Mj[c];
+ numOnes = 0; // Counts the number of ones in xor, modulo 2.
+ for (i = 0; i < numCols; i++) {
+ bool1 = (mask & originalMat[j * numCols + i]) != 0;
+ bool2 = ((1 << (outDigits-i-1)) & Mj[i]) != 0;
+ if (bool1 & bool2) numOnes++;
+ }
+ // Put a 1 in column c, row l, of C_j if numOnes is odd.
+ col += ((numOnes & 1) << (outDigits-l-1));
+ }
+ genMat[j * numCols + c] = col;
+ }
+ }
+*/
+
+ public void leftMatrixScramble (RandomStream stream) {
+ int j, d; // dimension j, subdiagonal d.
+ final int allOnes = (1 << outDigits) - 1; // outDigits ones.
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l in a single integer (binary repres.)
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ scrambleMat[j][0] = allOnes;
+ for (d = 1; d < outDigits; d++)
+ scrambleMat[j][d] = (stream.nextInt (0, allOnes >> d)) << d;
+ }
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+
+ public void iBinomialMatrixScramble (RandomStream stream) {
+ int j, d; // Dimension j, subdiagonal d of M_j.
+ final int allOnes = (1 << outDigits) - 1; // outDigits ones.
+ int lastRow; // Last row of M_j: w-1 random bits followed by 1.
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l of M_j.
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ scrambleMat[j][0] = allOnes;
+ lastRow = stream.nextInt (0, allOnes) | 1;
+ for (d = 1; d < outDigits; d++)
+ // Subdiagonal d contains either all ones or all zeros.
+ if (((1 << d) & lastRow) == 0) scrambleMat[j][d] = 0;
+ else scrambleMat[j][d] = (allOnes >> d) << d;
+ }
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ // leftMultiplyMat (scrambleMat);
+ }
+
+ public void stripedMatrixScramble (RandomStream stream) {
+ int j, d; // dimension j, subdiagonal d of M_j.
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+ // Constructs the lower-triangular scrambling matrix M, w by w,
+ // filled with 1's. scrambleMat[d] contains subdiagonal d of M.
+ int[] scrambleMat = new int[outDigits];
+ final int allOnes = (1 << outDigits) - 1; // outDigits ones.
+ for (d = 0; d < outDigits; d++)
+ scrambleMat[d] = (allOnes >> d) << d;
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat);
+ }
+
+
+/*
+ public void leftMatrixScramble (RandomStream stream) {
+ int j, l; // dimension j, row l.
+ int boundInt;
+
+ // If genMat contains the original gen. matrices, copy to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l in a single integer (binary repres.)
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ boundInt = 0;
+ for (l = 0; l < outDigits; l++) {
+ boundInt += (1 << l); // Integer repres. by string of l+1 ones.
+ scrambleMat[j][l] = (stream.nextInt (0, boundInt) | 1)
+ << (outDigits-l-1);
+ }
+ }
+
+ // Multiply M_j by the generator matrix C_j for each j.
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ }
+
+ public void iBinomialMatrixScramble (RandomStream stream) {
+ int j, l; // dimension j, row l of M_j.
+ int allOnes;
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrices M_j, w by w.
+ // scrambleMat[j][l] contains row l of M_j.
+ int[][] scrambleMat = new int[dim][outDigits];
+ for (j = 0 ; j < dim; j++) {
+ allOnes = ~0 >> (32 - outDigits); // outDigits ones.
+ scrambleMat[j][outDigits-1] = stream.nextInt (0, allOnes) | 1;
+ for (l = outDigits - 2; l >= 0; l--)
+ scrambleMat[j][l] = scrambleMat[j][l+1] << 1;
+ }
+
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat[j]);
+ // leftMultiplyMat (scrambleMat);
+ }
+
+ public void stripedMatrixScramble (RandomStream stream) {
+ int j, l; // dimension j, row l of M_j.
+ int allOnes;
+
+ // If genMat is original generator matrices, copy it to originalMat.
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Constructs the lower-triangular scrambling matrix M, w by w,
+ // filled with 1's. scrambleMat[l] contains row l of M.
+ int[] scrambleMat = new int[outDigits];
+ allOnes = ~0 >> (32 - outDigits); // outDigits ones.
+ for (l = 0; l < outDigits; l++)
+ scrambleMat[l] = (allOnes << (outDigits - 1 - l)) & allOnes;
+ for (j = 0; j < dim; j++) leftMultiplyMat (j, scrambleMat);
+ }
+*/
+
+ public void rightMatrixScramble (RandomStream stream) {
+ int j, c, l, i; // Dimension j, column c for new C_j.
+
+ if (originalMat == null) {
+ originalMat = genMat;
+ genMat = new int[dim * numCols];
+ }
+
+ // Generate an upper triangular matrix for Faure-Tezuka right-scramble.
+ // scrambleMat[c] contains column c of M.
+ int[] scrambleMat = new int[outDigits];
+ int boundInt = 0;
+ for (c = 0; c < numCols; c++) {
+ boundInt += (1 << c); // Integer repres. by string of c+1 ones.
+ scrambleMat[c] = (1 | stream.nextInt (0, boundInt))
+ << (outDigits-c-1);
+ }
+
+ // Right-multiply the generator matrices by the scrambling matrix.
+ for (j = 0; j < dim; j++)
+ rightMultiplyMat (j, scrambleMat);
+ }
+
+ //-----------------------------------------------------------------------
+ private void ScrambleError (String method) {
+ throw new UnsupportedOperationException
+ (PrintfFormat.NEWLINE + " " + method +
+ " is meaningless for DigitalNetBase2");
+ }
+
+ public void leftMatrixScrambleDiag (RandomStream stream) {
+ ScrambleError ("leftMatrixScrambleDiag");
+ }
+
+ public void leftMatrixScrambleFaurePermut (RandomStream stream, int sb) {
+ ScrambleError ("leftMatrixScrambleFaurePermut");
+ }
+
+ public void leftMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb) {
+ ScrambleError ("leftMatrixScrambleFaurePermutDiag");
+ }
+
+ public void leftMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ ScrambleError ("leftMatrixScrambleFaurePermutAll");
+ }
+
+ public void iBinomialMatrixScrambleFaurePermut (RandomStream stream,
+ int sb) {
+ ScrambleError ("iBinomialMatrixScrambleFaurePermut");
+ }
+
+ public void iBinomialMatrixScrambleFaurePermutDiag (RandomStream stream,
+ int sb) {
+ ScrambleError ("iBinomialMatrixScrambleFaurePermutDiag");
+ }
+
+ public void iBinomialMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ ScrambleError ("iBinomialMatrixScrambleFaurePermutAll");
+ }
+
+ public void stripedMatrixScrambleFaurePermutAll (RandomStream stream,
+ int sb) {
+ ScrambleError ("stripedMatrixScrambleFaurePermutAll");
+ }\end{hide}
+\end{code}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+
+ // *******************************************************************
+ protected class DigitalNetBase2Iterator extends DigitalNetIterator {
+
+ // Coordinates of the current point stored (cached) as integers.
+ // Initially contains zeros, because first point is always zero.
+ // Incorporates the random shift, except for the first point.
+ // There is one more dimension for the points because of the
+ // shift iterators children of DigitalNetBase2Iterator.
+ // dimS = dim, except for the shift iterator children where
+ // dimS = dim + 1.
+ protected int dimS;
+
+ public DigitalNetBase2Iterator() {
+ super();
+ EpsilonHalf = 0.5 / Num.TWOEXP[outDigits];
+ cachedCurPoint = new int[dim + 1];
+ dimS = dim;
+ init2();
+ }
+
+ public void init() { // This method is necessary to overload
+ } // the init() of DigitalNetIterator
+
+ public void init2() { // See constructor
+ resetCurPointIndex();
+ }
+
+ // We want to avoid generating 0 or 1
+ public double nextDouble() {
+ return nextCoordinate();
+ }
+
+ public double nextCoordinate() {
+ if (curPointIndex >= numPoints || curCoordIndex >= dimS)
+ outOfBounds();
+ if (digitalShift == null)
+ return cachedCurPoint[curCoordIndex++] * normFactor;
+ else
+ return cachedCurPoint[curCoordIndex++] * normFactor + EpsilonHalf;
+ }
+
+ protected void addShiftToCache () {
+ if (digitalShift == null)
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] = 0;
+ else {
+ if (dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] = digitalShift[j];
+ }
+ }
+
+ public void resetCurPointIndex() {
+ addShiftToCache ();
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex(); return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+
+ int j;
+ int grayCode = i ^ (i >> 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((grayCode >> pos) != 0) {
+ if (((grayCode >> pos) & 1) != 0)
+ for (j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ int pos = 0; // Will be position of change in Gray code,
+ // = pos. of first 0 in binary code of point index.
+ while (((curPointIndex >> pos) & 1) != 0)
+ pos++;
+ if (pos < numCols) {
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ public int nextPoint (double p[], int d){
+ if (curPointIndex >= numPoints || d > dimS)
+ outOfBounds();
+ if (digitalShift == null) {
+ for (int j=0; j < d; j++)
+ p[j] = cachedCurPoint[j] * normFactor;
+ } else {
+ for (int j=0; j < d; j++)
+ p[j] = cachedCurPoint[j] * normFactor + EpsilonHalf;
+ }
+ return resetToNextPoint();
+ }
+ }
+
+
+ // *******************************************************************
+ protected class DigitalNetBase2IteratorNoGray
+ extends DigitalNetBase2Iterator {
+
+ // Same as DigitalNetBase2Iterator,
+ // except that the Gray code is not used.
+
+ public DigitalNetBase2IteratorNoGray() {
+ super();
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+ int pos = 0; // Position of the bit that is examined.
+ while ((i >> pos) != 0) {
+ if ((((i >> pos) & 1) != 0) && (pos < numCols)) {
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ }
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ // Contains the bits of i that changed.
+ if (curPointIndex + 1 >= numPoints)
+ return ++curPointIndex;
+ int diff = curPointIndex ^ (curPointIndex + 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((diff >> pos) != 0) {
+ if ((((diff >> pos) & 1) != 0) && (pos < numCols)) {
+ for (int j = 0; j < dim; j++)
+ cachedCurPoint[j] ^= genMat[j * numCols + pos];
+ }
+ pos++;
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNetBase2FromFile.java b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2FromFile.java
new file mode 100644
index 0000000..03cf6b7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2FromFile.java
@@ -0,0 +1,248 @@
+
+
+/*
+ * Class: DigitalNetBase2FromFile
+ * Description: read the parameters defining a digital net in base 2
+ from a file or from a URL address
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import java.io.*;
+import java.net.URL;
+import java.net.MalformedURLException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class allows us to read the parameters defining a digital net
+ * <EM>in base 2</EM> either from a file, or from a URL address on the
+ * World Wide Web. See the documentation in
+ * {@link umontreal.iro.lecuyer.hups.DigitalNetFromFile DigitalNetFromFile}.
+ * The parameters used in building the net are those defined in class
+ * {@link umontreal.iro.lecuyer.hups.DigitalNetBase2 DigitalNetBase2}.
+ * The format of the data files must be the following (where <SPAN CLASS="MATH"><I>B</I></SPAN> is any <SPAN CLASS="MATH"><I>C</I><SUB>j</SUB></SPAN>):
+ * (see the format in <TT>guidehups.pdf</TT>)
+ * <DIV ALIGN="CENTER">
+ * 1#1
+ * </DIV>
+ *
+ * <P>
+ * For each dimension <SPAN CLASS="MATH"><I>j</I></SPAN>, there must be a <SPAN CLASS="MATH"><I>k</I></SPAN>-vector of 32-bit integers
+ * (the <SPAN CLASS="MATH"><I>a</I><SUB>i</SUB></SPAN>) corresponding to the columns of
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>. The
+ * correspondance is such that integer
+ *
+ * <SPAN CLASS="MATH"><I>a</I><SUB>i</SUB> = 2<SUP>30</SUP>(<B>C</B><SUB>j</SUB>)<SUB>1i</SUB> +2<SUP>29</SUP>(<B>C</B><SUB>j</SUB>)<SUB>2i</SUB> + <SUP> ... </SUP> +2<SUP>31-r</SUP>(<B>C</B><SUB>j</SUB>)<SUB>ri</SUB></SPAN>.
+ *
+ */
+public class DigitalNetBase2FromFile extends DigitalNetBase2 {
+ private String filename;
+
+ // s is the effective dimension if > 0, otherwise it is dim
+ private void readData (Reader re, int r1, int s1)
+ throws IOException, NumberFormatException
+ {
+ try {
+ StreamTokenizer st = new StreamTokenizer (re);
+ if (st == null) return;
+ st.eolIsSignificant (false);
+ st.slashSlashComments (true);
+
+ int i = st.nextToken ();
+ if (i != StreamTokenizer.TT_NUMBER)
+ throw new NumberFormatException();
+ b = (int) st.nval;
+ st.nextToken (); numCols = (int) st.nval;
+ st.nextToken (); numRows = (int) st.nval;
+ st.nextToken (); numPoints = (int) st.nval;
+ st.nextToken (); dim = (int) st.nval;
+ if (dim < 1) {
+ System.err.println (PrintfFormat.NEWLINE +
+ "DigitalNetBase2FromFile: dimension dim <= 0");
+ throw new IllegalArgumentException ("dimension dim <= 0");
+ }
+ if (r1 > numRows)
+ throw new IllegalArgumentException (
+ "DigitalNetBase2FromFile: One must have r1 <= Max num rows");
+ if (s1 > dim) {
+ throw new IllegalArgumentException ("s1 is too large");
+ }
+ if (s1 > 0)
+ dim = s1;
+ if (r1 > 0)
+ numRows = r1;
+
+ if (b != 2) {
+ System.err.println (
+ "***** DigitalNetBase2FromFile: only base 2 allowed");
+ throw new IllegalArgumentException ("only base 2 allowed");
+ }
+ genMat = new int[dim * numCols];
+ for (i = 0; i < dim; i++)
+ for (int c = 0; c < numCols; c++) {
+ st.nextToken ();
+ genMat[i*numCols + c] = (int) st.nval;
+ }
+
+ } catch (NumberFormatException e) {
+ System.err.println (
+ " DigitalNetBase2FromFile: not a number " + e);
+ throw e;
+ }
+ }
+
+
+ private void maskRows (int r, int w) {
+ // Keep only the r most significant bits and set the others to 0.
+ int mask = (int) ((1L << r) - 1);
+ mask <<= MAXBITS - r;
+ for (int i = 0; i < dim; i++)
+ for (int c = 0; c < numCols; c++) {
+ genMat[i*numCols + c] &= mask;
+ genMat[i*numCols + c] >>= MAXBITS - w;
+ }
+ }
+
+
+
+ /**
+ * Constructs a digital net in base 2 after reading its parameters from file
+ * <TT>filename</TT>. See the documentation in
+ * {@link umontreal.iro.lecuyer.hups.DigitalNetFromFile DigitalNetFromFile}.
+ * Parameter <TT>w</TT> gives the number of bits of resolution, <TT>r1</TT> is
+ * the number of rows, and <TT>s1</TT> is the dimension.
+ * Restrictions: <TT>s1</TT> must be less than the maximal dimension, and
+ * <TT>r1</TT> less than the maximal number of rows in the data file.
+ * Also <TT>w</TT> <SPAN CLASS="MATH"> >= </SPAN> <TT>r1</TT>.
+ *
+ * @param filename Name of the file to be read
+ *
+ * @param r1 Number of rows for the generating matrices
+ *
+ * @param w Number of bits of resolution
+ *
+ * @param s1 Number of dimensions
+ *
+ *
+ */
+ public DigitalNetBase2FromFile (String filename, int r1, int w, int s1)
+ throws IOException, MalformedURLException
+ {
+ super ();
+ if (w < r1 || w > MAXBITS)
+ throw new IllegalArgumentException (" Must have numRows <= w <= 31");
+
+ BufferedReader input;
+ if (filename.startsWith("http:") || filename.startsWith("ftp:"))
+ input = DigitalNetFromFile.openURL(filename);
+ else
+ input = DigitalNetFromFile.openFile(filename);
+
+ try {
+ readData (input, r1, s1);
+ } catch (NumberFormatException e) {
+ System.err.println (
+ " DigitalNetBase2FromFile: cannot read from " + filename);
+ throw e;
+
+ } catch (IOException e) {
+ System.err.println (
+ " DigitalNetBase2FromFile: cannot read from " + filename);
+ throw e;
+ }
+ input.close();
+ maskRows (numRows, w);
+ outDigits = w;
+ if (numCols >= MAXBITS)
+ throw new IllegalArgumentException (" Must have numCols < 31");
+
+ this.filename = filename;
+ int x = (1 << numCols);
+ if (x != numPoints) {
+ System.out.println ("numPoints != 2^k");
+ throw new IllegalArgumentException ("numPoints != 2^k");
+ }
+ // Compute the normalization factors.
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+
+ }
+
+
+ /**
+ * Same as {@link #DigitalNetBase2FromFile DigitalNetBase2FromFile}<TT>(filename, r, 31, s1)</TT> where
+ * <TT>s1</TT> is the dimension and <TT>r</TT> is given in data file <TT>filename</TT>.
+ *
+ * @param filename Name of the file to be read
+ *
+ * @param s1 Number of dimensions
+ *
+ */
+ public DigitalNetBase2FromFile (String filename, int s1)
+ throws IOException, MalformedURLException
+ {
+ this (filename, -1, 31, s1);
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("File: " + filename +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ /**
+ * Writes the parameters and the generating matrices of this digital net
+ * to a string.
+ * This is useful to check that the file parameters have been read correctly.
+ *
+ */
+ public String toStringDetailed() {
+ StringBuffer sb = new StringBuffer (toString() + PrintfFormat.NEWLINE);
+ sb.append ("dim = " + dim + PrintfFormat.NEWLINE);
+ for (int i = 0; i < dim; i++) {
+ sb.append (PrintfFormat.NEWLINE + "// dim = " + (1 + i) +
+ PrintfFormat.NEWLINE);
+ for (int c = 0; c < numCols; c++)
+ sb.append (genMat[i*numCols + c] + PrintfFormat.NEWLINE);
+ }
+ sb.append ("--------------------------------" + PrintfFormat.NEWLINE);
+ return sb.toString ();
+ }
+
+
+ /**
+ * Lists all files (or directories) in directory <TT>dirname</TT>. Only relative
+ * pathnames should be used. The files are parameter files used in defining
+ * digital nets. For example, calling <TT>listDir("")</TT> will give the list
+ * of the main data directory in SSJ, while calling <TT>listDir("Edel/OOA2")</TT>
+ * will give the list of all files in directory <TT>Edel/OOA2</TT>.
+ *
+ */
+ public static String listDir (String dirname) throws IOException {
+ return DigitalNetFromFile.listDir(dirname);
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNetBase2FromFile.tex b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2FromFile.tex
new file mode 100644
index 0000000..6f233f4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNetBase2FromFile.tex
@@ -0,0 +1,284 @@
+\defmodule {DigitalNetBase2FromFile}
+
+This class allows us to read the parameters defining a digital net
+ {\em in base 2\/} either from a file, or from a URL address on the
+ World Wide Web. See the documentation in
+\externalclass{umontreal.iro.lecuyer.hups}{DigitalNetFromFile}.
+The parameters used in building the net are those defined in class
+\externalclass{umontreal.iro.lecuyer.hups}{DigitalNetBase2}.
+ The format of the data files must be the following (where $B$ is any $C_j$):
+\begin{htmlonly} (see the format in \texttt{guidehups.pdf})\end{htmlonly}
+
+\begin{figure}
+\begin{center}
+\tt
+\fbox {
+\begin {tabular}{ll}
+ \multicolumn{2}{l}{// Any number of comment lines starting with //} \\
+ $2$ & // Base \\
+ $k$ & // Number of columns \\
+ $r$ & // Number of rows \\
+ $n$ & // Number of points = $2^k$ \\
+ $s$ & // Dimension of points \\
+\\
+ \multicolumn{2}{l}{// dim = 1} \\
+ $a_{1}$ & // $= 2^{30}B_{11} + 2^{29}B_{21} + \cdots + 2^{31 - r}B_{r1}$ \\
+ $a_{2}$ & // $= 2^{30}B_{12} + 2^{29}B_{22} + \cdots + 2^{31 - r}B_{r2}$ \\
+ $\vdots$ & \\
+ $a_{k}$ & \\
+\\
+ \multicolumn{2}{l}{// dim = 2} \\
+ $\vdots$ & \\
+\\
+ \multicolumn{2}{l}{// dim = $s$} \\
+ $a_{1}$ & \\
+ $a_{2}$ & \\
+ $\vdots$ &\\
+ $a_{k}$ & \\
+\end {tabular}
+}
+\end{center}
+\end{figure}
+
+ For each dimension $j$, there must be a $k$-vector of 32-bit integers
+ (the $a_i$) corresponding to the columns of $\mathbf{C}_j$. The
+ correspondance is such that integer
+ $a_i = 2^{30}(\mathbf{C}_j)_{1i} + 2^{29}(\mathbf{C}_j)_{2i} +
+ \cdots + 2^{31 - r}(\mathbf{C}_j)_{ri}$.
+
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DigitalNetBase2FromFile
+ * Description: read the parameters defining a digital net in base 2
+ from a file or from a URL address
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import java.io.*;
+import java.net.URL;
+import java.net.MalformedURLException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class DigitalNetBase2FromFile extends DigitalNetBase2 \begin{hide} {
+ private String filename;
+
+ // s is the effective dimension if > 0, otherwise it is dim
+ private void readData (Reader re, int r1, int s1)
+ throws IOException, NumberFormatException
+ {
+ try {
+ StreamTokenizer st = new StreamTokenizer (re);
+ if (st == null) return;
+ st.eolIsSignificant (false);
+ st.slashSlashComments (true);
+
+ int i = st.nextToken ();
+ if (i != StreamTokenizer.TT_NUMBER)
+ throw new NumberFormatException();
+ b = (int) st.nval;
+ st.nextToken (); numCols = (int) st.nval;
+ st.nextToken (); numRows = (int) st.nval;
+ st.nextToken (); numPoints = (int) st.nval;
+ st.nextToken (); dim = (int) st.nval;
+ if (dim < 1) {
+ System.err.println (PrintfFormat.NEWLINE +
+ "DigitalNetBase2FromFile: dimension dim <= 0");
+ throw new IllegalArgumentException ("dimension dim <= 0");
+ }
+ if (r1 > numRows)
+ throw new IllegalArgumentException (
+ "DigitalNetBase2FromFile: One must have r1 <= Max num rows");
+ if (s1 > dim) {
+ throw new IllegalArgumentException ("s1 is too large");
+ }
+ if (s1 > 0)
+ dim = s1;
+ if (r1 > 0)
+ numRows = r1;
+
+ if (b != 2) {
+ System.err.println (
+ "***** DigitalNetBase2FromFile: only base 2 allowed");
+ throw new IllegalArgumentException ("only base 2 allowed");
+ }
+ genMat = new int[dim * numCols];
+ for (i = 0; i < dim; i++)
+ for (int c = 0; c < numCols; c++) {
+ st.nextToken ();
+ genMat[i*numCols + c] = (int) st.nval;
+ }
+
+ } catch (NumberFormatException e) {
+ System.err.println (
+ " DigitalNetBase2FromFile: not a number " + e);
+ throw e;
+ }
+ }
+
+
+ private void maskRows (int r, int w) {
+ // Keep only the r most significant bits and set the others to 0.
+ int mask = (int) ((1L << r) - 1);
+ mask <<= MAXBITS - r;
+ for (int i = 0; i < dim; i++)
+ for (int c = 0; c < numCols; c++) {
+ genMat[i*numCols + c] &= mask;
+ genMat[i*numCols + c] >>= MAXBITS - w;
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public DigitalNetBase2FromFile (String filename, int r1, int w, int s1)
+ throws IOException, MalformedURLException\begin{hide}
+ {
+ super ();
+ if (w < r1 || w > MAXBITS)
+ throw new IllegalArgumentException (" Must have numRows <= w <= 31");
+
+ BufferedReader input;
+ if (filename.startsWith("http:") || filename.startsWith("ftp:"))
+ input = DigitalNetFromFile.openURL(filename);
+ else
+ input = DigitalNetFromFile.openFile(filename);
+
+ try {
+ readData (input, r1, s1);
+ } catch (NumberFormatException e) {
+ System.err.println (
+ " DigitalNetBase2FromFile: cannot read from " + filename);
+ throw e;
+
+ } catch (IOException e) {
+ System.err.println (
+ " DigitalNetBase2FromFile: cannot read from " + filename);
+ throw e;
+ }
+ input.close();
+ maskRows (numRows, w);
+ outDigits = w;
+ if (numCols >= MAXBITS)
+ throw new IllegalArgumentException (" Must have numCols < 31");
+
+ this.filename = filename;
+ int x = (1 << numCols);
+ if (x != numPoints) {
+ System.out.println ("numPoints != 2^k");
+ throw new IllegalArgumentException ("numPoints != 2^k");
+ }
+ // Compute the normalization factors.
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a digital net in base 2 after reading its parameters from file
+ {\texttt{filename}}. See the documentation in
+ \externalclass{umontreal.iro.lecuyer.hups}{DigitalNetFromFile}.
+ Parameter \texttt{w} gives the number of bits of resolution, \texttt{r1} is
+ the number of rows, and \texttt{s1} is the dimension.
+ Restrictions: \texttt{s1} must be less than the maximal dimension, and
+ \texttt{r1} less than the maximal number of rows in the data file.
+ Also \texttt{w} $\ge$ \texttt{r1}.
+\end{tabb}
+\begin{htmlonly}
+ \param{filename}{Name of the file to be read}
+ \param{r1}{Number of rows for the generating matrices}
+ \param{w}{Number of bits of resolution}
+ \param{s1}{Number of dimensions}
+\end{htmlonly}
+\begin{code}
+
+ public DigitalNetBase2FromFile (String filename, int s1)
+ throws IOException, MalformedURLException\begin{hide}
+ {
+ this (filename, -1, 31, s1);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{DigitalNetBase2FromFile}{}\texttt{(filename, r, 31, s1)} where
+ \texttt{s1} is the dimension and \texttt{r} is given in data file \texttt{filename}.
+\end{tabb}
+\begin{htmlonly}
+ \param{filename}{Name of the file to be read}
+ \param{s1}{Number of dimensions}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("File: " + filename +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }\end{hide}
+
+ public String toStringDetailed() \begin{hide} {
+ StringBuffer sb = new StringBuffer (toString() + PrintfFormat.NEWLINE);
+ sb.append ("dim = " + dim + PrintfFormat.NEWLINE);
+ for (int i = 0; i < dim; i++) {
+ sb.append (PrintfFormat.NEWLINE + "// dim = " + (1 + i) +
+ PrintfFormat.NEWLINE);
+ for (int c = 0; c < numCols; c++)
+ sb.append (genMat[i*numCols + c] + PrintfFormat.NEWLINE);
+ }
+ sb.append ("--------------------------------" + PrintfFormat.NEWLINE);
+ return sb.toString ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Writes the parameters and the generating matrices of this digital net
+ to a string.
+ This is useful to check that the file parameters have been read correctly.
+\end{tabb}
+\begin{code}
+
+ public static String listDir (String dirname) throws IOException \begin{hide} {
+ return DigitalNetFromFile.listDir(dirname);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Lists all files (or directories) in directory \texttt{dirname}. Only relative
+ pathnames should be used. The files are parameter files used in defining
+ digital nets. For example, calling \texttt{listDir("")} will give the list
+ of the main data directory in SSJ, while calling \texttt{listDir("Edel/OOA2")}
+ will give the list of all files in directory \texttt{Edel/OOA2}.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNetFromFile.java b/source/umontreal/iro/lecuyer/hups/DigitalNetFromFile.java
new file mode 100644
index 0000000..e504486
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNetFromFile.java
@@ -0,0 +1,596 @@
+
+
+/*
+ * Class: DigitalNetFromFile
+ * Description: read the parameters defining a digital net from a file
+ or from a URL address
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import java.io.*;
+import java.util.*;
+import java.net.URL;
+import java.net.MalformedURLException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class allows us to read the parameters defining a digital net either
+ * from a file, or from a URL address on the World Wide Web.
+ * The parameters used in building the net are those defined in class
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet DigitalNet}.
+ * The format of the data files must be the following:
+ * (see the format in <TT>guidehups.pdf</TT>)
+ * <DIV ALIGN="CENTER">
+ * 1#1
+ * </DIV>
+ *
+ * <P>
+ * The figure above gives the general format of the data file
+ * needed by <TT>DigitalNetFromFile</TT>.
+ * The values of the parameters on the left must appear in the file
+ * as integers. On the right of each parameter, there is an optional
+ * comment that is disregarded by the reader program. In general, the
+ * Java line comments <TT>//</TT> are accepted anywhere and will
+ * ensure that the rest of the line is dropped by the reader. Blank lines
+ * are also disregarded by the reader program. For each dimension, there must
+ * be a <SPAN CLASS="MATH"><I>k</I>×<I>r</I></SPAN> matrix of integers in
+ * <SPAN CLASS="MATH">{0, 1,…, <I>b</I> - 1}</SPAN> (note that
+ * the matrices must appear in transposed form).
+ *
+ * <P>
+ * The predefined files of parameters are kept in different directories,
+ * depending on the criteria used in the searches for the parameters defining
+ * the digital net. These files have all been stored at the address
+ * <TT><A NAME="tex2html1"
+ * HREF="http://simul.iro.umontreal.ca/ssj/data">http://simul.iro.umontreal.ca/ssj/data</A></TT>.
+ * Each file contains the parameters for a specific digital net.
+ * The name of the files gives information about the main parameters of
+ * the digital net. For example, the file named <TT>Edel/OOA2/B3S13R9C9St6</TT>
+ * contains the parameters for a digital net proposed by Yves Edel
+ * (see <TT><A NAME="tex2html2"
+ * HREF="http://www.mathi.uni-heidelberg.de/~yves/index.html">http://www.mathi.uni-heidelberg.de/~yves/index.html</A></TT>) based
+ * on ordered orthogonal arrays; the digital net has base <TT>B = 3</TT>,
+ * dimension <TT>S = 13</TT>, the generating matrices have <TT>R = 9</TT> rows
+ * and <TT>C = 9</TT> columns, and the strength of the net is <TT>St = 6</TT>.
+ *
+ */
+public class DigitalNetFromFile extends DigitalNet {
+ private String filename;
+
+ private void readMatrices (StreamTokenizer st,
+ int r, int k, int dim)
+ throws IOException, NumberFormatException {
+ // Read dim matrices with r rows and k columns.
+ // dim is the dimension of the digital net.
+ genMat = new int[dim * k][r];
+ for (int i = 0; i < dim; i++)
+ for (int c = 0; c < k; c++) {
+ for (int j = 0; j < r; j++) {
+ st.nextToken ();
+ genMat[i*numCols + c][j] = (int) st.nval;
+ }
+ // If we do not use all the rows, drop the unused ones.
+ for (int j = r; j < numRows; j++) {
+ st.nextToken ();
+ }
+ }
+ }
+
+
+ void readData (StreamTokenizer st) throws
+ IOException, NumberFormatException
+ {
+ // Read beginning of data file, but do not read the matrices
+ st.eolIsSignificant (false);
+ st.slashSlashComments (true);
+ int i = st.nextToken ();
+ if (i != StreamTokenizer.TT_NUMBER)
+ throw new NumberFormatException(" readData: cannot read base");
+ b = (int) st.nval;
+ st.nextToken (); numCols = (int) st.nval;
+ st.nextToken (); numRows = (int) st.nval;
+ st.nextToken (); numPoints = (int) st.nval;
+ st.nextToken (); dim = (int) st.nval;
+ if (dim < 1)
+ throw new IllegalArgumentException (" dimension dim <= 0");
+ }
+
+
+ static BufferedReader openURL (String filename)
+ throws MalformedURLException, IOException {
+ try {
+ URL url = new URL (filename);
+ BufferedReader input = new BufferedReader (
+ new InputStreamReader (
+ url.openStream()));
+ return input;
+
+ } catch (MalformedURLException e) {
+ System.err.println (e + " Invalid URL address: " + filename);
+ throw e;
+
+ } catch (IOException e) {
+ // This can receive a FileNotFoundException
+ System.err.println (e + " in openURL with " + filename);
+ throw e;
+ }
+ }
+
+ static BufferedReader openFile (String filename) throws
+ IOException {
+ try {
+ BufferedReader input;
+ File f = new File (filename);
+
+ // If file with relative path name exists, read it
+ if (f.exists()) {
+ if (f.isDirectory())
+ throw new IOException (filename + " is a directory");
+ input = new BufferedReader (new FileReader (filename));
+ } else { // else read it from ssj.jar
+ String pseudo = "umontreal/iro/lecuyer/hups/data/";
+ StringBuffer pathname = new StringBuffer (pseudo);
+ for (int ci = 0; ci < filename.length(); ci++) {
+ char ch = filename.charAt (ci);
+ if (ch == File.separatorChar)
+ pathname.append ('/');
+ else
+ pathname.append (ch);
+ }
+ InputStream dataInput =
+ DigitalNetFromFile.class.getClassLoader().getResourceAsStream (
+ pathname.toString());
+ if (dataInput == null)
+ throw new FileNotFoundException();
+ input = new BufferedReader (new InputStreamReader (dataInput));
+ }
+ return input;
+
+ } catch (FileNotFoundException e) {
+ System.err.println (e + " *** cannot find " + filename);
+ throw e;
+
+ } catch (IOException e) {
+ // This will never catch FileNotFoundException since there
+ // is a catch clause above.
+ System.err.println (e + " cannot read from " + filename);
+ throw e;
+ }
+ }
+
+
+
+
+ /**
+ * Constructs a digital net after reading its parameters from file
+ * <TT>filename</TT>. If a file named <TT>filename</TT>
+ * can be found relative to the program's directory, then the parameters
+ * will be read from this file; otherwise, they will be read from the file
+ * named <TT>filename</TT> in the <TT>ssj.jar</TT> archive.
+ * If <TT>filename</TT> is a URL string, it will be read on
+ * the World Wide Web.
+ * For example, to construct a digital net from the parameters in file
+ * <TT>B3S13R9C9St6</TT> in the current directory, one must give the string
+ * <TT>"B3S13R9C9St6"</TT> as argument to the constructor.
+ * As an example of a file read from the WWW, one may give
+ * as argument to the constructor the string
+ * <TT>
+ * "http://simul.iro.umontreal.ca/ssj/data/Edel/OOA3/B3S13R6C6St4"</TT>.
+ * Parameter <TT>w</TT> gives the number of digits of resolution, <TT>r1</TT> is
+ * the number of rows, and <TT>s1</TT> is the dimension.
+ * Restrictions: <TT>s1</TT> must be less than the maximal dimension, and
+ * <TT>r1</TT> less than the maximal number of rows in the data file.
+ * Also <TT>w</TT> <SPAN CLASS="MATH"> >= </SPAN> <TT>r1</TT>.
+ *
+ * @param filename Name of the file to be read
+ *
+ * @param r1 Number of rows for the generating matrices
+ *
+ * @param w Number of digits of resolution
+ *
+ * @param s1 Number of dimensions
+ *
+ *
+ */
+ public DigitalNetFromFile (String filename, int r1, int w, int s1)
+ throws MalformedURLException, IOException
+ {
+ super ();
+ BufferedReader input = null;
+ StreamTokenizer st = null;
+ try {
+ if (filename.startsWith("http:") || filename.startsWith("ftp:"))
+ input = openURL(filename);
+ else
+ input = openFile(filename);
+ st = new StreamTokenizer (input);
+ readData (st);
+
+ } catch (MalformedURLException e) {
+ System.err.println (" Invalid URL address: " + filename);
+ throw e;
+ } catch (FileNotFoundException e) {
+ System.err.println (" Cannot find " + filename);
+ throw e;
+ } catch (NumberFormatException e) {
+ System.err.println (" Cannot read number from " + filename);
+ throw e;
+ } catch (IOException e) {
+ System.err.println (" IOException: " + filename);
+ throw e;
+ }
+
+ if (b == 2) {
+ System.err.println (" base = 2, use DigitalNetBase2FromFile");
+ throw new IllegalArgumentException
+ ("base = 2, use DigitalNetBase2FromFile");
+ }
+ if ((double)numCols * Math.log ((double)b) > (31.0 * Math.log (2.0)))
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: too many points" + PrintfFormat.NEWLINE);
+ if (r1 > numRows)
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: One must have r1 <= Max num rows" +
+ PrintfFormat.NEWLINE);
+ if (s1 > dim)
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: One must have s1 <= Max dimension" +
+ PrintfFormat.NEWLINE);
+ if (w < 0) {
+ r1 = w = numRows;
+ s1 = dim;
+ }
+ if (w < numRows)
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: One must have w >= numRows" +
+ PrintfFormat.NEWLINE);
+
+ try {
+ readMatrices (st, r1, numCols, s1);
+ } catch (NumberFormatException e) {
+ System.err.println (e + " cannot read matrices from " + filename);
+ throw e;
+ } catch (IOException e) {
+ System.err.println (e + " cannot read matrices from " + filename);
+ throw e;
+ }
+ input.close();
+
+ this.filename = filename;
+ numRows = r1;
+ dim = s1;
+ outDigits = w;
+ int x = b;
+ for (int i=1; i<numCols; i++) x *= b;
+ if (x != numPoints) {
+ System.out.println ("DigitalNetFromFile: numPoints != b^k");
+ throw new IllegalArgumentException (" numPoints != b^k");
+ }
+
+ // Compute the normalization factors.
+ normFactor = 1.0 / Math.pow ((double) b, (double) outDigits);
+ double invb = 1.0 / b;
+ factor = new double[outDigits];
+ factor[0] = invb;
+ for (int j = 1; j < outDigits; j++)
+ factor[j] = factor[j-1] * invb;
+ }
+
+
+ /**
+ * Same as {@link #DigitalNetFromFile DigitalNetFromFile}<TT>(filename, r, r, s)</TT> where
+ * <TT>s</TT> is the dimension and <TT>r</TT> is given in data file <TT>filename</TT>.
+ *
+ * @param filename Name of the file to be read
+ *
+ * @param s Number of dimensions
+ *
+ */
+ public DigitalNetFromFile (String filename, int s)
+ throws MalformedURLException, IOException
+ {
+ this (filename, -1, -1, s);
+ }
+
+ DigitalNetFromFile ()
+ {
+ super ();
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("File: " + filename +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ /**
+ * Writes the parameters and the generating matrices of this digital net
+ * to a string. This is useful to check that the file parameters have been
+ * read correctly.
+ *
+ */
+ public String toStringDetailed() {
+ StringBuffer sb = new StringBuffer (toString());
+ sb.append (PrintfFormat.NEWLINE + "n = " + numPoints +
+ PrintfFormat.NEWLINE);
+ sb.append ("dim = " + dim + PrintfFormat.NEWLINE);
+ for (int i = 0; i < dim; i++) {
+ sb.append (PrintfFormat.NEWLINE + " // dim = " + (1 + i) +
+ PrintfFormat.NEWLINE);
+ for (int c = 0; c < numCols; c++) {
+ for (int r = 0; r < numRows; r++)
+ sb.append (genMat[i*numCols + c][r] + " ");
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ }
+ return sb.toString ();
+ }
+
+
+ static class NetComparator implements Comparator {
+ // Used to sort list of nets. Sort first by base, then by dimension,
+ // then by the number of rows. Don't forget that base = 4 are in files
+ // named B4_2* and that the computations are done in base 2.
+ public int compare (Object o1, Object o2) {
+ DigitalNetFromFile net1 = (DigitalNetFromFile) o1;
+ DigitalNetFromFile net2 = (DigitalNetFromFile) o2;
+ if (net1.b < net2.b)
+ return -1;
+ if (net1.b > net2.b)
+ return 1;
+ if (net1.filename.indexOf("_") >= 0 &&
+ net2.filename.indexOf("_") < 0 )
+ return 1;
+ if (net2.filename.indexOf("_") >= 0 &&
+ net1.filename.indexOf("_") < 0 )
+ return -1;
+ if (net1.dim < net2.dim)
+ return -1;
+ if (net1.dim > net2.dim)
+ return 1;
+ if (net1.numRows < net2.numRows)
+ return -1;
+ if (net1.numRows > net2.numRows)
+ return 1;
+ return 0;
+ }
+ }
+
+
+ private static List getListDir (String dirname) throws IOException {
+ try {
+ String pseudo = "umontreal/iro/lecuyer/hups/data/";
+ StringBuffer pathname = new StringBuffer (pseudo);
+ for (int ci = 0; ci < dirname.length(); ci++) {
+ char ch = dirname.charAt (ci);
+ if (ch == File.separatorChar)
+ pathname.append ('/');
+ else
+ pathname.append (ch);
+ }
+ URL url = DigitalNetFromFile.class.getClassLoader().getResource (
+ pathname.toString());
+ File dir = new File (url.getPath());
+ if (!dir.isDirectory())
+ throw new IllegalArgumentException (
+ dirname + " is not a directory");
+ File[] files = dir.listFiles();
+ List alist = new ArrayList (200);
+ if (!dirname.endsWith (File.separator))
+ dirname += File.separator;
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory())
+ continue;
+ if (files[i].getName().endsWith ("gz") ||
+ files[i].getName().endsWith ("zip"))
+ continue;
+ DigitalNetFromFile net = new DigitalNetFromFile();
+ BufferedReader input = net.openFile(dirname + files[i].getName());
+ StreamTokenizer st = new StreamTokenizer (input);
+ net.readData (st);
+ net.filename = files[i].getName();
+ alist.add (net);
+ }
+ if (alist != null && !files[0].isDirectory())
+ Collections.sort (alist, new NetComparator ());
+ return alist;
+
+ } catch (NullPointerException e) {
+ System.err.println ("getListDir: cannot find directory " + dirname);
+ throw e;
+
+ } catch (NumberFormatException e) {
+ System.err.println (e + "*** cannot read number ");
+ throw e;
+
+ } catch (IOException e) {
+ System.err.println (e);
+ throw e;
+ }
+ }
+
+
+ private static String listFiles (String dirname) {
+ try {
+ String pseudo = "umontreal/iro/lecuyer/hups/data/";
+ StringBuffer pathname = new StringBuffer (pseudo);
+ for (int ci = 0; ci < dirname.length(); ci++) {
+ char ch = dirname.charAt (ci);
+ if (ch == File.separatorChar)
+ pathname.append ('/');
+ else
+ pathname.append (ch);
+ }
+ URL url = DigitalNetFromFile.class.getClassLoader().getResource (
+ pathname.toString());
+ File dir = new File (url.getPath());
+ File[] list = dir.listFiles();
+ List alist = new ArrayList (200);
+ final int NPRI = 3;
+ StringBuffer sb = new StringBuffer(1000);
+ for (int i = 0; i < list.length; i++) {
+ if (list[i].isDirectory()) {
+ sb.append (PrintfFormat.s(-2, list[i].getName()));
+ sb.append (File.separator + PrintfFormat.NEWLINE);
+ } else {
+ sb.append (PrintfFormat.s(-25, list[i].getName()));
+ if (i % NPRI == 2)
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ }
+ if (list.length % NPRI > 0)
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+
+ } catch (NullPointerException e) {
+ System.err.println ("listFiles: cannot find directory " + dirname);
+ throw e;
+ }
+ }
+
+ /**
+ * Lists all files (or directories) in directory <TT>dirname</TT>. Only relative
+ * pathnames should be used. The files are parameter files used in defining
+ * digital nets. For example, calling <TT>listDir("")</TT> will give the list
+ * of the main data directory in SSJ, while calling <TT>listDir("Edel/OOA2")</TT>
+ * will give the list of all files in directory <TT>Edel/OOA2</TT>.
+ *
+ */
+ public static String listDir (String dirname) throws IOException {
+ try {
+ List list = getListDir (dirname);
+ if (list == null || list.size() == 0)
+ return listFiles (dirname);
+ StringBuffer sb = new StringBuffer(1000);
+
+ sb.append ("Directory: " + dirname + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+ sb.append (PrintfFormat.s(-25, " File") +
+ PrintfFormat.s(-15, " Base") +
+ PrintfFormat.s(-10, "Dimension") +
+ PrintfFormat.s(-10, " numRows") +
+ PrintfFormat.s(-10, "numColumns" +
+ PrintfFormat.NEWLINE));
+ int base = 0;
+ for (int i = 0; i < list.size(); i++) {
+ DigitalNet net = (DigitalNet) list.get(i);
+ int j = ((DigitalNetFromFile)net).filename.lastIndexOf
+ (File.separator);
+ if (net.b != base) {
+ sb.append (
+ "----------------------------------------------------------------------"
+ + PrintfFormat.NEWLINE);
+ base = net.b;
+ }
+ String name = ((DigitalNetFromFile)net).filename.substring(j+1);
+ sb.append (PrintfFormat.s(-25, name) +
+ PrintfFormat.d(10, net.b) +
+ PrintfFormat.d(10, net.dim) +
+ PrintfFormat.d(10, net.numRows) +
+ PrintfFormat.d(10, net.numCols) +
+ PrintfFormat.NEWLINE);
+ }
+ return sb.toString();
+
+ } catch (NullPointerException e) {
+ System.err.println (
+ "formatPlain: cannot find directory " + dirname);
+ throw e;
+ }
+ }
+
+
+ /**
+ * Creates a list of all data files in directory <TT>dirname</TT> and writes
+ * that list in format HTML in output file <TT>filename</TT>.
+ * Each data file contains the parameters required to build a digital net.
+ * The resulting list contains a line for each data file giving the
+ * name of the file, the base, the dimension, the number of rows and
+ * the number of columns of the corresponding digital net.
+ *
+ */
+ public static void listDirHTML (String dirname, String filename)
+ throws IOException {
+ String list = listDir(dirname);
+ StreamTokenizer st = new StreamTokenizer (new StringReader(list));
+ st.eolIsSignificant(true);
+ st.ordinaryChar('/');
+ st.ordinaryChar('_');
+ st.ordinaryChar('-');
+ st.wordChars('-', '-');
+ st.wordChars('_', '_');
+ st.slashSlashComments(false);
+ st.slashStarComments(false);
+ PrintWriter out = new PrintWriter (
+ new BufferedWriter (
+ new FileWriter (filename)));
+ out.println ("<html>" + PrintfFormat.NEWLINE +
+ "<head>" + PrintfFormat.NEWLINE + "<title>");
+ while (st.nextToken () != st.TT_EOL)
+ ;
+ out.println ( PrintfFormat.NEWLINE + "</title>" +
+ PrintfFormat.NEWLINE + "</head>");
+// out.println ("<body background bgcolor=#e1eae8 vlink=#c00000>");
+ out.println ("<body>");
+ out.println ("<table border>");
+ out.println ("<caption> Directory: " + dirname + "</caption>");
+ st.nextToken(); st.nextToken();
+ while (st.sval.compareTo ("File") != 0)
+ st.nextToken();
+ out.print ("<tr align=center><th>" + st.sval + "</th>");
+ while (st.nextToken () != st.TT_EOL) {
+ out.print ("<th>" + st.sval + "</th>" );
+ }
+ out.println ("</tr>" + PrintfFormat.NEWLINE);
+ while (st.nextToken () != st.TT_EOF) {
+ switch(st.ttype) {
+ case StreamTokenizer.TT_EOL:
+ out.println ("</tr>");
+ break;
+ case StreamTokenizer.TT_NUMBER:
+ out.print ("<td>" + (int) st.nval + "</td>" );
+ break;
+ case StreamTokenizer.TT_WORD:
+ if (st.sval.indexOf ("---") >= 0) {
+ st.nextToken ();
+ continue;
+ }
+ out.print ("<tr align=center><td>" + st.sval + "</td>");
+ break;
+ default:
+ out.print (st.sval);
+ break;
+ }
+ }
+
+ out.println ("</table>");
+ out.println ("</body>" + PrintfFormat.NEWLINE + "</html>");
+ out.close();
+}
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalNetFromFile.tex b/source/umontreal/iro/lecuyer/hups/DigitalNetFromFile.tex
new file mode 100644
index 0000000..8a95e75
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalNetFromFile.tex
@@ -0,0 +1,660 @@
+\defmodule {DigitalNetFromFile}
+
+This class allows us to read the parameters defining a digital net either
+from a file, or from a URL address on the World Wide Web.
+The parameters used in building the net are those defined in class
+\externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}.
+The format of the data files must be the following:
+\begin{htmlonly} (see the format in \texttt{guidehups.pdf})\end{htmlonly}
+
+\begin{figure}
+\begin{center}
+\tt
+\fbox {
+\begin {tabular}{llll}
+ \multicolumn{4}{l}{// Any number of comment lines starting with //} \\
+ $b$ & & & // $\mbox{Base}$ \\
+ $k$ & & & // Number of columns \\
+ $r$ & & & // Maximal number of rows \\
+ $n$ & & & // Number of points = $b^k$ \\
+ $s$ & & & // Maximal dimension of points \\
+\\
+ \multicolumn{4}{l}{// dim = 1} \\
+ $c_{11}$ & $c_{21}$ & $\cdots$ & $c_{r1}$ \\
+ $c_{12}$ & $c_{22}$ & $\cdots$ & $c_{r2}$ \\
+ & $\vdots$ && \\
+ $c_{1k}$ & $c_{2k}$ & $\cdots$ & $c_{rk}$ \\
+\\
+ \multicolumn{4}{l}{// dim = 2} \\
+ & $\vdots$ && \\
+\\
+ \multicolumn{4}{l}{// dim = $s$} \\
+ $c_{11}$ & $c_{21}$ & $\cdots$ & $c_{r1}$ \\
+ $c_{12}$ & $c_{22}$ & $\cdots$ & $c_{r2}$ \\
+ & $\vdots$ && \\
+ $c_{1k}$ & $c_{2k}$ & $\cdots$ & $c_{rk}$ \\
+\end {tabular}
+}
+\end{center}
+%\caption { General format of the parameter file for
+% \externalclass{umontreal.iro.lecuyer.hups}{DigitalNetFromFile}.
+%\label{formatdon}}
+\end{figure}
+
+The figure above gives the general format of the data file
+needed by \texttt{DigitalNetFromFile}.
+The values of the parameters on the left must appear in the file
+as integers. On the right of each parameter, there is an optional
+ comment that is disregarded by the reader program. In general, the
+ Java line comments \texttt{//} are accepted anywhere and will
+ensure that the rest of the line is dropped by the reader. Blank lines
+are also disregarded by the reader program. For each dimension, there must
+ be a $k\times r$ matrix of integers in $\{0, 1, \ldots, b-1\}$ (note that
+the matrices must appear in transposed form).
+
+The predefined files of parameters are kept in different directories,
+depending on the criteria used in the searches for the parameters defining
+the digital net. These files have all been stored at the address
+ \url{http://simul.iro.umontreal.ca/ssj/data}.
+ Each file contains the parameters for a specific digital net.
+% One may get a list of all available files in a directory by using
+% method \method{listDir}{} below.
+The name of the files gives information about the main parameters of
+the digital net. For example, the file named \texttt{Edel/OOA2/B3S13R9C9St6}
+ contains the parameters for a digital net proposed by Yves Edel
+(see \url{http://www.mathi.uni-heidelberg.de/~yves/index.html}) based
+on ordered orthogonal arrays; the digital net has base \texttt{B = 3},
+dimension \texttt{S = 13}, the generating matrices have \texttt{R = 9} rows
+and \texttt{C = 9} columns, and the strength of the net is \texttt{St = 6}.
+%At the moment, there are no existing subdirectories of predefined files in SSJ.
+\iffalse
+At the moment, the existing subdirectories of predefined files in SSJ
+are the following
+ (for details on the available files,
+ see \url{http://simul.iro.umontreal.ca/ssj/data})
+(in OOA, O is the letter O, not the number 0):
+\begin {table}[htb]
+\begin{center}
+% \caption {\label {tab:datadir1}}
+\begin {tabular}{|l|l|l|}
+\hline
+ $\mbox{Directory}$ & $\mbox{Remark}$ & $\mbox{Reference}$ \\
+\hline
+ \texttt{Edel/OOA2/} & Based on orthogonal ordered arrays & Yves Edel \\
+ \texttt{Edel/OOA3/} & Based on orthogonal ordered arrays & Yves Edel \\
+ \texttt{Edel/OOA4/} & Based on orthogonal ordered arrays & Yves Edel \\
+ \texttt{Edel/RSNet/} & Maximally equidistributed-collision free
+ \cite{rLEC99a} & Yves Edel \\
+\hline
+\end {tabular}
+\label {tab:datadir1}
+\end{center}
+\end {table}
+\fi
+
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DigitalNetFromFile
+ * Description: read the parameters defining a digital net from a file
+ or from a URL address
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import java.io.*;
+import java.util.*;
+import java.net.URL;
+import java.net.MalformedURLException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class DigitalNetFromFile extends DigitalNet \begin{hide} {
+ private String filename;
+
+ private void readMatrices (StreamTokenizer st,
+ int r, int k, int dim)
+ throws IOException, NumberFormatException {
+ // Read dim matrices with r rows and k columns.
+ // dim is the dimension of the digital net.
+ genMat = new int[dim * k][r];
+ for (int i = 0; i < dim; i++)
+ for (int c = 0; c < k; c++) {
+ for (int j = 0; j < r; j++) {
+ st.nextToken ();
+ genMat[i*numCols + c][j] = (int) st.nval;
+ }
+ // If we do not use all the rows, drop the unused ones.
+ for (int j = r; j < numRows; j++) {
+ st.nextToken ();
+ }
+ }
+ }
+
+
+ void readData (StreamTokenizer st) throws
+ IOException, NumberFormatException
+ {
+ // Read beginning of data file, but do not read the matrices
+ st.eolIsSignificant (false);
+ st.slashSlashComments (true);
+ int i = st.nextToken ();
+ if (i != StreamTokenizer.TT_NUMBER)
+ throw new NumberFormatException(" readData: cannot read base");
+ b = (int) st.nval;
+ st.nextToken (); numCols = (int) st.nval;
+ st.nextToken (); numRows = (int) st.nval;
+ st.nextToken (); numPoints = (int) st.nval;
+ st.nextToken (); dim = (int) st.nval;
+ if (dim < 1)
+ throw new IllegalArgumentException (" dimension dim <= 0");
+ }
+
+
+ static BufferedReader openURL (String filename)
+ throws MalformedURLException, IOException {
+ try {
+ URL url = new URL (filename);
+ BufferedReader input = new BufferedReader (
+ new InputStreamReader (
+ url.openStream()));
+ return input;
+
+ } catch (MalformedURLException e) {
+ System.err.println (e + " Invalid URL address: " + filename);
+ throw e;
+
+ } catch (IOException e) {
+ // This can receive a FileNotFoundException
+ System.err.println (e + " in openURL with " + filename);
+ throw e;
+ }
+ }
+
+ static BufferedReader openFile (String filename) throws
+ IOException {
+ try {
+ BufferedReader input;
+ File f = new File (filename);
+
+ // If file with relative path name exists, read it
+ if (f.exists()) {
+ if (f.isDirectory())
+ throw new IOException (filename + " is a directory");
+ input = new BufferedReader (new FileReader (filename));
+ } else { // else read it from ssj.jar
+ String pseudo = "umontreal/iro/lecuyer/hups/data/";
+ StringBuffer pathname = new StringBuffer (pseudo);
+ for (int ci = 0; ci < filename.length(); ci++) {
+ char ch = filename.charAt (ci);
+ if (ch == File.separatorChar)
+ pathname.append ('/');
+ else
+ pathname.append (ch);
+ }
+ InputStream dataInput =
+ DigitalNetFromFile.class.getClassLoader().getResourceAsStream (
+ pathname.toString());
+ if (dataInput == null)
+ throw new FileNotFoundException();
+ input = new BufferedReader (new InputStreamReader (dataInput));
+ }
+ return input;
+
+ } catch (FileNotFoundException e) {
+ System.err.println (e + " *** cannot find " + filename);
+ throw e;
+
+ } catch (IOException e) {
+ // This will never catch FileNotFoundException since there
+ // is a catch clause above.
+ System.err.println (e + " cannot read from " + filename);
+ throw e;
+ }
+ }
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public DigitalNetFromFile (String filename, int r1, int w, int s1)
+ throws MalformedURLException, IOException \begin{hide}
+ {
+ super ();
+ BufferedReader input = null;
+ StreamTokenizer st = null;
+ try {
+ if (filename.startsWith("http:") || filename.startsWith("ftp:"))
+ input = openURL(filename);
+ else
+ input = openFile(filename);
+ st = new StreamTokenizer (input);
+ readData (st);
+
+ } catch (MalformedURLException e) {
+ System.err.println (" Invalid URL address: " + filename);
+ throw e;
+ } catch (FileNotFoundException e) {
+ System.err.println (" Cannot find " + filename);
+ throw e;
+ } catch (NumberFormatException e) {
+ System.err.println (" Cannot read number from " + filename);
+ throw e;
+ } catch (IOException e) {
+ System.err.println (" IOException: " + filename);
+ throw e;
+ }
+
+ if (b == 2) {
+ System.err.println (" base = 2, use DigitalNetBase2FromFile");
+ throw new IllegalArgumentException
+ ("base = 2, use DigitalNetBase2FromFile");
+ }
+ if ((double)numCols * Math.log ((double)b) > (31.0 * Math.log (2.0)))
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: too many points" + PrintfFormat.NEWLINE);
+ if (r1 > numRows)
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: One must have r1 <= Max num rows" +
+ PrintfFormat.NEWLINE);
+ if (s1 > dim)
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: One must have s1 <= Max dimension" +
+ PrintfFormat.NEWLINE);
+ if (w < 0) {
+ r1 = w = numRows;
+ s1 = dim;
+ }
+ if (w < numRows)
+ throw new IllegalArgumentException
+ ("DigitalNetFromFile: One must have w >= numRows" +
+ PrintfFormat.NEWLINE);
+
+ try {
+ readMatrices (st, r1, numCols, s1);
+ } catch (NumberFormatException e) {
+ System.err.println (e + " cannot read matrices from " + filename);
+ throw e;
+ } catch (IOException e) {
+ System.err.println (e + " cannot read matrices from " + filename);
+ throw e;
+ }
+ input.close();
+
+ this.filename = filename;
+ numRows = r1;
+ dim = s1;
+ outDigits = w;
+ int x = b;
+ for (int i=1; i<numCols; i++) x *= b;
+ if (x != numPoints) {
+ System.out.println ("DigitalNetFromFile: numPoints != b^k");
+ throw new IllegalArgumentException (" numPoints != b^k");
+ }
+
+ // Compute the normalization factors.
+ normFactor = 1.0 / Math.pow ((double) b, (double) outDigits);
+ double invb = 1.0 / b;
+ factor = new double[outDigits];
+ factor[0] = invb;
+ for (int j = 1; j < outDigits; j++)
+ factor[j] = factor[j-1] * invb;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a digital net after reading its parameters from file
+ {\texttt{filename}}. If a file named \texttt{filename}
+ can be found relative to the program's directory, then the parameters
+ will be read from this file; otherwise, they will be read from the file
+ named \texttt{filename} in the \texttt{ssj.jar} archive.
+ If {\texttt{filename}} is a URL string, it will be read on
+ the World Wide Web.
+ For example, to construct a digital net from the parameters in file
+ \texttt{B3S13R9C9St6} in the current directory, one must give the string
+ \texttt{"B3S13R9C9St6"} as argument to the constructor.
+ As an example of a file read from the WWW, one may give
+ as argument to the constructor the string
+ \texttt{
+ "http://simul.iro.umontreal.ca/ssj/data/Edel/OOA3/B3S13R6C6St4"}.
+ Parameter \texttt{w} gives the number of digits of resolution, \texttt{r1} is
+ the number of rows, and \texttt{s1} is the dimension.
+ Restrictions: \texttt{s1} must be less than the maximal dimension, and
+ \texttt{r1} less than the maximal number of rows in the data file.
+ Also \texttt{w} $\ge$ \texttt{r1}.
+\end{tabb}
+\begin{htmlonly}
+ \param{filename}{Name of the file to be read}
+ \param{r1}{Number of rows for the generating matrices}
+ \param{w}{Number of digits of resolution}
+ \param{s1}{Number of dimensions}
+\end{htmlonly}
+\begin{code}
+
+ public DigitalNetFromFile (String filename, int s)
+ throws MalformedURLException, IOException \begin{hide}
+ {
+ this (filename, -1, -1, s);
+ }
+
+ DigitalNetFromFile ()
+ {
+ super ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{DigitalNetFromFile}{}\texttt{(filename, r, r, s)} where
+ \texttt{s} is the dimension and \texttt{r} is given in data file \texttt{filename}.
+\end{tabb}
+\begin{htmlonly}
+ \param{filename}{Name of the file to be read}
+ \param{s}{Number of dimensions}
+\end{htmlonly}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("File: " + filename +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }\end{hide}
+
+ public String toStringDetailed() \begin{hide} {
+ StringBuffer sb = new StringBuffer (toString());
+ sb.append (PrintfFormat.NEWLINE + "n = " + numPoints +
+ PrintfFormat.NEWLINE);
+ sb.append ("dim = " + dim + PrintfFormat.NEWLINE);
+ for (int i = 0; i < dim; i++) {
+ sb.append (PrintfFormat.NEWLINE + " // dim = " + (1 + i) +
+ PrintfFormat.NEWLINE);
+ for (int c = 0; c < numCols; c++) {
+ for (int r = 0; r < numRows; r++)
+ sb.append (genMat[i*numCols + c][r] + " ");
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ }
+ return sb.toString ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Writes the parameters and the generating matrices of this digital net
+ to a string. This is useful to check that the file parameters have been
+ read correctly.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ static class NetComparator implements Comparator {
+ // Used to sort list of nets. Sort first by base, then by dimension,
+ // then by the number of rows. Don't forget that base = 4 are in files
+ // named B4_2* and that the computations are done in base 2.
+ public int compare (Object o1, Object o2) {
+ DigitalNetFromFile net1 = (DigitalNetFromFile) o1;
+ DigitalNetFromFile net2 = (DigitalNetFromFile) o2;
+ if (net1.b < net2.b)
+ return -1;
+ if (net1.b > net2.b)
+ return 1;
+ if (net1.filename.indexOf("_") >= 0 &&
+ net2.filename.indexOf("_") < 0 )
+ return 1;
+ if (net2.filename.indexOf("_") >= 0 &&
+ net1.filename.indexOf("_") < 0 )
+ return -1;
+ if (net1.dim < net2.dim)
+ return -1;
+ if (net1.dim > net2.dim)
+ return 1;
+ if (net1.numRows < net2.numRows)
+ return -1;
+ if (net1.numRows > net2.numRows)
+ return 1;
+ return 0;
+ }
+ }
+
+
+ private static List getListDir (String dirname) throws IOException {
+ try {
+ String pseudo = "umontreal/iro/lecuyer/hups/data/";
+ StringBuffer pathname = new StringBuffer (pseudo);
+ for (int ci = 0; ci < dirname.length(); ci++) {
+ char ch = dirname.charAt (ci);
+ if (ch == File.separatorChar)
+ pathname.append ('/');
+ else
+ pathname.append (ch);
+ }
+ URL url = DigitalNetFromFile.class.getClassLoader().getResource (
+ pathname.toString());
+ File dir = new File (url.getPath());
+ if (!dir.isDirectory())
+ throw new IllegalArgumentException (
+ dirname + " is not a directory");
+ File[] files = dir.listFiles();
+ List alist = new ArrayList (200);
+ if (!dirname.endsWith (File.separator))
+ dirname += File.separator;
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory())
+ continue;
+ if (files[i].getName().endsWith ("gz") ||
+ files[i].getName().endsWith ("zip"))
+ continue;
+ DigitalNetFromFile net = new DigitalNetFromFile();
+ BufferedReader input = net.openFile(dirname + files[i].getName());
+ StreamTokenizer st = new StreamTokenizer (input);
+ net.readData (st);
+ net.filename = files[i].getName();
+ alist.add (net);
+ }
+ if (alist != null && !files[0].isDirectory())
+ Collections.sort (alist, new NetComparator ());
+ return alist;
+
+ } catch (NullPointerException e) {
+ System.err.println ("getListDir: cannot find directory " + dirname);
+ throw e;
+
+ } catch (NumberFormatException e) {
+ System.err.println (e + "*** cannot read number ");
+ throw e;
+
+ } catch (IOException e) {
+ System.err.println (e);
+ throw e;
+ }
+ }
+
+
+ private static String listFiles (String dirname) {
+ try {
+ String pseudo = "umontreal/iro/lecuyer/hups/data/";
+ StringBuffer pathname = new StringBuffer (pseudo);
+ for (int ci = 0; ci < dirname.length(); ci++) {
+ char ch = dirname.charAt (ci);
+ if (ch == File.separatorChar)
+ pathname.append ('/');
+ else
+ pathname.append (ch);
+ }
+ URL url = DigitalNetFromFile.class.getClassLoader().getResource (
+ pathname.toString());
+ File dir = new File (url.getPath());
+ File[] list = dir.listFiles();
+ List alist = new ArrayList (200);
+ final int NPRI = 3;
+ StringBuffer sb = new StringBuffer(1000);
+ for (int i = 0; i < list.length; i++) {
+ if (list[i].isDirectory()) {
+ sb.append (PrintfFormat.s(-2, list[i].getName()));
+ sb.append (File.separator + PrintfFormat.NEWLINE);
+ } else {
+ sb.append (PrintfFormat.s(-25, list[i].getName()));
+ if (i % NPRI == 2)
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ }
+ if (list.length % NPRI > 0)
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+
+ } catch (NullPointerException e) {
+ System.err.println ("listFiles: cannot find directory " + dirname);
+ throw e;
+ }
+ }\end{hide}
+
+ public static String listDir (String dirname) throws IOException \begin{hide} {
+ try {
+ List list = getListDir (dirname);
+ if (list == null || list.size() == 0)
+ return listFiles (dirname);
+ StringBuffer sb = new StringBuffer(1000);
+
+ sb.append ("Directory: " + dirname + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+ sb.append (PrintfFormat.s(-25, " File") +
+ PrintfFormat.s(-15, " Base") +
+ PrintfFormat.s(-10, "Dimension") +
+ PrintfFormat.s(-10, " numRows") +
+ PrintfFormat.s(-10, "numColumns" +
+ PrintfFormat.NEWLINE));
+ int base = 0;
+ for (int i = 0; i < list.size(); i++) {
+ DigitalNet net = (DigitalNet) list.get(i);
+ int j = ((DigitalNetFromFile)net).filename.lastIndexOf
+ (File.separator);
+ if (net.b != base) {
+ sb.append (
+ "----------------------------------------------------------------------"
+ + PrintfFormat.NEWLINE);
+ base = net.b;
+ }
+ String name = ((DigitalNetFromFile)net).filename.substring(j+1);
+ sb.append (PrintfFormat.s(-25, name) +
+ PrintfFormat.d(10, net.b) +
+ PrintfFormat.d(10, net.dim) +
+ PrintfFormat.d(10, net.numRows) +
+ PrintfFormat.d(10, net.numCols) +
+ PrintfFormat.NEWLINE);
+ }
+ return sb.toString();
+
+ } catch (NullPointerException e) {
+ System.err.println (
+ "formatPlain: cannot find directory " + dirname);
+ throw e;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Lists all files (or directories) in directory \texttt{dirname}. Only relative
+ pathnames should be used. The files are parameter files used in defining
+ digital nets. For example, calling \texttt{listDir("")} will give the list
+ of the main data directory in SSJ, while calling \texttt{listDir("Edel/OOA2")}
+ will give the list of all files in directory \texttt{Edel/OOA2}.
+ \end{tabb}
+\begin{code}
+
+ public static void listDirHTML (String dirname, String filename)
+ throws IOException \begin{hide} {
+ String list = listDir(dirname);
+ StreamTokenizer st = new StreamTokenizer (new StringReader(list));
+ st.eolIsSignificant(true);
+ st.ordinaryChar('/');
+ st.ordinaryChar('_');
+ st.ordinaryChar('-');
+ st.wordChars('-', '-');
+ st.wordChars('_', '_');
+ st.slashSlashComments(false);
+ st.slashStarComments(false);
+ PrintWriter out = new PrintWriter (
+ new BufferedWriter (
+ new FileWriter (filename)));
+ out.println ("<html>" + PrintfFormat.NEWLINE +
+ "<head>" + PrintfFormat.NEWLINE + "<title>");
+ while (st.nextToken () != st.TT_EOL)
+ ;
+ out.println ( PrintfFormat.NEWLINE + "</title>" +
+ PrintfFormat.NEWLINE + "</head>");
+// out.println ("<body background bgcolor=#e1eae8 vlink=#c00000>");
+ out.println ("<body>");
+ out.println ("<table border>");
+ out.println ("<caption> Directory: " + dirname + "</caption>");
+ st.nextToken(); st.nextToken();
+ while (st.sval.compareTo ("File") != 0)
+ st.nextToken();
+ out.print ("<tr align=center><th>" + st.sval + "</th>");
+ while (st.nextToken () != st.TT_EOL) {
+ out.print ("<th>" + st.sval + "</th>" );
+ }
+ out.println ("</tr>" + PrintfFormat.NEWLINE);
+ while (st.nextToken () != st.TT_EOF) {
+ switch(st.ttype) {
+ case StreamTokenizer.TT_EOL:
+ out.println ("</tr>");
+ break;
+ case StreamTokenizer.TT_NUMBER:
+ out.print ("<td>" + (int) st.nval + "</td>" );
+ break;
+ case StreamTokenizer.TT_WORD:
+ if (st.sval.indexOf ("---") >= 0) {
+ st.nextToken ();
+ continue;
+ }
+ out.print ("<tr align=center><td>" + st.sval + "</td>");
+ break;
+ default:
+ out.print (st.sval);
+ break;
+ }
+ }
+
+ out.println ("</table>");
+ out.println ("</body>" + PrintfFormat.NEWLINE + "</html>");
+ out.close();
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a list of all data files in directory \texttt{dirname} and writes
+that list in format HTML in output file \texttt{filename}.
+Each data file contains the parameters required to build a digital net.
+The resulting list contains a line for each data file giving the
+name of the file, the base, the dimension, the number of rows and
+the number of columns of the corresponding digital net.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalSequence.java b/source/umontreal/iro/lecuyer/hups/DigitalSequence.java
new file mode 100644
index 0000000..8daf842
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalSequence.java
@@ -0,0 +1,386 @@
+
+
+/*
+ * Class: DigitalSequence
+ * Description: abstract class with methods specific to digital sequences
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+
+/**
+ * This abstract class describes methods specific to digital sequences.
+ * Concrete classes must implement the {@link #extendSequence extendSequence} method
+ * that increases the number of points of the digital sequence.
+ * Calling the methods {@link #toNet toNet} or {@link #toNetShiftCj toNetShiftCj}
+ * will transform the digital sequence into a digital net, which has
+ * a fixed number of points <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+public abstract class DigitalSequence extends DigitalNet {
+
+
+
+ /**
+ * Increases the number of points to <SPAN CLASS="MATH"><I>n</I> = <I>b</I><SUP>k</SUP></SPAN> from now on.
+ *
+ * @param k there will be b^k points
+ *
+ *
+ */
+ public abstract void extendSequence (int k);
+
+
+ private int[][] copyDigitalShift (int[][] S) {
+ // Copy the shift S into T and returns T.
+ if (S == null) return null;
+ int[][] T = new int [S.length][S[0].length];
+ for (int i = 0; i < S.length; ++i)
+ for (int j = 0; j < S[0].length; ++j)
+ T[i][j] = S[i][j];
+ return T;
+ }
+
+ private DigitalNet initNetVar (boolean shiftFlag) {
+ // Initializes the net for the two toNet methods below.
+ DigitalNet net = new DigitalNet ();
+ if (shiftFlag)
+ net.dim = dim + 1;
+ else
+ net.dim = dim;
+ net.numPoints = numPoints;
+ net.numCols = numCols;
+ net.numRows = numRows;
+ net.outDigits = outDigits;
+ net.normFactor = normFactor;
+ net.b = b;
+ net.factor = new double[outDigits];
+ for (int i = 0; i < outDigits; i++)
+ net.factor[i] = factor[i];
+ net.genMat = new int[net.dim * numCols][numRows];
+ net.shiftStream = shiftStream;
+ net.capacityShift = capacityShift;
+ net.dimShift = dimShift;
+ net.digitalShift = copyDigitalShift (digitalShift);
+ if (shiftFlag && shiftStream != null) {
+ net.addRandomShift (dimShift, dimShift + 1, shiftStream);
+ }
+ return net;
+ }
+
+ /**
+ * Transforms this digital sequence into a digital net without changing
+ * the coordinates of the points. Returns the digital net.
+ *
+ */
+ public DigitalNet toNet() {
+ DigitalNet net = initNetVar (false);
+ final int N = dim * numCols;
+ for (int i = 0; i < N; i++)
+ for (int j = 0; j < numRows; j++)
+ net.genMat[i][j] = genMat[i][j];
+ return net;
+ }
+
+
+ /**
+ * Transforms this digital sequence into a digital net by adding one dimension
+ * and shifting all coordinates by one position. The first coordinate of point
+ * <SPAN CLASS="MATH"><I>i</I></SPAN> is <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the total number of points.
+ * Thus if the coordinates of a point of the digital sequence were
+ *
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>0</SUB>, <I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>s-1</SUB>)</SPAN>, then the coordinates of the
+ * point of the digital net will be
+ * <SPAN CLASS="MATH">(<I>i</I>/<I>n</I>, <I>x</I><SUB>0</SUB>, <I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>s-1</SUB>)</SPAN>.
+ * In other words, for the digital net,
+ * <SPAN CLASS="MATH"><B>C</B><SUB>0</SUB></SPAN> is the reflected
+ * identity and for <SPAN CLASS="MATH"><I>j</I> >= 1</SPAN>, the
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> used is the
+ *
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j-1</SUB></SPAN> of the digital sequence. If the digital sequence uses
+ * a digital shift, then the digital net will include the digital shift with
+ * one more dimension also. Returns the digital net.
+ *
+ */
+ public DigitalNet toNetShiftCj() {
+ DigitalNet net = initNetVar (true);
+ int j, c, l, start;
+
+ /* Shift all coordinates from the sequence by 1 dimension */
+ for (j = dim; j >= 1; j--) {
+ start = j * numCols;
+ for (c = 0; c < numCols; c++)
+ for (l = 0; l < numRows; l++)
+ net.genMat[start+c][l] = genMat[start - numCols + c][l];
+ }
+
+ // j = 0: initialize C_0 to the reflected identity.
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ net.genMat[c][l] = 0;
+ net.genMat[c][numCols-c-1] = 1;
+ }
+ return net;
+ }
+
+
+ /**
+ * Similar to {@link #iterator iterator}, except that the first coordinate
+ * of the points is <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, the second coordinate is obtained via
+ * the generating matrix
+ * <SPAN CLASS="MATH"><B>C</B><SUB>0</SUB></SPAN>, the next one via
+ * <SPAN CLASS="MATH"><B>C</B><SUB>1</SUB></SPAN>,
+ * and so on. Thus, this iterator shifts all coordinates of each point
+ * one position to the right and sets the first coordinate of point <SPAN CLASS="MATH"><I>i</I></SPAN>
+ * to <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, so that the points enumerated with this iterator have one more
+ * dimension. A digital shift, if present, will have one more dimension also.
+ * This iterator uses the Gray code.
+ *
+ */
+ public PointSetIterator iteratorShift() {
+ return new DigitalNetIteratorShiftGenerators();
+ }
+
+
+ /**
+ * This iterator shifts all coordinates of each point one position to the right
+ * and sets the first coordinate of point <SPAN CLASS="MATH"><I>i</I></SPAN> to <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, so that the points
+ * enumerated with this iterator have one more dimension.
+ * This iterator does not use the Gray code; the points are enumerated in the
+ * order of their first coordinate before randomization.
+ * A digital shift, if present, will have one more dimension also.
+ *
+ */
+ public PointSetIterator iteratorShiftNoGray() {
+ return new DigitalNetIteratorShiftNoGray();
+ }
+
+
+// ************************************************************************
+
+ protected class DigitalNetIteratorShiftGenerators
+ extends DigitalNetIterator {
+ // Similar to DigitalNetIterator; the first coordinate
+ // of point i is i/n, and all the others are shifted one position
+ // to the right. The points have dimension = dim + 1.
+
+ public DigitalNetIteratorShiftGenerators() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ init2 ();
+ }
+
+ public void init() { // This method is necessary to overload
+ } // the init() of DigitalNetIterator
+
+ public void init2() { // See constructor
+ resetCurPointIndex();
+ }
+
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+
+ // Digits of Gray code, used to reconstruct cachedCurPoint.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[(j - 1)*numCols+c][l] * gdigit[c];
+ cachedCurPoint [j*outDigits+l] = sum % b;
+ }
+ }
+ // The case j = 0
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[0][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ if (l == numCols-c-1)
+ sum += gdigit[c];
+ cachedCurPoint [l] = sum % b;
+ }
+ }
+
+ public int resetToNextPoint() {
+ // incremental computation.
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Update the digital expansion of i in base b, and find the
+ // position of change in the Gray code. Set all digits == b-1 to 0
+ // and increase the first one after by 1.
+ int pos; // Position of change in the Gray code.
+ for (pos = 0; gdigit[pos] == b-1; pos++)
+ gdigit[pos] = 0;
+ gdigit[pos]++;
+
+ // Update the cachedCurPoint by adding the column of the gener.
+ // matrix that corresponds to the Gray code digit that has changed.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < lsup; l++) {
+ cachedCurPoint[j*outDigits + l] +=
+ genMat[(j-1)*numCols + pos][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ // The case j = 0
+ l = numCols-pos-1;
+ if (l < lsup) {
+ cachedCurPoint[l] += 1;
+ cachedCurPoint[l] %= b;
+ }
+
+ return curPointIndex;
+ }
+ }
+
+
+// ************************************************************************
+
+ protected class DigitalNetIteratorShiftNoGray
+ extends DigitalNetIterator {
+ // Similar to DigitalNetIterator; the first coordinate
+ // of point i is i/n, and all the others are shifted one position
+ // to the right. The points have dimension = dim + 1.
+
+ public DigitalNetIteratorShiftNoGray() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ init2();
+ }
+
+ public void init() { // This method is necessary to overload
+ } // the init() of DigitalNetIterator
+
+ public void init2() { // See constructor
+ resetCurPointIndex();
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+
+ // Convert i to b-ary representation, put digits in bdigit.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++) {
+ sum += genMat[(j-1)*numCols+c][l] * bdigit[c];
+ sum %= b;
+ }
+ cachedCurPoint [j*outDigits+l] = sum;
+ }
+ }
+ // The case j = 0
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[0][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ if (l == numCols-c-1)
+ sum += bdigit[c];
+ cachedCurPoint [l] = sum % b;
+ }
+ }
+
+ public int resetToNextPoint() {
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Find the position of change in the digits of curPointIndex in base
+ // b. Set all digits = b-1 to 0; increase the first one after by 1.
+ int pos;
+ for (pos = 0; bdigit[pos] == b-1; pos++)
+ bdigit[pos] = 0;
+ bdigit[pos]++;
+
+ // Update the digital expansion of curPointIndex in base b.
+ // Update the cachedCurPoint by adding 1 unit at the digit pos.
+ // If pos > 0, remove b-1 units in the positions < pos. Since
+ // calculations are mod b, this is equivalent to adding 1 unit.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < lsup; l++) {
+ for (c = 0; c <= pos; c++)
+ cachedCurPoint[j*outDigits + l] +=
+ genMat[(j-1)*numCols + c][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ // The case j = 0
+ for (l = 0; l < lsup; l++) {
+ for (c = 0; c <= pos; c++)
+ if (l == numCols-c-1) {
+ cachedCurPoint[l] += 1;
+ cachedCurPoint[l] %= b;
+ }
+ }
+
+ return curPointIndex;
+ }
+
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalSequence.tex b/source/umontreal/iro/lecuyer/hups/DigitalSequence.tex
new file mode 100644
index 0000000..4aaee2a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalSequence.tex
@@ -0,0 +1,383 @@
+\defmodule{DigitalSequence}
+
+This abstract class describes methods specific to digital sequences.
+Concrete classes must implement the \method{extendSequence}{} method
+that increases the number of points of the digital sequence.
+Calling the methods \method{toNet}{} or \method{toNetShiftCj}{}
+will transform the digital sequence into a digital net, which has
+a fixed number of points $n$.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DigitalSequence
+ * Description: abstract class with methods specific to digital sequences
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+
+
+public abstract class DigitalSequence extends DigitalNet \begin{hide} {
+
+\end{hide}
+
+ public abstract void extendSequence (int k);
+\end{code}
+\begin{tabb}
+ Increases the number of points to $n = b^k$ from now on.
+\end{tabb}
+\begin{htmlonly}
+ \param{k}{there will be b^k points}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private int[][] copyDigitalShift (int[][] S) {
+ // Copy the shift S into T and returns T.
+ if (S == null) return null;
+ int[][] T = new int [S.length][S[0].length];
+ for (int i = 0; i < S.length; ++i)
+ for (int j = 0; j < S[0].length; ++j)
+ T[i][j] = S[i][j];
+ return T;
+ }
+
+ private DigitalNet initNetVar (boolean shiftFlag) {
+ // Initializes the net for the two toNet methods below.
+ DigitalNet net = new DigitalNet ();
+ if (shiftFlag)
+ net.dim = dim + 1;
+ else
+ net.dim = dim;
+ net.numPoints = numPoints;
+ net.numCols = numCols;
+ net.numRows = numRows;
+ net.outDigits = outDigits;
+ net.normFactor = normFactor;
+ net.b = b;
+ net.factor = new double[outDigits];
+ for (int i = 0; i < outDigits; i++)
+ net.factor[i] = factor[i];
+ net.genMat = new int[net.dim * numCols][numRows];
+ net.shiftStream = shiftStream;
+ net.capacityShift = capacityShift;
+ net.dimShift = dimShift;
+ net.digitalShift = copyDigitalShift (digitalShift);
+ if (shiftFlag && shiftStream != null) {
+ net.addRandomShift (dimShift, dimShift + 1, shiftStream);
+ }
+ return net;
+ } \end{hide}
+
+ public DigitalNet toNet() \begin{hide} {
+ DigitalNet net = initNetVar (false);
+ final int N = dim * numCols;
+ for (int i = 0; i < N; i++)
+ for (int j = 0; j < numRows; j++)
+ net.genMat[i][j] = genMat[i][j];
+ return net;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Transforms this digital sequence into a digital net without changing
+ the coordinates of the points. Returns the digital net.
+\end{tabb}
+\begin{code}
+
+ public DigitalNet toNetShiftCj() \begin{hide} {
+ DigitalNet net = initNetVar (true);
+ int j, c, l, start;
+
+ /* Shift all coordinates from the sequence by 1 dimension */
+ for (j = dim; j >= 1; j--) {
+ start = j * numCols;
+ for (c = 0; c < numCols; c++)
+ for (l = 0; l < numRows; l++)
+ net.genMat[start+c][l] = genMat[start - numCols + c][l];
+ }
+
+ // j = 0: initialize C_0 to the reflected identity.
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ net.genMat[c][l] = 0;
+ net.genMat[c][numCols-c-1] = 1;
+ }
+ return net;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Transforms this digital sequence into a digital net by adding one dimension
+ and shifting all coordinates by one position. The first coordinate of point
+ $i$ is $i/n$, where $n$ is the total number of points.
+ Thus if the coordinates of a point of the digital sequence were
+ $(x_0, x_1, x_2, \ldots, x_{s-1})$, then the coordinates of the
+ point of the digital net will be $(i/n, x_0, x_1, \ldots, x_{s-1})$.
+ In other words, for the digital net, $\mathbf{C}_0$ is the reflected
+ identity and for $j\ge 1$, the $\mathbf{C}_j$ used is the
+ $\mathbf{C}_{j-1}$ of the digital sequence. If the digital sequence uses
+ a digital shift, then the digital net will include the digital shift with
+ one more dimension also. Returns the digital net.
+\end{tabb}
+\begin{code}
+
+ public PointSetIterator iteratorShift()\begin{hide} {
+ return new DigitalNetIteratorShiftGenerators();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to \method{iterator}{}, except that the first coordinate
+ of the points is $i/n$, the second coordinate is obtained via
+ the generating matrix $\mathbf{C}_0$, the next one via $\mathbf{C}_1$,
+ and so on. Thus, this iterator shifts all coordinates of each point
+ one position to the right and sets the first coordinate of point $i$
+ to $i/n$, so that the points enumerated with this iterator have one more
+ dimension. A digital shift, if present, will have one more dimension also.
+ This iterator uses the Gray code.
+\end{tabb}
+\begin{code}
+
+ public PointSetIterator iteratorShiftNoGray()\begin{hide} {
+ return new DigitalNetIteratorShiftNoGray();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ This iterator shifts all coordinates of each point one position to the right
+ and sets the first coordinate of point $i$ to $i/n$, so that the points
+ enumerated with this iterator have one more dimension.
+ This iterator does not use the Gray code; the points are enumerated in the
+ order of their first coordinate before randomization.
+ A digital shift, if present, will have one more dimension also.
+\end{tabb}
+\begin{code}\begin{hide}
+
+// ************************************************************************
+
+ protected class DigitalNetIteratorShiftGenerators
+ extends DigitalNetIterator {
+ // Similar to DigitalNetIterator; the first coordinate
+ // of point i is i/n, and all the others are shifted one position
+ // to the right. The points have dimension = dim + 1.
+
+ public DigitalNetIteratorShiftGenerators() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ init2 ();
+ }
+
+ public void init() { // This method is necessary to overload
+ } // the init() of DigitalNetIterator
+
+ public void init2() { // See constructor
+ resetCurPointIndex();
+ }
+
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+
+ // Digits of Gray code, used to reconstruct cachedCurPoint.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ sum += genMat[(j - 1)*numCols+c][l] * gdigit[c];
+ cachedCurPoint [j*outDigits+l] = sum % b;
+ }
+ }
+ // The case j = 0
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[0][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ if (l == numCols-c-1)
+ sum += gdigit[c];
+ cachedCurPoint [l] = sum % b;
+ }
+ }
+
+ public int resetToNextPoint() {
+ // incremental computation.
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Update the digital expansion of i in base b, and find the
+ // position of change in the Gray code. Set all digits == b-1 to 0
+ // and increase the first one after by 1.
+ int pos; // Position of change in the Gray code.
+ for (pos = 0; gdigit[pos] == b-1; pos++)
+ gdigit[pos] = 0;
+ gdigit[pos]++;
+
+ // Update the cachedCurPoint by adding the column of the gener.
+ // matrix that corresponds to the Gray code digit that has changed.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < lsup; l++) {
+ cachedCurPoint[j*outDigits + l] +=
+ genMat[(j-1)*numCols + pos][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ // The case j = 0
+ l = numCols-pos-1;
+ if (l < lsup) {
+ cachedCurPoint[l] += 1;
+ cachedCurPoint[l] %= b;
+ }
+
+ return curPointIndex;
+ }
+ }
+
+
+// ************************************************************************
+
+ protected class DigitalNetIteratorShiftNoGray
+ extends DigitalNetIterator {
+ // Similar to DigitalNetIterator; the first coordinate
+ // of point i is i/n, and all the others are shifted one position
+ // to the right. The points have dimension = dim + 1.
+
+ public DigitalNetIteratorShiftNoGray() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ init2();
+ }
+
+ public void init() { // This method is necessary to overload
+ } // the init() of DigitalNetIterator
+
+ public void init2() { // See constructor
+ resetCurPointIndex();
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ curPointIndex = i;
+ curCoordIndex = 0;
+
+ // Convert i to b-ary representation, put digits in bdigit.
+ idigits = intToDigitsGray (b, i, numCols, bdigit, gdigit);
+ int c, j, l, sum;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[j][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++) {
+ sum += genMat[(j-1)*numCols+c][l] * bdigit[c];
+ sum %= b;
+ }
+ cachedCurPoint [j*outDigits+l] = sum;
+ }
+ }
+ // The case j = 0
+ for (l = 0; l < outDigits; l++) {
+ if (digitalShift == null)
+ sum = 0;
+ else
+ sum = digitalShift[0][l];
+ if (l < numRows)
+ for (c = 0; c < idigits; c++)
+ if (l == numCols-c-1)
+ sum += bdigit[c];
+ cachedCurPoint [l] = sum % b;
+ }
+ }
+
+ public int resetToNextPoint() {
+ curPointIndex++;
+ curCoordIndex = 0;
+ if (curPointIndex >= numPoints)
+ return curPointIndex;
+
+ // Find the position of change in the digits of curPointIndex in base
+ // b. Set all digits = b-1 to 0; increase the first one after by 1.
+ int pos;
+ for (pos = 0; bdigit[pos] == b-1; pos++)
+ bdigit[pos] = 0;
+ bdigit[pos]++;
+
+ // Update the digital expansion of curPointIndex in base b.
+ // Update the cachedCurPoint by adding 1 unit at the digit pos.
+ // If pos > 0, remove b-1 units in the positions < pos. Since
+ // calculations are mod b, this is equivalent to adding 1 unit.
+ // The digital shift is already incorporated in the cached point.
+ int c, j, l;
+ int lsup = numRows; // Max index l
+ if (outDigits < numRows)
+ lsup = outDigits;
+ for (j = 1; j <= dim; j++) {
+ for (l = 0; l < lsup; l++) {
+ for (c = 0; c <= pos; c++)
+ cachedCurPoint[j*outDigits + l] +=
+ genMat[(j-1)*numCols + c][l];
+ cachedCurPoint[j * outDigits + l] %= b;
+ }
+ }
+ // The case j = 0
+ for (l = 0; l < lsup; l++) {
+ for (c = 0; c <= pos; c++)
+ if (l == numCols-c-1) {
+ cachedCurPoint[l] += 1;
+ cachedCurPoint[l] %= b;
+ }
+ }
+
+ return curPointIndex;
+ }
+
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalSequenceBase2.java b/source/umontreal/iro/lecuyer/hups/DigitalSequenceBase2.java
new file mode 100644
index 0000000..29e15f3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalSequenceBase2.java
@@ -0,0 +1,305 @@
+
+
+/*
+ * Class: DigitalSequenceBase2
+ * Description: abstract class with methods specific to digital sequences
+ in base 2
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+
+/**
+ * This abstract class describes methods specific to digital sequences in base 2.
+ * Concrete classes must implement the {@link #extendSequence extendSequence} method
+ * that increases the number of points of the digital sequence.
+ * Calling the methods {@link #toNet toNet} or {@link #toNetShiftCj toNetShiftCj}
+ * will transform the digital sequence into a digital net, which has
+ * a fixed number of points <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+public abstract class DigitalSequenceBase2 extends DigitalNetBase2 {
+
+
+
+ /**
+ * Increases the number of points to <SPAN CLASS="MATH"><I>n</I> = 2<SUP>k</SUP></SPAN> from now on.
+ *
+ * @param k there will be 2^k points
+ *
+ *
+ */
+ public abstract void extendSequence (int k);
+
+
+ private int[] copyDigitalShift (int[] S) {
+ // Copy the shift S into T and returns T.
+ if (S == null) return null;
+ int[] T = new int [S.length];
+ for (int i = 0; i < S.length; ++i)
+ T[i] = S[i];
+ return T;
+ }
+
+ private DigitalNetBase2 initNetVar (boolean shiftFlag) {
+ // Initializes the net for the two toNet methods below.
+ DigitalNetBase2 net = new DigitalNetBase2 ();
+ if (shiftFlag)
+ net.dim = dim + 1;
+ else
+ net.dim = dim;
+ net.numPoints = numPoints;
+ net.numCols = numCols;
+ net.numRows = numRows;
+ net.outDigits = outDigits;
+ net.normFactor = normFactor;
+ net.factor = new double[outDigits];
+ net.genMat = new int[net.dim * numCols];
+ net.shiftStream = shiftStream;
+ net.capacityShift = capacityShift;
+ net.dimShift = dimShift;
+ net.digitalShift = copyDigitalShift (digitalShift);
+ if (shiftFlag && shiftStream != null) {
+ net.addRandomShift (dimShift, dimShift + 1, shiftStream);
+ }
+ return net;
+ }
+
+ /**
+ * Transforms this digital sequence into a digital net without changing
+ * the coordinates of the points. Returns the digital net.
+ *
+ */
+ public DigitalNetBase2 toNet() {
+ DigitalNetBase2 net = initNetVar (false);
+ for (int i = 0; i < dim * numCols; i++)
+ net.genMat[i] = genMat[i];
+ return net;
+ }
+
+
+ /**
+ * Transforms this digital sequence into a digital net by adding one dimension
+ * and shifting all coordinates by one position. The first coordinate of point
+ * <SPAN CLASS="MATH"><I>i</I></SPAN> is <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the total number of points.
+ * Thus if the coordinates of a point of the digital sequence were
+ *
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>0</SUB>, <I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>s-1</SUB>)</SPAN>, then the coordinates of the
+ * point of the digital net will be
+ * <SPAN CLASS="MATH">(<I>i</I>/<I>n</I>, <I>x</I><SUB>0</SUB>, <I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>s-1</SUB>)</SPAN>.
+ * In other words, for the digital net,
+ * <SPAN CLASS="MATH"><B>C</B><SUB>0</SUB></SPAN> is the reflected
+ * identity and for <SPAN CLASS="MATH"><I>j</I> >= 1</SPAN>, the
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> used is the
+ *
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j-1</SUB></SPAN> of the digital sequence. If the digital sequence uses
+ * a digital shift, then the digital net will include the digital shift with
+ * one more dimension also. Returns the digital net.
+ *
+ */
+ public DigitalNetBase2 toNetShiftCj() {
+ DigitalNetBase2 net = initNetVar (true);
+ int c;
+ for (c = (dim + 1) * numCols - 1; c >= numCols; --c)
+ net.genMat[c] = genMat[c - numCols];
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ net.genMat[c] = (1 << (outDigits-numCols+c));
+ return net;
+ }
+
+
+ /**
+ * Similar to {@link #iterator iterator}, except that the first coordinate
+ * of the points is <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, the second coordinate is obtained via
+ * the generating matrix
+ * <SPAN CLASS="MATH"><B>C</B><SUB>0</SUB></SPAN>, the next one via
+ * <SPAN CLASS="MATH"><B>C</B><SUB>1</SUB></SPAN>,
+ * and so on. Thus, this iterator shifts all coordinates of each point
+ * one position to the right and sets the first coordinate of point <SPAN CLASS="MATH"><I>i</I></SPAN>
+ * to <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, so that the points enumerated with this iterator have one more
+ * dimension. A digital shift, if present, will have one more dimension also.
+ * This iterator uses the Gray code.
+ *
+ */
+ public PointSetIterator iteratorShift() {
+ return new DigitalNetBase2IteratorShiftGenerators();
+ }
+
+
+ /**
+ * This iterator shifts all coordinates of each point one position to the right
+ * and sets the first coordinate of point <SPAN CLASS="MATH"><I>i</I></SPAN> to <SPAN CLASS="MATH"><I>i</I>/<I>n</I></SPAN>, so that the points
+ * enumerated with this iterator have one more dimension. This iterator
+ * does not use the Gray code: the points are enumerated in the order of
+ * their first coordinate before randomization.
+ * A digital shift, if present, will have one more dimension also.
+ *
+ */
+ public PointSetIterator iteratorShiftNoGray() {
+ return new DigitalNetBase2IteratorShiftNoGray();
+ }
+
+
+
+ // *******************************************************************
+
+ protected class DigitalNetBase2IteratorShiftGenerators
+ extends DigitalNetBase2Iterator {
+ // Similar to DigitalNetBase2Iterator; the first coordinate
+ // of point i is i/n, and all the others are shifted one position
+ // to the right. The points have one more dimension.
+
+ public DigitalNetBase2IteratorShiftGenerators() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ resetCurPointIndex();
+ }
+
+ public void init2() { // This method is necessary to overload
+ } // the init2() of DigitalNetBase2Iterator
+
+ protected void addShiftToCache () {
+ if (digitalShift == null)
+ for (int j = 0; j < dimS; j++)
+ cachedCurPoint[j] = 0;
+ else
+ for (int j = 0; j < dimS; j++)
+ cachedCurPoint[j] = digitalShift[j];
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex(); return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+
+ int j;
+ int grayCode = i ^ (i >> 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((grayCode >> pos) != 0) {
+ if (((grayCode >> pos) & 1) != 0) {
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ int pos = 0; // Will be position of change in Gray code,
+ // = pos. of first 0 in binary code of point index.
+ while (((curPointIndex >> pos) & 1) != 0)
+ pos++;
+ if (pos < numCols) {
+ // The matrix C for first coord. is a reflected identity.
+ // Col. pos has a 1 in line k-1-pos.
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (int j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ }
+
+
+ // *******************************************************************
+
+ protected class DigitalNetBase2IteratorShiftNoGray
+ extends DigitalNetBase2Iterator {
+ // Similar to DigitalNetBase2IteratorShiftGenerators,
+ // except that the Gray code is not used.
+ private boolean shiftDimFlag = false; // ensures that the random shift
+ // has been initialized with dim + 1 components
+
+ public DigitalNetBase2IteratorShiftNoGray() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ resetCurPointIndex();
+ }
+
+ public void init2() { // This method is necessary to overload
+ } // the init2() of DigitalNetBase2Iterator
+
+ protected void addShiftToCache () {
+ if (digitalShift == null)
+ for (int j = 0; j <= dim; j++)
+ cachedCurPoint[j] = 0;
+ else
+ for (int j = 0; j <= dim; j++)
+ cachedCurPoint[j] = digitalShift[j];
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+
+ int pos = 0; // Position of the bit that is examined.
+ while ((i >> pos) != 0) {
+ if (((i >> pos) & 1) != 0) {
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (int j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ // Contains the bits of i that changed.
+ if (curPointIndex + 1 >= numPoints)
+ return ++curPointIndex;
+ int diff = curPointIndex ^ (curPointIndex + 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((diff >> pos) != 0) {
+ if (((diff >> pos) & 1) != 0) {
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (int j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ pos++;
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/DigitalSequenceBase2.tex b/source/umontreal/iro/lecuyer/hups/DigitalSequenceBase2.tex
new file mode 100644
index 0000000..63b148f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/DigitalSequenceBase2.tex
@@ -0,0 +1,303 @@
+\defmodule{DigitalSequenceBase2}
+
+This abstract class describes methods specific to digital sequences in base 2.
+Concrete classes must implement the \method{extendSequence}{} method
+that increases the number of points of the digital sequence.
+Calling the methods \method{toNet}{} or \method{toNetShiftCj}{}
+will transform the digital sequence into a digital net, which has
+a fixed number of points $n$.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DigitalSequenceBase2
+ * Description: abstract class with methods specific to digital sequences
+ in base 2
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+
+
+public abstract class DigitalSequenceBase2 extends DigitalNetBase2 \begin{hide} {
+
+\end{hide}
+
+ public abstract void extendSequence (int k);
+\end{code}
+\begin{tabb}
+ Increases the number of points to $n = 2^k$ from now on.
+\end{tabb}
+\begin{htmlonly}
+ \param{k}{there will be 2^k points}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private int[] copyDigitalShift (int[] S) {
+ // Copy the shift S into T and returns T.
+ if (S == null) return null;
+ int[] T = new int [S.length];
+ for (int i = 0; i < S.length; ++i)
+ T[i] = S[i];
+ return T;
+ }
+
+ private DigitalNetBase2 initNetVar (boolean shiftFlag) {
+ // Initializes the net for the two toNet methods below.
+ DigitalNetBase2 net = new DigitalNetBase2 ();
+ if (shiftFlag)
+ net.dim = dim + 1;
+ else
+ net.dim = dim;
+ net.numPoints = numPoints;
+ net.numCols = numCols;
+ net.numRows = numRows;
+ net.outDigits = outDigits;
+ net.normFactor = normFactor;
+ net.factor = new double[outDigits];
+ net.genMat = new int[net.dim * numCols];
+ net.shiftStream = shiftStream;
+ net.capacityShift = capacityShift;
+ net.dimShift = dimShift;
+ net.digitalShift = copyDigitalShift (digitalShift);
+ if (shiftFlag && shiftStream != null) {
+ net.addRandomShift (dimShift, dimShift + 1, shiftStream);
+ }
+ return net;
+ } \end{hide}
+
+ public DigitalNetBase2 toNet() \begin{hide} {
+ DigitalNetBase2 net = initNetVar (false);
+ for (int i = 0; i < dim * numCols; i++)
+ net.genMat[i] = genMat[i];
+ return net;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Transforms this digital sequence into a digital net without changing
+ the coordinates of the points. Returns the digital net.
+\end{tabb}
+\begin{code}
+
+ public DigitalNetBase2 toNetShiftCj() \begin{hide} {
+ DigitalNetBase2 net = initNetVar (true);
+ int c;
+ for (c = (dim + 1) * numCols - 1; c >= numCols; --c)
+ net.genMat[c] = genMat[c - numCols];
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ net.genMat[c] = (1 << (outDigits-numCols+c));
+ return net;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Transforms this digital sequence into a digital net by adding one dimension
+ and shifting all coordinates by one position. The first coordinate of point
+ $i$ is $i/n$, where $n$ is the total number of points.
+ Thus if the coordinates of a point of the digital sequence were
+ $(x_0, x_1, x_2, \ldots, x_{s-1})$, then the coordinates of the
+ point of the digital net will be $(i/n, x_0, x_1, \ldots, x_{s-1})$.
+ In other words, for the digital net, $\mathbf{C}_0$ is the reflected
+ identity and for $j\ge 1$, the $\mathbf{C}_j$ used is the
+ $\mathbf{C}_{j-1}$ of the digital sequence. If the digital sequence uses
+ a digital shift, then the digital net will include the digital shift with
+ one more dimension also. Returns the digital net.
+\end{tabb}
+\begin{code}
+
+ public PointSetIterator iteratorShift()\begin{hide} {
+ return new DigitalNetBase2IteratorShiftGenerators();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to \method{iterator}{}, except that the first coordinate
+ of the points is $i/n$, the second coordinate is obtained via
+ the generating matrix $\mathbf{C}_0$, the next one via $\mathbf{C}_1$,
+ and so on. Thus, this iterator shifts all coordinates of each point
+ one position to the right and sets the first coordinate of point $i$
+ to $i/n$, so that the points enumerated with this iterator have one more
+ dimension. A digital shift, if present, will have one more dimension also.
+ This iterator uses the Gray code.
+\end{tabb}
+\begin{code}
+
+ public PointSetIterator iteratorShiftNoGray()\begin{hide} {
+ return new DigitalNetBase2IteratorShiftNoGray();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ This iterator shifts all coordinates of each point one position to the right
+ and sets the first coordinate of point $i$ to $i/n$, so that the points
+ enumerated with this iterator have one more dimension. This iterator
+ does not use the Gray code: the points are enumerated in the order of
+ their first coordinate before randomization.
+ A digital shift, if present, will have one more dimension also.
+\end{tabb}
+\begin{code}\begin{hide}
+
+
+ // *******************************************************************
+
+ protected class DigitalNetBase2IteratorShiftGenerators
+ extends DigitalNetBase2Iterator {
+ // Similar to DigitalNetBase2Iterator; the first coordinate
+ // of point i is i/n, and all the others are shifted one position
+ // to the right. The points have one more dimension.
+
+ public DigitalNetBase2IteratorShiftGenerators() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ resetCurPointIndex();
+ }
+
+ public void init2() { // This method is necessary to overload
+ } // the init2() of DigitalNetBase2Iterator
+
+ protected void addShiftToCache () {
+ if (digitalShift == null)
+ for (int j = 0; j < dimS; j++)
+ cachedCurPoint[j] = 0;
+ else
+ for (int j = 0; j < dimS; j++)
+ cachedCurPoint[j] = digitalShift[j];
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex(); return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+
+ int j;
+ int grayCode = i ^ (i >> 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((grayCode >> pos) != 0) {
+ if (((grayCode >> pos) & 1) != 0) {
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ int pos = 0; // Will be position of change in Gray code,
+ // = pos. of first 0 in binary code of point index.
+ while (((curPointIndex >> pos) & 1) != 0)
+ pos++;
+ if (pos < numCols) {
+ // The matrix C for first coord. is a reflected identity.
+ // Col. pos has a 1 in line k-1-pos.
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (int j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ }
+
+
+ // *******************************************************************
+
+ protected class DigitalNetBase2IteratorShiftNoGray
+ extends DigitalNetBase2Iterator {
+ // Similar to DigitalNetBase2IteratorShiftGenerators,
+ // except that the Gray code is not used.
+ private boolean shiftDimFlag = false; // ensures that the random shift
+ // has been initialized with dim + 1 components
+
+ public DigitalNetBase2IteratorShiftNoGray() {
+ super();
+ dimS = dim + 1;
+ if (digitalShift != null && dimShift < dimS)
+ addRandomShift (dimShift, dimS, shiftStream);
+ resetCurPointIndex();
+ }
+
+ public void init2() { // This method is necessary to overload
+ } // the init2() of DigitalNetBase2Iterator
+
+ protected void addShiftToCache () {
+ if (digitalShift == null)
+ for (int j = 0; j <= dim; j++)
+ cachedCurPoint[j] = 0;
+ else
+ for (int j = 0; j <= dim; j++)
+ cachedCurPoint[j] = digitalShift[j];
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i == 0) {
+ resetCurPointIndex();
+ return;
+ }
+ // Out of order computation, must recompute the cached current
+ // point from scratch.
+ curPointIndex = i;
+ curCoordIndex = 0;
+ addShiftToCache ();
+
+ int pos = 0; // Position of the bit that is examined.
+ while ((i >> pos) != 0) {
+ if (((i >> pos) & 1) != 0) {
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (int j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ pos++;
+ }
+ }
+
+ public int resetToNextPoint() {
+ // Contains the bits of i that changed.
+ if (curPointIndex + 1 >= numPoints)
+ return ++curPointIndex;
+ int diff = curPointIndex ^ (curPointIndex + 1);
+ int pos = 0; // Position of the bit that is examined.
+ while ((diff >> pos) != 0) {
+ if (((diff >> pos) & 1) != 0) {
+ cachedCurPoint[0] ^= 1 << (outDigits - numCols + pos);
+ for (int j = 1; j <= dim; j++)
+ cachedCurPoint[j] ^= genMat[(j-1) * numCols + pos];
+ }
+ pos++;
+ }
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/EmptyRandomization.java b/source/umontreal/iro/lecuyer/hups/EmptyRandomization.java
new file mode 100644
index 0000000..18021ad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/EmptyRandomization.java
@@ -0,0 +1,84 @@
+
+
+/*
+ * Class: EmptyRandomization
+ * Description: implements an empty PointSetRandomization
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+ import umontreal.iro.lecuyer.rng.MRG32k3a;
+ import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This class implements an empty
+ * {@link umontreal.iro.lecuyer.hups.PointSetRandomization PointSetRandomization}.
+ * The method {@link #randomize(PointSet) randomize} does nothing.
+ * The internal stream is never used.
+ * This class can be used in methods where a randomization is needed
+ * but you don't want one.
+ *
+ */
+public class EmptyRandomization implements PointSetRandomization {
+ protected RandomStream stream = new MRG32k3a();
+
+
+
+ /**
+ * This method does nothing.
+ *
+ * @param p Point set to randomize
+ *
+ *
+ */
+ public void randomize (PointSet p) {
+ // Does nothing
+ }
+
+
+ /**
+ * Sets the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} to
+ * <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ *
+ */
+ public void setStream (RandomStream stream) {
+ this.stream = stream;
+ }
+
+
+ /**
+ * Returns the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}.
+ *
+ * @return stream used in the randomization
+ *
+ */
+ public RandomStream getStream() {
+ return stream;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/EmptyRandomization.tex b/source/umontreal/iro/lecuyer/hups/EmptyRandomization.tex
new file mode 100644
index 0000000..d682e1d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/EmptyRandomization.tex
@@ -0,0 +1,93 @@
+\defmodule{EmptyRandomization}
+
+This class implements an empty
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetRandomization}.
+ The method \method{randomize}{PointSet} does nothing.
+The internal stream is never used.
+This class can be used in methods where a randomization is needed
+but you don't want one.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: EmptyRandomization
+ * Description: implements an empty PointSetRandomization
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+\begin{hide}
+ import umontreal.iro.lecuyer.rng.MRG32k3a;
+ import umontreal.iro.lecuyer.rng.RandomStream;
+
+\end{hide}
+public class EmptyRandomization implements PointSetRandomization\begin{hide} {
+ protected RandomStream stream = new MRG32k3a();
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void randomize (PointSet p) \begin{hide} {
+ // Does nothing
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ This method does nothing.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{Point set to randomize}
+\end{htmlonly}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ this.stream = stream;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Sets the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} to
+ \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ return stream;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Returns the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+\end{tabb}
+\begin{htmlonly}
+ \return{stream used in the randomization}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/F2wCycleBasedLFSR.java b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedLFSR.java
new file mode 100644
index 0000000..94ec1d7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedLFSR.java
@@ -0,0 +1,178 @@
+
+
+/*
+ * Class: F2wCycleBasedLFSR
+ * Description: point set based upon a linear feedback shift register
+ sequence
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+import cern.colt.list.*;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+
+/**
+ * This class creates a point set based upon a linear feedback shift register
+ * sequence. The recurrence used to produce the point set is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>m</I><SUB>n</SUB> = ∑<SUB>i=1</SUB><SUP>r</SUP><I>b</I><SUB>i</SUB><I>m</I><SUB>n-i</SUB>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>m</I><SUB>n</SUB>∈<B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN>, <SPAN CLASS="MATH"><I>n</I> >= 0</SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>b</I><SUB>i</SUB>∈<B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN>.
+ * There is a polynomial in
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]</SPAN>
+ * associated with this recurrence called
+ * the <SPAN CLASS="textit">characteristic polynomial</SPAN>. It is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>(<I>z</I>) = <I>z</I><SUP>r</SUP> + ∑<SUB>i=1</SUB><SUP>r</SUP><I>b</I><SUB>i</SUB><I>z</I><SUP>r-i</SUP>.
+ * </DIV><P></P>
+ * In the implementation, this polynomial is stored in an object
+ * <TT>F2wStructure</TT>.
+ *
+ * <P>
+ * Let
+ * <SPAN CLASS="MATH"><B>x</B> = (<I>x</I><SUP>(0)</SUP>,…, <I>x</I><SUP>(p-1)</SUP>)∈<B>F</B><SUB>2</SUB><SUP>p</SUP></SPAN>
+ * be a <SPAN CLASS="MATH"><I>p</I></SPAN>-bit vector.
+ * Let us define the function
+ * <SPAN CLASS="MATH"><I>φ</I>(<B>x</B>) = ∑<SUB>i=1</SUB><SUP>p</SUP>2<SUP>-i</SUP><I>x</I><SUP>(i-1)</SUP></SPAN>.
+ * The point set in <SPAN CLASS="MATH"><I>t</I></SPAN> dimensions produced by this class is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * {(<I>φ</I>(<B>y</B><SUB>0</SUB>), <I>φ</I>(<B>y</B><SUB>s</SUB>),…, <I>φ</I>(<B>y</B><SUB>s(t-1)</SUB>) : (<B>v</B><SUB>0</SUB>,…,<B>v</B><SUB>r-1</SUB>)∈<B>F</B><SUB>2</SUB><SUP>rw</SUP>}
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><B>y</B><SUB>n</SUB> = trunc<SUB>h</SUB>(<B>v</B><SUB>n</SUB>,<B>v</B><SUB>n+1</SUB>,…)</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><B>v</B><SUB>n</SUB></SPAN> is the representation of <SPAN CLASS="MATH"><I>m</I><SUB>n</SUB></SPAN> under the polynomial basis of
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN> over
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2</SUB></SPAN>, and
+ *
+ * <SPAN CLASS="MATH"><I>h</I> = <I>w</I> floor(31/<I>w</I>)</SPAN>.
+ * The parameter <SPAN CLASS="MATH"><I>s</I></SPAN> is called the stepping parameter of the recurrence.
+ *
+ */
+public class F2wCycleBasedLFSR extends CycleBasedPointSetBase2 {
+
+ private F2wStructure param;
+
+
+ /**
+ * Constructs a point set with <SPAN CLASS="MATH">2<SUP>rw</SUP></SPAN> points. See the description of the class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure} for the meaning
+ * of the parameters.
+ *
+ */
+ public F2wCycleBasedLFSR (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[])
+ /* *
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ init ();
+ }
+
+
+ /**
+ * Constructs a point set after reading its parameters from
+ * file <TT>filename</TT>; the parameters are located at line numbered <TT>no</TT>
+ * of <TT>filename</TT>. The available files are listed in the description of class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure}.
+ *
+ */
+ public F2wCycleBasedLFSR (String filename, int no)
+ {
+ param = new F2wStructure (filename, no);
+ init ();
+ }
+
+
+
+ private void init ()
+ {
+ param.initParamLFSR ();
+ normFactor = param.normFactor;
+ EpsilonHalf = param.EpsilonHalf;
+ numBits = param.numBits;
+ fillCyclesLFSR ();
+ }
+
+ public String toString ()
+ {
+ String s = "F2wCycleBasedLFSR:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+ private void fillCyclesLFSR ()
+ {
+ int n = 1 << param.getLog2N ();
+ IntArrayList c; // Array used to store the current cycle.
+ int currentState; // The state currently visited.
+ int i;
+ boolean stateVisited[] = new boolean[n];
+
+ // Indicates which states have been visited so far.
+ for (i = 0; i < n; i++)
+ stateVisited[i] = false;
+ int startState = 0; // First state of the cycle currently considered.
+ numPoints = 0;
+ while (startState < n) {
+ stateVisited[startState] = true;
+ c = new IntArrayList ();
+ param.state = startState;
+ param.initF2wLFSR ();
+ c.add (param.output);
+ param.F2wLFSR ();
+ // Warning: watch for overflow !!!
+ while (param.state != startState) {
+ stateVisited[param.state] = true;
+ c.add (param.output);
+ param.F2wLFSR ();
+ }
+ addCycle (c);
+ for (i = startState + 1; i < n; i++)
+ if (stateVisited[i] == false)
+ break;
+ startState = i;
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/F2wCycleBasedLFSR.tex b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedLFSR.tex
new file mode 100644
index 0000000..cdeca38
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedLFSR.tex
@@ -0,0 +1,175 @@
+\defmodule{F2wCycleBasedLFSR}
+
+This class creates a point set based upon a linear feedback shift register
+ sequence. The recurrence used to produce the point set is
+$$
+ m_n = \sum_{i=1}^r b_i m_{n-i}
+$$
+where $m_n\in \latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$, $n\geq 0$
+ and $b_i\in \latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$.
+There is a polynomial in $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]$
+ associated with this recurrence called
+the \emph{characteristic polynomial}. It is
+$$
+ P(z) = z^r + \sum_{i=1}^r b_i z^{r-i}.
+$$
+In the implementation, this polynomial is stored in an object
+% \externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}.
+ \texttt{F2wStructure}.
+% See the description of this class for more details.
+
+Let ${\mathbf{x}} = (x^{(0)}, \ldots, x^{(p-1)}) \in
+\latex{\mathbb{F}}\html{\mathbf{F}}_{2}^p$
+be a $p$-bit vector.
+Let us define the function $\phi(\mathbf{x}) = \sum_{i=1}^{p} 2^{-i} x^{(i-1)}$.
+The point set in $t$ dimensions produced by this class is
+$$
+ \left\{ (\phi(\mathbf{y}_0),\phi(\mathbf{y}_s),\ldots,\phi(\mathbf{y}_{s(t-1)}):
+ (\mathbf{v}_0,\ldots,\mathbf{v}_{r-1})\in
+ \latex{\mathbb{F}}\html{\mathbf{F}}_{2}^{rw}\right\}
+$$
+where $\mathbf{y}_n = \mbox{trunc}_h(\mathbf{v}_n, \mathbf{v}_{n+1},\ldots)$,
+$\mathbf{v}_n$ is the representation of $m_n$ under the polynomial basis of
+$\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$ over
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_2$, and
+ $h=w\latex{\lfloor 31/w\rfloor}\html{\mbox{ floor}(31/w)}$.
+The parameter $s$ is called the stepping parameter of the recurrence.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: F2wCycleBasedLFSR
+ * Description: point set based upon a linear feedback shift register
+ sequence
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups; \begin{hide}
+import cern.colt.list.*;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+
+public class F2wCycleBasedLFSR extends CycleBasedPointSetBase2 \begin{hide} {
+
+ private F2wStructure param;
+\end{hide}\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public F2wCycleBasedLFSR (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[])\begin{hide}
+ /**
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ init ();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+Constructs a point set with $2^{rw}$ points. See the description of the class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wStructure} for the meaning
+ of the parameters.
+ \end{tabb}
+\begin{code}
+
+ public F2wCycleBasedLFSR (String filename, int no) \begin{hide}
+ {
+ param = new F2wStructure (filename, no);
+ init ();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a point set after reading its parameters from
+ file \texttt{filename}; the parameters are located at line numbered \texttt{no}
+ of \texttt{filename}. The available files are listed in the description of class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+
+ private void init ()
+ {
+ param.initParamLFSR ();
+ normFactor = param.normFactor;
+ EpsilonHalf = param.EpsilonHalf;
+ numBits = param.numBits;
+ fillCyclesLFSR ();
+ }
+
+ public String toString ()
+ {
+ String s = "F2wCycleBasedLFSR:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+ private void fillCyclesLFSR ()
+ {
+ int n = 1 << param.getLog2N ();
+ IntArrayList c; // Array used to store the current cycle.
+ int currentState; // The state currently visited.
+ int i;
+ boolean stateVisited[] = new boolean[n];
+
+ // Indicates which states have been visited so far.
+ for (i = 0; i < n; i++)
+ stateVisited[i] = false;
+ int startState = 0; // First state of the cycle currently considered.
+ numPoints = 0;
+ while (startState < n) {
+ stateVisited[startState] = true;
+ c = new IntArrayList ();
+ param.state = startState;
+ param.initF2wLFSR ();
+ c.add (param.output);
+ param.F2wLFSR ();
+ // Warning: watch for overflow !!!
+ while (param.state != startState) {
+ stateVisited[param.state] = true;
+ c.add (param.output);
+ param.F2wLFSR ();
+ }
+ addCycle (c);
+ for (i = startState + 1; i < n; i++)
+ if (stateVisited[i] == false)
+ break;
+ startState = i;
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/F2wCycleBasedPolyLCG.java b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedPolyLCG.java
new file mode 100644
index 0000000..c839502
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedPolyLCG.java
@@ -0,0 +1,179 @@
+
+
+/*
+ * Class: F2wCycleBasedPolyLCG
+ * Description: point set based upon a linear congruential sequence in a
+ finite field
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.*;
+import cern.colt.list.*;
+
+
+
+/**
+ * This class creates a point set based upon
+ * a linear congruential sequence in the finite field
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]/<I>P</I>(<I>z</I>)</SPAN>.
+ * The recurrence is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>q</I><SUB>n</SUB>(<I>z</I>) = <I>z</I><SUP>s</SUP><I>q</I><SUB>n-1</SUB>(<I>z</I>) mod <I>P</I>(<I>z</I>)
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)∈<B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]</SPAN> has degree <SPAN CLASS="MATH"><I>r</I></SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>q</I><SUB>n</SUB>(<I>z</I>) = <I>q</I><SUB>n, 1</SUB><I>z</I><SUP>r-1</SUP> + <SUP> ... </SUP> + <I>q</I><SUB>n, r</SUB>∈<B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]/<I>P</I>(<I>z</I>)</SPAN>.
+ * The parameter <SPAN CLASS="MATH"><I>s</I></SPAN> is
+ * called the stepping parameter of the recurrence.
+ * The polynomial <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)</SPAN> is not necessarily the characteristic polynomial
+ * of this recurrence, but it can still be used to store the parameters of
+ * the recurrence.
+ * In the implementation, it is stored in an object of the class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure}. See the description
+ * of this class for more details on how the polynomial
+ * is stored.
+ *
+ * <P>
+ * Let
+ * <SPAN CLASS="MATH"><B>x</B> = (<I>x</I><SUP>(0)</SUP>,…, <I>x</I><SUP>(p-1)</SUP>)∈<B>F</B><SUB>2</SUB><SUP>p</SUP></SPAN> be a <SPAN CLASS="MATH"><I>p</I></SPAN>-bit vector.
+ * Let us define the function
+ * <SPAN CLASS="MATH"><I>φ</I>(<B>x</B>) = ∑<SUB>i=1</SUB><SUP>p</SUP>2<SUP>-i</SUP><I>x</I><SUP>(i-1)</SUP></SPAN>.
+ * The point set in <SPAN CLASS="MATH"><I>t</I></SPAN> dimensions produced by this class is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * {(<I>φ</I>(<B>y</B><SUB>0</SUB>), <I>φ</I>(<B>y</B><SUB>1</SUB>),…, <I>φ</I>(<B>y</B><SUB>t-1</SUB>) : (<B>q</B><SUB>0, 1</SUB>,…,<B>q</B><SUB>0, r-1</SUB>)∈<B>F</B><SUB>2</SUB><SUP>rw</SUP>}
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><B>y</B><SUB>n</SUB> = (<B>q</B><SUB>n, 1</SUB>,…,<B>q</B><SUB>n, r</SUB>)</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><B>q</B><SUB>n, i</SUB></SPAN>
+ * is the representation of <SPAN CLASS="MATH"><I>q</I><SUB>n, i</SUB></SPAN> under the polynomial basis of
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN> over
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2</SUB></SPAN>.
+ *
+ */
+public class F2wCycleBasedPolyLCG extends CycleBasedPointSetBase2 {
+
+ private F2wStructure param;
+
+
+ /**
+ * Constructs a point set with <SPAN CLASS="MATH">2<SUP>rw</SUP></SPAN> points. See the description of the class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure}
+ * for the meaning of the parameters.
+ *
+ */
+ public F2wCycleBasedPolyLCG (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[])
+ /* *
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ numBits = param.numBits;
+ normFactor = param.normFactor;
+ EpsilonHalf = param.EpsilonHalf;
+ fillCyclesPolyLCG ();
+ }
+
+
+ /**
+ * Constructs a point set after reading its parameters from
+ * file <TT>filename</TT>; the parameters are located at line numbered <TT>no</TT>
+ * of <TT>filename</TT>. The available files are listed in the description of class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure}.
+ *
+ */
+ public F2wCycleBasedPolyLCG (String filename, int no)
+ {
+ param = new F2wStructure (filename, no);
+ numBits = param.numBits;
+ normFactor = param.normFactor;
+ fillCyclesPolyLCG ();
+ }
+
+
+
+ public String toString ()
+ {
+ String s = "F2wCycleBasedPolyLCG:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+ private void fillCyclesPolyLCG ()
+ {
+ int n = 1 << param.getLog2N();
+ int i;
+ int mask1 = (1 << (31 - param.r * param.w)) - 1;
+ int mask2 = ~mask1;
+ RandomStream random = new MRG32k3a();
+ IntArrayList c; // Array used to store the current cycle.
+ int currentState; // The state currently visited.
+
+ boolean stateVisited[] = new boolean[n];
+ // Indicates which states have been visited so far.
+ for (i = 0; i < n; i++)
+ stateVisited[i] = false;
+ int startState = 0; // First state of the cycle currently considered.
+ numPoints = 0;
+ while (startState < n) {
+ stateVisited[startState] = true;
+ c = new IntArrayList ();
+ param.state = startState << param.S;
+ c.add (param.state);
+ // c.add ((state & mask2) | (mask1 &
+ // (random.nextInt(0,2147483647))));
+ param.output = param.F2wPolyLCG ();
+ // Warning: watch for overflow !!!
+ while (param.state != (startState << param.S)) {
+ stateVisited[param.state >> param.S] = true;
+ // c.add ((param.state&mask2) | (mask1 &
+ // (random.nextInt(0,2147483647))));
+ c.add (param.state);
+ param.output = param.F2wPolyLCG ();
+ }
+ addCycle (c);
+ for (i = startState + 1; i < n; i++)
+ if (stateVisited[i] == false)
+ break;
+ startState = i;
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/F2wCycleBasedPolyLCG.tex b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedPolyLCG.tex
new file mode 100644
index 0000000..122efda
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wCycleBasedPolyLCG.tex
@@ -0,0 +1,180 @@
+\defmodule{F2wCycleBasedPolyLCG}
+
+This class creates a point set based upon
+a linear congruential sequence in the finite field
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]/P(z)$.
+The recurrence is
+$$
+q_n(z) = z^s q_{n-1}(z)\ \mod P(z)
+$$
+where $P(z)\in \latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]$ has degree $r$ and
+$q_n(z) = q_{n,1} z^{r-1} + \cdots + q_{n,r} \in
+ \latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]/P(z)$.
+The parameter $s$ is
+called the stepping parameter of the recurrence.
+The polynomial $P(z)$ is not necessarily the characteristic polynomial
+of this recurrence, but it can still be used to store the parameters of
+ the recurrence.
+In the implementation, it is stored in an object of the class
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}. See the description
+of this class for more details on how the polynomial
+is stored.
+
+
+Let $\mathbf{x} = (x^{(0)}, \ldots, x^{(p-1)}) \in
+ \latex{\mathbb{F}}\html{\mathbf{F}}_{2}^p$ be a $p$-bit vector.
+Let us define the function $\phi(\mathbf{x}) =
+\sum_{i=1}^{p} 2^{-i} x^{(i-1)}$.
+The point set in $t$ dimensions produced by this class is
+$$
+\{ (\phi(\mathbf{y}_0),\phi(\mathbf{y}_1),\ldots,\phi(\mathbf{y}_{t-1}):
+ (\mathbf{q}_{0,1},\ldots,\mathbf{q}_{0,r-1})\in
+ \latex{\mathbb{F}}\html{\mathbf{F}}_{2}^{rw}\}
+$$
+where $\mathbf{y}_n = (\mathbf{q}_{n,1},\ldots,\mathbf{q}_{n,r})$,
+ $\mathbf{q}_{n,i}$
+ is the representation of $q_{n,i}$ under the polynomial basis of
+$\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$ over
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_2$.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: F2wCycleBasedPolyLCG
+ * Description: point set based upon a linear congruential sequence in a
+ finite field
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups; \begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.*;
+import cern.colt.list.*;
+\end{hide}
+
+
+public class F2wCycleBasedPolyLCG extends CycleBasedPointSetBase2 \begin{hide} {
+
+ private F2wStructure param;
+\end{hide}\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public F2wCycleBasedPolyLCG (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[]) \begin{hide}
+ /**
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ numBits = param.numBits;
+ normFactor = param.normFactor;
+ EpsilonHalf = param.EpsilonHalf;
+ fillCyclesPolyLCG ();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a point set with $2^{rw}$ points. See the description of the class
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}
+ for the meaning of the parameters.
+ \end{tabb}
+\begin{code}
+
+ public F2wCycleBasedPolyLCG (String filename, int no) \begin{hide}
+ {
+ param = new F2wStructure (filename, no);
+ numBits = param.numBits;
+ normFactor = param.normFactor;
+ fillCyclesPolyLCG ();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a point set after reading its parameters from
+ file \texttt{filename}; the parameters are located at line numbered \texttt{no}
+ of \texttt{filename}. The available files are listed in the description of class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+
+ public String toString ()
+ {
+ String s = "F2wCycleBasedPolyLCG:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+ private void fillCyclesPolyLCG ()
+ {
+ int n = 1 << param.getLog2N();
+ int i;
+ int mask1 = (1 << (31 - param.r * param.w)) - 1;
+ int mask2 = ~mask1;
+ RandomStream random = new MRG32k3a();
+ IntArrayList c; // Array used to store the current cycle.
+ int currentState; // The state currently visited.
+
+ boolean stateVisited[] = new boolean[n];
+ // Indicates which states have been visited so far.
+ for (i = 0; i < n; i++)
+ stateVisited[i] = false;
+ int startState = 0; // First state of the cycle currently considered.
+ numPoints = 0;
+ while (startState < n) {
+ stateVisited[startState] = true;
+ c = new IntArrayList ();
+ param.state = startState << param.S;
+ c.add (param.state);
+ // c.add ((state & mask2) | (mask1 &
+ // (random.nextInt(0,2147483647))));
+ param.output = param.F2wPolyLCG ();
+ // Warning: watch for overflow !!!
+ while (param.state != (startState << param.S)) {
+ stateVisited[param.state >> param.S] = true;
+ // c.add ((param.state&mask2) | (mask1 &
+ // (random.nextInt(0,2147483647))));
+ c.add (param.state);
+ param.output = param.F2wPolyLCG ();
+ }
+ addCycle (c);
+ for (i = startState + 1; i < n; i++)
+ if (stateVisited[i] == false)
+ break;
+ startState = i;
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/F2wNetLFSR.java b/source/umontreal/iro/lecuyer/hups/F2wNetLFSR.java
new file mode 100644
index 0000000..d583527
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wNetLFSR.java
@@ -0,0 +1,128 @@
+
+
+/*
+ * Class: F2wNetLFSR
+ * Description: digital net in base 2 starting from a linear feedback
+ shift register generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class implements a digital net in base 2 starting from a
+ * linear feedback shift register generator. It is exactly the same
+ * point set as the one defined in the class
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedLFSR F2wCycleBasedLFSR}.
+ * See the description
+ * of this class for more details on the way the point set is constructed.
+ *
+ * <P>
+ * Constructing a point set using this class, instead of using
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedLFSR F2wCycleBasedLFSR},
+ * makes SSJ construct a digital net in base 2. This is useful if one
+ * wants to randomize using one of the randomizations included in the class
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet DigitalNet}.
+ *
+ */
+public class F2wNetLFSR extends DigitalNetBase2
+{
+ private F2wStructure param;
+
+ /* *
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+
+
+
+ /**
+ * Constructs a point set with <SPAN CLASS="MATH">2<SUP>rw</SUP></SPAN> points. See the description of the class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure}
+ * for the meaning of the parameters.
+ *
+ */
+ public F2wNetLFSR (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[], int dim)
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ param.initParamLFSR ();
+ initNet (r, w, dim);
+ }
+
+
+
+ /**
+ * Constructs a point set after reading its parameters from
+ * file <TT>filename</TT>; the parameters are located at line numbered <TT>no</TT>
+ * of <TT>filename</TT>. The available files are listed in the description of class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure}.
+ *
+ */
+ public F2wNetLFSR (String filename, int no, int dim)
+ {
+ param = new F2wStructure (filename, no);
+ param.initParamLFSR ();
+ initNet (param.r, param.w, dim);
+ }
+
+
+
+ public String toString ()
+ {
+ String s = "F2wNetLFSR:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+
+ private void initNet (int r, int w, int dim)
+ {
+ numCols = r * w;
+ numRows = 31;
+ outDigits = 31;
+ numPoints = (1 << numCols);
+ this.dim = dim;
+ normFactor = 1.0 / (1L << 31);
+ genMat = new int[dim * numCols];
+
+ for (int j = 0; j < numCols; j++) {
+ param.state = 1 << (r * w - 1 - j);
+ param.initF2wLFSR ();
+ genMat[j] = param.output;
+ for (int i = 1; i < dim; i++) {
+ param.F2wLFSR ();
+ genMat[i * numCols + j] = param.output;
+ }
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/F2wNetLFSR.tex b/source/umontreal/iro/lecuyer/hups/F2wNetLFSR.tex
new file mode 100644
index 0000000..399e951
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wNetLFSR.tex
@@ -0,0 +1,135 @@
+\defmodule{F2wNetLFSR}
+
+This class implements a digital net in base 2 starting from a
+linear feedback shift register generator. It is exactly the same
+point set as the one defined in the class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedLFSR}.
+ See the description
+of this class for more details on the way the point set is constructed.
+
+Constructing a point set using this class, instead of using
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedLFSR},
+makes SSJ construct a digital net in base 2. This is useful if one
+wants to randomize using one of the randomizations included in the class
+\externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: F2wNetLFSR
+ * Description: digital net in base 2 starting from a linear feedback
+ shift register generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups; \begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class F2wNetLFSR extends DigitalNetBase2 \begin{hide}
+{
+ private F2wStructure param;
+
+ /**
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public F2wNetLFSR (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[], int dim) \begin{hide}
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ param.initParamLFSR ();
+ initNet (r, w, dim);
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a point set with $2^{rw}$ points. See the description of the class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}
+for the meaning of the parameters.
+ \end{tabb}
+\begin{code}
+
+ public F2wNetLFSR (String filename, int no, int dim) \begin{hide}
+ {
+ param = new F2wStructure (filename, no);
+ param.initParamLFSR ();
+ initNet (param.r, param.w, dim);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a point set after reading its parameters from
+ file \texttt{filename}; the parameters are located at line numbered \texttt{no}
+ of \texttt{filename}. The available files are listed in the description of class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+
+ public String toString ()
+ {
+ String s = "F2wNetLFSR:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+
+ private void initNet (int r, int w, int dim)
+ {
+ numCols = r * w;
+ numRows = 31;
+ outDigits = 31;
+ numPoints = (1 << numCols);
+ this.dim = dim;
+ normFactor = 1.0 / (1L << 31);
+ genMat = new int[dim * numCols];
+
+ for (int j = 0; j < numCols; j++) {
+ param.state = 1 << (r * w - 1 - j);
+ param.initF2wLFSR ();
+ genMat[j] = param.output;
+ for (int i = 1; i < dim; i++) {
+ param.F2wLFSR ();
+ genMat[i * numCols + j] = param.output;
+ }
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/F2wNetPolyLCG.java b/source/umontreal/iro/lecuyer/hups/F2wNetPolyLCG.java
new file mode 100644
index 0000000..544ded5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wNetPolyLCG.java
@@ -0,0 +1,114 @@
+
+
+/*
+ * Class: F2wNetPolyLCG
+ * Description: digital nets in base 2 starting from a polynomial LCG
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class implements a digital net in base 2 starting from a
+ * polynomial LCG in
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]/<I>P</I>(<I>z</I>)</SPAN>.
+ * It is exactly the same
+ * point set as the one defined in the class
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedPolyLCG F2wCycleBasedPolyLCG}.
+ * See the description
+ * of this class for more details on the way the point set is constructed.
+ *
+ * <P>
+ * Constructing a point set using this class, instead of using
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedPolyLCG F2wCycleBasedPolyLCG},
+ * makes SSJ construct a digital net in base 2. This is useful if one
+ * wants to randomize using one of the randomizations included in the class
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet DigitalNet}.
+ *
+ * <P>
+ * <SPAN CLASS="textbf">Note: This class in not operational yet!</SPAN>
+ *
+ */
+public class F2wNetPolyLCG extends DigitalNetBase2
+{
+ private F2wStructure param;
+
+ /* *
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+
+
+
+ /**
+ * Constructs a point set with <SPAN CLASS="MATH">2<SUP>rw</SUP></SPAN> points. See the description of the class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure} for the meaning of the
+ * parameters.
+ *
+ */
+ public F2wNetPolyLCG (int type, int w, int r, int modQ, int step,
+ int nbcoeff, int coeff[], int nocoeff[], int dim)
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ initNet (r, w, dim);
+ }
+
+
+
+ /**
+ * Constructs a point set after reading its parameters from
+ * file <TT>filename</TT>; the parameters are located at line numbered <TT>no</TT>
+ * of <TT>filename</TT>. The available files are listed in the description of class
+ * {@link umontreal.iro.lecuyer.hups.F2wStructure F2wStructure}.
+ *
+ */
+ public F2wNetPolyLCG (String filename, int no, int dim)
+ {
+ param = new F2wStructure (filename, no);
+ initNet (param.r, param.w, dim);
+ }
+
+
+
+ public String toString ()
+ {
+ String s = "F2wNetPolyLCG:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+
+ private void initNet (int r, int w, int dim)
+ {
+ normFactor = param.normFactor;
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/F2wNetPolyLCG.tex b/source/umontreal/iro/lecuyer/hups/F2wNetPolyLCG.tex
new file mode 100644
index 0000000..23de088
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wNetPolyLCG.tex
@@ -0,0 +1,120 @@
+\defmodule{F2wNetPolyLCG}
+
+
+This class implements a digital net in base 2 starting from a
+polynomial LCG in $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]/P(z)$.
+ It is exactly the same
+point set as the one defined in the class
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedPolyLCG}.
+ See the description
+of this class for more details on the way the point set is constructed.
+
+Constructing a point set using this class, instead of using
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedPolyLCG},
+makes SSJ construct a digital net in base 2. This is useful if one
+wants to randomize using one of the randomizations included in the class
+ \externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}.
+
+\textbf{Note: This class in not operational yet!}
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: F2wNetPolyLCG
+ * Description: digital nets in base 2 starting from a polynomial LCG
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups; \begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class F2wNetPolyLCG extends DigitalNetBase2 \begin{hide}
+{
+ private F2wStructure param;
+
+ /**
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If pgcd<SPAN CLASS="MATH">(<I>a</I>, <I>n</I>) = 1</SPAN>, this constructs a full-period LCG which has two
+ * cycles, one containing 0 and one, the LCG period.
+ *
+ * @param n required number of points and modulo of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public F2wNetPolyLCG (int type, int w, int r, int modQ, int step,
+ int nbcoeff, int coeff[], int nocoeff[], int dim) \begin{hide}
+ {
+ param = new F2wStructure (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ initNet (r, w, dim);
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+Constructs a point set with $2^{rw}$ points. See the description of the class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wStructure} for the meaning of the
+ parameters.
+ \end{tabb}
+\begin{code}
+
+ public F2wNetPolyLCG (String filename, int no, int dim) \begin{hide}
+ {
+ param = new F2wStructure (filename, no);
+ initNet (param.r, param.w, dim);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a point set after reading its parameters from
+ file \texttt{filename}; the parameters are located at line numbered \texttt{no}
+ of \texttt{filename}. The available files are listed in the description of class
+\externalclass{umontreal.iro.lecuyer.hups}{F2wStructure}.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+
+ public String toString ()
+ {
+ String s = "F2wNetPolyLCG:" + PrintfFormat.NEWLINE;
+ return s + param.toString ();
+ }
+
+
+ private void initNet (int r, int w, int dim)
+ {
+ normFactor = param.normFactor;
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/F2wStructure.java b/source/umontreal/iro/lecuyer/hups/F2wStructure.java
new file mode 100644
index 0000000..b99a495
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wStructure.java
@@ -0,0 +1,784 @@
+
+
+/*
+ * Class: F2wStructure
+ * Description: Tools for point sets and sequences based on field F_{2^w}
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * This class implements methods and fields needed by the classes
+ * {@link umontreal.iro.lecuyer.hups.F2wNetLFSR F2wNetLFSR},
+ * {@link umontreal.iro.lecuyer.hups.F2wNetPolyLCG F2wNetPolyLCG},
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedLFSR F2wCycleBasedLFSR} and
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedPolyLCG F2wCycleBasedPolyLCG}.
+ * It also stores the parameters of these point sets which will contain
+ * <SPAN CLASS="MATH">2<SUP>rw</SUP></SPAN> points (see the meaning of <SPAN CLASS="MATH"><I>r</I></SPAN> and <SPAN CLASS="MATH"><I>w</I></SPAN> below).
+ * The parameters can be stored as a polynomial <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)</SPAN> over
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]</SPAN>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>(<I>z</I>) = <I>z</I><SUP>r</SUP> + ∑<SUB>i=1</SUB><SUP>r</SUP><I>b</I><SUB>i</SUB><I>z</I><SUP>r-i</SUP>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>b</I><SUB>i</SUB>∈<B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>r</I></SPAN>.
+ * Let <SPAN CLASS="MATH"><I>ζ</I></SPAN> be the root of an irreducible polynomial
+ *
+ * <SPAN CLASS="MATH"><I>Q</I>(<I>z</I>)∈<B>F</B><SUB>2</SUB>[<I>z</I>]</SPAN>. It is well known
+ * that <SPAN CLASS="MATH"><I>ζ</I></SPAN> is a generator of the finite field
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN>.
+ * The elements of
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN> are
+ * represented using the polynomial ordered
+ * basis
+ * <SPAN CLASS="MATH">(1, <I>ζ</I>,…, <I>ζ</I><SUP>w-1</SUP>)</SPAN>.
+ *
+ * <P>
+ * In this class, only the non-zero coefficients of <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)</SPAN> are stored.
+ * It is stored as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>(<I>z</I>) = <I>z</I><SUP><TT>r</TT></SUP> + ∑<SUB>i=0</SUB><SUP><TT>nbcoeff</TT></SUP><TT>coeff</TT>[<I>i</I>]<I>z</I><SUP><TT>nocoeff</TT>[i]</SUP>
+ * </DIV><P></P>
+ * where the coefficients in <TT>coeff[]</TT> represent the non-zero
+ * coefficients <SPAN CLASS="MATH"><I>b</I><SUB>i</SUB></SPAN> of <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)</SPAN> using the polynomial basis.
+ * The finite field
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN> used is
+ * defined by the polynomial
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>Q</I>(<I>z</I>) = <I>z</I><SUP>w</SUP> + ∑<SUB>i=1</SUB><SUP>w</SUP><I>a</I><SUB>i</SUB><I>z</I><SUP>w-i</SUP>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>a</I><SUB>i</SUB>∈<B>F</B><SUB>2</SUB></SPAN>,
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>w</I></SPAN>. Polynomial <SPAN CLASS="MATH"><I>Q</I></SPAN> is
+ * stored as the bit vector <TT>modQ</TT> =
+ * <SPAN CLASS="MATH">(<I>a</I><SUB>w</SUB>,…, <I>a</I><SUB>1</SUB>)</SPAN>.
+ *
+ * <P>
+ * The class also stores the parameter <TT>step</TT> that is used by the classes
+ * {@link umontreal.iro.lecuyer.hups.F2wNetLFSR F2wNetLFSR},
+ * {@link umontreal.iro.lecuyer.hups.F2wNetPolyLCG F2wNetPolyLCG},
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedLFSR F2wCycleBasedLFSR} and
+ * {@link umontreal.iro.lecuyer.hups.F2wCycleBasedPolyLCG F2wCycleBasedPolyLCG}.
+ * This parameter is such that the implementation of the recurrence
+ * will output a value at every <TT>step</TT> iterations.
+ *
+ */
+public class F2wStructure {
+
+ private final int ALLONES = 2147483647; // 2^31-1 --> 01111...1
+ int w;
+ int r;
+ int numBits;
+ private int modQ;
+ private int step;
+ private int[] coeff;
+ private int[] nocoeff;
+ private int nbcoeff;
+ int S;
+ private int maskw;
+ private int maskrw;
+ private int maskZrm1;
+ private int mask31;
+ private int t;
+ private int masktrw;
+ private int[] maskv;
+ int state;
+ int output; // augmented state
+ double normFactor;
+ double EpsilonHalf;
+ final static int MBL = 140; //maximum of bytes in 1 line
+ //92 bytes for a number of coeff = 15
+
+
+ private void init (int w, int r, int modQ, int step,
+ int nbcoeff, int coeff[], int nocoeff[])
+ {
+ normFactor = 1.0 / (1L << 31); // 4.65661287307739258e-10;
+ EpsilonHalf = 0.5*normFactor;
+ numBits = 31;
+ this.step = step;
+ this.w = w;
+ this.r = r;
+ S = 31 - r * w;
+ mask31 = ~(1 << 31);
+ maskw = (1 << w) - 1;
+ maskrw = ((1 << (r * w)) - 1) << S;
+ maskZrm1 = (ALLONES >> (r * w)) ^ (ALLONES >> ((r - 1) * w));
+ this.modQ = modQ;
+ this.nbcoeff = nbcoeff;
+ this.nocoeff = new int[nbcoeff];
+ this.coeff = new int[nbcoeff];
+ for (int j = 0; j < nbcoeff; j++) {
+ this.nocoeff[j] = nocoeff[j];
+ this.coeff[j] = coeff[j];
+ }
+ }
+
+ void initParamLFSR ()
+ {
+ t = (31 - r * w) / w;
+ masktrw = (~0) << (31 - (t + r) * w) & mask31;
+ maskv = new int[r];
+ for (int j = 0; j < r; j++) {
+ maskv[j] = maskw << (S + ((r - 1 - j) * w));
+ }
+ }
+
+
+
+
+ /**
+ * Constructs a <TT>F2wStructure</TT> object that contains the parameters of a
+ * polynomial in
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]</SPAN>,
+ * as well as a stepping parameter.
+ *
+ */
+ F2wStructure (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[])
+ {
+ init (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ }
+
+
+
+ /**
+ * Constructs a polynomial in
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB>[<I>z</I>]</SPAN>
+ * after reading its parameters from file <TT>filename</TT>;
+ * the parameters of this polynomial are stored at line number
+ * <TT>no</TT> of <TT>filename</TT>.
+ * The files are kept in different
+ * directories depending on the criteria used in the searches for the
+ * parameters defining the polynomials. The different criteria for the
+ * searches and the theory behind it are described in.
+ * The existing files and the number of polynomials they contain are
+ * given in the following tables.
+ * The first table below contains files in subdirectory
+ * <TT>LFSR_equid_max</TT>. The name of each
+ * file indicates the value of <SPAN CLASS="MATH"><I>r</I></SPAN> and <SPAN CLASS="MATH"><I>w</I></SPAN> for the polynomials.
+ * For example, file <TT>f2wR2_W5.dat</TT> in directory
+ * <TT>LFSR_equid_max</TT> contains the parameters of 2358
+ * polynomials with <SPAN CLASS="MATH"><I>r</I> = 2</SPAN> and <SPAN CLASS="MATH"><I>w</I> = 5</SPAN>. For example, to use the 5<SPAN CLASS="textit">-th</SPAN>
+ * polynomial of file <TT>f2wR2_W5.dat</TT>, one may call
+ * <TT>F2wStructure("f2wR2_W5.dat", 5)</TT>.
+ * The files of parameters have been stored at the address
+ * <TT><A NAME="tex2html1"
+ * HREF="http://simul.iro.umontreal.ca/ssj/dataF2w/Panneton/">http://simul.iro.umontreal.ca/ssj/dataF2w/Panneton/</A></TT>.
+ * The files should be copied in the user directory, and passed
+ * as parameter to the constructor.
+ *
+ */
+ F2wStructure (String filename, int no)
+ {
+ // If filename can be found starting from the program's directory,
+ // it will be used; otherwise, the filename in the Jar archive will
+ // be used.
+ BufferedReader input;
+ try {
+ if ((new File (filename)).exists()) {
+ input = new BufferedReader (new FileReader (filename));
+ } else {
+ // does not work anymore since the files and directories have been removed
+ // from package hups and put instead on the WWW page.
+ // Should be read with protocol http as in class DigitalNetFromFile
+ DataInputStream dataInput;
+ dataInput = new DataInputStream (
+ F2wStructure.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataF2w/Panneton/" + filename));
+ input = new BufferedReader (new InputStreamReader (dataInput));
+ }
+ initFromReader (filename, input, no);
+ input.close ();
+
+ } catch (Exception e) {
+ System.out.println ("IO Error: problems finding file " + filename);
+ System.exit (1);
+ }
+ }
+
+
+
+ private int multiplyz (int a, int k)
+ {
+ int i;
+ if (k == 0)
+ return a & maskw;
+ else {
+ for (i = 0; i < k; i++) {
+ if ((1 & a) == 1) {
+ a = (a >> 1) ^ modQ;
+ } else
+ a = a >> 1;
+ }
+ return a & maskw;
+ }
+ }
+
+
+ /**
+ * This method returns the product <SPAN CLASS="MATH"><I>rw</I></SPAN>.
+ *
+ */
+ int getLog2N ()
+ {
+ return r * w;
+ }
+
+
+ /**
+ * Method that multiplies two elements in
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN>.
+ *
+ */
+ int multiply (int a, int b)
+
+ {
+ int i;
+ int res = 0, verif = 1;
+ for (i = 0; i < w; i++) {
+ if ((b & verif) == verif)
+ res ^= multiplyz (a, w - 1 - i);
+ verif <<= 1;
+ }
+ return res & maskw;
+ }
+
+
+ void initF2wLFSR () // Initialisation de l'etat d'un LFSR
+ {
+ int v, d = 0;
+ int tempState;
+
+ tempState = state << S;
+ output = tempState;
+ for (int i = 1; i <= t; i++) {
+ d = 0;
+ for (int j = 0; j < nbcoeff; j++) {
+ v = (tempState & maskv[nocoeff[j]]) >>
+ (S + (r - 1 - nocoeff[j]) * w);
+ d ^= multiply (coeff[j], v);
+ }
+ output |= d << (S - i * w);
+ tempState = (output << (i * w)) & maskrw;
+ }
+ }
+
+
+ void F2wLFSR () // Une iteration d'un LFSR
+ {
+ int v, d = 0;
+ int tempState;
+ for (int i = 0; i < step; i++) {
+ tempState = (output << (t * w)) & maskrw;
+ d = 0;
+ for (int j = 0; j < nbcoeff; j++) {
+ v = (tempState & maskv[nocoeff[j]]) >>
+ (S + (r - 1 - nocoeff[j]) * w);
+ d ^= multiply (coeff[j], v);
+ }
+ output = ((output << w) & masktrw) |
+ (d << (31 - (r + t) * w));
+ }
+ state = (output & maskrw) >> S;
+ }
+
+
+ int F2wPolyLCG () // Une iteration d'un PolyLCG
+ {
+ int Zrm1, d;
+ for (int i = 0; i < step; i++) {
+ Zrm1 = (state & maskZrm1) >> S;
+ state = (state >> w) & maskrw;
+ for (int j = 0; j < nbcoeff; j++)
+ state ^=
+ multiply (coeff[j], Zrm1) << (S + (r - 1 - nocoeff[j]) * w);
+ }
+ return state;
+ }
+
+ /**
+ * Prints the content of file <TT>filename</TT>. See the constructor
+ * above for the conditions on <TT>filename</TT>.
+ *
+ */
+ public static void print (String filename)
+ {
+ BufferedReader input;
+ try {
+ if ((new File (filename)).exists()) {
+ input = new BufferedReader (new FileReader (filename));
+ } else {
+ DataInputStream dataInput;
+ dataInput = new DataInputStream (
+ F2wStructure.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataF2w/" + filename));
+ input = new BufferedReader (new InputStreamReader (dataInput));
+ }
+
+ String s;
+ for (int i = 0; i < 4; i++)
+ input.readLine ();
+ while ((s = input.readLine ()) != null)
+ System.out.println (s);
+ input.close ();
+
+ } catch (Exception e) {
+ System.out.println ("IO Error: problems reading file " + filename);
+ System.exit (1);
+ }
+ }
+
+
+ /**
+ * This method returns a string containing the polynomial <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)</SPAN> and
+ * the stepping parameter.
+ *
+ */
+ public String toString ()
+ {
+ StringBuffer sb = new StringBuffer ("z^");
+ sb.append (r);
+ for (int j = nbcoeff - 1; j >= 0; j--)
+ sb.append (" + (" + coeff[j] + ") z^" + nocoeff[j]);
+ sb.append (" modQ = " + modQ + " w = " + w + " step = " + step);
+ return sb.toString ();
+ }
+
+
+ private void initFromReader (String filename, BufferedReader input, int no)
+ {
+ int w, r, modQ, step, nbcoeff;
+ int coeff[], nocoeff[];
+ StringTokenizer line;
+ int nl = no + 4;
+
+ try {
+ for (int j = 1; j < nl ; j++)
+ input.readLine ();
+
+ line = new StringTokenizer (input.readLine ());
+ w = Integer.parseInt (line.nextToken ());
+ r = Integer.parseInt (line.nextToken ());
+ modQ = Integer.parseInt (line.nextToken ());
+ step = Integer.parseInt (line.nextToken ());
+ nbcoeff = Integer.parseInt (line.nextToken ());
+ nocoeff = new int[nbcoeff];
+ coeff = new int[nbcoeff];
+ for (int i = 0; i < nbcoeff; i++) {
+ coeff[i] = Integer.parseInt (line.nextToken ());
+ nocoeff[i] = Integer.parseInt (line.nextToken ());
+ }
+ init (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ input.close ();
+
+ } catch (Exception e) {
+ System.out.println ("Input Error: problems reading file " + filename);
+ System.exit (1);
+ }
+ }
+ }
+
+ /**
+ * .
+ * <TABLE WIDTH="317">
+ * <TR><TD>
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER" COLSPAN=2><SPAN> Directory LFSR_equid_max</SPAN></TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">Filename</TD>
+ * <TD ALIGN="CENTER">Num of poly.</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W5.dat</TD>
+ * <TD ALIGN="CENTER">2358</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W6.dat</TD>
+ * <TD ALIGN="CENTER">1618</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W7.dat</TD>
+ * <TD ALIGN="CENTER">507</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W8.dat</TD>
+ * <TD ALIGN="CENTER">26</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W9.dat</TD>
+ * <TD ALIGN="CENTER">3</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W4.dat</TD>
+ * <TD ALIGN="CENTER">369</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W5.dat</TD>
+ * <TD ALIGN="CENTER">26</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W3.dat</TD>
+ * <TD ALIGN="CENTER">117</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W4.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W2.dat</TD>
+ * <TD ALIGN="CENTER">165</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W2.dat</TD>
+ * <TD ALIGN="CENTER">36</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR7_W2.dat</TD>
+ * <TD ALIGN="CENTER">10</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR8_W2.dat</TD>
+ * <TD ALIGN="CENTER">11</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR9_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * </TABLE></TD></TR>
+ * </TABLE><TABLE WIDTH="317">
+ * <TR><TD>
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER" COLSPAN=2><SPAN> Directory LFSR_equid_sum</SPAN></TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">Filename</TD>
+ * <TD ALIGN="CENTER">Num of poly.</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W5.dat</TD>
+ * <TD ALIGN="CENTER">2276</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W6.dat</TD>
+ * <TD ALIGN="CENTER">1121</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W7.dat</TD>
+ * <TD ALIGN="CENTER">474</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W8.dat</TD>
+ * <TD ALIGN="CENTER">37</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W9.dat</TD>
+ * <TD ALIGN="CENTER">6</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W4.dat</TD>
+ * <TD ALIGN="CENTER">381</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W5.dat</TD>
+ * <TD ALIGN="CENTER">65</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W6.dat</TD>
+ * <TD ALIGN="CENTER">7</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W3.dat</TD>
+ * <TD ALIGN="CENTER">154</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W4.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W2.dat</TD>
+ * <TD ALIGN="CENTER">688</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W3.dat</TD>
+ * <TD ALIGN="CENTER">5</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W2.dat</TD>
+ * <TD ALIGN="CENTER">70</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR7_W2.dat</TD>
+ * <TD ALIGN="CENTER">9</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR8_W2.dat</TD>
+ * <TD ALIGN="CENTER">3</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR9_W2.dat</TD>
+ * <TD ALIGN="CENTER">3</TD>
+ * </TR>
+ * </TABLE></TD></TR>
+ * </TABLE>
+ * <P><P>
+ * <BR>
+ * <P><TABLE WIDTH="317">
+ * <TR><TD>
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER" COLSPAN=2><SPAN> Directory LFSR_mindist_max</SPAN></TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">Filename</TD>
+ * <TD ALIGN="CENTER">Num of poly.</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W5.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W7.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W8.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W9.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W4.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W5.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W4.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W2.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W2.dat</TD>
+ * <TD ALIGN="CENTER">4</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR7_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR8_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR9_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * </TABLE></TD></TR>
+ * </TABLE><TABLE WIDTH="317">
+ * <TR><TD>
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER" COLSPAN=2><SPAN> Directory LFSR_mindist_sum</SPAN></TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">Filename</TD>
+ * <TD ALIGN="CENTER">Num of poly.</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W5.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W7.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W8.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W9.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W4.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W5.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W4.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W2.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W3.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR7_W2.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR8_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR9_W2.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * </TABLE></TD></TR>
+ * </TABLE>
+ * <P>
+ * <P><P>
+ * <BR><TABLE WIDTH="317">
+ * <TR><TD>
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER" COLSPAN=2><SPAN> Directory LFSR_tvalue_max</SPAN></TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">Filename</TD>
+ * <TD ALIGN="CENTER">Num of poly.</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W5.dat</TD>
+ * <TD ALIGN="CENTER">7</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W7.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W8.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W9.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W4.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W5.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W3.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W4.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W2.dat</TD>
+ * <TD ALIGN="CENTER">14</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W2.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR7_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR8_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR9_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * </TABLE></TD></TR>
+ * </TABLE><TABLE WIDTH="317">
+ * <TR><TD>
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER" COLSPAN=2><SPAN> Directory LFSR_tvalue_sum</SPAN></TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">Filename</TD>
+ * <TD ALIGN="CENTER">Num of poly.</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W5.dat</TD>
+ * <TD ALIGN="CENTER">15</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W7.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W8.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR2_W9.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W4.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W5.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR3_W6.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W3.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR4_W4.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W2.dat</TD>
+ * <TD ALIGN="CENTER">13</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR5_W3.dat</TD>
+ * <TD ALIGN="CENTER">2</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W2.dat</TD>
+ * <TD ALIGN="CENTER">12</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR6_W3.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR7_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR8_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">f2wR9_W2.dat</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * </TR>
+ * </TABLE></TD></TR>
+ * </TABLE>
+ */
diff --git a/source/umontreal/iro/lecuyer/hups/F2wStructure.tex b/source/umontreal/iro/lecuyer/hups/F2wStructure.tex
new file mode 100644
index 0000000..ea271b3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/F2wStructure.tex
@@ -0,0 +1,570 @@
+\defmodule{F2wStructure}
+
+This class implements methods and fields needed by the classes
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wNetLFSR},
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wNetPolyLCG},
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedLFSR} and
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedPolyLCG}.
+It also stores the parameters of these point sets which will contain
+$2^{rw}$ points (see the meaning of $r$ and $w$ below).
+The parameters can be stored as a polynomial $P(z)$ over
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]$
+$$
+P(z) = z^{r} + \sum_{i=1}^{r} b_i z^{r-i}
+$$
+where $b_i\in \latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$ for $i=1,\ldots,r$.
+ Let $\zeta$ be the root of an irreducible polynomial
+ $Q(z)\in \latex{\mathbb{F}}\html{\mathbf{F}}_2[z]$. It is well known
+that $\zeta$ is a generator of the finite field
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$.
+The elements of $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$ are
+ represented using the polynomial ordered
+ basis $(1,\zeta,\ldots,\zeta^{w-1})$.
+
+In this class, only the non-zero coefficients of $P(z)$ are stored.
+ It is stored as
+$$
+P(z) = z^{\mathtt{r}} + \sum_{i=0}^{\mathtt{nbcoeff}} {\mathtt{coeff[}}i{\mathtt{]}}
+ z^{{\mathtt{nocoeff[}}i{\mathtt{]}}}
+$$
+where the coefficients in \texttt{coeff[]} represent the non-zero
+ coefficients $b_i$ of $P(z)$ using the polynomial basis.
+The finite field $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$ used is
+ defined by the polynomial
+$$
+Q(z) = z^{w} + \sum_{i=1}^{w} a_i z^{w-i}
+$$
+where $a_i\in \latex{\mathbb{F}}\html{\mathbf{F}}_{2}$,
+ for $i=1,\ldots,w$. Polynomial $Q$ is
+ stored as the bit vector {\texttt{modQ}} = $(a_w,\ldots,a_1)$.
+
+The class also stores the parameter \texttt{step} that is used by the classes
+\externalclass{umontreal.iro.lecuyer.hups}{F2wNetLFSR},
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wNetPolyLCG},
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedLFSR} and
+ \externalclass{umontreal.iro.lecuyer.hups}{F2wCycleBasedPolyLCG}.
+This parameter is such that the implementation of the recurrence
+ will output a value at every {\texttt{step}} iterations.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: F2wStructure
+ * Description: Tools for point sets and sequences based on field F_{2^w}
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+import java.io.*;
+import java.util.*;
+\end{hide}
+
+public class F2wStructure \begin{hide} {
+
+ private final int ALLONES = 2147483647; // 2^31-1 --> 01111...1
+ int w;
+ int r;
+ int numBits;
+ private int modQ;
+ private int step;
+ private int[] coeff;
+ private int[] nocoeff;
+ private int nbcoeff;
+ int S;
+ private int maskw;
+ private int maskrw;
+ private int maskZrm1;
+ private int mask31;
+ private int t;
+ private int masktrw;
+ private int[] maskv;
+ int state;
+ int output; // augmented state
+ double normFactor;
+ double EpsilonHalf;
+ final static int MBL = 140; //maximum of bytes in 1 line
+ //92 bytes for a number of coeff = 15
+
+
+ private void init (int w, int r, int modQ, int step,
+ int nbcoeff, int coeff[], int nocoeff[])
+ {
+ normFactor = 1.0 / (1L << 31); // 4.65661287307739258e-10;
+ EpsilonHalf = 0.5*normFactor;
+ numBits = 31;
+ this.step = step;
+ this.w = w;
+ this.r = r;
+ S = 31 - r * w;
+ mask31 = ~(1 << 31);
+ maskw = (1 << w) - 1;
+ maskrw = ((1 << (r * w)) - 1) << S;
+ maskZrm1 = (ALLONES >> (r * w)) ^ (ALLONES >> ((r - 1) * w));
+ this.modQ = modQ;
+ this.nbcoeff = nbcoeff;
+ this.nocoeff = new int[nbcoeff];
+ this.coeff = new int[nbcoeff];
+ for (int j = 0; j < nbcoeff; j++) {
+ this.nocoeff[j] = nocoeff[j];
+ this.coeff[j] = coeff[j];
+ }
+ }
+
+ void initParamLFSR ()
+ {
+ t = (31 - r * w) / w;
+ masktrw = (~0) << (31 - (t + r) * w) & mask31;
+ maskv = new int[r];
+ for (int j = 0; j < r; j++) {
+ maskv[j] = maskw << (S + ((r - 1 - j) * w));
+ }
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+
+ F2wStructure (int w, int r, int modQ, int step, int nbcoeff,
+ int coeff[], int nocoeff[]) \begin{hide}
+ {
+ init (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{F2wStructure} object that contains the parameters of a
+ polynomial in $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]$,
+ as well as a stepping parameter.
+\end{tabb}
+\begin{code}
+
+ F2wStructure (String filename, int no)\begin{hide}
+ {
+ // If filename can be found starting from the program's directory,
+ // it will be used; otherwise, the filename in the Jar archive will
+ // be used.
+ BufferedReader input;
+ try {
+ if ((new File (filename)).exists()) {
+ input = new BufferedReader (new FileReader (filename));
+ } else {
+ // does not work anymore since the files and directories have been removed
+ // from package hups and put instead on the WWW page.
+ // Should be read with protocol http as in class DigitalNetFromFile
+ DataInputStream dataInput;
+ dataInput = new DataInputStream (
+ F2wStructure.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataF2w/Panneton/" + filename));
+ input = new BufferedReader (new InputStreamReader (dataInput));
+ }
+ initFromReader (filename, input, no);
+ input.close ();
+
+ } catch (Exception e) {
+ System.out.println ("IO Error: problems finding file " + filename);
+ System.exit (1);
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a polynomial in $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}[z]$
+ after reading its parameters from file {\texttt{filename}};
+ the parameters of this polynomial are stored at line number
+ {\texttt{no}} of {\texttt{filename}}.
+ The files are kept in different
+ directories depending on the criteria used in the searches for the
+ parameters defining the polynomials. The different criteria for the
+ searches and the theory behind it are described in \cite{rPAN04d,rPAN04t}.
+ The existing files and the number of polynomials they contain are
+ given in the following tables.
+ The first table below contains files in subdirectory
+ \texttt{LFSR\_equid\_max}. The name of each
+ file indicates the value of $r$ and $w$ for the polynomials.
+ For example, file \texttt{f2wR2\_W5.dat} in directory
+ \texttt{LFSR\_equid\_max} contains the parameters of 2358
+ polynomials with $r=2$ and $w=5$. For example, to use the 5\textit{-th}
+ polynomial of file \texttt{f2wR2\_W5.dat}, one may call
+ \texttt{F2wStructure("f2wR2\_W5.dat", 5)}.
+ The files of parameters have been stored at the address
+ \url{http://simul.iro.umontreal.ca/ssj/dataF2w/Panneton/}.
+ The files should be copied in the user directory, and passed
+ as parameter to the constructor.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+
+ private int multiplyz (int a, int k)
+ {
+ int i;
+ if (k == 0)
+ return a & maskw;
+ else {
+ for (i = 0; i < k; i++) {
+ if ((1 & a) == 1) {
+ a = (a >> 1) ^ modQ;
+ } else
+ a = a >> 1;
+ }
+ return a & maskw;
+ }
+ }\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ int getLog2N ()\begin{hide}
+ {
+ return r * w;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ This method returns the product $rw$.
+ \end{tabb}
+\begin{code}
+
+ int multiply (int a, int b)\begin{hide}
+
+ {
+ int i;
+ int res = 0, verif = 1;
+ for (i = 0; i < w; i++) {
+ if ((b & verif) == verif)
+ res ^= multiplyz (a, w - 1 - i);
+ verif <<= 1;
+ }
+ return res & maskw;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Method that multiplies two elements in
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$.
+ \end{tabb}
+\begin{code} \begin{hide}
+
+ void initF2wLFSR () // Initialisation de l'etat d'un LFSR
+ {
+ int v, d = 0;
+ int tempState;
+
+ tempState = state << S;
+ output = tempState;
+ for (int i = 1; i <= t; i++) {
+ d = 0;
+ for (int j = 0; j < nbcoeff; j++) {
+ v = (tempState & maskv[nocoeff[j]]) >>
+ (S + (r - 1 - nocoeff[j]) * w);
+ d ^= multiply (coeff[j], v);
+ }
+ output |= d << (S - i * w);
+ tempState = (output << (i * w)) & maskrw;
+ }
+ }
+
+
+ void F2wLFSR () // Une iteration d'un LFSR
+ {
+ int v, d = 0;
+ int tempState;
+ for (int i = 0; i < step; i++) {
+ tempState = (output << (t * w)) & maskrw;
+ d = 0;
+ for (int j = 0; j < nbcoeff; j++) {
+ v = (tempState & maskv[nocoeff[j]]) >>
+ (S + (r - 1 - nocoeff[j]) * w);
+ d ^= multiply (coeff[j], v);
+ }
+ output = ((output << w) & masktrw) |
+ (d << (31 - (r + t) * w));
+ }
+ state = (output & maskrw) >> S;
+ }
+
+
+ int F2wPolyLCG () // Une iteration d'un PolyLCG
+ {
+ int Zrm1, d;
+ for (int i = 0; i < step; i++) {
+ Zrm1 = (state & maskZrm1) >> S;
+ state = (state >> w) & maskrw;
+ for (int j = 0; j < nbcoeff; j++)
+ state ^=
+ multiply (coeff[j], Zrm1) << (S + (r - 1 - nocoeff[j]) * w);
+ }
+ return state;
+ }\end{hide}
+
+ public static void print (String filename)\begin{hide}
+ {
+ BufferedReader input;
+ try {
+ if ((new File (filename)).exists()) {
+ input = new BufferedReader (new FileReader (filename));
+ } else {
+ DataInputStream dataInput;
+ dataInput = new DataInputStream (
+ F2wStructure.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataF2w/" + filename));
+ input = new BufferedReader (new InputStreamReader (dataInput));
+ }
+
+ String s;
+ for (int i = 0; i < 4; i++)
+ input.readLine ();
+ while ((s = input.readLine ()) != null)
+ System.out.println (s);
+ input.close ();
+
+ } catch (Exception e) {
+ System.out.println ("IO Error: problems reading file " + filename);
+ System.exit (1);
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Prints the content of file \texttt{filename}. See the constructor
+ above for the conditions on \texttt{filename}.
+ \end{tabb}
+\begin{code}
+
+ public String toString ()\begin{hide}
+ {
+ StringBuffer sb = new StringBuffer ("z^");
+ sb.append (r);
+ for (int j = nbcoeff - 1; j >= 0; j--)
+ sb.append (" + (" + coeff[j] + ") z^" + nocoeff[j]);
+ sb.append (" modQ = " + modQ + " w = " + w + " step = " + step);
+ return sb.toString ();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ This method returns a string containing the polynomial $P(z)$ and
+ the stepping parameter.
+\end{tabb}
+
+\begin{code} \begin{hide}
+ private void initFromReader (String filename, BufferedReader input, int no)
+ {
+ int w, r, modQ, step, nbcoeff;
+ int coeff[], nocoeff[];
+ StringTokenizer line;
+ int nl = no + 4;
+
+ try {
+ for (int j = 1; j < nl ; j++)
+ input.readLine ();
+
+ line = new StringTokenizer (input.readLine ());
+ w = Integer.parseInt (line.nextToken ());
+ r = Integer.parseInt (line.nextToken ());
+ modQ = Integer.parseInt (line.nextToken ());
+ step = Integer.parseInt (line.nextToken ());
+ nbcoeff = Integer.parseInt (line.nextToken ());
+ nocoeff = new int[nbcoeff];
+ coeff = new int[nbcoeff];
+ for (int i = 0; i < nbcoeff; i++) {
+ coeff[i] = Integer.parseInt (line.nextToken ());
+ nocoeff[i] = Integer.parseInt (line.nextToken ());
+ }
+ init (w, r, modQ, step, nbcoeff, coeff, nocoeff);
+ input.close ();
+
+ } catch (Exception e) {
+ System.out.println ("Input Error: problems reading file " + filename);
+ System.exit (1);
+ }
+ }
+ }
+ \end{hide}
+\end{code}
+
+\tt
+\begin{minipage}{7cm}
+ \begin {tabular}{|c|c|}
+ \multicolumn{2}{c} {{\rm Directory} LFSR\_equid\_max} \\
+\hline
+ Filename & Num of poly. \\
+\hline
+ f2wR2\_W5.dat & 2358 \\
+ f2wR2\_W6.dat & 1618 \\
+ f2wR2\_W7.dat & 507 \\
+ f2wR2\_W8.dat & 26 \\
+ f2wR2\_W9.dat & 3 \\
+ f2wR3\_W4.dat & 369 \\
+ f2wR3\_W5.dat & 26 \\
+ f2wR3\_W6.dat & 1 \\
+ f2wR4\_W3.dat & 117 \\
+ f2wR4\_W4.dat & 1 \\
+ f2wR5\_W2.dat & 165 \\
+ f2wR5\_W3.dat & 1 \\
+ f2wR6\_W2.dat & 36 \\
+ f2wR6\_W3.dat & 1 \\
+ f2wR7\_W2.dat & 10 \\
+ f2wR8\_W2.dat & 11 \\
+ f2wR9\_W2.dat & 1 \\
+\hline
+\end {tabular}
+\end{minipage}
+\hfill
+\begin{minipage}{7cm}
+\begin {tabular}{|c|c|}
+ \multicolumn{2}{c} {{\rm Directory} LFSR\_equid\_sum} \\
+\hline
+ Filename & Num of poly. \\
+\hline
+ f2wR2\_W5.dat & 2276 \\
+ f2wR2\_W6.dat & 1121 \\
+ f2wR2\_W7.dat & 474 \\
+ f2wR2\_W8.dat & 37 \\
+ f2wR2\_W9.dat & 6 \\
+ f2wR3\_W4.dat & 381 \\
+ f2wR3\_W5.dat & 65 \\
+ f2wR3\_W6.dat & 7 \\
+ f2wR4\_W3.dat & 154 \\
+ f2wR4\_W4.dat & 2 \\
+ f2wR5\_W2.dat & 688 \\
+ f2wR5\_W3.dat & 5 \\
+ f2wR6\_W2.dat & 70 \\
+ f2wR6\_W3.dat & 1 \\
+ f2wR7\_W2.dat & 9 \\
+ f2wR8\_W2.dat & 3 \\
+ f2wR9\_W2.dat & 3 \\
+\hline
+\end {tabular}
+\end{minipage}
+\bigskip
+
+\begin{minipage}{7cm}
+\begin {tabular}{|c|c|}
+ \multicolumn{2}{c} {{\rm Directory} LFSR\_mindist\_max} \\
+\hline
+ Filename & Num of poly. \\
+\hline
+ f2wR2\_W5.dat & 1 \\
+ f2wR2\_W6.dat & 1 \\
+ f2wR2\_W7.dat & 2 \\
+ f2wR2\_W8.dat & 2 \\
+ f2wR2\_W9.dat & 1 \\
+ f2wR3\_W4.dat & 2 \\
+ f2wR3\_W5.dat & 2 \\
+ f2wR3\_W6.dat & 1 \\
+ f2wR4\_W3.dat & 1 \\
+ f2wR4\_W4.dat & 1 \\
+ f2wR5\_W2.dat & 2 \\
+ f2wR5\_W3.dat & 1 \\
+ f2wR6\_W2.dat & 4 \\
+ f2wR6\_W3.dat & 1 \\
+ f2wR7\_W2.dat & 1 \\
+ f2wR8\_W2.dat & 1 \\
+ f2wR9\_W2.dat & 1 \\
+\hline
+\end {tabular}
+\end{minipage}
+\hfill
+\begin{minipage}{7cm}
+\begin {tabular}{|c|c|}
+\multicolumn{2}{c} {{\rm Directory} LFSR\_mindist\_sum} \\
+\hline
+ Filename & Num of poly. \\
+\hline
+ f2wR2\_W5.dat & 1 \\
+ f2wR2\_W6.dat & 1 \\
+ f2wR2\_W7.dat & 1 \\
+ f2wR2\_W8.dat & 1 \\
+ f2wR2\_W9.dat & 1 \\
+ f2wR3\_W4.dat & 1 \\
+ f2wR3\_W5.dat & 1 \\
+ f2wR3\_W6.dat & 1 \\
+ f2wR4\_W3.dat & 1 \\
+ f2wR4\_W4.dat & 2 \\
+ f2wR5\_W2.dat & 2 \\
+ f2wR5\_W3.dat & 2 \\
+ f2wR6\_W2.dat & 1 \\
+ f2wR6\_W3.dat & 1 \\
+ f2wR7\_W2.dat & 2 \\
+ f2wR8\_W2.dat & 1 \\
+ f2wR9\_W2.dat & 2 \\
+\hline
+\end {tabular}
+\end{minipage}
+
+\bigskip
+\begin{minipage}{7cm}
+\begin {tabular}{|c|c|}
+ \multicolumn{2}{c} {{\rm Directory} LFSR\_tvalue\_max} \\
+\hline
+ Filename & Num of poly. \\
+\hline
+ f2wR2\_W5.dat & 7 \\
+ f2wR2\_W6.dat & 1 \\
+ f2wR2\_W7.dat & 1 \\
+ f2wR2\_W8.dat & 1 \\
+ f2wR2\_W9.dat & 1 \\
+ f2wR3\_W4.dat & 1 \\
+ f2wR3\_W5.dat & 1 \\
+ f2wR3\_W6.dat & 1 \\
+ f2wR4\_W3.dat & 2 \\
+ f2wR4\_W4.dat & 1 \\
+ f2wR5\_W2.dat & 14 \\
+ f2wR5\_W3.dat & 1 \\
+ f2wR6\_W2.dat & 2 \\
+ f2wR6\_W3.dat & 1 \\
+ f2wR7\_W2.dat & 1 \\
+ f2wR8\_W2.dat & 1 \\
+ f2wR9\_W2.dat & 1 \\
+\hline
+\end {tabular}
+\end{minipage}
+\hfill
+\begin{minipage}{7cm}
+\begin {tabular}{|c|c|}
+ \multicolumn{2}{c} {{\rm Directory} LFSR\_tvalue\_sum} \\
+\hline
+ Filename & Num of poly. \\
+\hline
+ f2wR2\_W5.dat & 15 \\
+ f2wR2\_W6.dat & 1 \\
+ f2wR2\_W7.dat & 1 \\
+ f2wR2\_W8.dat & 2 \\
+ f2wR2\_W9.dat & 1 \\
+ f2wR3\_W4.dat & 1 \\
+ f2wR3\_W5.dat & 1 \\
+ f2wR3\_W6.dat & 1 \\
+ f2wR4\_W3.dat & 2 \\
+ f2wR4\_W4.dat & 1 \\
+ f2wR5\_W2.dat & 13 \\
+ f2wR5\_W3.dat & 2 \\
+ f2wR6\_W2.dat & 12 \\
+ f2wR6\_W3.dat & 1 \\
+ f2wR7\_W2.dat & 1 \\
+ f2wR8\_W2.dat & 1 \\
+ f2wR9\_W2.dat & 1 \\
+\hline
+\end {tabular}
+\end{minipage}
+\rm
diff --git a/source/umontreal/iro/lecuyer/hups/FaureSequence.java b/source/umontreal/iro/lecuyer/hups/FaureSequence.java
new file mode 100644
index 0000000..1979cb0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/FaureSequence.java
@@ -0,0 +1,367 @@
+
+
+/*
+ * Class: FaureSequence
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+
+/**
+ * This class implements digital nets or digital sequences formed by the
+ * first <SPAN CLASS="MATH"><I>n</I> = <I>b</I><SUP>k</SUP></SPAN> points of the Faure sequence in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * Values of <SPAN CLASS="MATH"><I>n</I></SPAN> up to <SPAN CLASS="MATH">2<SUP>31</SUP></SPAN> are allowed.
+ * One has <SPAN CLASS="MATH"><I>r</I> = <I>k</I></SPAN>.
+ * The generator matrices are
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>C</B><SUB>j</SUB> = <B>P</B><SUP>j</SUP> mod <I>b</I>
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>j</I> = 0,..., <I>s</I> - 1</SPAN>, where
+ * <SPAN CLASS="MATH"><B>P</B></SPAN> is a <SPAN CLASS="MATH"><I>k</I>×<I>k</I></SPAN> upper
+ * triangular matrix whose entry <SPAN CLASS="MATH">(<I>l</I>, <I>c</I>)</SPAN> is the number of combinations
+ * of <SPAN CLASS="MATH"><I>l</I></SPAN> objects among <SPAN CLASS="MATH"><I>c</I></SPAN>,
+ * for <SPAN CLASS="MATH"><I>l</I> <= <I>c</I></SPAN> and is 0 for <SPAN CLASS="MATH"><I>l</I> > <I>c</I></SPAN>.
+ * The matrix
+ * <SPAN CLASS="MATH"><B>C</B><SUB>0</SUB></SPAN> is the identity,
+ * <SPAN CLASS="MATH"><B>C</B><SUB>1</SUB> = <B>P</B></SPAN>,
+ * and the other
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>'s can be defined recursively via
+ *
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB> = <B>P</B><B>C</B><SUB>j-1</SUB>mod <I>b</I></SPAN>.
+ * Our implementation uses the recursion
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * Combination(<I>c</I>, <I>l</I> ) = Combination(<I>c</I> - 1, <I>l</I> ) + Combination(<I>c</I> - 1, <I>l</I> - 1)
+ * </DIV><P></P>
+ * to evaluate the binomial coefficients in the matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>,
+ * as suggested by Fox.
+ * The entries <SPAN CLASS="MATH"><I>x</I><SUB>j, l, c</SUB></SPAN> of
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> are computed as follows:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="LEFT"><I>x</I><SUB>j, c, c</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * <TD ALIGN="LEFT"> for <I>c</I> = 0,..., <I>k</I> - 1,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="LEFT"><I>x</I><SUB>j, 0, c</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT"><I>jx</I><SUB>j, 0, c-1</SUB></TD>
+ * <TD ALIGN="LEFT"> for <I>c</I> = 1,..., <I>k</I> - 1,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="LEFT"><I>x</I><SUB>j, l, c</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT"><I>x</I><SUB>j, l-1, c-1</SUB> + <I>jx</I><SUB>j, l, c-1</SUB></TD>
+ * <TD ALIGN="LEFT"> for 2 <= <I>c</I> < <I>l</I> <= <I>k</I> - 1,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="LEFT"><I>x</I><SUB>j, l, c</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT">0</TD>
+ * <TD ALIGN="LEFT"> for <I>c</I> > <I>l</I> or <I>l</I> >= <I>k</I>.</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ *
+ * <P>
+ * For any integer <SPAN CLASS="MATH"><I>m</I> > 0</SPAN> and <SPAN CLASS="MATH"><I>ν</I> >= 0</SPAN>, if we look at the
+ * vector
+ * <SPAN CLASS="MATH">(<I>u</I><SUB>i, j, 1</SUB>,..., <I>u</I><SUB>i, j, m</SUB>)</SPAN> (the first <SPAN CLASS="MATH"><I>m</I></SPAN> digits
+ * of coordinate <SPAN CLASS="MATH"><I>j</I></SPAN> of the output) when <SPAN CLASS="MATH"><I>i</I></SPAN> goes from
+ * <SPAN CLASS="MATH"><I>νb</I><SUP>m</SUP></SPAN> to
+ * <SPAN CLASS="MATH">(<I>ν</I> +1)<I>b</I><SUP>m</SUP> - 1</SPAN>, this vector takes each of its <SPAN CLASS="MATH"><I>b</I><SUP>m</SUP></SPAN>
+ * possible values exactly once.
+ * In particular, for <SPAN CLASS="MATH"><I>ν</I> = 0</SPAN>, <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> visits each value in the
+ * set
+ * <SPAN CLASS="MATH">{0, 1/<I>b</I><SUP>m</SUP>, 2/<I>b</I><SUP>m</SUP>,...,(<I>b</I><SUP>m</SUP> -1)/<I>b</I><SUP>m</SUP>}</SPAN> exactly once, so all
+ * one-dimensional projections of the point set are identical.
+ * However, the values are visited in a different order for
+ * the different values of <SPAN CLASS="MATH"><I>j</I></SPAN> (otherwise all coordinates would be identical).
+ * For <SPAN CLASS="MATH"><I>j</I> = 0</SPAN>, they are visited in the same
+ * order as in the van der Corput sequence in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * <P>
+ * An important property of Faure nets is that for any integers <SPAN CLASS="MATH"><I>m</I> > 0</SPAN>
+ * and <SPAN CLASS="MATH"><I>ν</I> >= 0</SPAN>, the point set
+ *
+ * <SPAN CLASS="MATH">{<SPAN CLASS="MATH"><B><I>u</I></B></SPAN><SUB>i</SUB></SPAN> for
+ * <SPAN CLASS="MATH"><B><I>i</I> = <I>νb</I><SUP>m</SUP>,...,(<I>ν</I> +1)<I>b</I><SUP>m</SUP> -1}</B></SPAN>
+ * is a <SPAN CLASS="MATH"><B>(0, <I>m</I>, <I>s</I>)</B></SPAN>-net in base <SPAN CLASS="MATH"><B><I>b</I></B></SPAN>.
+ * In particular, for <SPAN CLASS="MATH"><B><I>n</I> = <I>b</I><SUP>k</SUP></B></SPAN>, the first <SPAN CLASS="MATH"><B><I>n</I></B></SPAN> points form a
+ * <SPAN CLASS="MATH"><B>(0, <I>k</I>, <I>s</I>)</B></SPAN>-net in base <SPAN CLASS="MATH"><B><I>b</I></B></SPAN>.
+ * The Faure nets are also projection-regular and dimension-stationary.
+ *
+ * <P>
+ * To obtain digital nets from the <SPAN CLASS="textit">generalized Faure sequence</SPAN>
+ * , where
+ * <SPAN CLASS="MATH"><B><B>P</B><SUB>j</SUB></B></SPAN> is left-multiplied by some
+ * invertible matrix
+ * <SPAN CLASS="MATH"><B><B>A</B><SUB>j</SUB></B></SPAN>, it suffices to apply an appropriate
+ * matrix scramble (e.g., via {@link #leftMatrixScramble leftMatrixScramble}).
+ * This changes the order in which <SPAN CLASS="MATH"><B><I>u</I><SUB>i, j</SUB></B></SPAN> visits its different
+ * values, for each coordinate <SPAN CLASS="MATH"><B><I>j</I></B></SPAN>, but does not change the set of values
+ * that are visited. The <SPAN CLASS="MATH"><B>(0, <I>m</I>, <I>s</I>)</B></SPAN>-net property stated above remains valid.
+ *
+ */
+public class FaureSequence extends DigitalSequence {
+
+ // Maximum dimension for the case where b is not specified.
+ // Can be extended by extending the precomputed array prime[].
+ private static final int MAXDIM = 500;
+
+ // For storing the generator matrices for given dim and numPoints.
+ private int[][][] v;
+
+
+
+ /**
+ * Constructs a digital net in base <SPAN CLASS="MATH"><B><I>b</I></B></SPAN>,
+ * with <SPAN CLASS="MATH"><B><I>n</I> = <I>b</I><SUP>k</SUP></B></SPAN> points and <SPAN CLASS="MATH"><B><I>w</I></B></SPAN> output digits,
+ * in <TT>dim</TT> dimensions.
+ * The points are the first <SPAN CLASS="MATH"><B><I>n</I></B></SPAN> points of the Faure sequence.
+ * The generator matrices
+ * <SPAN CLASS="MATH"><B><B>C</B><SUB>j</SUB></B></SPAN> are <SPAN CLASS="MATH"><B><I>r</I>×<I>k</I></B></SPAN>.
+ * Unless, one plans to apply a randomization on more than <SPAN CLASS="MATH"><B><I>k</I></B></SPAN> digits
+ * (e.g., a random digital shift for <SPAN CLASS="MATH"><B><I>w</I> > <I>k</I></B></SPAN> digits, or a linear
+ * scramble yielding <SPAN CLASS="MATH"><B><I>r</I> > <I>k</I></B></SPAN> digits), one should
+ * take <SPAN CLASS="MATH"><B><I>w</I> = <I>r</I> = <I>k</I></B></SPAN> for better computational efficiency.
+ * Restrictions: <TT>dim</TT> <SPAN CLASS="MATH"><B> <= 500</B></SPAN> and
+ * <SPAN CLASS="MATH"><B><I>b</I><SUP>k</SUP> <= 2<SUP>31</SUP></B></SPAN>.
+ *
+ * @param b base
+ *
+ * @param k there will be b^k points
+ *
+ * @param r number of rows in the generator matrices
+ *
+ * @param w number of output digits
+ *
+ * @param dim dimension of the point set
+ *
+ *
+ */
+ public FaureSequence (int b, int k, int r, int w, int dim) {
+ init (b, k, r, w, dim);
+ }
+
+ private void init (int b, int k, int r, int w, int dim) {
+ if (dim < 1)
+ throw new IllegalArgumentException
+ ("Dimension for FaureSequence must be > 1");
+ if ((double)k * Math.log ((double)b) > (31.0 * Math.log (2.0)))
+ throw new IllegalArgumentException
+ ("Trying to construct a FaureSequence with too many points");
+ if (r < k || w < r)
+ throw new IllegalArgumentException
+ ("One must have k <= r <= w for FaureSequence");
+ this.b = b;
+ numCols = k;
+ numRows = r;
+ outDigits = w;
+ this.dim = dim;
+
+ int i, j;
+ numPoints = b;
+ for (i=1; i<k; i++) numPoints *= b;
+
+ // Compute the normalization factors.
+ normFactor = 1.0 / Math.pow ((double) b, (double) outDigits);
+// EpsilonHalf = 0.5*normFactor;
+ double invb = 1.0 / b;
+ factor = new double[outDigits];
+ factor[0] = invb;
+ for (j = 1; j < outDigits; j++)
+ factor[j] = factor[j-1] * invb;
+
+ genMat = new int[dim * numCols][numRows];
+ initGenMat();
+ }
+
+
+
+ /**
+ * Same as {@link #FaureSequence FaureSequence}<TT>(b, k, w, w, dim)</TT>
+ * with base <SPAN CLASS="MATH"><B><I>b</I></B></SPAN> equal to the smallest prime larger or equal to <TT>dim</TT>,
+ * and with <SPAN CLASS="textit">at least</SPAN> <TT>n</TT> points.
+ *
+ * The values of <SPAN CLASS="MATH"><B><I>k</I></B></SPAN>, <SPAN CLASS="MATH"><B><I>r</I></B></SPAN>, and <SPAN CLASS="MATH"><B><I>w</I></B></SPAN> are taken as
+ *
+ * <SPAN CLASS="MATH"><B><I>k</I> = ceil(log<SUB>b</SUB><I>n</I>)</B></SPAN> and
+ *
+ * <SPAN CLASS="MATH"><B><I>r</I> = <I>w</I> = max(<I>k</I>, floor(30/log<SUB>2</SUB><I>b</I>))</B></SPAN>.
+ *
+ * @param n minimal number of points
+ *
+ * @param dim dimension of the point set
+ *
+ *
+ */
+ public FaureSequence (int n, int dim) {
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for Faure net must be > 1 and < " + MAXDIM);
+ b = getSmallestPrime (dim);
+ numCols = (int) Math.ceil (Math.log ((double) n)
+ / Math.log ((double) b));
+ outDigits = (int) Math.floor (Math.log ((double)(1 << (MAXBITS - 1)))
+ / Math.log ((double)b));
+ outDigits = Math.max (outDigits, numCols);
+ numRows = outDigits;
+ init (b, numCols, numRows, outDigits, dim);
+ }
+
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Faure sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+
+ public void extendSequence (int k) {
+ init (b, k, numRows, outDigits, dim);
+ }
+
+
+ // Fills up the generator matrices in genMat for a Faure sequence.
+ // See Glasserman (2004), \cite{fGLA04a}, page 301.
+ private void initGenMat() {
+ int j, c, l;
+ // Initialize C_0 to the identity (for first coordinate).
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ genMat[c][l] = 0;
+ genMat[c][c] = 1;
+ }
+ // Compute C_1, ... ,C_{dim-1}.
+ for (j = 1; j < dim; j++) {
+ genMat[j*numCols][0] = 1;
+ for (c = 1; c < numCols; c++) {
+ genMat[j*numCols+c][c] = 1;
+ genMat[j*numCols+c][0] = (j * genMat[j*numCols+c-1][0]) % b;
+ }
+ for (c = 2; c < numCols; c++) {
+ for (l = 1; l < c; l++)
+ genMat[j*numCols+c][l] = (genMat[j*numCols+c-1][l-1]
+ + j * genMat[j*numCols+c-1][l]) % b;
+ }
+ for (c = 0; c < numCols; c++)
+ for (l = c+1; l < numRows; l++)
+ genMat[j*numCols+c][l] = 0;
+ }
+/*
+ for (j = 0; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numCols; c++)
+ System.out.print (" " + genMat[j*numCols+c][l]);
+ System.out.println ("");
+ }
+ System.out.println ("");
+ }
+*/
+ }
+
+/*
+ // Fills up the generator matrices in genMat for a Faure net.
+ // See Glasserman (2004), \cite{fGLA04a}, page 301.
+ protected void initGenMatNet() {
+ int j, c, l, start;
+ // Initialize C_0 to the reflected identity (for first coordinate).
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ genMat[c][l] = 0;
+ genMat[c][numCols-c-1] = 1;
+ }
+ // Initialize C_1 to the identity (for second coordinate).
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ genMat[numCols+c][l] = 0;
+ genMat[numCols+c][c] = 1;
+ }
+ // Compute C_2, ... ,C_{dim-1}.
+ for (j = 2; j < dim; j++) {
+ start = j * numCols;
+ genMat[start][0] = 1;
+ for (c = 1; c < numCols; c++) {
+ genMat[start+c][c] = 1;
+ genMat[start+c][0] = ((j-1) * genMat[start+c-1][0]) % b;
+ }
+ for (c = 2; c < numCols; c++) {
+ for (l = 1; l < c; l++)
+ genMat[start+c][l] = (genMat[start+c-1][l-1]
+ + (j-1) * genMat[start+c-1][l]) % b;
+ }
+ for (c = 0; c < numCols; c++)
+ for (l = c+1; l < numRows; l++)
+ genMat[start+c][l] = 0;
+ }
+ }
+*/
+
+ // Returns the smallest prime larger or equal to d.
+ private int getSmallestPrime (int d) {
+ return primes[d-1];
+ }
+
+ // Gives the appropriate prime base for each dimension.
+ // Perhaps should be internal to getPrime, and non-static, to avoid
+ // wasting time and memory when this array is not needed ???
+ static final int primes[] =
+ {2,2,3,5,5,7,7,11,11,11,11,13,13,17,17,17,17,19,19,23,
+ 23,23,23,29,29,29,29,29,29,31,31,37,37,37,37,37,37,41,41,41,
+ 41,43,43,47,47,47,47,53,53,53,53,53,53,59,59,59,59,59,59,61,
+ 61,67,67,67,67,67,67,71,71,71,71,73,73,79,79,79,79,79,79,83,
+ 83,83,83,89,89,89,89,89,89,97,97,97,97,97,97,97,97,101,101,101,
+ 101,103,103,107,107,107,107,109,109,113,113,113,113,127,127,127,127,127,127,127,
+ 127,127,127,127,127,127,127,131,131,131,131,137,137,137,137,137,137,139,139,149,
+ 149,149,149,149,149,149,149,149,149,151,151,157,157,157,157,157,157,163,163,163,
+ 163,163,163,167,167,167,167,173,173,173,173,173,173,179,179,179,179,179,179,181,
+ 181,191,191,191,191,191,191,191,191,191,191,193,193,197,197,197,197,199,199,211,
+ 211,211,211,211,211,211,211,211,211,211,211,223,223,223,223,223,223,223,223,223,
+ 223,223,223,227,227,227,227,229,229,233,233,233,233,239,239,239,239,239,239,241,
+ 241,251,251,251,251,251,251,251,251,251,251,257,257,257,257,257,257,263,263,263,
+ 263,263,263,269,269,269,269,269,269,271,271,277,277,277,277,277,277,281,281,281,
+ 281,283,283,293,293,293,293,293,293,293,293,293,293,307,307,307,307,307,307,307,
+ 307,307,307,307,307,307,307,311,311,311,311,313,313,317,317,317,317,331,331,331,
+ 331,331,331,331,331,331,331,331,331,331,331,337,337,337,337,337,337,347,347,347,
+ 347,347,347,347,347,347,347,349,349,353,353,353,353,359,359,359,359,359,359,367,
+ 367,367,367,367,367,367,367,373,373,373,373,373,373,379,379,379,379,379,379,383,
+ 383,383,383,389,389,389,389,389,389,397,397,397,397,397,397,397,397,401,401,401,
+ 401,409,409,409,409,409,409,409,409,419,419,419,419,419,419,419,419,419,419,421,
+ 421,431,431,431,431,431,431,431,431,431,431,433,433,439,439,439,439,439,439,443,
+ 443,443,443,449,449,449,449,449,449,457,457,457,457,457,457,457,457,461,461,461,
+ 461,463,463,467,467,467,467,479,479,479,479,479,479,479,479,479,479,479,479,487,
+ 487,487,487,487,487,487,487,491,491,491,491,499,499,499,499,499,499,499,499,503};
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/FaureSequence.tex b/source/umontreal/iro/lecuyer/hups/FaureSequence.tex
new file mode 100644
index 0000000..6d6e1fa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/FaureSequence.tex
@@ -0,0 +1,337 @@
+\defmodule{FaureSequence}
+
+This class implements digital nets or digital sequences formed by the
+ first $n = b^k$ points of the Faure sequence in base $b$.
+Values of $n$ up to $2^{31}$ are allowed.
+% When the base $b$ is not specified, it is taken as the smallest
+% prime number greater or equal to the selected dimension.
+One has $r = k$.
+The generator matrices are
+\eq
+ \mathbf{C}_j = \mathbf{P}^j\ \mod b
+\endeq
+for $j=0,\dots,s-1$, where $\mathbf{P}$ is a $k\times k$ upper
+ triangular matrix whose entry $(l,c)$ is the number of combinations
+of $l$ objects among $c$\latex{, ${c\choose l}$},
+for $l\le c$ and is 0 for $l > c$.
+% This matrix $\mathbf{P}$ is the transpose of the $k\times k$ \emph{Pascal matrix}.
+The matrix $\mathbf{C}_0$ is the identity, $\mathbf{C}_1 = \mathbf{P}$,
+and the other $\mathbf{C}_j$'s can be defined recursively via
+$\mathbf{C}_j = \mathbf{P} \mathbf{C}_{j-1} \mod b$.
+Our implementation uses the recursion
+\begin{equation}
+\begin{latexonly}
+ {c \choose l} = {{c-1} \choose l} + {{c-1} \choose {l-1}}
+\end{latexonly}
+\begin{htmlonly}
+ \mbox{Combination}(c, l)\ = \ \mbox{Combination}(c-1, l) \; + \;
+ \mbox{Combination}(c-1, l-1)
+\end{htmlonly}
+\end{equation}
+to evaluate the binomial coefficients in the matrices $\mathbf{C}_j$,
+as suggested by Fox\latex{ \cite{rFOX86a} (see also \cite{fGLA04a}, page 301)}.
+The entries $x_{j,l,c}$ of $\mathbf{C}_j$ are computed as follows:
+\[
+\begin{array}{lcll}
+ x_{j,c,c} &=& 1 &\quad\mbox{ for } c=0,\dots,k-1,\\[4pt]
+ x_{j,0,c} &=& j x_{j,0,c-1} &\quad\mbox{ for } c=1,\dots,k-1, \\[4pt]
+ x_{j,l,c} &=& x_{j,l-1,c-1} + j x_{j,l,c-1}
+ &\quad\mbox{ for } 2\le c < l \le k-1, \\[4pt]
+ x_{j,l,c} &=& 0 &\quad\mbox{ for } c>l \mbox{ or } l \ge k.
+\end{array}
+\]
+
+For any integer $m > 0$ and $\nu\ge 0$, if we look at the
+vector $(u_{i,j,1},\dots,u_{i,j,m})$ (the first $m$ digits
+of coordinate $j$ of the output) when $i$ goes from
+$\nu b^m$ to $(\nu+1)b^m - 1$, this vector takes each of its $b^m$
+possible values exactly once.
+In particular, for $\nu = 0$, $u_{i,j}$ visits each value in the
+set $\{0, 1/b^m, 2/b^m, \dots, (b^m-1)/b^m\}$ exactly once, so all
+one-dimensional projections of the point set are identical.
+However, the values are visited in a different order for
+the different values of $j$ (otherwise all coordinates would be identical).
+For $j=0$, they are visited in the same
+order as in the van der Corput sequence in base $b$.
+
+An important property of Faure nets is that for any integers $m > 0$
+and $\nu\ge 0$, the point set
+$\{\bu_i$ for $i = \nu b^m,\dots, (\nu+1)b^m -1\}$
+is a $(0,m,s)$-net in base $b$.
+In particular, for $n = b^k$, the first $n$ points form a
+ $(0,k,s)$-net in base $b$.
+The Faure nets are also projection-regular and dimension-stationary\latex{
+(see \cite{vLEC02a} for definitions of these properties)}.
+
+To obtain digital nets from the \emph{generalized Faure sequence}
+\latex{ \cite{rTEZ95a}}, where $\mathbf{P}_j$ is left-multiplied by some
+invertible matrix $\mathbf{A}_j$, it suffices to apply an appropriate
+matrix scramble (e.g., via \method{leftMatrixScramble}{}).
+This changes the order in which $u_{i,j}$ visits its different
+values, for each coordinate $j$, but does not change the set of values
+that are visited. The $(0,m,s)$-net property stated above remains valid.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FaureSequence
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+\end{hide}
+
+public class FaureSequence extends DigitalSequence \begin{hide} {
+
+ // Maximum dimension for the case where b is not specified.
+ // Can be extended by extending the precomputed array prime[].
+ private static final int MAXDIM = 500;
+
+ // For storing the generator matrices for given dim and numPoints.
+ private int[][][] v;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public FaureSequence (int b, int k, int r, int w, int dim) \begin{hide} {
+ init (b, k, r, w, dim);
+ }
+
+ private void init (int b, int k, int r, int w, int dim) {
+ if (dim < 1)
+ throw new IllegalArgumentException
+ ("Dimension for FaureSequence must be > 1");
+ if ((double)k * Math.log ((double)b) > (31.0 * Math.log (2.0)))
+ throw new IllegalArgumentException
+ ("Trying to construct a FaureSequence with too many points");
+ if (r < k || w < r)
+ throw new IllegalArgumentException
+ ("One must have k <= r <= w for FaureSequence");
+ this.b = b;
+ numCols = k;
+ numRows = r;
+ outDigits = w;
+ this.dim = dim;
+
+ int i, j;
+ numPoints = b;
+ for (i=1; i<k; i++) numPoints *= b;
+
+ // Compute the normalization factors.
+ normFactor = 1.0 / Math.pow ((double) b, (double) outDigits);
+// EpsilonHalf = 0.5*normFactor;
+ double invb = 1.0 / b;
+ factor = new double[outDigits];
+ factor[0] = invb;
+ for (j = 1; j < outDigits; j++)
+ factor[j] = factor[j-1] * invb;
+
+ genMat = new int[dim * numCols][numRows];
+ initGenMat();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a digital net in base $b$,
+ with $n = b^k$ points and $w$ output digits,
+ in \texttt{dim} dimensions.
+ The points are the first $n$ points of the Faure sequence.
+ The generator matrices $\mathbf{C}_j$ are $r\times k$.
+ Unless, one plans to apply a randomization on more than $k$ digits
+ (e.g., a random digital shift for $w > k$ digits, or a linear
+ scramble yielding $r > k$ digits), one should
+ take $w = r = k$ for better computational efficiency.
+ Restrictions: \texttt{dim} $\le 500$ and $b^k \le 2^{31}$.
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{base}
+ \param{k}{there will be b^k points}
+ \param{r}{number of rows in the generator matrices}
+ \param{w}{number of output digits}
+ \param{dim}{dimension of the point set}
+\end{htmlonly}
+\begin{code}
+
+ public FaureSequence (int n, int dim) \begin{hide} {
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for Faure net must be > 1 and < " + MAXDIM);
+ b = getSmallestPrime (dim);
+ numCols = (int) Math.ceil (Math.log ((double) n)
+ / Math.log ((double) b));
+ outDigits = (int) Math.floor (Math.log ((double)(1 << (MAXBITS - 1)))
+ / Math.log ((double)b));
+ outDigits = Math.max (outDigits, numCols);
+ numRows = outDigits;
+ init (b, numCols, numRows, outDigits, dim);
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{FaureSequence}{}\texttt{(b, k, w, w, dim)}
+ with base $b$ equal to the smallest prime larger or equal to \texttt{dim},
+ and with \emph{at least} \texttt{n} points.
+\hrichard{Ce constructeur devrait-il dispara\^\i tre?}
+ The values of $k$, $r$, and $w$ are taken as
+ $k = \lceil \log_b n\rceil$ and
+ $r = w = \max(k, \lfloor 30 / \log_2 b\rfloor)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{minimal number of points}
+ \param{dim}{dimension of the point set}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Faure sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+
+ public void extendSequence (int k) {
+ init (b, k, numRows, outDigits, dim);
+ }
+
+
+ // Fills up the generator matrices in genMat for a Faure sequence.
+ // See Glasserman (2004), \cite{fGLA04a}, page 301.
+ private void initGenMat() {
+ int j, c, l;
+ // Initialize C_0 to the identity (for first coordinate).
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ genMat[c][l] = 0;
+ genMat[c][c] = 1;
+ }
+ // Compute C_1, ... ,C_{dim-1}.
+ for (j = 1; j < dim; j++) {
+ genMat[j*numCols][0] = 1;
+ for (c = 1; c < numCols; c++) {
+ genMat[j*numCols+c][c] = 1;
+ genMat[j*numCols+c][0] = (j * genMat[j*numCols+c-1][0]) % b;
+ }
+ for (c = 2; c < numCols; c++) {
+ for (l = 1; l < c; l++)
+ genMat[j*numCols+c][l] = (genMat[j*numCols+c-1][l-1]
+ + j * genMat[j*numCols+c-1][l]) % b;
+ }
+ for (c = 0; c < numCols; c++)
+ for (l = c+1; l < numRows; l++)
+ genMat[j*numCols+c][l] = 0;
+ }
+/*
+ for (j = 0; j < dim; j++) {
+ for (l = 0; l < numRows; l++) {
+ for (c = 0; c < numCols; c++)
+ System.out.print (" " + genMat[j*numCols+c][l]);
+ System.out.println ("");
+ }
+ System.out.println ("");
+ }
+*/
+ }
+
+/*
+ // Fills up the generator matrices in genMat for a Faure net.
+ // See Glasserman (2004), \cite{fGLA04a}, page 301.
+ protected void initGenMatNet() {
+ int j, c, l, start;
+ // Initialize C_0 to the reflected identity (for first coordinate).
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ genMat[c][l] = 0;
+ genMat[c][numCols-c-1] = 1;
+ }
+ // Initialize C_1 to the identity (for second coordinate).
+ for (c = 0; c < numCols; c++) {
+ for (l = 0; l < numRows; l++)
+ genMat[numCols+c][l] = 0;
+ genMat[numCols+c][c] = 1;
+ }
+ // Compute C_2, ... ,C_{dim-1}.
+ for (j = 2; j < dim; j++) {
+ start = j * numCols;
+ genMat[start][0] = 1;
+ for (c = 1; c < numCols; c++) {
+ genMat[start+c][c] = 1;
+ genMat[start+c][0] = ((j-1) * genMat[start+c-1][0]) % b;
+ }
+ for (c = 2; c < numCols; c++) {
+ for (l = 1; l < c; l++)
+ genMat[start+c][l] = (genMat[start+c-1][l-1]
+ + (j-1) * genMat[start+c-1][l]) % b;
+ }
+ for (c = 0; c < numCols; c++)
+ for (l = c+1; l < numRows; l++)
+ genMat[start+c][l] = 0;
+ }
+ }
+*/
+
+ // Returns the smallest prime larger or equal to d.
+ private int getSmallestPrime (int d) {
+ return primes[d-1];
+ }
+
+ // Gives the appropriate prime base for each dimension.
+ // Perhaps should be internal to getPrime, and non-static, to avoid
+ // wasting time and memory when this array is not needed ???
+ static final int primes[] =
+ {2,2,3,5,5,7,7,11,11,11,11,13,13,17,17,17,17,19,19,23,
+ 23,23,23,29,29,29,29,29,29,31,31,37,37,37,37,37,37,41,41,41,
+ 41,43,43,47,47,47,47,53,53,53,53,53,53,59,59,59,59,59,59,61,
+ 61,67,67,67,67,67,67,71,71,71,71,73,73,79,79,79,79,79,79,83,
+ 83,83,83,89,89,89,89,89,89,97,97,97,97,97,97,97,97,101,101,101,
+ 101,103,103,107,107,107,107,109,109,113,113,113,113,127,127,127,127,127,127,127,
+ 127,127,127,127,127,127,127,131,131,131,131,137,137,137,137,137,137,139,139,149,
+ 149,149,149,149,149,149,149,149,149,151,151,157,157,157,157,157,157,163,163,163,
+ 163,163,163,167,167,167,167,173,173,173,173,173,173,179,179,179,179,179,179,181,
+ 181,191,191,191,191,191,191,191,191,191,191,193,193,197,197,197,197,199,199,211,
+ 211,211,211,211,211,211,211,211,211,211,211,223,223,223,223,223,223,223,223,223,
+ 223,223,223,227,227,227,227,229,229,233,233,233,233,239,239,239,239,239,239,241,
+ 241,251,251,251,251,251,251,251,251,251,251,257,257,257,257,257,257,263,263,263,
+ 263,263,263,269,269,269,269,269,269,271,271,277,277,277,277,277,277,281,281,281,
+ 281,283,283,293,293,293,293,293,293,293,293,293,293,307,307,307,307,307,307,307,
+ 307,307,307,307,307,307,307,311,311,311,311,313,313,317,317,317,317,331,331,331,
+ 331,331,331,331,331,331,331,331,331,331,331,337,337,337,337,337,337,347,347,347,
+ 347,347,347,347,347,347,347,349,349,353,353,353,353,359,359,359,359,359,359,367,
+ 367,367,367,367,367,367,367,373,373,373,373,373,373,379,379,379,379,379,379,383,
+ 383,383,383,389,389,389,389,389,389,397,397,397,397,397,397,397,397,401,401,401,
+ 401,409,409,409,409,409,409,409,409,419,419,419,419,419,419,419,419,419,419,421,
+ 421,431,431,431,431,431,431,431,431,431,431,433,433,439,439,439,439,439,439,443,
+ 443,443,443,449,449,449,449,449,449,457,457,457,457,457,457,457,457,461,461,461,
+ 461,463,463,467,467,467,467,479,479,479,479,479,479,479,479,479,479,479,479,487,
+ 487,487,487,487,487,487,487,491,491,491,491,499,499,499,499,499,499,499,499,503};
+
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/HaltonSequence.java b/source/umontreal/iro/lecuyer/hups/HaltonSequence.java
new file mode 100644
index 0000000..de27490
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/HaltonSequence.java
@@ -0,0 +1,207 @@
+
+
+
+/*
+ * Class: HaltonSequence
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+
+/**
+ * This class implements the sequence of Halton,
+ * which is essentially a modification of Hammersley nets for producing
+ * an infinite sequence of points having low discrepancy.
+ * The <SPAN CLASS="MATH"><I>i</I></SPAN>th point in <SPAN CLASS="MATH"><I>s</I></SPAN> dimensions is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * 1#1<SUB>i</SUB> = (<I>ψ</I><SUB>b<SUB>1</SUB></SUB>(<I>i</I>), <I>ψ</I><SUB>b<SUB>2</SUB></SUB>(<I>i</I>),..., <I>ψ</I><SUB>b<SUB>s</SUB></SUB>(<I>i</I>)),
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1, 2,...</SPAN>, where <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB></SPAN> is the radical inverse function
+ * in base <SPAN CLASS="MATH"><I>b</I></SPAN>, defined in class {@link RadicalInverse}, and where
+ *
+ * <SPAN CLASS="MATH">2 = <I>b</I><SUB>1</SUB> < <SUP> ... </SUP> < <I>b</I><SUB>s</SUB></SPAN> are the <SPAN CLASS="MATH"><I>s</I></SPAN> smallest prime numbers in
+ * increasing order.
+ *
+ * <P>
+ * A fast method is implemented to generate randomized Halton sequences, starting from an arbitrary point <SPAN CLASS="MATH"><I>x</I><SUB>0</SUB></SPAN>.
+ *
+ * <P>
+ * The points can be ``scrambled'' by applying a permutation to the
+ * digits of <SPAN CLASS="MATH"><I>i</I></SPAN> before computing each coordinate, in the same way as for the class
+ * {@link HammersleyPointSet}, for all coordinates <SPAN CLASS="MATH"><I>j</I> >= 0</SPAN>.
+ *
+ */
+public class HaltonSequence extends PointSet {
+ private int[] base; // Vector of prime bases.
+ private int[][] permutation; // Digits permutation, for each dimension.
+ private boolean permuted; // Permute digits?
+ private RadicalInverse[] radinv; // Vector of RadicalInverse's.
+ private int[] start; // starting indices
+ private final static int positiveBitMask = ~Integer.reverse(1);
+
+
+ /**
+ * Constructs a new Halton sequence
+ * in <TT>dim</TT> dimensions.
+ *
+ * @param dim dimension
+ *
+ */
+ public HaltonSequence (int dim) {
+ if (dim < 1)
+ throw new IllegalArgumentException
+ ("Halton sequence must have positive dimension dim");
+ this.dim = dim;
+ numPoints = Integer.MAX_VALUE;
+ base = RadicalInverse.getPrimes (dim);
+ start = new int[dim];
+ java.util.Arrays.fill(start, 0);
+ }
+
+
+ /**
+ * Initializes the Halton sequence starting at point <TT>x0</TT>.
+ * For each coordinate <SPAN CLASS="MATH"><I>j</I></SPAN>, the sequence starts at index <SPAN CLASS="MATH"><I>i</I><SUB>j</SUB></SPAN> such that
+ * <TT>x0[<SPAN CLASS="MATH"><I>j</I></SPAN>]</TT> is the radical inverse of <SPAN CLASS="MATH"><I>i</I><SUB>j</SUB></SPAN>.
+ * The dimension of <TT>x0</TT> must be at least as large as the dimension
+ * of this object.
+ *
+ * @param x0 starting point of the Halton sequence
+ *
+ *
+ */
+ public void setStart (double[] x0) {
+ for (int i = 0; i < dim; i++)
+ start[i] = RadicalInverse.radicalInverseInteger(base[i], x0[i]);
+ }
+
+
+ /**
+ * Initializes the Halton sequence starting at point <TT>x0</TT>.
+ * The dimension of <TT>x0</TT> must be at least as large as the dimension
+ * of this object.
+ *
+ * @param x0 starting point of the Halton sequence
+ *
+ *
+ */
+ public void init (double[] x0) {
+ radinv = new RadicalInverse[dim];
+ for (int i = 0; i < dim; i++)
+ radinv[i] = new RadicalInverse (base[i], x0[i]);
+ }
+
+
+ /**
+ * Permutes the digits using permutations from for all coordinates.
+ * After the method is called, the coordinates <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> are generated via
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>u</I><SUB>i, j</SUB> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>π</I><SUB>j</SUB>[<I>a</I><SUB>r</SUB>]<I>b</I><SUB>j</SUB><SUP>-r-1</SUP>,
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>j</I> = 0,..., <I>s</I> - 1</SPAN>,
+ * where <SPAN CLASS="MATH"><I>π</I><SUB>j</SUB></SPAN> is the Faure-Lemieux (2008) permutation of
+ * <SPAN CLASS="MATH">{0,..., <I>b</I><SUB>j</SUB> -1}</SPAN>.
+ *
+ */
+ public void addFaureLemieuxPermutations() {
+ permutation = new int[dim][];
+ for (int i = 0; i < dim; i++) {
+ permutation[i] = new int[base[i]];
+ RadicalInverse.getFaureLemieuxPermutation (i, permutation[i]);
+ }
+ permuted = true;
+ }
+
+
+
+ /**
+ * Permutes the digits using Faure permutations for all coordinates.
+ * After the method is called, the coordinates <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> are generated via
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>u</I><SUB>i, j</SUB> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>π</I><SUB>j</SUB>[<I>a</I><SUB>r</SUB>]<I>b</I><SUB>j</SUB><SUP>-r-1</SUP>,
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>j</I> = 0,..., <I>s</I> - 1</SPAN>,
+ * where <SPAN CLASS="MATH"><I>π</I><SUB>j</SUB></SPAN> is the Faure permutation of
+ * <SPAN CLASS="MATH">{0,..., <I>b</I><SUB>j</SUB> -1}</SPAN>.
+ *
+ */
+ public void addFaurePermutations() {
+ permutation = new int[dim][];
+ for (int i = 0; i < dim; i++) {
+ permutation[i] = new int[base[i]];
+ RadicalInverse.getFaurePermutation (base[i], permutation[i]);
+ }
+ permuted = true;
+ }
+
+
+
+ /**
+ * Erases the permutations: from now on, the digits will not be
+ * permuted.
+ *
+ */
+ public void ErasePermutations() {
+ permuted = false;
+ permutation = null;
+ }
+
+
+
+
+ public int getNumPoints () {
+ return Integer.MAX_VALUE;
+ }
+
+ public double getCoordinate (int i, int j) {
+ if (radinv != null) {
+ if (!permuted) {
+ return radinv[j].nextRadicalInverse ();
+ } else {
+ throw new UnsupportedOperationException (
+ "Fast radical inverse is not implemented in case of permutation");
+ }
+ } else {
+ int k = start[j] + i;
+ // if overflow, restart at first nonzero point
+ // (Struckmeier restarts at zero)
+ if (k < 0)
+ k = (k & positiveBitMask) + 1;
+ if (permuted)
+ return RadicalInverse.permutedRadicalInverse
+ (base[j], permutation[j], k);
+ else
+ return RadicalInverse.radicalInverse (base[j], k);
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/HaltonSequence.tex b/source/umontreal/iro/lecuyer/hups/HaltonSequence.tex
new file mode 100644
index 0000000..138c012
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/HaltonSequence.tex
@@ -0,0 +1,213 @@
+\defmodule{HaltonSequence}
+
+This class implements the sequence of Halton \cite{rHAL60a},
+which is essentially a modification of Hammersley nets for producing
+an infinite sequence of points having low discrepancy.
+The $i$th point in $s$ dimensions is
+\eq
+ \bu_i = (\psi_{b_1}(i),\psi_{b_2}(i),\dots, \psi_{b_s}(i)),
+ \eqlabel{eq:Halton-point2}
+\endeq
+for $i=0,1,2,\dots$, where $\psi_b$ is the radical inverse function
+in base $b$, defined in class \class{RadicalInverse}, and where
+$2 = b_1 < \cdots < b_s$ are the $s$ smallest prime numbers in
+increasing order.
+
+A fast method is implemented to generate randomized Halton sequences\latex{
+\cite{rSTR95a,vWAN99a}}, starting from an arbitrary point $x_0$.
+
+The points can be ``scrambled'' by applying a permutation to the
+digits of $i$ before computing each coordinate\latex{
+via (\ref{eq:Halton-point})}, in the same way as for the class
+\class{HammersleyPointSet}, for all coordinates $j\ge 0$.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+
+\begin{hide}
+/*
+ * Class: HaltonSequence
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+
+
+public class HaltonSequence extends PointSet\begin{hide} {
+ private int[] base; // Vector of prime bases.
+ private int[][] permutation; // Digits permutation, for each dimension.
+ private boolean permuted; // Permute digits?
+ private RadicalInverse[] radinv; // Vector of RadicalInverse's.
+ private int[] start; // starting indices
+ private final static int positiveBitMask = ~Integer.reverse(1);
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+ public HaltonSequence (int dim) \begin{hide} {
+ if (dim < 1)
+ throw new IllegalArgumentException
+ ("Halton sequence must have positive dimension dim");
+ this.dim = dim;
+ numPoints = Integer.MAX_VALUE;
+ base = RadicalInverse.getPrimes (dim);
+ start = new int[dim];
+ java.util.Arrays.fill(start, 0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a new Halton sequence %% of $n$ points
+ in \texttt{dim} dimensions.
+%% The number of points is infinite.
+ \end{tabb}
+\begin{htmlonly}
+ \param{dim}{dimension}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public void setStart (double[] x0) \begin{hide} {
+ for (int i = 0; i < dim; i++)
+ start[i] = RadicalInverse.radicalInverseInteger(base[i], x0[i]);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Initializes the Halton sequence starting at point \texttt{x0}.
+ For each coordinate $j$, the sequence starts at index $i_j$ such that
+ \texttt{x0[$j$]} is the radical inverse of $i_j$.
+ The dimension of \texttt{x0} must be at least as large as the dimension
+ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x0}{starting point of the Halton sequence}
+\end{htmlonly}
+\begin{code}
+
+ public void init (double[] x0) \begin{hide} {
+ radinv = new RadicalInverse[dim];
+ for (int i = 0; i < dim; i++)
+ radinv[i] = new RadicalInverse (base[i], x0[i]);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Initializes the Halton sequence starting at point \texttt{x0}.
+ The dimension of \texttt{x0} must be at least as large as the dimension
+ of this object.
+%% The number of points is infinite.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x0}{starting point of the Halton sequence}
+\end{htmlonly}
+\begin{code}
+
+ public void addFaureLemieuxPermutations()\begin{hide} {
+ permutation = new int[dim][];
+ for (int i = 0; i < dim; i++) {
+ permutation[i] = new int[base[i]];
+ RadicalInverse.getFaureLemieuxPermutation (i, permutation[i]);
+ }
+ permuted = true;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+Permutes the digits using permutations from \cite{vFAU09a} for all coordinates.
+After the method is called, the coordinates $u_{i,j}$ are generated via
+\[
+ u_{i,j} = \sum_{r=0}^{k-1} \pi_j[a_r] b_j^{-r-1},
+\]
+ for $j=0,\dots,s-1$,
+ where $\pi_j$ is the Faure-Lemieux (2008) permutation of $\{0,\dots,b_j-1\}$.
+ \end{tabb}
+\begin{code}
+
+ public void addFaurePermutations()\begin{hide} {
+ permutation = new int[dim][];
+ for (int i = 0; i < dim; i++) {
+ permutation[i] = new int[base[i]];
+ RadicalInverse.getFaurePermutation (base[i], permutation[i]);
+ }
+ permuted = true;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Permutes the digits using Faure permutations for all coordinates.
+ After the method is called, the coordinates $u_{i,j}$ are generated via
+\[
+ u_{i,j} = \sum_{r=0}^{k-1} \pi_j[a_r] b_j^{-r-1},
+\]
+ for $j=0,\dots,s-1$,
+ where $\pi_j$ is the Faure permutation of $\{0,\dots,b_j-1\}$.
+ \end{tabb}
+\begin{code}
+
+ public void ErasePermutations()\begin{hide} {
+ permuted = false;
+ permutation = null;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Erases the permutations: from now on, the digits will not be
+ permuted.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+
+ public int getNumPoints () {
+ return Integer.MAX_VALUE;
+ }
+
+ public double getCoordinate (int i, int j) {
+ if (radinv != null) {
+ if (!permuted) {
+ return radinv[j].nextRadicalInverse ();
+ } else {
+ throw new UnsupportedOperationException (
+ "Fast radical inverse is not implemented in case of permutation");
+ }
+ } else {
+ int k = start[j] + i;
+ // if overflow, restart at first nonzero point
+ // (Struckmeier restarts at zero)
+ if (k < 0)
+ k = (k & positiveBitMask) + 1;
+ if (permuted)
+ return RadicalInverse.permutedRadicalInverse
+ (base[j], permutation[j], k);
+ else
+ return RadicalInverse.radicalInverse (base[j], k);
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/HammersleyPointSet.java b/source/umontreal/iro/lecuyer/hups/HammersleyPointSet.java
new file mode 100644
index 0000000..753a578
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/HammersleyPointSet.java
@@ -0,0 +1,159 @@
+
+
+/*
+ * Class: HammersleyPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+
+/**
+ * This class implements <SPAN CLASS="textit">Hammersley point sets</SPAN>,
+ * which are defined as follows.
+ * Let
+ * <SPAN CLASS="MATH">2 = <I>b</I><SUB>1</SUB> < <I>b</I><SUB>2</SUB> < <SUP> ... </SUP></SPAN> denote the sequence of all prime
+ * numbers by increasing order.
+ * The Hammersley point set with <SPAN CLASS="MATH"><I>n</I></SPAN> points in <SPAN CLASS="MATH"><I>s</I></SPAN> dimensions contains
+ * the points
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>u</B><SUB>i</SUB> = (<I>i</I>/<I>n</I>, <I>ψ</I><SUB>b<SUB>1</SUB></SUB>(<I>i</I>), <I>ψ</I><SUB>b<SUB>2</SUB></SUB>(<I>i</I>),..., <I>ψ</I><SUB>b<SUB>s-1</SUB></SUB>(<I>i</I>)),
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,..., <I>n</I> - 1</SPAN>, where <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB></SPAN> is the radical inverse function
+ * in base <SPAN CLASS="MATH"><I>b</I></SPAN>, defined in {@link RadicalInverse}.
+ * This class is not a subclass of {@link DigitalNet}, because the basis
+ * is not the same for all coordinates.
+ * We do obtain a net in a generalized sense if
+ *
+ * <SPAN CLASS="MATH"><I>n</I> = <I>b</I><SUB>1</SUB><SUP>k<SUB>1</SUB></SUP> = <I>b</I><SUB>2</SUB><SUP>k<SUB>2</SUB></SUP> = <SUP> ... </SUP> = <I>b</I><SUB>s-1</SUB><SUP>k<SUB>s-1</SUB></SUP></SPAN>
+ * for some integers
+ * <SPAN CLASS="MATH"><I>k</I><SUB>1</SUB>,..., <I>k</I><SUB>s-1</SUB></SPAN>.
+ *
+ * <P>
+ * The points of a Hammersley point set can be ``scrambled'' by applying a
+ * permutation to the digits of <SPAN CLASS="MATH"><I>i</I></SPAN> before computing each coordinate. If
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>i</I> = <I>a</I><SUB>0</SUB> + <I>a</I><SUB>1</SUB><I>b</I><SUB>j</SUB> + ... + <I>a</I><SUB>k<SUB>j</SUB>-1</SUB><I>b</I><SUB>j</SUB><SUP>k<SUB>j</SUB>-1</SUP>,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>π</I><SUB>j</SUB></SPAN> is a permutation of the digits
+ * <SPAN CLASS="MATH">{0,..., <I>b</I><SUB>j</SUB> -1}</SPAN>, then
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>ψ</I><SUB>b<SUB>j</SUB></SUB>(<I>i</I>) = ∑<SUB>r=0</SUB><SUP>k<SUB>j</SUB>-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUB>j</SUB><SUP>-r-1</SUP>
+ * </DIV><P></P>
+ * is replaced by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>u</I><SUB>i, j</SUB> = ∑<SUB>r=0</SUB><SUP>k<SUB>j</SUB>-1</SUP><I>π</I><SUB>j</SUB>[<I>a</I><SUB>r</SUB>]<I>b</I><SUB>j</SUB><SUP>-r-1</SUP>.
+ * </DIV><P></P>
+ * The permutations <SPAN CLASS="MATH"><I>π</I><SUB>j</SUB></SPAN> can be deterministic or random.
+ * One (deterministic) possibility implemented here is to use
+ * the Faure permutation of
+ * <SPAN CLASS="MATH">{0,..., <I>b</I><SUB>j</SUB>}</SPAN> for <SPAN CLASS="MATH"><I>π</I><SUB>j</SUB></SPAN>, for each
+ * coordinate <SPAN CLASS="MATH"><I>j</I> > 0</SPAN>.
+ *
+ */
+public class HammersleyPointSet extends PointSet {
+ private int[] base; // Vector of prime bases.
+ private int[][] permutation; // Digits permutation, for each dimension.
+ private boolean permuted; // Permute digits?
+
+
+
+ /**
+ * Constructs a new Hammersley point set with <TT>n</TT> points in <TT>dim</TT>
+ * dimensions.
+ *
+ * @param n number of points
+ *
+ * @param dim dimension of the point set
+ *
+ */
+ public HammersleyPointSet (int n, int dim) {
+ if (n < 0 || dim < 1)
+ throw new IllegalArgumentException
+ ("Hammersley point sets must have positive dimension and n >= 0");
+ numPoints = n;
+ this.dim = dim;
+ if (dim > 1)
+ base = RadicalInverse.getPrimes (dim - 1);
+ }
+
+
+ /**
+ * Permutes the digits using Faure permutations for all coordinates.
+ * After the method is called, the coordinates <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> are generated via
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>u</I><SUB>i, j</SUB> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>π</I><SUB>j</SUB>[<I>a</I><SUB>r</SUB>]<I>b</I><SUB>j</SUB><SUP>-r-1</SUP>,
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>j</I> = 1,..., <I>s</I> - 1</SPAN> and
+ * <SPAN CLASS="MATH"><I>u</I><SUB>i, 0</SUB> = <I>i</I>/<I>n</I></SPAN>,
+ * where <SPAN CLASS="MATH"><I>π</I><SUB>j</SUB></SPAN> is the Faure permutation of
+ * <SPAN CLASS="MATH">{0,..., <I>b</I><SUB>j</SUB> -1}</SPAN>.
+ *
+ */
+ public void addFaurePermutations() {
+ if (dim > 1) {
+ permutation = new int[dim][];
+ for (int i = 0; i < dim - 1; i++) {
+ permutation[i] = new int[base[i]];
+ RadicalInverse.getFaurePermutation (base[i], permutation[i]);
+ }
+ }
+ permuted = true;
+ }
+
+
+ /**
+ * Erases the Faure permutations: from now on, the digits will not be
+ * Faure permuted.
+ *
+ */
+ public void ErasePermutations() {
+ permuted = false;
+ permutation = null;
+ }
+
+
+
+
+ public double getCoordinate (int i, int j) {
+ if (j == 0)
+ return (double) i / (double) numPoints;
+ if (permuted)
+ return RadicalInverse.permutedRadicalInverse
+ (base[j - 1], permutation[j - 1], i);
+ else
+ return RadicalInverse.radicalInverse (base[j - 1], i);
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/HammersleyPointSet.tex b/source/umontreal/iro/lecuyer/hups/HammersleyPointSet.tex
new file mode 100644
index 0000000..59f6385
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/HammersleyPointSet.tex
@@ -0,0 +1,155 @@
+\defmodule{HammersleyPointSet}
+
+This class implements \emph{Hammersley point sets},
+which are defined as follows.
+Let $2 = b_1 < b_2 < \cdots$ denote the sequence of all prime
+numbers by increasing order.
+The Hammersley point set with $n$ points in $s$ dimensions contains
+the points
+\eq
+ \mathbf{u}_i = (i/n,\psi_{b_1}(i),\psi_{b_2}(i),\dots, \psi_{b_{s-1}}(i)),
+ \eqlabel{eq:Hammersley-point2}
+\endeq
+for $i=0,\dots,n-1$, where $\psi_b$ is the radical inverse function
+in base $b$, defined in \class{RadicalInverse}.
+This class is not a subclass of \class{DigitalNet}, because the basis
+is not the same for all coordinates.
+We do obtain a net in a generalized sense if
+$n = b_1^{k_1} = b_2^{k_2} = \cdots = b_{s-1}^{k_{s-1}}$
+for some integers $k_1,\dots,k_{s-1}$.
+
+The points of a Hammersley point set can be ``scrambled'' by applying a
+permutation to the digits of $i$ before computing each coordinate\latex{
+via (\ref{eq:Hammersley-point})}. If
+\[
+ i = a_0 + a_1 b_j + \dots + a_{k_j-1} b_j^{k_j-1},
+\]
+and $\pi_j$ is a permutation of the digits $\{0,\dots,b_j-1\}$, then
+\[
+ \psi_{b_j}(i) = \sum_{r=0}^{k_j-1} a_r b_j^{-r-1}
+\]
+is replaced \latex{in (\ref{eq:Hammersley-point})} by
+\[
+ u_{i,j}= \sum_{r=0}^{k_j-1} \pi_j[a_r] b_j^{-r-1}.
+\]
+The permutations $\pi_j$ can be deterministic or random.
+One (deterministic) possibility implemented here is to use
+the Faure permutation of $\{0,\dots,b_j\}$ for $\pi_j$, for each
+coordinate $j > 0$.
+
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: HammersleyPointSet
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+
+
+public class HammersleyPointSet extends PointSet\begin{hide} {
+ private int[] base; // Vector of prime bases.
+ private int[][] permutation; // Digits permutation, for each dimension.
+ private boolean permuted; // Permute digits?
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public HammersleyPointSet (int n, int dim) \begin{hide} {
+ if (n < 0 || dim < 1)
+ throw new IllegalArgumentException
+ ("Hammersley point sets must have positive dimension and n >= 0");
+ numPoints = n;
+ this.dim = dim;
+ if (dim > 1)
+ base = RadicalInverse.getPrimes (dim - 1);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a new Hammersley point set with \texttt{n} points in \texttt{dim}
+ dimensions.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{number of points}
+ \param{dim}{dimension of the point set}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public void addFaurePermutations()\begin{hide} {
+ if (dim > 1) {
+ permutation = new int[dim][];
+ for (int i = 0; i < dim - 1; i++) {
+ permutation[i] = new int[base[i]];
+ RadicalInverse.getFaurePermutation (base[i], permutation[i]);
+ }
+ }
+ permuted = true;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Permutes the digits using Faure permutations for all coordinates.
+ After the method is called, the coordinates $u_{i,j}$ are generated via
+\[
+ u_{i,j} = \sum_{r=0}^{k-1} \pi_j[a_r] b_j^{-r-1},
+\]
+ for $j=1,\dots,s-1$ and $u_{i,0}=i/n$,
+ where $\pi_j$ is the Faure permutation of $\{0,\dots,b_j-1\}$.
+ \end{tabb}
+\begin{code}
+
+ public void ErasePermutations()\begin{hide} {
+ permuted = false;
+ permutation = null;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Erases the Faure permutations: from now on, the digits will not be
+ Faure permuted.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ if (j == 0)
+ return (double) i / (double) numPoints;
+ if (permuted)
+ return RadicalInverse.permutedRadicalInverse
+ (base[j - 1], permutation[j - 1], i);
+ else
+ return RadicalInverse.radicalInverse (base[j - 1], i);
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/KorobovLattice.java b/source/umontreal/iro/lecuyer/hups/KorobovLattice.java
new file mode 100644
index 0000000..94caff5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/KorobovLattice.java
@@ -0,0 +1,144 @@
+
+
+/*
+ * Class: KorobovLattice
+ * Description: Korobov lattice
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class implements <SPAN CLASS="textit">Korobov lattices</SPAN>, which represents the same point
+ * sets as in class {@link LCGPointSet}, but implemented differently.
+ * The parameters are the modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and the multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>, for an arbitrary
+ * integer
+ * <SPAN CLASS="MATH">1 <= <I>a</I> < <I>n</I></SPAN>. [When <SPAN CLASS="MATH"><I>a</I></SPAN> is outside the interval <SPAN CLASS="MATH">[1, <I>n</I>)</SPAN>, then we
+ * replace <SPAN CLASS="MATH"><I>a</I></SPAN> by (<SPAN CLASS="MATH"><I>a</I> mod <I>n</I></SPAN>) in all calculations.]
+ * The number of points is <SPAN CLASS="MATH"><I>n</I></SPAN>, their dimension is
+ * <SPAN CLASS="MATH"><I>s</I></SPAN>, and they are defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>u</B><SUB>i</SUB> = (<I>i</I>/<I>n</I>)(1, <I>a</I>, <I>a</I><SUP>2</SUP>,…, <I>a</I><SUP>s-1</SUP>) mod 1
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,..., <I>n</I> - 1</SPAN>.
+ *
+ * <P>
+ * It is also possible to build a ``shifted'' Korobov lattice with the first
+ * <SPAN CLASS="MATH"><I>t</I></SPAN> coordinates rejected. The <SPAN CLASS="MATH"><I>s</I></SPAN>-dimensionnal points are then defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>u</B><SUB>i</SUB> = (<I>i</I>/<I>n</I>)(<I>a</I><SUP>t</SUP>, <I>a</I><SUP>t+1</SUP>, <I>a</I><SUP>t+2</SUP>,…, <I>a</I><SUP>t+s-1</SUP>) mod 1
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,..., <I>n</I> - 1</SPAN> and fixed <SPAN CLASS="MATH"><I>t</I></SPAN>.
+ *
+ */
+public class KorobovLattice extends Rank1Lattice {
+ protected int genA; // multiplier a
+ private int genT; // shift t
+
+ private void initN (int n, int t) {
+ int a = (genA % n) + (genA < 0 ? n : 0);
+ genT = t;
+ long[] B = new long[dim];
+ B[0] = 1;
+ int j;
+ for (j = 0; j < t; j++)
+ B[0] *= a;
+ v[0] = B[0] * normFactor;
+ for (j = 1; j < dim; j++) {
+ B[j] = (a * B[j - 1]) % n;
+ v[j] = normFactor * B[j];
+ }
+ }
+
+ // Method modPower is inherited from Rank1Lattice.
+
+
+ /**
+ * Instantiates a Korobov lattice point set with modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and
+ * multiplier <SPAN CLASS="MATH"><I>a</I></SPAN> in dimension <SPAN CLASS="MATH"><I>s</I></SPAN>.
+ *
+ */
+ public KorobovLattice (int n, int a, int s) {
+ super (n, null, 0);
+ genA = a;
+ dim = s;
+ v = new double[s];
+ initN(n, 0);
+ }
+
+
+ /**
+ * Instantiates a shifted Korobov lattice point set with modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and
+ * multiplier <SPAN CLASS="MATH"><I>a</I></SPAN> in dimension <SPAN CLASS="MATH"><I>s</I></SPAN>. The first <SPAN CLASS="MATH"><I>t</I></SPAN> coordinates of a
+ * standard Korobov lattice are dropped as described above.
+ * The case <SPAN CLASS="MATH"><I>t</I> = 0</SPAN> corresponds to the standard Korobov lattice.
+ *
+ */
+ public KorobovLattice (int n, int a, int s, int t) {
+ super (n, null, 0);
+ if (t < 1)
+ throw new IllegalArgumentException
+ ("KorobovLattice: must have 0 < t");
+ dim = s;
+ genA = a;
+ v = new double[s];
+ initN(n, t);
+ }
+
+
+ /**
+ * Resets the number of points of the lattice to <SPAN CLASS="MATH"><I>n</I></SPAN>. The values of <SPAN CLASS="MATH"><I>s</I></SPAN>,
+ * <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>t</I></SPAN> are unchanged.
+ *
+ */
+ public void setNumPoints (int n) {
+ initN(n, genT);
+ }
+
+
+ /**
+ * Returns the multiplier <SPAN CLASS="MATH"><I>a</I></SPAN> of the lattice.
+ * (The original one before it is reset to <SPAN CLASS="MATH"><I>a</I> mod <I>n</I></SPAN>).
+ *
+ */
+ public int getA() {
+ return genA;
+ }
+
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("KorobovLattice:" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Multiplier a: " + genA + PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/KorobovLattice.tex b/source/umontreal/iro/lecuyer/hups/KorobovLattice.tex
new file mode 100644
index 0000000..4e2e772
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/KorobovLattice.tex
@@ -0,0 +1,147 @@
+\defmodule{KorobovLattice}
+
+This class implements \emph{Korobov lattices}, which represents the same point
+sets as in class \class{LCGPointSet}, but implemented differently.
+The parameters are the modulus $n$ and the multiplier $a$, for an arbitrary
+integer $1 \le a < n$. [When $a$ is outside the interval $[1,n)$, then we
+replace $a$ by ($a \bmod n$) in all calculations.]
+The number of points is $n$, their dimension is
+$s$, and they are defined by
+\[
+ \mathbf{u}_i = (i/n)(1, a, a^2, \ldots, a^{s-1}) \bmod 1
+\]
+for $i=0,\dots,n-1$.
+
+It is also possible to build a ``shifted'' Korobov lattice with the first
+$t$ coordinates rejected. The $s$-dimensionnal points are then defined as
+$$
+ \mathbf{u}_i = (i/n)(a^{t}, a^{t+1}, a^{t+2}, \ldots, a^{t+s-1}) \bmod 1
+$$
+for $i=0,\dots,n-1$ and fixed $t$.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: KorobovLattice
+ * Description: Korobov lattice
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class KorobovLattice extends Rank1Lattice \begin{hide} {
+ protected int genA; // multiplier a
+ private int genT; // shift t
+
+ private void initN (int n, int t) {
+ int a = (genA % n) + (genA < 0 ? n : 0);
+ genT = t;
+ long[] B = new long[dim];
+ B[0] = 1;
+ int j;
+ for (j = 0; j < t; j++)
+ B[0] *= a;
+ v[0] = B[0] * normFactor;
+ for (j = 1; j < dim; j++) {
+ B[j] = (a * B[j - 1]) % n;
+ v[j] = normFactor * B[j];
+ }
+ }
+
+ // Method modPower is inherited from Rank1Lattice.
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+ public KorobovLattice (int n, int a, int s) \begin{hide} {
+ super (n, null, 0);
+ genA = a;
+ dim = s;
+ v = new double[s];
+ initN(n, 0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Instantiates a Korobov lattice point set with modulus $n$ and
+ multiplier $a$ in dimension $s$.
+ \end{tabb}
+\begin{code}
+
+ public KorobovLattice (int n, int a, int s, int t) \begin{hide} {
+ super (n, null, 0);
+ if (t < 1)
+ throw new IllegalArgumentException
+ ("KorobovLattice: must have 0 < t");
+ dim = s;
+ genA = a;
+ v = new double[s];
+ initN(n, t);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Instantiates a shifted Korobov lattice point set with modulus $n$ and
+ multiplier $a$ in dimension $s$. The first $t$ coordinates of a
+ standard Korobov lattice are dropped as described above.
+ The case $t=0$ corresponds to the standard Korobov lattice.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void setNumPoints (int n) \begin{hide} {
+ initN(n, genT);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Resets the number of points of the lattice to $n$. The values of $s$,
+ $a$ and $t$ are unchanged.
+\end{tabb}
+\begin{code}
+
+ public int getA() \begin{hide} {
+ return genA;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the multiplier $a$ of the lattice.
+(The original one before it is reset to $a \bmod n$).
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("KorobovLattice:" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Multiplier a: " + genA + PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/KorobovLatticeSequence.java b/source/umontreal/iro/lecuyer/hups/KorobovLatticeSequence.java
new file mode 100644
index 0000000..f90ea27
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/KorobovLatticeSequence.java
@@ -0,0 +1,125 @@
+
+
+
+/*
+ * Class: KorobovLatticeSequence
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+
+/**
+ * This class implements Korobov lattice sequences, defined as follows.
+ * One selects a <SPAN CLASS="textit">basis</SPAN> <SPAN CLASS="MATH"><I>b</I></SPAN> and a (large) multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * For each integer <SPAN CLASS="MATH"><I>k</I> >= 0</SPAN>, we may consider the
+ * <SPAN CLASS="MATH"><I>n</I></SPAN>-point Korobov lattice with modulus <SPAN CLASS="MATH"><I>n</I> = <I>b</I><SUP>k</SUP></SPAN> and multiplier
+ *
+ * <SPAN CLASS="MATH">ã = <I>a</I> mod <I>n</I></SPAN>.
+ * Its points have the form
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>u</B><SUB>i</SUB> = (<I>a</I><SUP>i</SUP>(1, <I>a</I>, <I>a</I><SUP>2</SUP>,…) mod<I>n</I>)/<I>n</I> = (ã<SUP>i</SUP>(1,ã,ã<SUP>2</SUP>,…) mod<I>n</I>)/<I>n</I>
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,..., <I>n</I> - 1</SPAN>.
+ * For
+ * <SPAN CLASS="MATH"><I>k</I> = 0, 1,...</SPAN>, we have an increasing sequence of lattices
+ * contained in one another.
+ *
+ * <P>
+ * These embedded lattices contain an infinite sequence of points that
+ * can be enumerated as follows:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>u</B><SUB>i</SUB> = <I>ψ</I><SUB>b</SUB>(<I>i</I>)(1, <I>a</I>, <I>a</I><SUP>2</SUP>,…) mod 1.
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN> is the radical inverse function in base <SPAN CLASS="MATH"><I>b</I></SPAN>,
+ * defined in {@link RadicalInverse}.
+ * The first <SPAN CLASS="MATH"><I>n</I> = <I>b</I><SUP>k</SUP></SPAN> points in this sequence are exactly the same as
+ * the <SPAN CLASS="MATH"><I>n</I></SPAN> points in, for each <SPAN CLASS="MATH"><I>k</I> >= 0</SPAN>.
+ *
+ */
+public class KorobovLatticeSequence extends KorobovLattice {
+ int base; // Base for radical inversion
+ int inverse; // global variables for radical inverssion,
+ int n; // since bloody JAVA cannot pass references
+
+ // Method modPower is inherited from Rank1Lattice.
+
+
+
+ /**
+ * Constructs a new lattice sequence with base <TT>b</TT> and
+ * <TT>generator</TT> <SPAN CLASS="MATH">= <I>a</I></SPAN>.
+ *
+ * @param b number of points (modulus) is a power of b
+ *
+ * @param a multiplier <SPAN CLASS="MATH"><I>a</I></SPAN> of this lattice sequence
+ *
+ *
+ */
+ public KorobovLatticeSequence (int b, int a) {
+// Pas termine: ne fonctionne pas
+ super (2, 3, 1);
+ if (a < 1)
+ throw new IllegalArgumentException
+ ("KorobovLatticeSequence: Multiplier a must be >= 1");
+// dim = Integer.MAX_VALUE;
+// numPoints = Integer.MAX_VALUE;
+ base = b;
+throw new UnsupportedOperationException ("NOT FINISHED");
+ }
+
+
+ // A very inefficient way of generating the points!
+ public double getCoordinate (int i, int j) {
+ int n;
+ int inverse;
+ if (i == 0)
+ return 0.0;
+ else if (j == 0)
+ return radicalInverse (base, i);
+ else {
+ // integerRadicalInverse (i);
+ n = 1;
+ for (inverse = 0; i > 0; i /= base) {
+ inverse = inverse * base + (i % base);
+ n *= base;
+ }
+ return (double) ((inverse * modPower (genA, j, n)) % n) / (double) n;
+ }
+ }
+
+ // ... has been unrolled in getCoordinate.
+ private void integerRadicalInverse (int i) {
+ // Attention: returns results in variables n and inverse.
+ n = 1;
+ for (inverse = 0; i > 0; i /= base) {
+ inverse = inverse * base + (i % base);
+ n *= base;
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/KorobovLatticeSequence.tex b/source/umontreal/iro/lecuyer/hups/KorobovLatticeSequence.tex
new file mode 100644
index 0000000..942ee77
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/KorobovLatticeSequence.tex
@@ -0,0 +1,130 @@
+\defmodule{KorobovLatticeSequence}
+
+\pierre{This class is not yet fully implemented}
+
+This class implements Korobov lattice sequences, defined as follows.
+One selects a \emph{basis} $b$ and a (large) multiplier $a$.
+For each integer $k\ge 0$, we may consider the
+$n$-point Korobov lattice with modulus $n = b^k$ and multiplier
+$\mbox{\~a} = a \bmod n$.
+Its points have the form
+\eq
+ \mathbf{u}_i = (a^i (1, a, a^2, \ldots) \bmod n) / n
+ = (\mbox{\~a}^i (1, \mbox{\~a}, \mbox{\~a}^2, \ldots) \bmod n) / n
+ \eqlabel{eq:Korobov-seq1}
+\endeq
+for $i=0,\dots,n-1$.
+For $k = 0,1,\dots$, we have an increasing sequence of lattices
+contained in one another.
+
+These embedded lattices contain an infinite sequence of points that
+can be enumerated as follows \cite{vHIC01a}:
+\eq
+ \mathbf{u}_i = \psi_b(i) \left(1, a, a^2, \ldots \right) \bmod 1.
+ \eqlabel{eq:Korobov-seq2}
+\endeq
+where $\psi_b(i)$ is the radical inverse function in base $b$,
+defined in \class{RadicalInverse}.
+The first $n=b^k$ points in this sequence are exactly the same as
+the $n$ points in (\ref{eq:Korobov-seq1}), for each $k\ge 0$.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+
+\begin{hide}
+/*
+ * Class: KorobovLatticeSequence
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+
+
+public class KorobovLatticeSequence extends KorobovLattice \begin{hide} {
+ int base; // Base for radical inversion
+ int inverse; // global variables for radical inverssion,
+ int n; // since bloody JAVA cannot pass references
+
+ // Method modPower is inherited from Rank1Lattice.
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public KorobovLatticeSequence (int b, int a) \begin{hide} {
+// Pas termine: ne fonctionne pas
+ super (2, 3, 1);
+ if (a < 1)
+ throw new IllegalArgumentException
+ ("KorobovLatticeSequence: Multiplier a must be >= 1");
+// dim = Integer.MAX_VALUE;
+// numPoints = Integer.MAX_VALUE;
+ base = b;
+throw new UnsupportedOperationException ("NOT FINISHED");
+ } \end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a new lattice sequence with base \texttt{b} and
+ \texttt{generator} $ = a$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{number of points (modulus) is a power of b}
+ \param{a}{multiplier $a$ of this lattice sequence}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ // A very inefficient way of generating the points!
+ public double getCoordinate (int i, int j) {
+ int n;
+ int inverse;
+ if (i == 0)
+ return 0.0;
+ else if (j == 0)
+ return radicalInverse (base, i);
+ else {
+ // integerRadicalInverse (i);
+ n = 1;
+ for (inverse = 0; i > 0; i /= base) {
+ inverse = inverse * base + (i % base);
+ n *= base;
+ }
+ return (double) ((inverse * modPower (genA, j, n)) % n) / (double) n;
+ }
+ }
+
+ // ... has been unrolled in getCoordinate.
+ private void integerRadicalInverse (int i) {
+ // Attention: returns results in variables n and inverse.
+ n = 1;
+ for (inverse = 0; i > 0; i /= base) {
+ inverse = inverse * base + (i % base);
+ n *= base;
+ }
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/LCGPointSet.java b/source/umontreal/iro/lecuyer/hups/LCGPointSet.java
new file mode 100644
index 0000000..8de5dcd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/LCGPointSet.java
@@ -0,0 +1,133 @@
+
+
+/*
+ * Class: LCGPointSet
+ * Description: point set defined via a linear congruential recurrence
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import cern.colt.list.*;
+
+
+/**
+ * Implements a recurrence-based point set defined via a linear
+ * congruential recurrence of the form
+ * <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB> = <I>ax</I><SUB>i-1</SUB>mod <I>n</I></SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>u</I><SUB>i</SUB> = <I>x</I><SUB>i</SUB>/<I>n</I></SPAN>. The implementation is done by storing the values
+ * of <SPAN CLASS="MATH"><I>u</I><SUB>i</SUB></SPAN> over the set of all cycles of the recurrence.
+ *
+ */
+public class LCGPointSet extends CycleBasedPointSet {
+
+ private int a; // Multiplier.
+
+
+
+
+ /**
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus <SPAN CLASS="MATH"><I>n</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ * If the LCG has full period length <SPAN CLASS="MATH"><I>n</I> - 1</SPAN>,
+ * there are two cycles, the first one containing only 0
+ * and the second one of period length <SPAN CLASS="MATH"><I>n</I> - 1</SPAN>.
+ *
+ * @param n required number of points and modulus of the LCG
+ *
+ * @param a generator <SPAN CLASS="MATH"><I>a</I></SPAN> of the LCG
+ *
+ *
+ */
+ public LCGPointSet (int n, int a) {
+ this.a = a;
+ double invn = 1.0 / (double)n; // 1/n
+ DoubleArrayList c; // Array used to store the current cycle.
+ long currentState; // The state currently visited.
+ int i;
+ boolean stateVisited[] = new boolean[n];
+ // Indicates which states have been visited so far.
+ for (i = 0; i < n; i++)
+ stateVisited[i] = false;
+ int startState = 0; // First state of the cycle currently considered.
+ numPoints = 0;
+ while (startState < n) {
+ stateVisited[startState] = true;
+ c = new DoubleArrayList();
+ c.add (startState * invn);
+ // We use the fact that a "long" has 64 bits in Java.
+ currentState = (startState * (long)a) % (long)n;
+ while (currentState != startState) {
+ stateVisited[(int)currentState] = true;
+ c.add (currentState * invn);
+ currentState = (currentState * (long)a) % (long)n;
+ }
+ addCycle (c);
+ for (i = startState+1; i < n; i++)
+ if (stateVisited[i] == false)
+ break;
+ startState = i;
+ }
+ }
+
+
+ /**
+ * Constructs and stores the set of cycles for an LCG with
+ * modulus
+ * <SPAN CLASS="MATH"><I>n</I> = <I>b</I><SUP>e</SUP> + <I>c</I></SPAN> and multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public LCGPointSet (int b, int e, int c, int a) {
+ this (computeModulus (b, e, c), a);
+ }
+
+ private static int computeModulus (int b, int e, int c) {
+ int n;
+ int i;
+ if (b == 2)
+ n = (1 << e);
+ else {
+ for (i = 1, n = b; i < e; i++) n *= b;
+ }
+ n += c;
+ return n;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("LCGPointSet:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ sb.append (PrintfFormat.NEWLINE + "Multiplier a: ");
+ sb.append (a);
+ return sb.toString();
+ }
+
+
+ /**
+ * Returns the value of the multiplier <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public int geta () {
+ return a;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/LCGPointSet.tex b/source/umontreal/iro/lecuyer/hups/LCGPointSet.tex
new file mode 100644
index 0000000..9e64d6f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/LCGPointSet.tex
@@ -0,0 +1,135 @@
+\defmodule {LCGPointSet}
+
+Implements a recurrence-based point set defined via a linear
+congruential recurrence of the form $x_i = a x_{i-1} \mod n$
+and $u_i = x_i / n$. The implementation is done by storing the values
+of $u_i$ over the set of all cycles of the recurrence.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: LCGPointSet
+ * Description: point set defined via a linear congruential recurrence
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import cern.colt.list.*;
+\end{hide}
+
+public class LCGPointSet extends CycleBasedPointSet \begin{hide} {
+
+ private int a; // Multiplier.
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public LCGPointSet (int n, int a) \begin{hide} {
+ this.a = a;
+ double invn = 1.0 / (double)n; // 1/n
+ DoubleArrayList c; // Array used to store the current cycle.
+ long currentState; // The state currently visited.
+ int i;
+ boolean stateVisited[] = new boolean[n];
+ // Indicates which states have been visited so far.
+ for (i = 0; i < n; i++)
+ stateVisited[i] = false;
+ int startState = 0; // First state of the cycle currently considered.
+ numPoints = 0;
+ while (startState < n) {
+ stateVisited[startState] = true;
+ c = new DoubleArrayList();
+ c.add (startState * invn);
+ // We use the fact that a "long" has 64 bits in Java.
+ currentState = (startState * (long)a) % (long)n;
+ while (currentState != startState) {
+ stateVisited[(int)currentState] = true;
+ c.add (currentState * invn);
+ currentState = (currentState * (long)a) % (long)n;
+ }
+ addCycle (c);
+ for (i = startState+1; i < n; i++)
+ if (stateVisited[i] == false)
+ break;
+ startState = i;
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs and stores the set of cycles for an LCG with
+ modulus $n$ and multiplier $a$.
+ If the LCG has full period length $n-1$,
+% pgcd$(a,n)=1$,
+ there are two cycles, the first one containing only 0
+ and the second one of period length $n-1$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{required number of points and modulus of the LCG}
+ \param{a}{generator $a$ of the LCG}
+\end{htmlonly}
+\begin{code}
+
+ public LCGPointSet (int b, int e, int c, int a) \begin{hide} {
+ this (computeModulus (b, e, c), a);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs and stores the set of cycles for an LCG with
+ modulus $n = b^e + c$ and multiplier $a$.
+ \end{tabb}
+\begin{code}\begin{hide}
+ private static int computeModulus (int b, int e, int c) {
+ int n;
+ int i;
+ if (b == 2)
+ n = (1 << e);
+ else {
+ for (i = 1, n = b; i < e; i++) n *= b;
+ }
+ n += c;
+ return n;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("LCGPointSet:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ sb.append (PrintfFormat.NEWLINE + "Multiplier a: ");
+ sb.append (a);
+ return sb.toString();
+ }
+\end{hide}
+
+ public int geta () \begin{hide} {
+ return a;
+ }
+}\end{hide}
+\end{code}
+ \begin{tabb} Returns the value of the multiplier $a$.
+ \end{tabb}
diff --git a/source/umontreal/iro/lecuyer/hups/LMScrambleShift.java b/source/umontreal/iro/lecuyer/hups/LMScrambleShift.java
new file mode 100644
index 0000000..a0758fb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/LMScrambleShift.java
@@ -0,0 +1,92 @@
+
+
+/*
+ * Class: LMScrambleShift
+ * Description: performs a left matrix scramble and adds a random digital shift
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import java.lang.IllegalArgumentException;
+
+
+/**
+ * This class implements a
+ * {@link umontreal.iro.lecuyer.hups.PointSetRandomization PointSetRandomization}
+ * that performs a left matrix scrambling and adds a random digital
+ * shift. Point set must be a
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet DigitalNet} or an
+ * {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ *
+ */
+public class LMScrambleShift extends RandomShift {
+
+
+
+ /**
+ * Empty constructor.
+ *
+ */
+ public LMScrambleShift() {
+ }
+
+
+
+ /**
+ * Sets internal variable <TT>stream</TT> to the given
+ * <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ */
+ public LMScrambleShift (RandomStream stream) {
+ super(stream);
+ }
+
+
+
+ /**
+ * This method calls
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet#leftMatrixScramble(RandomStream) leftMatrixScramble},
+ * then
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet#addRandomShift(RandomStream) addRandomShift}.
+ * If <TT>p</TT> is not a
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet DigitalNet}, an
+ * {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ *
+ * @param p Point set to randomize
+ *
+ *
+ */
+ public void randomize (PointSet p) {
+ if (p instanceof DigitalNet) {
+ ((DigitalNet)p).leftMatrixScramble (stream);
+ ((DigitalNet)p).addRandomShift (stream);
+ } else {
+ throw new IllegalArgumentException("LMScrambleShift"+
+ " can only randomize a DigitalNet");
+ }
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/LMScrambleShift.tex b/source/umontreal/iro/lecuyer/hups/LMScrambleShift.tex
new file mode 100644
index 0000000..c91189a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/LMScrambleShift.tex
@@ -0,0 +1,104 @@
+\defmodule{LMScrambleShift}
+
+This class implements a
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetRandomization}
+that performs a left matrix scrambling and adds a random digital
+shift. Point set must be a
+\externalclass{umontreal.iro.lecuyer.hups}{DigitalNet} or an
+\externalclass{java.lang}{IllegalArgumentException} is thrown.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LMScrambleShift
+ * Description: performs a left matrix scramble and adds a random digital shift
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+\begin{hide}
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import java.lang.IllegalArgumentException;
+\end{hide}
+
+public class LMScrambleShift extends RandomShift \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public LMScrambleShift() \begin{hide} {
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Empty constructor.
+\end{tabb}
+\begin{code}
+
+ public LMScrambleShift (RandomStream stream) \begin{hide} {
+ super(stream);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Sets internal variable \texttt{stream} to the given
+ \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void randomize (PointSet p) \begin{hide} {
+ if (p instanceof DigitalNet) {
+ ((DigitalNet)p).leftMatrixScramble (stream);
+ ((DigitalNet)p).addRandomShift (stream);
+ } else {
+ throw new IllegalArgumentException("LMScrambleShift"+
+ " can only randomize a DigitalNet");
+ }
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ This method calls
+ \externalmethod{umontreal.iro.lecuyer.hups}{DigitalNet}{leftMatrixScramble}{RandomStream},
+ then
+ \externalmethod{umontreal.iro.lecuyer.hups}{DigitalNet}{addRandomShift}{RandomStream}.
+ If \texttt{p} is not a
+ \externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}, an
+\externalclass{java.lang}{IllegalArgumentException} is thrown.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{Point set to randomize}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/NiedSequenceBase2.java b/source/umontreal/iro/lecuyer/hups/NiedSequenceBase2.java
new file mode 100644
index 0000000..3e5195b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/NiedSequenceBase2.java
@@ -0,0 +1,166 @@
+
+
+/*
+ * Class: NiedSequenceBase2
+ * Description: digital Niederreiter sequences in base 2.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class implements digital sequences constructed from the
+ * Niederreiter sequence in base 2.
+ *
+ */
+public class NiedSequenceBase2 extends DigitalSequenceBase2 {
+
+ private static final int MAXDIM = 318; // Maximum dimension.
+ private static final int NUMCOLS = 30; // Maximum number of columns.
+
+
+
+ /**
+ * Constructs a new digital sequence in base 2 from the first <SPAN CLASS="MATH"><I>n</I> = 2<SUP>k</SUP></SPAN> points
+ * of the Niederreiter sequence,
+ * with <SPAN CLASS="MATH"><I>w</I></SPAN> output digits, in <TT>dim</TT> dimensions.
+ * The generator matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> are <SPAN CLASS="MATH"><I>w</I>×<I>k</I></SPAN>.
+ * Restrictions:
+ * <SPAN CLASS="MATH">0 <= <I>k</I> <= 30</SPAN>, <SPAN CLASS="MATH"><I>k</I> <= <I>w</I></SPAN>, and <TT>dim</TT> <SPAN CLASS="MATH"> <= 318</SPAN>.
+ *
+ * @param k there will be 2^k points
+ *
+ * @param w number of output digits
+ *
+ * @param dim dimension of the point set
+ *
+ *
+ */
+ public NiedSequenceBase2 (int k, int w, int dim) {
+ init (k, w, w, dim);
+ }
+
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Niederreiter sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ private void init (int k, int r, int w, int dim) {
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for NiedSequenceBase2 must be > 1 and <= " + MAXDIM);
+ if (r < k || w < r || w > MAXBITS || k >= MAXBITS)
+ throw new IllegalArgumentException
+ ("One must have k < 31 and k <= r <= w <= 31 for NiedSequenceBase2");
+ numCols = k;
+ numRows = r; // Unused!
+ outDigits = w;
+ numPoints = (1 << k);
+ this.dim = dim;
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+ genMat = new int[dim * numCols];
+ initGenMat();
+ }
+
+
+ public void extendSequence (int k) {
+ init (k, numRows, outDigits, dim);
+ }
+
+
+ // Initializes the generator matrices for a sequence.
+ /* I multiply by 2 because the relevant columns are in the 30 least
+ significant bits of NiedMat, but if I understand correctly,
+ SSJ assumes that they are in bits [31, ..., 1].
+ Then I shift right if w < 31. */
+ private void initGenMat () {
+ for (int j = 0; j < dim; j++)
+ for (int c = 0; c < numCols; c++) {
+ genMat[j*numCols + c] = NiedMat[j*NUMCOLS + c] << 1;
+ genMat[j*numCols + c] >>= MAXBITS - outDigits;
+ }
+ }
+
+/*
+ // Initializes the generator matrices for a net.
+ protected void initGenMatNet() {
+ int j, c;
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-numCols+c));
+
+ for (j = 1; j < dim; j++)
+ for (c = 0; c < numCols; c++)
+ genMat[j*numCols + c] = 2 * NiedMat[(j - 1)*NUMCOLS + c];
+ }
+*/
+
+ // ******************************************************************
+ // Generator matrices of Niederreiter sequence.
+ // This array stores explicitly NUMCOLS columns in 318 dimensions.
+
+ private static int[] NiedMat;
+ private static final int MAXN = 9540;
+
+ static {
+ NiedMat = new int[MAXN];
+
+ try {
+ InputStream is =
+ NiedSequenceBase2.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser");
+ if (is == null)
+ throw new FileNotFoundException (
+ "Cannot find NiedSequenceBase2.ser");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ NiedMat = (int[]) ois.readObject();
+ ois.close();
+
+ } catch(FileNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/NiedSequenceBase2.tex b/source/umontreal/iro/lecuyer/hups/NiedSequenceBase2.tex
new file mode 100644
index 0000000..2f37a22
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/NiedSequenceBase2.tex
@@ -0,0 +1,182 @@
+\defmodule {NiedSequenceBase2}
+
+This class implements digital sequences constructed from the
+Niederreiter sequence in base 2.
+\latex{For details on these point sets, see \cite{rBRA92a}.}
+\hpierre{The generator matrices do not seem to be computed correctly
+ in this implementation!}
+\hrichard{Corrig\'e: elles le sont maintenant.}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: NiedSequenceBase2
+ * Description: digital Niederreiter sequences in base 2.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class NiedSequenceBase2 extends DigitalSequenceBase2\begin{hide} {
+
+ private static final int MAXDIM = 318; // Maximum dimension.
+ private static final int NUMCOLS = 30; // Maximum number of columns.
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public NiedSequenceBase2 (int k, int w, int dim) \begin{hide} {
+ init (k, w, w, dim);
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new digital sequence in base 2 from the first $n=2^k$ points
+ of the Niederreiter sequence,
+ with $w$ output digits, in \texttt{dim} dimensions.
+ The generator matrices $\mathbf{C}_j$ are $w\times k$.
+% Unless, one plans to apply a randomization on more than $k$ digits
+% (e.g., a random digital shift for $w > k$ digits, or a linear
+% scramble yielding $r > k$ digits), one should
+% take $w = r = k$ for better computational efficiency.
+ Restrictions: $0\le k\le 30$, $k\le w$, and \texttt{dim} $\le 318$.
+\end{tabb}
+\begin{htmlonly}
+ \param{k}{there will be 2^k points}
+ \param{w}{number of output digits}
+ \param{dim}{dimension of the point set}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Niederreiter sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ private void init (int k, int r, int w, int dim) {
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for NiedSequenceBase2 must be > 1 and <= " + MAXDIM);
+ if (r < k || w < r || w > MAXBITS || k >= MAXBITS)
+ throw new IllegalArgumentException
+ ("One must have k < 31 and k <= r <= w <= 31 for NiedSequenceBase2");
+ numCols = k;
+ numRows = r; // Unused!
+ outDigits = w;
+ numPoints = (1 << k);
+ this.dim = dim;
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+ genMat = new int[dim * numCols];
+ initGenMat();
+ }
+
+
+ public void extendSequence (int k) {
+ init (k, numRows, outDigits, dim);
+ }
+
+
+ // Initializes the generator matrices for a sequence.
+ /* I multiply by 2 because the relevant columns are in the 30 least
+ significant bits of NiedMat, but if I understand correctly,
+ SSJ assumes that they are in bits [31, ..., 1].
+ Then I shift right if w < 31. */
+ private void initGenMat () {
+ for (int j = 0; j < dim; j++)
+ for (int c = 0; c < numCols; c++) {
+ genMat[j*numCols + c] = NiedMat[j*NUMCOLS + c] << 1;
+ genMat[j*numCols + c] >>= MAXBITS - outDigits;
+ }
+ }
+
+/*
+ // Initializes the generator matrices for a net.
+ protected void initGenMatNet() {
+ int j, c;
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-numCols+c));
+
+ for (j = 1; j < dim; j++)
+ for (c = 0; c < numCols; c++)
+ genMat[j*numCols + c] = 2 * NiedMat[(j - 1)*NUMCOLS + c];
+ }
+*/
+
+ // ******************************************************************
+ // Generator matrices of Niederreiter sequence.
+ // This array stores explicitly NUMCOLS columns in 318 dimensions.
+
+ private static int[] NiedMat;
+ private static final int MAXN = 9540;
+
+ static {
+ NiedMat = new int[MAXN];
+
+ try {
+ InputStream is =
+ NiedSequenceBase2.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser");
+ if (is == null)
+ throw new FileNotFoundException (
+ "Cannot find NiedSequenceBase2.ser");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ NiedMat = (int[]) ois.readObject();
+ ois.close();
+
+ } catch(FileNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+}
+\end{hide}
+\end{code}
+
+
+
+
+
diff --git a/source/umontreal/iro/lecuyer/hups/NiedXingSequenceBase2.java b/source/umontreal/iro/lecuyer/hups/NiedXingSequenceBase2.java
new file mode 100644
index 0000000..8751f5f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/NiedXingSequenceBase2.java
@@ -0,0 +1,208 @@
+
+
+/*
+ * Class: NiedXingSequenceBase2
+ * Description: Niederreiter-Xing sequences in base 2
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class implements digital sequences based on the
+ * Niederreiter-Xing sequence in base 2.
+ *
+ */
+public class NiedXingSequenceBase2 extends DigitalSequenceBase2 {
+
+ private static final int MAXDIM = 32; // Maximum dimension.
+ private static final int NUMCOLS = 30; // Maximum number of columns.
+ private static final boolean isTrans = true;
+
+
+
+ /**
+ * Constructs a new Niederreiter-Xing digital sequence in base 2
+ * with <SPAN CLASS="MATH"><I>n</I> = 2<SUP>k</SUP></SPAN> points and <SPAN CLASS="MATH"><I>w</I></SPAN> output digits, in <TT>dim</TT> dimensions.
+ * The generator matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> are <SPAN CLASS="MATH"><I>w</I>×<I>k</I></SPAN> and
+ * the numbers making the bit matrices are taken from
+ * <A NAME="tex2html1"
+ * HREF="http://www.ricam.oeaw.ac.at/people/page/pirsic/niedxing/index.html">Pirsic's site</A>.
+ * The bit matrices from Pirsic's site are transposed to be consistent with SSJ,
+ * and at most 30 bits of the matrices are used.
+ * Restrictions:
+ * <SPAN CLASS="MATH">0 <= <I>k</I> <= 30</SPAN>, <SPAN CLASS="MATH"><I>k</I> <= <I>w</I></SPAN>, and <TT>dim</TT> <SPAN CLASS="MATH"> <= 32</SPAN>.
+ *
+ * @param k there will be 2^k points
+ *
+ * @param w number of output digits
+ *
+ * @param dim dimension of the point set
+ *
+ *
+ */
+ public NiedXingSequenceBase2 (int k, int w, int dim) {
+ init (k, w, w, dim);
+ }
+
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Niederreiter-Xing sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ private void init (int k, int r, int w, int dim) {
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for NiedXingSequenceBase2 must be > 1 and <= " + MAXDIM);
+ if (r < k || w < r || w > MAXBITS || k >= MAXBITS)
+ throw new IllegalArgumentException
+ ("One must have k < 31 and k <= r <= w <= 31 for NiedXingSequenceBase2");
+ numCols = k;
+ numRows = r;
+ outDigits = w;
+ numPoints = (1 << k);
+ this.dim = dim;
+ // 1L otherwise gives wrong results for outDigits >= 31
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+ genMat = new int[dim * numCols];
+ initGenMat();
+ }
+
+
+ public void extendSequence (int k) {
+ init (k, numRows, outDigits, dim);
+ }
+
+
+ // Initializes the generator matrices for a sequence.
+ private void initGenMat () {
+ // Compute where we should start reading matrices.
+ /* Pirsic gives matrices starting at dimension 4; there are j matrices
+ of dimension j. Thus if we want to use matrices in dimension
+ dim, we must jump over all matrices of dimension < dim. If they
+ started at dimension 1, there would be dim*(dim-1)/2 elements to
+ disregard. But the first 3 dimensions are not there, thus subtract
+ 3*(3 - 1)/2 = 6 to get the starting element of dimension dim.
+ I also multiply by 2 because the relevant columns are in the 30 least
+ significant bits of NiedXingMat, but if I understand correctly,
+ SSJ assumes that they are in bits [31, ..., 1].
+ At last, I shift right if w < 31 bits. */
+
+ int start;
+ if (dim <= 4)
+ start = 0;
+ else
+ start = ((dim*(dim-1)/2)-6)*NUMCOLS;
+
+ long x;
+ if (isTrans) {
+ for (int j = 0; j < dim; j++)
+ for (int c = 0; c < numCols; c++) {
+ x = NiedXingMatTrans[start + j*NUMCOLS + c];
+ x <<= 1;
+ genMat[j*numCols + c] = (int) (x >> (MAXBITS - outDigits));
+ }
+ } else {
+ for (int j = 0; j < dim; j++)
+ for (int c = 0; c < numCols; c++) {
+ x = NiedXingMat[start + j*NUMCOLS + c];
+ x <<= 1;
+ genMat[j*numCols + c] = (int) (x >> (MAXBITS - outDigits));
+ }
+ }
+ }
+
+
+ // ******************************************************************
+ // Generator matrices of Niederreite-Xing sequences.
+ // This array stores explicitly NUMCOLS columns in MAXDIM dimensions.
+ // The implemented generator matrices are provided by Gottlieb Pirsic
+ // and were downloaded on 15 July 2004. (RS)
+ // These are the numbers given by Pirsic: I kept the first 30 of each
+ // row vector in each dimension and shifted them to get the 30 most
+ // significant bits. These numbers considered as 30 X 30 bit matrices
+ // are given in matrix NiedXingMat below. The same numbers but transposed
+ // as 30 X 30 bit matrices are given in matrix NiedXingMatTrans below.
+ // According to Yves Edel, the correct matrices for Niederreiter-Xing
+ // are NiedXingMatTrans.
+ //
+ // The matrices were given up to MAXDIM = 32, but the javac compiler
+ // cannot compile code that is too big. So I serialized them in
+ // file NiedXingSequenceBase2.ser. (RS)
+
+ private static int[] NiedXingMat;
+ private static int[] NiedXingMatTrans;
+ private static final int MAXN = 15660;
+
+ static {
+// NiedXingMat = new int[MAXN];
+ NiedXingMatTrans = new int[MAXN];
+
+ try {
+/*
+ InputStream is =
+ NiedXingSequenceBase2.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2.ser");
+ if (is == null)
+ throw new FileNotFoundException (
+ "Cannot find NiedXingSequenceBase2.ser");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ NiedXingMat = (int[]) ois.readObject();
+ ois.close();
+*/
+ InputStream is =
+ NiedXingSequenceBase2.class.getClassLoader().getResourceAsStream(
+ "umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser");
+ if (is == null)
+ throw new FileNotFoundException (
+ "Cannot find NiedXingSequenceBase2Trans.ser");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ NiedXingMatTrans = (int[]) ois.readObject();
+ ois.close();
+
+ } catch(FileNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/NiedXingSequenceBase2.tex b/source/umontreal/iro/lecuyer/hups/NiedXingSequenceBase2.tex
new file mode 100644
index 0000000..3c8cd97
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/NiedXingSequenceBase2.tex
@@ -0,0 +1,221 @@
+\defmodule {NiedXingSequenceBase2}
+
+This class implements digital sequences based on the
+ Niederreiter-Xing sequence in base 2.
+\latex{For details on these point sets, see \cite{rNIE98a,rPIR01c}.}
+\hpierre{The current numbers used to fill up the generating matrices
+ were taken from the web page of Gottlieb Pirsic some time ago
+ and contain errors. We should check for an update on his web page.}
+\hrichard{Les nouveaux nombres ont \'et\'e recopi\'es le 15 juillet 2004.}
+\hrichard{Nouvelle r\'ef\'erence int\'eressante? \cite{rCLA99a}}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: NiedXingSequenceBase2
+ * Description: Niederreiter-Xing sequences in base 2
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups; \begin{hide}
+
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class NiedXingSequenceBase2 extends DigitalSequenceBase2 \begin{hide} {
+
+ private static final int MAXDIM = 32; // Maximum dimension.
+ private static final int NUMCOLS = 30; // Maximum number of columns.
+ private static final boolean isTrans = true;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NiedXingSequenceBase2 (int k, int w, int dim) \begin{hide} {
+ init (k, w, w, dim);
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new Niederreiter-Xing digital sequence in base 2
+ with $n = 2^k$ points and $w$ output digits, in \texttt{dim} dimensions.
+ The generator matrices $\mathbf{C}_j$ are $w\times k$ and
+ the numbers making the bit matrices are taken from
+ \htmladdnormallink{Pirsic's site}{http://www.ricam.oeaw.ac.at/people/page/pirsic/niedxing/index.html}.
+ The bit matrices from Pirsic's site are transposed to be consistent with SSJ,
+ and at most 30 bits of the matrices are used.
+% Unless, one plans to apply a randomization on more than $k$ digits
+% (e.g., a random digital shift for $w > k$ digits, or a linear
+% scramble yielding $r > k$ digits), one should
+% take $w = r = k$ for better computational efficiency.
+ Restrictions: $0\le k\le 30$, $k\le w$, and \texttt{dim} $\le 32$.
+\end{tabb}
+\begin{htmlonly}
+ \param{k}{there will be 2^k points}
+ \param{w}{number of output digits}
+ \param{dim}{dimension of the point set}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Niederreiter-Xing sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+ private void init (int k, int r, int w, int dim) {
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for NiedXingSequenceBase2 must be > 1 and <= " + MAXDIM);
+ if (r < k || w < r || w > MAXBITS || k >= MAXBITS)
+ throw new IllegalArgumentException
+ ("One must have k < 31 and k <= r <= w <= 31 for NiedXingSequenceBase2");
+ numCols = k;
+ numRows = r;
+ outDigits = w;
+ numPoints = (1 << k);
+ this.dim = dim;
+ // 1L otherwise gives wrong results for outDigits >= 31
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+ genMat = new int[dim * numCols];
+ initGenMat();
+ }
+
+
+ public void extendSequence (int k) {
+ init (k, numRows, outDigits, dim);
+ }
+
+
+ // Initializes the generator matrices for a sequence.
+ private void initGenMat () {
+ // Compute where we should start reading matrices.
+ /* Pirsic gives matrices starting at dimension 4; there are j matrices
+ of dimension j. Thus if we want to use matrices in dimension
+ dim, we must jump over all matrices of dimension < dim. If they
+ started at dimension 1, there would be dim*(dim-1)/2 elements to
+ disregard. But the first 3 dimensions are not there, thus subtract
+ 3*(3 - 1)/2 = 6 to get the starting element of dimension dim.
+ I also multiply by 2 because the relevant columns are in the 30 least
+ significant bits of NiedXingMat, but if I understand correctly,
+ SSJ assumes that they are in bits [31, ..., 1].
+ At last, I shift right if w < 31 bits. */
+
+ int start;
+ if (dim <= 4)
+ start = 0;
+ else
+ start = ((dim*(dim-1)/2)-6)*NUMCOLS;
+
+ long x;
+ if (isTrans) {
+ for (int j = 0; j < dim; j++)
+ for (int c = 0; c < numCols; c++) {
+ x = NiedXingMatTrans[start + j*NUMCOLS + c];
+ x <<= 1;
+ genMat[j*numCols + c] = (int) (x >> (MAXBITS - outDigits));
+ }
+ } else {
+ for (int j = 0; j < dim; j++)
+ for (int c = 0; c < numCols; c++) {
+ x = NiedXingMat[start + j*NUMCOLS + c];
+ x <<= 1;
+ genMat[j*numCols + c] = (int) (x >> (MAXBITS - outDigits));
+ }
+ }
+ }
+
+
+ // ******************************************************************
+ // Generator matrices of Niederreite-Xing sequences.
+ // This array stores explicitly NUMCOLS columns in MAXDIM dimensions.
+ // The implemented generator matrices are provided by Gottlieb Pirsic
+ // and were downloaded on 15 July 2004. (RS)
+ // These are the numbers given by Pirsic: I kept the first 30 of each
+ // row vector in each dimension and shifted them to get the 30 most
+ // significant bits. These numbers considered as 30 X 30 bit matrices
+ // are given in matrix NiedXingMat below. The same numbers but transposed
+ // as 30 X 30 bit matrices are given in matrix NiedXingMatTrans below.
+ // According to Yves Edel, the correct matrices for Niederreiter-Xing
+ // are NiedXingMatTrans.
+ //
+ // The matrices were given up to MAXDIM = 32, but the javac compiler
+ // cannot compile code that is too big. So I serialized them in
+ // file NiedXingSequenceBase2.ser. (RS)
+
+ private static int[] NiedXingMat;
+ private static int[] NiedXingMatTrans;
+ private static final int MAXN = 15660;
+
+ static {
+// NiedXingMat = new int[MAXN];
+ NiedXingMatTrans = new int[MAXN];
+
+ try {
+/*
+ InputStream is =
+ NiedXingSequenceBase2.class.getClassLoader().getResourceAsStream (
+ "umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2.ser");
+ if (is == null)
+ throw new FileNotFoundException (
+ "Cannot find NiedXingSequenceBase2.ser");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ NiedXingMat = (int[]) ois.readObject();
+ ois.close();
+*/
+ InputStream is =
+ NiedXingSequenceBase2.class.getClassLoader().getResourceAsStream(
+ "umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser");
+ if (is == null)
+ throw new FileNotFoundException (
+ "Cannot find NiedXingSequenceBase2Trans.ser");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ NiedXingMatTrans = (int[]) ois.readObject();
+ ois.close();
+
+ } catch(FileNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
+\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/hups/PaddedPointSet.java b/source/umontreal/iro/lecuyer/hups/PaddedPointSet.java
new file mode 100644
index 0000000..07407d3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PaddedPointSet.java
@@ -0,0 +1,332 @@
+
+/*
+ * Class: PaddedPointSet
+ * Description: container class
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+/**
+ * This container class realizes <SPAN CLASS="textit">padded point sets</SPAN>, constructed
+ * by taking some coordinates from a point set <SPAN CLASS="MATH"><I>P</I><SUB>1</SUB></SPAN>, other coordinates
+ * from a point set <SPAN CLASS="MATH"><I>P</I><SUB>2</SUB></SPAN>, and so on.
+ * This can be used to implement <SPAN CLASS="textit">latin supercube sampling</SPAN>, for example.
+ * After calling the constructor to create the structure, component
+ * point sets can be padded to it by calling {@link #padPointSet padPointSet} or
+ * {@link #padPointSetPermute padPointSetPermute}.
+ *
+ * <P>
+ * Only sets with the same number of points can be padded.
+ * Point sets with too many points or coordinates can be trimmed down
+ * by using the class {@link SubsetOfPointSet} before they are padded.
+ * Infinite-dimensional point sets are allowed, but once one is padded,
+ * no additional point set can be padded.
+ *
+ * <P>
+ * The points of each padded set can be permuted randomly,
+ * independently across the padded sets.
+ * If such a random permutation is desired, the point set should be
+ * padded via {@link #padPointSetPermute padPointSetPermute}.
+ * When calling {@link #randomize randomize}, random permutations are generated for
+ * all point sets that have been padded by {@link #padPointSetPermute padPointSetPermute}.
+ *
+ */
+public class PaddedPointSet extends PointSet {
+ protected int curPointSets = 0; // Current number of padded point sets.
+ protected int maxPointSets; // Max. number of padded point sets.
+ protected PointSet pointSet[]; // List of padded point sets
+ protected int startDim[]; // Starting dim. for padded points sets.
+ protected int permutation[][]; // One permutation for each point set.
+
+
+
+
+ /**
+ * Constructs a structure for padding at most <TT>maxPointSets</TT> point sets.
+ * This structure is initially empty and will eventually contain the different
+ * point sets that are padded.
+ *
+ * @param maxPointSets maximum number of point sets authorized
+ * by the constructed object
+ *
+ */
+ public PaddedPointSet (int maxPointSets) {
+ this.maxPointSets = maxPointSets;
+ pointSet = new PointSet[maxPointSets];
+ startDim = new int[maxPointSets];
+ permutation = new int[maxPointSets][];
+ }
+
+
+ /**
+ * Pads the point set <TT>P</TT> to the present structure.
+ *
+ * @param P point set being padded
+ *
+ *
+ */
+ public void padPointSet (PointSet P) {
+ if (curPointSets == maxPointSets)
+ throw new IllegalArgumentException
+ ("Cannot pad more, increase maxPointSets parameter");
+
+ if (dim == Integer.MAX_VALUE)
+ throw new IllegalArgumentException
+ ("Cannot pad more, dimension already infinite");
+
+ if (curPointSets > 0 && numPoints != P.getNumPoints())
+ throw new IllegalArgumentException
+ ("Padded points must have same number of points");
+
+ if (curPointSets == 0)
+ numPoints = P.getNumPoints();
+
+ if (P.getDimension() == Integer.MAX_VALUE)
+ dim = Integer.MAX_VALUE;
+ else
+ dim += P.getDimension();
+
+ pointSet[curPointSets] = P;
+ startDim[curPointSets] = dim;
+ ++curPointSets;
+ }
+
+
+ /**
+ * Pads the point set <TT>P</TT>, which is assumed to be <SPAN CLASS="textit">finite</SPAN>.
+ * A random permutation will be
+ * generated (when calling {@link #randomize randomize}) and used to access the
+ * coordinates taken from the points of <TT>P</TT> (i.e., these points
+ * are randomly permuted).
+ *
+ * @param P point set being padded
+ *
+ *
+ */
+ public void padPointSetPermute (PointSet P) {
+ if (curPointSets == 0)
+ numPoints = P.getNumPoints();
+ if (numPoints == Integer.MAX_VALUE)
+ throw new IllegalArgumentException
+ ("Cannot generate infinite permutation");
+ permutation[curPointSets] = new int[numPoints];
+ for (int i = 0; i < numPoints; i++)
+ permutation[curPointSets][i] = i;
+ padPointSet (P);
+ }
+
+
+ public double getCoordinate (int i, int j) {
+ int set = 0;
+ if (j >= dim)
+ throw new IllegalArgumentException ("Not enough dimensions");
+ while (j >= startDim[set])
+ set++;
+
+ /*
+ if (permutation[set] == null)
+ pointSet[set].resetPoint(i);
+ else
+ pointSet[set].resetPoint(permutation[set][i]);
+
+ if (set == 0)
+ pointSet[0].resetCoordinate (j);
+ else
+ pointSet[set].resetCoordinate (j - startDim[set - 1]);
+
+ return pointSet[set].nextCoordinate();
+ */
+
+ if (permutation[set] != null)
+ i = permutation[set][i];
+ if (set != 0)
+ j = j - startDim[set - 1];
+ return pointSet[set].getCoordinate (i, j);
+ }
+
+ public void unrandomize() {
+ for (int set = 0; set < curPointSets; set++) {
+ if (permutation[set] != null) {
+ for (int i = 0; i < numPoints; i++)
+ permutation[set][i] = i;
+ }
+ }
+ }
+
+ public void randomize (RandomStream stream) {
+ // Executes the randomizations of the list
+ super.randomize (stream);
+ /* can also use lazy permutations */
+ for (int set = 0; set < curPointSets; set++)
+ if (permutation[set] != null) {
+ for (int i = 0; i < numPoints - 1; i++) {
+ int u = stream.nextInt(0, numPoints - i - 1);
+ int h = permutation[set][i];
+ permutation[set][i] = permutation[set][i + u];
+ permutation[set][i + u] = h;
+ }
+ }
+ }
+
+ public PointSetIterator iterator() {
+ return new PaddedIterator();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Padded point set" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Maximal number of point sets: " + maxPointSets +
+ PrintfFormat.NEWLINE);
+ sb.append ("Current number of point sets: " + curPointSets +
+ PrintfFormat.NEWLINE);
+ sb.append ("Number of points: " + numPoints + PrintfFormat.NEWLINE);
+ for (int i = 0; i < curPointSets; i++) {
+ if (i != 0)
+ sb.append (PrintfFormat.NEWLINE);
+ if (permutation[i] == null)
+ sb.append ("Point set ");
+ else
+ sb.append ("Permuted point set ");
+ sb.append ( i + " information: {" + PrintfFormat.NEWLINE
+ + pointSet[i].toString() + PrintfFormat.NEWLINE + "}");
+ }
+ return sb.toString();
+ }
+
+
+ // ************************************************************
+
+ private class PaddedIterator extends DefaultPointSetIterator {
+
+ private PointSetIterator[] pointSetIterators; // One for each padded set.
+ private int currentSet = 0;
+ private double[] temp;
+
+ public PaddedIterator() {
+ pointSetIterators = new PointSetIterator[curPointSets];
+ int maxdim = 0;
+ for (int i = 0; i < curPointSets; i++) {
+ pointSetIterators[i] = pointSet[i].iterator();
+ if (pointSet[i].getDimension() > maxdim)
+ maxdim = pointSet[i].getDimension();
+ if (permutation[i] != null)
+ pointSetIterators[i].setCurPointIndex (permutation[i][0]);
+ }
+ if (maxdim == Integer.MAX_VALUE)
+ temp = new double[16];
+ else
+ temp = new double[maxdim];
+ }
+
+ public void setCurCoordIndex (int j) {
+ int set = 0;
+ if (j >= dim)
+ throw new IllegalArgumentException ("Not enough dimensions");
+ while (j >= startDim[set])
+ set++;
+ currentSet = set;
+ pointSetIterators[currentSet].setCurCoordIndex
+ (set == 0 ? j : j - startDim[set-1]);
+ for (set = currentSet+1; set < pointSetIterators.length; set++)
+ pointSetIterators[set].resetCurCoordIndex();
+ curCoordIndex = j;
+ }
+
+ public void resetCurCoordIndex() {
+ currentSet = 0;
+ for (int i = 0; i < pointSetIterators.length; i++)
+ pointSetIterators[i].resetCurCoordIndex();
+ curCoordIndex = 0;
+ }
+
+ public double nextCoordinate() {
+ if (curPointIndex >= numPoints || curCoordIndex >= dim)
+ outOfBounds();
+ if (curCoordIndex >= startDim[currentSet])
+ currentSet++;
+ double coord = pointSetIterators[currentSet].nextCoordinate();
+ curCoordIndex++;
+ return coord;
+ }
+
+ public void nextCoordinates (double[] p, int d) {
+ if (curPointIndex >= numPoints || d > dim)
+ outOfBounds();
+ int i = 0;
+ while (i < d) {
+ int dimen = pointSet[currentSet].getDimension();
+ if (dimen == Integer.MAX_VALUE)
+ dimen = d - i;
+ else
+ dimen -= pointSetIterators[currentSet].getCurCoordIndex();
+ pointSetIterators[currentSet].nextCoordinates (temp, dimen);
+ System.arraycopy (temp, 0, p, i, dimen);
+ i += dimen;
+ curCoordIndex += dimen;
+ if (i < d)
+ currentSet++;
+ }
+ }
+
+ public void setCurPointIndex (int i) {
+ for (int it = 0; it < pointSetIterators.length; it++)
+ pointSetIterators[it].setCurPointIndex
+ (permutation[it] == null ? i : permutation[it][i]);
+ curPointIndex = i;
+ curCoordIndex = 0;
+ currentSet = 0;
+ }
+
+ public void resetCurPointIndex() {
+ for (int i = 0; i < pointSetIterators.length; i++) {
+ if (permutation[i] == null)
+ pointSetIterators[i].resetCurPointIndex();
+ else
+ pointSetIterators[i].setCurPointIndex (permutation[i][0]);
+ }
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ currentSet = 0;
+ }
+
+ public int resetToNextPoint() {
+ for (int i = 0; i < pointSetIterators.length; i++) {
+ if (permutation[i] == null)
+ pointSetIterators[i].resetToNextPoint();
+ else
+ pointSetIterators[i].setCurPointIndex
+ (permutation[i][curPointIndex+1]);
+ }
+ currentSet = 0;
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ public String formatState() {
+ return super.formatState() + PrintfFormat.NEWLINE +
+ "Current padded set: " + currentSet;
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/PaddedPointSet.tex b/source/umontreal/iro/lecuyer/hups/PaddedPointSet.tex
new file mode 100644
index 0000000..364c759
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PaddedPointSet.tex
@@ -0,0 +1,345 @@
+\defmodule{PaddedPointSet}
+
+This container class realizes \emph{padded point sets}, constructed
+by taking some coordinates from a point set $P_1$, other coordinates
+from a point set $P_2$, and so on.
+This can be used to implement \emph{latin supercube sampling}
+\cite{vOWE98a}, for example.
+After calling the constructor to create the structure, component
+point sets can be padded to it by calling \method{padPointSet}{} or
+\method{padPointSetPermute}{}.
+
+Only sets with the same number of points can be padded.
+Point sets with too many points or coordinates can be trimmed down
+by using the class \class{SubsetOfPointSet} before they are padded.
+Infinite-dimensional point sets are allowed, but once one is padded,
+no additional point set can be padded.
+
+The points of each padded set can be permuted randomly,
+independently across the padded sets.
+If such a random permutation is desired, the point set should be
+padded via \method{padPointSetPermute}{}.
+When calling \method{randomize}{}, random permutations are generated for
+all point sets that have been padded by \method{padPointSetPermute}{}.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: PaddedPointSet
+ * Description: container class
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;\end{hide}
+
+public class PaddedPointSet extends PointSet \begin{hide} {
+ protected int curPointSets = 0; // Current number of padded point sets.
+ protected int maxPointSets; // Max. number of padded point sets.
+ protected PointSet pointSet[]; // List of padded point sets
+ protected int startDim[]; // Starting dim. for padded points sets.
+ protected int permutation[][]; // One permutation for each point set.
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public PaddedPointSet (int maxPointSets) \begin{hide} {
+ this.maxPointSets = maxPointSets;
+ pointSet = new PointSet[maxPointSets];
+ startDim = new int[maxPointSets];
+ permutation = new int[maxPointSets][];
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a structure for padding at most \texttt{maxPointSets} point sets.
+ This structure is initially empty and will eventually contain the different
+ point sets that are padded.
+ \end{tabb}
+\begin{htmlonly}
+ \param{maxPointSets}{maximum number of point sets authorized
+ by the constructed object}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public void padPointSet (PointSet P) \begin{hide} {
+ if (curPointSets == maxPointSets)
+ throw new IllegalArgumentException
+ ("Cannot pad more, increase maxPointSets parameter");
+
+ if (dim == Integer.MAX_VALUE)
+ throw new IllegalArgumentException
+ ("Cannot pad more, dimension already infinite");
+
+ if (curPointSets > 0 && numPoints != P.getNumPoints())
+ throw new IllegalArgumentException
+ ("Padded points must have same number of points");
+
+ if (curPointSets == 0)
+ numPoints = P.getNumPoints();
+
+ if (P.getDimension() == Integer.MAX_VALUE)
+ dim = Integer.MAX_VALUE;
+ else
+ dim += P.getDimension();
+
+ pointSet[curPointSets] = P;
+ startDim[curPointSets] = dim;
+ ++curPointSets;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Pads the point set \texttt{P} to the present structure.
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set being padded}
+\end{htmlonly}
+\begin{code}
+
+ public void padPointSetPermute (PointSet P) \begin{hide} {
+ if (curPointSets == 0)
+ numPoints = P.getNumPoints();
+ if (numPoints == Integer.MAX_VALUE)
+ throw new IllegalArgumentException
+ ("Cannot generate infinite permutation");
+ permutation[curPointSets] = new int[numPoints];
+ for (int i = 0; i < numPoints; i++)
+ permutation[curPointSets][i] = i;
+ padPointSet (P);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Pads the point set \texttt{P}, which is assumed to be \emph{finite}.
+ A random permutation will be
+ generated (when calling \method{randomize}{}) and used to access the
+ coordinates taken from the points of \texttt{P} (i.e., these points
+ are randomly permuted).
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set being padded}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ int set = 0;
+ if (j >= dim)
+ throw new IllegalArgumentException ("Not enough dimensions");
+ while (j >= startDim[set])
+ set++;
+
+ /*
+ if (permutation[set] == null)
+ pointSet[set].resetPoint(i);
+ else
+ pointSet[set].resetPoint(permutation[set][i]);
+
+ if (set == 0)
+ pointSet[0].resetCoordinate (j);
+ else
+ pointSet[set].resetCoordinate (j - startDim[set - 1]);
+
+ return pointSet[set].nextCoordinate();
+ */
+
+ if (permutation[set] != null)
+ i = permutation[set][i];
+ if (set != 0)
+ j = j - startDim[set - 1];
+ return pointSet[set].getCoordinate (i, j);
+ }
+
+ public void unrandomize() {
+ for (int set = 0; set < curPointSets; set++) {
+ if (permutation[set] != null) {
+ for (int i = 0; i < numPoints; i++)
+ permutation[set][i] = i;
+ }
+ }
+ }
+
+ public void randomize (RandomStream stream) {
+ // Executes the randomizations of the list
+ super.randomize (stream);
+ /* can also use lazy permutations */
+ for (int set = 0; set < curPointSets; set++)
+ if (permutation[set] != null) {
+ for (int i = 0; i < numPoints - 1; i++) {
+ int u = stream.nextInt(0, numPoints - i - 1);
+ int h = permutation[set][i];
+ permutation[set][i] = permutation[set][i + u];
+ permutation[set][i + u] = h;
+ }
+ }
+ }
+
+ public PointSetIterator iterator() {
+ return new PaddedIterator();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Padded point set" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Maximal number of point sets: " + maxPointSets +
+ PrintfFormat.NEWLINE);
+ sb.append ("Current number of point sets: " + curPointSets +
+ PrintfFormat.NEWLINE);
+ sb.append ("Number of points: " + numPoints + PrintfFormat.NEWLINE);
+ for (int i = 0; i < curPointSets; i++) {
+ if (i != 0)
+ sb.append (PrintfFormat.NEWLINE);
+ if (permutation[i] == null)
+ sb.append ("Point set ");
+ else
+ sb.append ("Permuted point set ");
+ sb.append ( i + " information: {" + PrintfFormat.NEWLINE
+ + pointSet[i].toString() + PrintfFormat.NEWLINE + "}");
+ }
+ return sb.toString();
+ }
+
+
+ // ************************************************************
+
+ private class PaddedIterator extends DefaultPointSetIterator {
+
+ private PointSetIterator[] pointSetIterators; // One for each padded set.
+ private int currentSet = 0;
+ private double[] temp;
+
+ public PaddedIterator() {
+ pointSetIterators = new PointSetIterator[curPointSets];
+ int maxdim = 0;
+ for (int i = 0; i < curPointSets; i++) {
+ pointSetIterators[i] = pointSet[i].iterator();
+ if (pointSet[i].getDimension() > maxdim)
+ maxdim = pointSet[i].getDimension();
+ if (permutation[i] != null)
+ pointSetIterators[i].setCurPointIndex (permutation[i][0]);
+ }
+ if (maxdim == Integer.MAX_VALUE)
+ temp = new double[16];
+ else
+ temp = new double[maxdim];
+ }
+
+ public void setCurCoordIndex (int j) {
+ int set = 0;
+ if (j >= dim)
+ throw new IllegalArgumentException ("Not enough dimensions");
+ while (j >= startDim[set])
+ set++;
+ currentSet = set;
+ pointSetIterators[currentSet].setCurCoordIndex
+ (set == 0 ? j : j - startDim[set-1]);
+ for (set = currentSet+1; set < pointSetIterators.length; set++)
+ pointSetIterators[set].resetCurCoordIndex();
+ curCoordIndex = j;
+ }
+
+ public void resetCurCoordIndex() {
+ currentSet = 0;
+ for (int i = 0; i < pointSetIterators.length; i++)
+ pointSetIterators[i].resetCurCoordIndex();
+ curCoordIndex = 0;
+ }
+
+ public double nextCoordinate() {
+ if (curPointIndex >= numPoints || curCoordIndex >= dim)
+ outOfBounds();
+ if (curCoordIndex >= startDim[currentSet])
+ currentSet++;
+ double coord = pointSetIterators[currentSet].nextCoordinate();
+ curCoordIndex++;
+ return coord;
+ }
+
+ public void nextCoordinates (double[] p, int d) {
+ if (curPointIndex >= numPoints || d > dim)
+ outOfBounds();
+ int i = 0;
+ while (i < d) {
+ int dimen = pointSet[currentSet].getDimension();
+ if (dimen == Integer.MAX_VALUE)
+ dimen = d - i;
+ else
+ dimen -= pointSetIterators[currentSet].getCurCoordIndex();
+ pointSetIterators[currentSet].nextCoordinates (temp, dimen);
+ System.arraycopy (temp, 0, p, i, dimen);
+ i += dimen;
+ curCoordIndex += dimen;
+ if (i < d)
+ currentSet++;
+ }
+ }
+
+ public void setCurPointIndex (int i) {
+ for (int it = 0; it < pointSetIterators.length; it++)
+ pointSetIterators[it].setCurPointIndex
+ (permutation[it] == null ? i : permutation[it][i]);
+ curPointIndex = i;
+ curCoordIndex = 0;
+ currentSet = 0;
+ }
+
+ public void resetCurPointIndex() {
+ for (int i = 0; i < pointSetIterators.length; i++) {
+ if (permutation[i] == null)
+ pointSetIterators[i].resetCurPointIndex();
+ else
+ pointSetIterators[i].setCurPointIndex (permutation[i][0]);
+ }
+ curPointIndex = 0;
+ curCoordIndex = 0;
+ currentSet = 0;
+ }
+
+ public int resetToNextPoint() {
+ for (int i = 0; i < pointSetIterators.length; i++) {
+ if (permutation[i] == null)
+ pointSetIterators[i].resetToNextPoint();
+ else
+ pointSetIterators[i].setCurPointIndex
+ (permutation[i][curPointIndex+1]);
+ }
+ currentSet = 0;
+ curCoordIndex = 0;
+ return ++curPointIndex;
+ }
+
+ public String formatState() {
+ return super.formatState() + PrintfFormat.NEWLINE +
+ "Current padded set: " + currentSet;
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/PointSet.java b/source/umontreal/iro/lecuyer/hups/PointSet.java
new file mode 100644
index 0000000..890f3e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PointSet.java
@@ -0,0 +1,724 @@
+
+
+/*
+ * Class: PointSet
+ * Description: Base class of all point sets
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import java.util.NoSuchElementException;
+import java.util.List;
+import java.util.ArrayList;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This abstract class defines the basic methods
+ * for accessing and manipulating point sets.
+ * A point set can be represented as a two-dimensional array, whose element
+ * <SPAN CLASS="MATH">(<I>i</I>, <I>j</I>)</SPAN> contains <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN>, the <SPAN CLASS="textit">coordinate</SPAN> <SPAN CLASS="MATH"><I>j</I></SPAN> of point <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ * Each coordinate <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> is assumed to be in the unit interval <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ * Whether the values 0 and 1 can occur may depend on the
+ * actual implementation of the point set.
+ *
+ * <P>
+ * All points have the same number of coordinates (their <SPAN CLASS="textit">dimension</SPAN>)
+ * and this number can be queried by {@link #getDimension getDimension}.
+ * The number of points is queried by {@link #getNumPoints getNumPoints}.
+ * The points and coordinates are both numbered starting from 0
+ * and their number can actually be infinite.
+ *
+ * <P>
+ * The {@link #iterator iterator} method provides a <SPAN CLASS="textit">point set iterator</SPAN>
+ * which permits one to enumerate the points and their coordinates.
+ * Several iterators over the same point set can coexist at any given time.
+ * These iterators are instances of a hidden inner-class that implements
+ * the {@link PointSetIterator} interface.
+ * The default implementation of iterator provided here relies on
+ * the method {@link #getCoordinate getCoordinate} to access the coordinates directly.
+ * However, this approach is rarely efficient.
+ * Specialized implementations that dramatically improve the performance
+ * are provided in subclasses of {@link PointSet}.
+ * The {@link PointSetIterator} interface actually extends the
+ * {@link RandomStream} interface, so that the
+ * iterator can also be seen as a {@link RandomStream} and used wherever
+ * such a stream is required for generating uniform random numbers.
+ * This permits one to easily replace pseudorandom numbers by the
+ * coordinates of a selected set of highly-uniform points, i.e.,
+ * to replace Monte Carlo by quasi-Monte Carlo in a simulation program.
+ *
+ * <P>
+ * This abstract class has only one abstract method:
+ * {@link #getCoordinate getCoordinate}.
+ * Providing an implementation for this method is already sufficient
+ * for the subclass to work.
+ * However, in practically all cases, efficiency can be dramatically improved by
+ * overwriting {@link #iterator iterator} to provide a custom iterator that does not
+ * necessarily rely on {@link #getCoordinate getCoordinate}.
+ * In fact, direct use of {@link #getCoordinate getCoordinate} to access the coordinates
+ * is discouraged.
+ * One should access the coordinates only via the iterators.
+ *
+ */
+public abstract class PointSet {
+
+ // The maximum number of usable bits (binary digits).
+ // Since Java has no unsigned type, the
+ // 32nd bit cannot be used efficiently. This mainly affects digit
+ // scrambling and bit vectors. This also limits the maximum number
+ // of columns for the generating matrices of digital nets in base 2.
+ protected static final int MAXBITS = 31;
+ // To avoid 0 for nextCoordinate when random shifting
+ protected double EpsilonHalf = 1.0 / Num.TWOEXP[55]; // 1/2^55
+
+ protected int dim = 0;
+ protected int numPoints = 0;
+ protected int dimShift = 0; // Current dimension of the shift.
+ protected int capacityShift = 0; // Number of array elements of shift;
+ // it is always >= dimShift
+ protected RandomStream shiftStream; // Used to generate random shifts.
+
+
+
+ /**
+ * Returns the dimension (number of available coordinates) of the point set.
+ * If the dimension is actually infinite, <TT>Integer.MAX_VALUE</TT> is returned.
+ *
+ * @return the dimension of the point set or <TT>Integer.MAX_VALUE</TT>
+ * if it is infinite
+ *
+ */
+ public int getDimension() {
+ return dim;
+ }
+
+
+ /**
+ * Returns the number of points.
+ * If this number is actually infinite, <TT>Integer.MAX_VALUE</TT> is returned.
+ *
+ * @return the number of points in the point set or <TT>Integer.MAX_VALUE</TT>
+ * if the point set has an infinity of points.
+ *
+ */
+ public int getNumPoints() {
+ return numPoints;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN>, the coordinate <SPAN CLASS="MATH"><I>j</I></SPAN> of the point <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ *
+ * @param i index of the point to look for
+ *
+ * @param j index of the coordinate to look for
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN>
+ *
+ */
+ public abstract double getCoordinate (int i, int j);
+
+
+ /**
+ * Constructs and returns a point set iterator.
+ * The default implementation returns an iterator that uses the method
+ * {@link #getCoordinate getCoordinate} <TT>(i,j)</TT> to iterate over the
+ * points and coordinates, but subclasses can reimplement it
+ * for better efficiency.
+ *
+ * @return point set iterator for the point set
+ *
+ */
+ public PointSetIterator iterator() {
+ return new DefaultPointSetIterator();
+ }
+
+
+ /**
+ * Sets the random stream used to generate random shifts to <TT>stream</TT>.
+ *
+ * @param stream the new random stream
+ *
+ *
+ */
+ public void setStream (RandomStream stream) {
+ shiftStream = stream;
+ }
+
+
+ /**
+ * Returns the random stream used to generate random shifts.
+ *
+ * @return the random stream used
+ *
+ */
+ public RandomStream getStream() {
+ return shiftStream;
+ }
+
+
+ /**
+ * Randomizes the point set using the given <TT>rand</TT>.
+ *
+ * @param rand {@link PointSetRandomization} to use
+ *
+ *
+ */
+ public void randomize (PointSetRandomization rand) {
+ rand.randomize(this);
+ }
+
+
+ /**
+ * This method does nothing for this generic class.
+ * In some subclasses, it adds a random shift to all the points
+ * of the point set, using stream <TT>stream</TT> to generate the random numbers,
+ * for coordinates <TT>d1</TT> to <TT>d2-1</TT>.
+ *
+ */
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+// throw new UnsupportedOperationException
+// ("addRandomShift in PointSet called");
+ System.out.println (
+ "******* WARNING: addRandomShift in PointSet does nothing");
+ }
+
+
+ /**
+ * This method does nothing for this generic class.
+ * Similar to <TT>addRandomShift (0, d2, stream)</TT>,
+ * with <TT>d2</TT> the dimension of the current random shift.
+ *
+ *
+ */
+ public void addRandomShift (RandomStream stream) {
+ addRandomShift (0, dimShift, stream);
+ }
+
+
+ /**
+ * Similar to <TT>addRandomShift(d1, d2, stream)</TT>, with
+ * the current random stream.
+ */
+ @Deprecated
+ public void addRandomShift (int d1, int d2) {
+ addRandomShift (d1, d2, shiftStream);
+ }
+
+
+ @Deprecated
+ public void addRandomShift () {
+ addRandomShift (0, dimShift, shiftStream);
+ }
+
+
+ /**
+ * Erases the current random shift, if any.
+ *
+ */
+ public void clearRandomShift() {
+ capacityShift = 0;
+ dimShift = 0;
+// shiftStream = null;
+ }
+
+
+ /**
+ * By default, this method simply calls
+ * <TT>addRandomShift(d1, d2, stream)</TT>.
+ *
+ */
+ public void randomize (int d1, int d2, RandomStream stream) {
+ addRandomShift (d1, d2, stream);
+ }
+
+
+ /**
+ * By default, this method simply calls
+ * <TT>addRandomShift(stream)</TT>.
+ *
+ *
+ */
+ public void randomize (RandomStream stream) {
+ addRandomShift (stream);
+ }
+
+
+ /**
+ * By default, this method simply calls <TT>addRandomShift(d1, d2)</TT>.
+ *
+ */
+ @Deprecated
+ public void randomize (int d1, int d2) {
+ addRandomShift (d1, d2);
+ }
+
+
+ @Deprecated
+ public void randomize () {
+ addRandomShift();
+ }
+
+
+ /**
+ * By default, this method simply calls
+ * <TT>clearRandomShift()</TT>.
+ *
+ */
+ public void unrandomize() {
+ clearRandomShift();
+ }
+
+
+ /**
+ * Formats a string that contains information about the point set.
+ *
+ * @return string representation of the point set information
+ *
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Number of points: ");
+ int x = getNumPoints();
+ if (x == Integer.MAX_VALUE)
+ sb.append ("infinite");
+ else
+ sb.append (x);
+ sb.append (PrintfFormat.NEWLINE + "Point set dimension: ");
+ x = getDimension();
+ if (x == Integer.MAX_VALUE)
+ sb.append ("infinite");
+ else
+ sb.append (x);
+ return sb.toString();
+ }
+
+
+ /**
+ * Same as invoking {@link #formatPoints(int,int) formatPoints}<TT>(n, d)</TT> with <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>d</I></SPAN> equal to the
+ * number of points and the dimension of this object, respectively.
+ *
+ * @return string representation of all the points in the point set
+ * @exception UnsupportedOperationException if the number of points
+ * or dimension of the point set is infinite
+ *
+ *
+ */
+ public String formatPoints() {
+ PointSetIterator iter = iterator();
+ return formatPoints (iter);
+ }
+
+
+ /**
+ * Formats a string that displays the same information as returned by
+ * {@link #toString toString}, together with the first <SPAN CLASS="MATH"><I>d</I></SPAN> coordinates of the
+ * first <SPAN CLASS="MATH"><I>n</I></SPAN> points. If <SPAN CLASS="MATH"><I>n</I></SPAN> is larger than the number of points in the point
+ * set, it is reset to that number. If <SPAN CLASS="MATH"><I>d</I></SPAN> is larger than the dimension of the
+ * points, it is reset to that dimension. The points are printed in the
+ * simplest format, separated by spaces, by calling the default iterator
+ * repeatedly.
+ *
+ * @param n number of points
+ *
+ * @param d dimension
+ *
+ * @return string representation of first d coordinates of first n points
+ * in the point set
+ *
+ */
+ public String formatPoints (int n, int d) {
+ PointSetIterator iter = iterator();
+ return formatPoints (iter, n, d);
+ }
+
+
+ /**
+ * Same as invoking {@link #formatPoints(PointSetIterator,int,int) formatPoints}<TT>(iter, n, d)</TT>
+ * with <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>d</I></SPAN> equal to the number of points and the dimension, respectively.
+ *
+ * @param iter iterator associated to the point set
+ *
+ * @return string representation of all the points in the point set
+ * @exception UnsupportedOperationException if the number of points
+ * or dimension of the point set is infinite
+ *
+ *
+ */
+ public String formatPoints (PointSetIterator iter) {
+ int n = getNumPoints();
+ if (n == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException (
+ "Number of points is infinite");
+ int d = getDimension();
+ if (d == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException ("Dimension is infinite");
+ return formatPoints (iter, n, d);
+ }
+
+
+ /**
+ * Same as invoking {@link #formatPoints(int,int) formatPoints}<TT>(n, d)</TT>, but
+ * prints the points by calling <TT>iter</TT> repeatedly. The order of
+ * the printed points may be different than the one resulting from the
+ * default iterator.
+ *
+ * @param iter iterator associated to the point set
+ *
+ * @param n number of points
+ *
+ * @param d dimension
+ *
+ * @return string representation of first d coordinates of first n points
+ * in the point set
+ *
+ */
+ public String formatPoints (PointSetIterator iter, int n, int d) {
+ if (getNumPoints() < n)
+ n = getNumPoints();
+ if (getDimension() < d)
+ d = getDimension();
+ StringBuffer sb = new StringBuffer (toString());
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE
+ + "Points of the point set:" + PrintfFormat.NEWLINE);
+ for (int i=0; i<n; i++) {
+ for (int j=0; j<d; j++) {
+ sb.append (" ");
+ sb.append (iter.nextCoordinate());
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ iter.resetToNextPoint();
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Similar to {@link #formatPoints formatPoints}<TT>()</TT>, but the
+ * points coordinates are printed in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @param b base
+ *
+ * @return string representation of all the points in the point set
+ * @exception UnsupportedOperationException if the number of points
+ * or dimension of the point set is infinite
+ *
+ *
+ */
+ public String formatPointsBase (int b) {
+ PointSetIterator iter = iterator();
+ return formatPointsBase (iter, b);
+ }
+
+
+ /**
+ * Similar to {@link #formatPoints(int,int) formatPoints}<TT>(n, d)</TT>, but the
+ * points coordinates are printed in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @param n number of points
+ *
+ * @param d dimension
+ *
+ * @param b base
+ *
+ * @return string representation of first d coordinates of first n points
+ * in the point set
+ *
+ */
+ public String formatPointsBase (int n, int d, int b) {
+ PointSetIterator iter = iterator();
+ return formatPointsBase(iter, n, d, b);
+ }
+
+
+ /**
+ * Similar to
+ * {@link #formatPoints(PointSetIterator) formatPoints}<TT>(iter)</TT>,
+ * but the points coordinates are printed in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @param iter iterator associated to the point set
+ *
+ * @param b base
+ *
+ * @return string representation of all the points in the point set
+ * @exception UnsupportedOperationException if the number of points
+ * or dimension of the point set is infinite
+ *
+ *
+ */
+ public String formatPointsBase (PointSetIterator iter, int b) {
+ int n = getNumPoints();
+ if (n == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException (
+ "Number of points is infinite");
+ int d = getDimension();
+ if (d == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException ("Dimension is infinite");
+ return formatPointsBase (iter, n, d, b);
+ }
+
+
+ /**
+ * Similar to
+ * {@link #formatPoints(PointSetIterator,int,int) formatPoints}<TT>(iter, n, d)</TT>,
+ * but the points coordinates are printed in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @param iter iterator associated to the point set
+ *
+ * @param n number of points
+ *
+ * @param d dimension
+ *
+ * @param b base
+ *
+ * @return string representation of first d coordinates of first n points
+ * in the point set
+ *
+ */
+ public String formatPointsBase (PointSetIterator iter, int n, int d, int b) {
+ if (getNumPoints() < n)
+ n = getNumPoints();
+ if (getDimension() < d)
+ d = getDimension();
+ StringBuffer sb = new StringBuffer (toString());
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE
+ + "Points of the point set:" + PrintfFormat.NEWLINE);
+ double x;
+ int acc = 10;
+ if (b == 2)
+ acc = 20;
+ else if (b == 3)
+ acc = 13;
+ else
+ acc = 10;
+ if (null != shiftStream)
+ acc += 6;
+ int width = acc + 3;
+ String chaine;
+ for (int i=0; i<n; i++) {
+ for (int j=0; j<d; j++) {
+ sb.append (" ");
+ x = iter.nextCoordinate();
+ chaine = PrintfFormat.formatBase (-width, acc, b, x);
+ sb.append (chaine);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ iter.resetToNextPoint();
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Same as invoking {@link #formatPointsNumbered(int,int) formatPointsNumbered}<TT>(n, d)</TT>
+ * with <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>d</I></SPAN> equal to the number of points and the dimension,
+ * respectively.
+ *
+ * @return string representation of all the points in the point set
+ * @exception UnsupportedOperationException if the number of points
+ * or dimension of the point set is infinite
+ *
+ *
+ */
+ public String formatPointsNumbered() {
+ int n = getNumPoints();
+ if (n == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException (
+ "Number of points is infinite");
+ int d = getDimension();
+ if (d == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException ("Dimension is infinite");
+ return formatPointsNumbered (n, d);
+ }
+
+
+ /**
+ * Same as invoking {@link #formatPoints(int,int) formatPoints}<TT>(n,d)</TT>, except that the points are numbered.
+ *
+ * @param n number of points
+ *
+ * @param d dimension
+ *
+ * @return string representation of first d coordinates of first n points
+ * in the point set
+ *
+ */
+ public String formatPointsNumbered (int n, int d) {
+ if (getNumPoints() < n)
+ n = getNumPoints();
+ if (getDimension() < d)
+ d = getDimension();
+ StringBuffer sb = new StringBuffer (toString());
+ PointSetIterator itr = iterator();
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE
+ + "Points of the point set:");
+ for (int i=0; i<n; i++) {
+ sb.append (PrintfFormat.NEWLINE + "Point " +
+ // itr.getCurPointIndex() + " = (");
+ i + " = (");
+ boolean first = true;
+ for (int j=0; j<d; j++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (itr.nextCoordinate());
+ }
+ sb.append (")");
+ itr.resetToNextPoint();
+ }
+ return sb.toString();
+ }
+
+
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// This class implements a default point set iterator.
+// Since it is inherited by subclasses, it can be used as a base class
+// for iterators.
+// It is implemented as an inner class because it can then use directly
+// the variables of the PointSet class. It would be more difficult and
+// cumbersome to access those variables if it was implemented as a
+// separate class.
+
+ protected class DefaultPointSetIterator implements PointSetIterator {
+
+ protected int curPointIndex = 0; // Index of the current point.
+ protected int curCoordIndex = 0; // Index of the current coordinate.
+ protected double EpsilonHalf = 1.0 / Num.TWOEXP[55];
+ // protected double EpsilonHalf = PointSet.this.EpsilonHalf;
+
+ protected void outOfBounds () {
+ if (getCurPointIndex() >= numPoints)
+ throw new NoSuchElementException ("Not enough points available");
+ else
+ throw new NoSuchElementException ("Not enough coordinates available");
+ }
+
+ public void setCurCoordIndex (int j) {
+ curCoordIndex = j;
+ }
+
+ public void resetCurCoordIndex() {
+ setCurCoordIndex (0);
+ }
+
+ public int getCurCoordIndex() {
+ return curCoordIndex;
+ }
+
+ public boolean hasNextCoordinate() {
+ return getCurCoordIndex() < getDimension();
+ }
+
+ public double nextCoordinate() {
+ if (getCurPointIndex() >= numPoints || getCurCoordIndex() >= dim)
+ outOfBounds();
+ return getCoordinate (curPointIndex, curCoordIndex++);
+ }
+
+ public void nextCoordinates (double p[], int d) {
+ if (getCurCoordIndex() + d > getDimension()) outOfBounds();
+ for (int j = 0; j < d; j++)
+ p[j] = nextCoordinate();
+ }
+
+ // This is called with i = numPoints when nextPoint generates the
+ // last point, so i = numPoints must be allowed.
+ // The "no more point" error will be raised if we ask for
+ // a new coordinate or point.
+ public void setCurPointIndex (int i) {
+ curPointIndex = i;
+ resetCurCoordIndex();
+ }
+
+ public void resetCurPointIndex() {
+ setCurPointIndex (0);
+ }
+
+ public int resetToNextPoint() {
+ setCurPointIndex (curPointIndex + 1);
+ return curPointIndex;
+ }
+
+ public int getCurPointIndex() {
+ return curPointIndex;
+ }
+
+ public boolean hasNextPoint() {
+ return getCurPointIndex() < getNumPoints();
+ }
+
+ public int nextPoint (double p[], int d) {
+ resetCurCoordIndex();
+ nextCoordinates (p, d);
+ return resetToNextPoint();
+ }
+
+
+ public void resetStartStream() { // Same as resetCurPointIndex();
+ resetCurPointIndex();
+ }
+
+ public void resetStartSubstream() { // Same as resetCurCoordIndex();
+ resetCurCoordIndex();
+ }
+
+ public void resetNextSubstream() { // Same as resetToNextPoint();
+ resetToNextPoint();
+ }
+
+ public void setAntithetic (boolean b) {
+ throw new UnsupportedOperationException();
+ }
+
+ public double nextDouble() { // Same as nextCoordinate();
+ return nextCoordinate();
+ }
+
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = start; i < start+n; i++)
+ u[i] = nextDouble();
+ }
+
+ public int nextInt (int i, int j) {
+ return (i + (int)(nextDouble() * (j - i + 1.0)));
+ }
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int k = start; k < start+n; k++)
+ u[k] = nextInt (i, j);
+ }
+
+ public String formatState() {
+ return "Current point index: " + getCurPointIndex() +
+ PrintfFormat.NEWLINE + "Current coordinate index: " +
+ getCurCoordIndex();
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/PointSet.tex b/source/umontreal/iro/lecuyer/hups/PointSet.tex
new file mode 100644
index 0000000..15ee03e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PointSet.tex
@@ -0,0 +1,751 @@
+\defmodule{PointSet}
+
+This abstract class defines the basic methods
+for accessing and manipulating point sets.
+A point set can be represented as a two-dimensional array, whose element
+$(i,j)$ contains $u_{i,j}$, the \emph{coordinate} $j$ of point $i$.
+Each coordinate $u_{i,j}$ is assumed to be in the unit interval $[0,1]$.
+% and can be accessed directly via the (abstract) method
+% \method{getCoordinate}{}.
+% (implemented in subclasses).
+Whether the values 0 and 1 can occur may depend on the
+actual implementation of the point set.
+
+All points have the same number of coordinates (their \emph{dimension})
+and this number can be queried by \method{getDimension}{}.
+The number of points is queried by \method{getNumPoints}{}.
+The points and coordinates are both numbered starting from 0
+and their number can actually be infinite.
+
+The \method{iterator}{} method provides a \emph{point set iterator}
+which permits one to enumerate the points and their coordinates.
+Several iterators over the same point set can coexist at any given time.
+These iterators are instances of a hidden inner-class that implements
+the \class{PointSetIterator} interface.
+The default implementation of iterator provided here relies on
+the method \method{getCoordinate}{} to access the coordinates directly.
+However, this approach is rarely efficient.
+Specialized implementations that dramatically improve the performance
+are provided in subclasses of \class{PointSet}.
+The \class{PointSetIterator}{} interface actually extends the
+\class{RandomStream}{} interface, so that the
+iterator can also be seen as a \class{RandomStream} and used wherever
+such a stream is required for generating uniform random numbers.
+This permits one to easily replace pseudorandom numbers by the
+coordinates of a selected set of highly-uniform points, i.e.,
+to replace Monte Carlo by quasi-Monte Carlo in a simulation program.
+
+%%%%%%%%
+\begin{comment}
+The class also offers tools to manipulate a list of randomizations
+that can be applied to this point set.
+\pierre{So far, the general types of randomizations have been implemented
+ as containers. We may remove this concept of list. }
+\richard{La nouvelle randomisation d'Adam rend l'ancienne liste de
+ randomisations obsol\`ete: nous n'avons jamais utilis\'e l'ancienne version.}
+\end{comment}
+%%%%%%
+
+This abstract class has only one abstract method:
+\method{getCoordinate}{}.
+Providing an implementation for this method is already sufficient
+for the subclass to work.
+However, in practically all cases, efficiency can be dramatically improved by
+overwriting \method{iterator}{} to provide a custom iterator that does not
+necessarily rely on \method{getCoordinate}{}.
+In fact, direct use of \method{getCoordinate}{} to access the coordinates
+is discouraged.
+One should access the coordinates only via the iterators.
+
+\begin{detailed} %%%%%
+The built-in range checks require some extra time and also
+assumes that nobody ever uses negative indices (Java does not offer unsigned
+integers). If \method{getCoordinate}{} is not accessed directly by the user,
+it may be implemented without range checks.
+\end{detailed} %%%
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PointSet
+ * Description: Base class of all point sets
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import java.util.NoSuchElementException;
+import java.util.List;
+import java.util.ArrayList;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public abstract class PointSet \begin{hide} {
+
+ // The maximum number of usable bits (binary digits).
+ // Since Java has no unsigned type, the
+ // 32nd bit cannot be used efficiently. This mainly affects digit
+ // scrambling and bit vectors. This also limits the maximum number
+ // of columns for the generating matrices of digital nets in base 2.
+ protected static final int MAXBITS = 31;
+ // To avoid 0 for nextCoordinate when random shifting
+ protected double EpsilonHalf = 1.0 / Num.TWOEXP[55]; // 1/2^55
+
+ protected int dim = 0;
+ protected int numPoints = 0;
+ protected int dimShift = 0; // Current dimension of the shift.
+ protected int capacityShift = 0; // Number of array elements of shift;
+ // it is always >= dimShift
+ protected RandomStream shiftStream; // Used to generate random shifts.
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Sizes
+\begin{code}
+
+ public int getDimension()\begin{hide} {
+ return dim;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the dimension (number of available coordinates) of the point set.
+ If the dimension is actually infinite, \texttt{Integer.MAX\_VALUE} is returned.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the dimension of the point set or \texttt{Integer.MAX\_VALUE}
+ if it is infinite}
+\end{htmlonly}
+\begin{code}
+
+ public int getNumPoints()\begin{hide} {
+ return numPoints;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the number of points.
+ If this number is actually infinite, \texttt{Integer.MAX\_VALUE} is returned.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the number of points in the point set or \texttt{Integer.MAX\_VALUE}
+ if the point set has an infinity of points.
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Coordinates
+\begin{code}
+
+ public abstract double getCoordinate (int i, int j);
+\end{code}
+ \begin{tabb}
+ Returns $u_{i,j}$, the coordinate $j$ of the point $i$.
+\richard{La m\'ethode \texttt{getCoordinate} de certaines classes ne tient
+pas compte du random shift, contrairement \`a l'it\'erateur de la m\^eme classe.
+Faut-il que toutes les \texttt{getCoordinate} impl\'ementent le random shift
+quand il existe?}
+ \end{tabb}
+\begin{htmlonly}
+ \param{i}{index of the point to look for}
+ \param{j}{index of the coordinate to look for}
+ \return{the value of $u_{i,j}$}
+\end{htmlonly}
+\begin{code}
+
+ public PointSetIterator iterator()\begin{hide} {
+ return new DefaultPointSetIterator();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs and returns a point set iterator.
+ The default implementation returns an iterator that uses the method
+ \method{getCoordinate}{}~\texttt{(i,j)} to iterate over the
+ points and coordinates, but subclasses can reimplement it
+ for better efficiency.
+\end{tabb}
+\begin{htmlonly}
+ \return{point set iterator for the point set}
+\end{htmlonly}
+\begin{code}
+
+ public void setStream (RandomStream stream)\begin{hide} {
+ shiftStream = stream;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the random stream used to generate random shifts to \texttt{stream}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{stream}{the new random stream}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStream getStream()\begin{hide} {
+ return shiftStream;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the random stream used to generate random shifts.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the random stream used}
+\end{htmlonly}
+\begin{code}
+
+ public void randomize (PointSetRandomization rand) \begin{hide} {
+ rand.randomize(this);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Randomizes the point set using the given \texttt{rand}.
+\end{tabb}
+\begin{htmlonly}
+ \param{rand}{\class{PointSetRandomization} to use}
+\end{htmlonly}
+\begin{code}
+
+ public void addRandomShift (int d1, int d2, RandomStream stream)\begin{hide} {
+// throw new UnsupportedOperationException
+// ("addRandomShift in PointSet called");
+ System.out.println (
+ "******* WARNING: addRandomShift in PointSet does nothing");
+ }\end{hide}
+\end{code}
+\begin{tabb} This method does nothing for this generic class.
+ In some subclasses, it adds a random shift to all the points
+ of the point set, using stream \texttt{stream} to generate the random numbers,
+ for coordinates \texttt{d1} to \texttt{d2-1}.
+\end{tabb}
+\begin{code}
+
+ public void addRandomShift (RandomStream stream)\begin{hide} {
+ addRandomShift (0, dimShift, stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} This method does nothing for this generic class.
+ Similar to \texttt{addRandomShift (0, d2, stream)},
+ with \texttt{d2} the dimension of the current random shift.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public void addRandomShift (int d1, int d2) {
+ addRandomShift (d1, d2, shiftStream);
+ }
+\end{code}
+\begin{tabb} Similar to \texttt{addRandomShift(d1, d2, stream)}, with
+ the current random stream.% This method does nothing for this generic class.
+\end{tabb}
+\begin{code}
+
+ @Deprecated
+ public void addRandomShift () {
+ addRandomShift (0, dimShift, shiftStream);
+ }
+\end{code}
+\begin{tabb} Similar to \texttt{addRandomShift(0, d2, stream)}
+ with the current random stream and \texttt{d2} the dimension of the current
+ random shift.% This method does nothing for this generic class.
+\end{tabb}\end{hide}
+\begin{code}
+
+ public void clearRandomShift()\begin{hide} {
+ capacityShift = 0;
+ dimShift = 0;
+// shiftStream = null;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Erases the current random shift, if any.
+\end{tabb}
+\begin{code}
+
+ public void randomize (int d1, int d2, RandomStream stream)\begin{hide} {
+ addRandomShift (d1, d2, stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} By default, this method simply calls
+ \texttt{addRandomShift(d1, d2, stream)}.
+\end{tabb}
+\begin{code}
+
+ public void randomize (RandomStream stream)\begin{hide} {
+ addRandomShift (stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} By default, this method simply calls
+ \texttt{addRandomShift(stream)}.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public void randomize (int d1, int d2) {
+ addRandomShift (d1, d2);
+ }
+\end{code}
+\begin{tabb} By default, this method simply calls \texttt{addRandomShift(d1, d2)}.
+\end{tabb}
+\begin{code}
+
+ @Deprecated
+ public void randomize () {
+ addRandomShift();
+ }
+\end{code}
+\begin{tabb} By default, this method simply calls \texttt{addRandomShift()}.
+\end{tabb}
+\end{hide}\begin{code}
+
+ public void unrandomize()\begin{hide} {
+ clearRandomShift();
+ }\end{hide}
+\end{code}
+\begin{tabb} By default, this method simply calls
+ \texttt{clearRandomShift()}.
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+
+ public String toString() \begin{hide} {
+ StringBuffer sb = new StringBuffer ("Number of points: ");
+ int x = getNumPoints();
+ if (x == Integer.MAX_VALUE)
+ sb.append ("infinite");
+ else
+ sb.append (x);
+ sb.append (PrintfFormat.NEWLINE + "Point set dimension: ");
+ x = getDimension();
+ if (x == Integer.MAX_VALUE)
+ sb.append ("infinite");
+ else
+ sb.append (x);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Formats a string that contains information about the point set.
+\end{tabb}
+\begin{htmlonly}
+ \return{string representation of the point set information}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPoints() \begin{hide} {
+ PointSetIterator iter = iterator();
+ return formatPoints (iter);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as invoking \method{formatPoints}{int,int}\texttt{(n, d)} with $n$ and $d$ equal to the
+ number of points and the dimension of this object, respectively.
+\end{tabb}
+\begin{htmlonly}
+ \return{string representation of all the points in the point set}
+ \exception{UnsupportedOperationException}{if the number of points
+ or dimension of the point set is infinite}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPoints (int n, int d) \begin{hide} {
+ PointSetIterator iter = iterator();
+ return formatPoints (iter, n, d);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Formats a string that displays the same information as returned by
+ \method{toString}{}, together with the first $d$ coordinates of the
+ first $n$ points. If $n$ is larger than the number of points in the point
+ set, it is reset to that number. If $d$ is larger than the dimension of the
+ points, it is reset to that dimension. The points are printed in the
+ simplest format, separated by spaces, by calling the default iterator
+ repeatedly.
+ % \hpierre{Could perhaps just print what exists.}
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{number of points}
+ \param{d}{dimension}
+ \return{string representation of first d coordinates of first n points
+ in the point set}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPoints (PointSetIterator iter) \begin{hide} {
+ int n = getNumPoints();
+ if (n == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException (
+ "Number of points is infinite");
+ int d = getDimension();
+ if (d == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException ("Dimension is infinite");
+ return formatPoints (iter, n, d);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as invoking \method{formatPoints}{PointSetIterator,int,int}\texttt{(iter, n, d)}
+ with $n$ and $d$ equal to the number of points and the dimension, respectively.
+\end{tabb}
+\begin{htmlonly}
+ \param{iter}{iterator associated to the point set}
+ \return{string representation of all the points in the point set}
+ \exception{UnsupportedOperationException}{if the number of points
+ or dimension of the point set is infinite}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPoints (PointSetIterator iter, int n, int d) \begin{hide} {
+ if (getNumPoints() < n)
+ n = getNumPoints();
+ if (getDimension() < d)
+ d = getDimension();
+ StringBuffer sb = new StringBuffer (toString());
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE
+ + "Points of the point set:" + PrintfFormat.NEWLINE);
+ for (int i=0; i<n; i++) {
+ for (int j=0; j<d; j++) {
+ sb.append (" ");
+ sb.append (iter.nextCoordinate());
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ iter.resetToNextPoint();
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as invoking \method{formatPoints}{int,int}\texttt{(n, d)}, but
+ prints the points by calling \texttt{iter} repeatedly. The order of
+ the printed points may be different than the one resulting from the
+ default iterator.
+\end{tabb}
+\begin{htmlonly}
+ \param{iter}{iterator associated to the point set}
+ \param{n}{number of points}
+ \param{d}{dimension}
+ \return{string representation of first d coordinates of first n points
+ in the point set}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPointsBase (int b) \begin{hide} {
+ PointSetIterator iter = iterator();
+ return formatPointsBase (iter, b);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Similar to \method{formatPoints}{}\texttt{()}, but the
+points coordinates are printed in base $b$.
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{base}
+ \return{string representation of all the points in the point set}
+ \exception{UnsupportedOperationException}{if the number of points
+ or dimension of the point set is infinite}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPointsBase (int n, int d, int b) \begin{hide} {
+ PointSetIterator iter = iterator();
+ return formatPointsBase(iter, n, d, b);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Similar to \method{formatPoints}{int,int}\texttt{(n, d)}, but the
+ points coordinates are printed in base $b$.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{number of points}
+ \param{d}{dimension}
+ \param{b}{base}
+ \return{string representation of first d coordinates of first n points
+ in the point set}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPointsBase (PointSetIterator iter, int b) \begin{hide} {
+ int n = getNumPoints();
+ if (n == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException (
+ "Number of points is infinite");
+ int d = getDimension();
+ if (d == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException ("Dimension is infinite");
+ return formatPointsBase (iter, n, d, b);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Similar to
+\method{formatPoints}{PointSetIterator}\texttt{(iter)},
+but the points coordinates are printed in base $b$.
+\end{tabb}
+\begin{htmlonly}
+ \param{iter}{iterator associated to the point set}
+ \param{b}{base}
+ \return{string representation of all the points in the point set}
+ \exception{UnsupportedOperationException}{if the number of points
+ or dimension of the point set is infinite}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPointsBase (PointSetIterator iter, int n, int d, int b) \begin{hide} {
+ if (getNumPoints() < n)
+ n = getNumPoints();
+ if (getDimension() < d)
+ d = getDimension();
+ StringBuffer sb = new StringBuffer (toString());
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE
+ + "Points of the point set:" + PrintfFormat.NEWLINE);
+ double x;
+ int acc = 10;
+ if (b == 2)
+ acc = 20;
+ else if (b == 3)
+ acc = 13;
+ else
+ acc = 10;
+ if (null != shiftStream)
+ acc += 6;
+ int width = acc + 3;
+ String chaine;
+ for (int i=0; i<n; i++) {
+ for (int j=0; j<d; j++) {
+ sb.append (" ");
+ x = iter.nextCoordinate();
+ chaine = PrintfFormat.formatBase (-width, acc, b, x);
+ sb.append (chaine);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ iter.resetToNextPoint();
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Similar to
+\method{formatPoints}{PointSetIterator,int,int}\texttt{(iter, n, d)},
+but the points coordinates are printed in base $b$.
+\end{tabb}
+\begin{htmlonly}
+ \param{iter}{iterator associated to the point set}
+ \param{n}{number of points}
+ \param{d}{dimension}
+ \param{b}{base}
+ \return{string representation of first d coordinates of first n points
+ in the point set}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPointsNumbered() \begin{hide} {
+ int n = getNumPoints();
+ if (n == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException (
+ "Number of points is infinite");
+ int d = getDimension();
+ if (d == Integer.MAX_VALUE)
+ throw new UnsupportedOperationException ("Dimension is infinite");
+ return formatPointsNumbered (n, d);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as invoking \method{formatPointsNumbered}{int,int}{\texttt{(n, d)}}
+ with $n$ and $d$ equal to the number of points and the dimension,
+ respectively.
+\end{tabb}
+\begin{htmlonly}
+ \return{string representation of all the points in the point set}
+ \exception{UnsupportedOperationException}{if the number of points
+ or dimension of the point set is infinite}
+\end{htmlonly}
+\begin{code}
+
+ public String formatPointsNumbered (int n, int d) \begin{hide} {
+ if (getNumPoints() < n)
+ n = getNumPoints();
+ if (getDimension() < d)
+ d = getDimension();
+ StringBuffer sb = new StringBuffer (toString());
+ PointSetIterator itr = iterator();
+ sb.append (PrintfFormat.NEWLINE + PrintfFormat.NEWLINE
+ + "Points of the point set:");
+ for (int i=0; i<n; i++) {
+ sb.append (PrintfFormat.NEWLINE + "Point " +
+ // itr.getCurPointIndex() + " = (");
+ i + " = (");
+ boolean first = true;
+ for (int j=0; j<d; j++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (itr.nextCoordinate());
+ }
+ sb.append (")");
+ itr.resetToNextPoint();
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as invoking \method{formatPoints}{int,int}{\texttt{(n,d)}}, except that the points are numbered.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{number of points}
+ \param{d}{dimension}
+ \return{string representation of first d coordinates of first n points
+ in the point set}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// This class implements a default point set iterator.
+// Since it is inherited by subclasses, it can be used as a base class
+// for iterators.
+// It is implemented as an inner class because it can then use directly
+// the variables of the PointSet class. It would be more difficult and
+// cumbersome to access those variables if it was implemented as a
+// separate class.
+
+ protected class DefaultPointSetIterator implements PointSetIterator {
+
+ protected int curPointIndex = 0; // Index of the current point.
+ protected int curCoordIndex = 0; // Index of the current coordinate.
+ protected double EpsilonHalf = 1.0 / Num.TWOEXP[55];
+ // protected double EpsilonHalf = PointSet.this.EpsilonHalf;
+
+ protected void outOfBounds () {
+ if (getCurPointIndex() >= numPoints)
+ throw new NoSuchElementException ("Not enough points available");
+ else
+ throw new NoSuchElementException ("Not enough coordinates available");
+ }
+
+ public void setCurCoordIndex (int j) {
+ curCoordIndex = j;
+ }
+
+ public void resetCurCoordIndex() {
+ setCurCoordIndex (0);
+ }
+
+ public int getCurCoordIndex() {
+ return curCoordIndex;
+ }
+
+ public boolean hasNextCoordinate() {
+ return getCurCoordIndex() < getDimension();
+ }
+
+ public double nextCoordinate() {
+ if (getCurPointIndex() >= numPoints || getCurCoordIndex() >= dim)
+ outOfBounds();
+ return getCoordinate (curPointIndex, curCoordIndex++);
+ }
+
+ public void nextCoordinates (double p[], int d) {
+ if (getCurCoordIndex() + d > getDimension()) outOfBounds();
+ for (int j = 0; j < d; j++)
+ p[j] = nextCoordinate();
+ }
+
+ // This is called with i = numPoints when nextPoint generates the
+ // last point, so i = numPoints must be allowed.
+ // The "no more point" error will be raised if we ask for
+ // a new coordinate or point.
+ public void setCurPointIndex (int i) {
+ curPointIndex = i;
+ resetCurCoordIndex();
+ }
+
+ public void resetCurPointIndex() {
+ setCurPointIndex (0);
+ }
+
+ public int resetToNextPoint() {
+ setCurPointIndex (curPointIndex + 1);
+ return curPointIndex;
+ }
+
+ public int getCurPointIndex() {
+ return curPointIndex;
+ }
+
+ public boolean hasNextPoint() {
+ return getCurPointIndex() < getNumPoints();
+ }
+
+ public int nextPoint (double p[], int d) {
+ resetCurCoordIndex();
+ nextCoordinates (p, d);
+ return resetToNextPoint();
+ }
+
+
+ public void resetStartStream() { // Same as resetCurPointIndex();
+ resetCurPointIndex();
+ }
+
+ public void resetStartSubstream() { // Same as resetCurCoordIndex();
+ resetCurCoordIndex();
+ }
+
+ public void resetNextSubstream() { // Same as resetToNextPoint();
+ resetToNextPoint();
+ }
+
+ public void setAntithetic (boolean b) {
+ throw new UnsupportedOperationException();
+ }
+
+ public double nextDouble() { // Same as nextCoordinate();
+ return nextCoordinate();
+ }
+
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = start; i < start+n; i++)
+ u[i] = nextDouble();
+ }
+
+ public int nextInt (int i, int j) {
+ return (i + (int)(nextDouble() * (j - i + 1.0)));
+ }
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int k = start; k < start+n; k++)
+ u[k] = nextInt (i, j);
+ }
+
+ public String formatState() {
+ return "Current point index: " + getCurPointIndex() +
+ PrintfFormat.NEWLINE + "Current coordinate index: " +
+ getCurCoordIndex();
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/PointSetIterator.java b/source/umontreal/iro/lecuyer/hups/PointSetIterator.java
new file mode 100644
index 0000000..3d1f431
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PointSetIterator.java
@@ -0,0 +1,238 @@
+
+
+/*
+ * Interface: PointSetIterator
+ * Description: Iterator over point sets
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+/**
+ * Objects of classes that implement this interface are <SPAN CLASS="textit">iterators</SPAN>
+ * that permit one to enumerate
+ * (or observe) the successive points of a point set and the successive
+ * coordinates of these points.
+ * Each {@link PointSetIterator} is associated with a given point set
+ * and maintains a <SPAN CLASS="textit">current point</SPAN> index <SPAN CLASS="MATH"><I>i</I></SPAN> and a <SPAN CLASS="textit">current coordinate</SPAN>
+ * index <SPAN CLASS="MATH"><I>j</I></SPAN>, which are both initialized to zero.
+ *
+ * <P>
+ * Successive coordinates can be accessed one or many at a time by the methods
+ * {@link #nextCoordinate nextCoordinate} and {@link #nextCoordinates nextCoordinates}, respectively.
+ * The current coordinate index <SPAN CLASS="MATH"><I>j</I></SPAN> can be set explicitely by
+ * {@link #setCurCoordIndex setCurCoordIndex} and {@link #resetCurCoordIndex resetCurCoordIndex}.
+ * Similar methods are available for resetting and accessing the current point.
+ * The method {@link #nextPoint nextPoint} permits one to
+ * enumerate the successive points in natural order.
+ *
+ * <P>
+ * This class also implements the {@link RandomStream} interface.
+ * This permits one to replace random numbers by the coordinates of
+ * (randomized) quasi-Monte Carlo points without changing the code that calls
+ * the generators in a simulation program.
+ * That is, the same simulation program can be used for both Monte Carlo
+ * and quasi-Monte Carlo simulations.
+ * The method {@link #nextDouble nextDouble} does exactly the same as
+ * {@link #nextCoordinate nextCoordinate}, it returns the current coordinate of the
+ * current point and advances the current coordinate by one.
+ * The substreams correspond to the points, so
+ * {@link #resetStartSubstream resetStartSubstream} resets the current point coordinate to zero,
+ * {@link #resetNextSubstream resetNextSubstream} resets the iterator to the next point, and
+ * {@link #resetStartStream resetStartStream} resets the iterator to the first point of
+ * the point set.
+ *
+ * <P>
+ * There can be several iterators over the same point set.
+ * These iterators are independent from each other.
+ * Classes that implement this interface must maintain enough information
+ * so that each iterator is unaffected by other iterator's operations.
+ * However, the iterator does not need to be independent of the underlying
+ * point set. If the point set is modified (e.g., randomized), the iterator
+ * may continue to work as usual.
+ *
+ * <P>
+ * Point set iterators are implemented as inner classes because
+ * this gives a direct access to the private members (or variables)
+ * of the class. This is important for efficiency.
+ * They are quite similar to the iterators in Java <SPAN CLASS="textit">collections</SPAN>.
+ *
+ */
+public interface PointSetIterator extends RandomStream {
+
+ /**
+ * Sets the current coordinate index to <SPAN CLASS="MATH"><I>j</I></SPAN>, so that
+ * the next calls to {@link #nextCoordinate nextCoordinate} or {@link #nextCoordinates nextCoordinates}
+ * will return the values
+ * <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB>, <I>u</I><SUB>i, j+1</SUB>,...</SPAN>, where <SPAN CLASS="MATH"><I>i</I></SPAN> is the
+ * index of the current point.
+ *
+ * @param j index of the new current coordinate
+ *
+ *
+ */
+ public void setCurCoordIndex (int j);
+
+
+ /**
+ * Equivalent to {@link #setCurCoordIndex setCurCoordIndex} <TT>(0)</TT>.
+ *
+ */
+ public void resetCurCoordIndex();
+
+
+ /**
+ * Returns the index <SPAN CLASS="MATH"><I>j</I></SPAN> of the current coordinate. This may be useful,
+ * e.g., for testing if enough coordinates are still available.
+ *
+ * @return index of the current coordinate
+ *
+ */
+ public int getCurCoordIndex();
+
+
+ /**
+ * Returns <TT>true</TT> if the current point has another coordinate.
+ * This can be useful for testing if coordinates are still available.
+ *
+ * @return <TT>true</TT> if the current point has another coordinate
+ *
+ */
+ public boolean hasNextCoordinate();
+
+
+ /**
+ * Returns the current coordinate <SPAN CLASS="MATH"><I>u</I><SUB>i, j</SUB></SPAN> and advances to the next one.
+ * If no current coordinate is available (either because the current
+ * point index has reached the number of points or because the current
+ * coordinate index has reached the number of dimensions), it throws a
+ * {@link java.util.NoSuchElementException NoSuchElementException}.
+ *
+ * @return value of the current coordinate
+ * @exception NoSuchElementException if no such coordinate is available
+ *
+ *
+ */
+ public double nextCoordinate();
+
+
+ /**
+ * Returns the next <TT>d</TT> coordinates of the current point in <TT>p</TT>
+ * and advances the current coordinate index by <TT>d</TT>.
+ * If the remaining number of coordinates is too small, a
+ * <TT>NoSuchElementException</TT> is thrown, as in {@link #nextCoordinate nextCoordinate}.
+ *
+ * @param p array to be filled with the coordinates, starting at index 0
+ *
+ * @param d number of coordinates to get
+ *
+ * @exception NoSuchElementException if there are not enough
+ * remaining coordinates in the current point
+ *
+ *
+ */
+ public void nextCoordinates (double[] p, int d);
+
+
+ /**
+ * Resets the current point index to <SPAN CLASS="MATH"><I>i</I></SPAN> and the current coordinate
+ * index to zero. If <TT>i</TT> is larger or equal to the number of points,
+ * an exception will <SPAN CLASS="textit">not</SPAN> be raised here, but only later if we
+ * ask for a new coordinate or point.
+ *
+ * @param i new index of the current point
+ *
+ *
+ */
+ public void setCurPointIndex (int i);
+
+
+ /**
+ * Equivalent to {@link #setCurPointIndex setCurPointIndex} <TT>(0)</TT>.
+ *
+ */
+ public void resetCurPointIndex();
+
+
+ /**
+ * Increases the current point index by 1 and returns its new value.
+ * If there is no more point, an exception will be raised only if we
+ * ask for a new coordinate or point later on.
+ *
+ * @return index of the new current point
+ *
+ */
+ public int resetToNextPoint();
+
+
+ /**
+ * Returns the index <SPAN CLASS="MATH"><I>i</I></SPAN> of the current point.
+ * This can be useful, e.g., for caching point sets.
+ *
+ * @return index of the current point
+ *
+ */
+ public int getCurPointIndex();
+
+
+ /**
+ * Returns <TT>true</TT> if there is a next point.
+ * This can be useful for testing if points are still available.
+ *
+ * @return <TT>true</TT> if a next point is available from the iterated point set
+ *
+ */
+ public boolean hasNextPoint();
+
+
+ /**
+ * Returns the <SPAN CLASS="textit">first</SPAN> <TT>d</TT> coordinates of the <SPAN CLASS="textit">current</SPAN>
+ * point in <TT>p</TT>, advances to the next point, and
+ * returns the index of the <SPAN CLASS="textit">new</SPAN> current point.
+ * Even if the current coordinate index is 0, the point returned
+ * starts from coordinate 0.
+ * After obtaining the last point via this method, the current point
+ * index (returned by the method) is equal to the number of points,
+ * so it is no longer a valid point index.
+ * An exception will then be raised if we attempt to generate additional
+ * points or coordinates.
+ *
+ * <P>
+ * Specialized implementations of this method often allow for increased
+ * efficiency, e.g., for cycle-based point sets where the cycles
+ * (but not the points)
+ * are stored explicitly or for digital nets
+ * by allowing non-incremental point enumerations via Gray-code counters.
+ *
+ * @param p array to be filled with the coordinates,
+ * starting from array index 0
+ *
+ * @param d number of coordinates to return
+ *
+ * @return index of the new current point
+ * @exception NoSuchElementException if there are not enough coordinates
+ * available in the current point for filling <TT>p</TT>
+ *
+ */
+ public int nextPoint (double[] p, int d);
+}
diff --git a/source/umontreal/iro/lecuyer/hups/PointSetIterator.tex b/source/umontreal/iro/lecuyer/hups/PointSetIterator.tex
new file mode 100644
index 0000000..e213884
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PointSetIterator.tex
@@ -0,0 +1,244 @@
+\defmodule{PointSetIterator}
+
+Objects of classes that implement this interface are \emph{iterators}
+that permit one to enumerate
+(or observe) the successive points of a point set and the successive
+coordinates of these points.
+Each \class{PointSetIterator}{} is associated with a given point set
+and maintains a \emph{current point} index $i$ and a \emph{current coordinate}
+index $j$, which are both initialized to zero.
+
+Successive coordinates can be accessed one or many at a time by the methods
+\method{nextCoor\-di\-nate}{} and \method{nextCoordi\-nates}{}, respectively.
+The current coordinate index $j$ can be set explicitely by
+\method{setCurCoordIndex}{} and \method{resetCurCoordIndex}{}.
+Similar methods are available for resetting and accessing the current point.
+The method \method{nextPoint}{} permits one to
+enumerate the successive points in natural order.
+
+This class also implements the \class{RandomStream} interface.
+This permits one to replace random numbers by the coordinates of
+(randomized) quasi-Monte Carlo points without changing the code that calls
+the generators in a simulation program.
+That is, the same simulation program can be used for both Monte Carlo
+and quasi-Monte Carlo simulations.
+The method \method{nextDouble}{} does exactly the same as
+\method{nextCoordinate}{}, it returns the current coordinate of the
+current point and advances the current coordinate by one.
+The substreams correspond to the points, so
+\method{resetStartSubstream}{} resets the current point coordinate to zero,
+\method{resetNextSubstream}{} resets the iterator to the next point, and
+\method{resetStartStream}{} resets the iterator to the first point of
+the point set.
+
+There can be several iterators over the same point set.
+These iterators are independent from each other.
+Classes that implement this interface must maintain enough information
+so that each iterator is unaffected by other iterator's operations.
+However, the iterator does not need to be independent of the underlying
+point set. If the point set is modified (e.g., randomized), the iterator
+may continue to work as usual.
+
+Point set iterators are implemented as inner classes because
+this gives a direct access to the private members (or variables)
+of the class. This is important for efficiency.
+They are quite similar to the iterators in Java \emph{collections}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Interface: PointSetIterator
+ * Description: Iterator over point sets
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.rng.RandomStream;\end{hide}
+
+public interface PointSetIterator extends RandomStream\begin{hide} {\end{hide}
+
+ public void setCurCoordIndex (int j);
+\end{code}
+ \begin{tabb}
+ Sets the current coordinate index to $j$, so that
+ the next calls to \method{nextCoordinate}{} or \method{nextCoordinates}{}
+ will return the values $u_{i,j}, u_{i,j+1}, \dots$, where $i$ is the
+ index of the current point.
+ \end{tabb}
+\begin{htmlonly}
+ \param{j}{index of the new current coordinate}
+\end{htmlonly}
+\begin{code}
+
+ public void resetCurCoordIndex();
+\end{code}
+ \begin{tabb}
+ Equivalent to \method{setCurCoordIndex}{}~\texttt{(0)}.
+ \end{tabb}
+\begin{code}
+
+ public int getCurCoordIndex();
+\end{code}
+ \begin{tabb}
+ Returns the index $j$ of the current coordinate. This may be useful,
+ e.g., for testing if enough coordinates are still available.
+ \end{tabb}
+\begin{htmlonly}
+ \return{index of the current coordinate}
+\end{htmlonly}
+\begin{code}
+
+ public boolean hasNextCoordinate();
+\end{code}
+ \begin{tabb}
+ Returns \texttt{true} if the current point has another coordinate.
+ This can be useful for testing if coordinates are still available.
+ \end{tabb}
+\begin{htmlonly}
+ \return{\texttt{true} if the current point has another coordinate}
+\end{htmlonly}
+\begin{code}
+
+ public double nextCoordinate();
+\end{code}
+ \begin{tabb}
+ Returns the current coordinate $u_{i,j}$ and advances to the next one.
+ If no current coordinate is available (either because the current
+ point index has reached the number of points or because the current
+ coordinate index has reached the number of dimensions), it throws a
+ \externalclass{java.util}{NoSuchElementException}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{value of the current coordinate}
+ \exception{NoSuchElementException}{if no such coordinate is available}
+\end{htmlonly}
+\begin{code}
+
+ public void nextCoordinates (double[] p, int d);
+\end{code}
+ \begin{tabb}
+ Returns the next \texttt{d} coordinates of the current point in \texttt{p}
+ and advances the current coordinate index by \texttt{d}.
+ If the remaining number of coordinates is too small, a
+ \texttt{NoSuchElementException} is thrown, as in \method{nextCoordinate}{}.
+% This method does not necessarily check if \texttt{p} is large enough.
+ \end{tabb}
+\begin{htmlonly}
+ \param{p}{array to be filled with the coordinates, starting at index 0}
+ \param{d}{number of coordinates to get}
+ \exception{NoSuchElementException}{if there are not enough
+ remaining coordinates in the current point}
+\end{htmlonly}
+\begin{code}
+
+ public void setCurPointIndex (int i);
+\end{code}
+ \begin{tabb}
+ Resets the current point index to $i$ and the current coordinate
+ index to zero. If \texttt{i} is larger or equal to the number of points,
+ an exception will \emph{not} be raised here, but only later if we
+ ask for a new coordinate or point.
+% The next call to either \method{nextCoordinates}{}
+% or \method{nextCoordinate}{} will return coordinates starting from $u_{i,0}$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{i}{new index of the current point}
+\end{htmlonly}
+\begin{code}
+
+ public void resetCurPointIndex();
+\end{code}
+ \begin{tabb}
+ Equivalent to \method{setCurPointIndex}{}~\texttt{(0)}.
+ \end{tabb}
+\begin{code}
+
+ public int resetToNextPoint();
+\end{code}
+ \begin{tabb}
+ Increases the current point index by 1 and returns its new value.
+ If there is no more point, an exception will be raised only if we
+ ask for a new coordinate or point later on.
+% Equivalent to
+% \method{setCurPointIndex}{}~\texttt{(}\method{getCurPointIndex()}{}~\texttt{+ 1)}
+ \end{tabb}
+\begin{htmlonly}
+ \return{index of the new current point}
+\end{htmlonly}
+\begin{code}
+
+ public int getCurPointIndex();
+\end{code}
+ \begin{tabb}
+ Returns the index $i$ of the current point.
+ This can be useful, e.g., for caching point sets.
+% and efficient implementations.
+% (see examples in the code).
+ \end{tabb}
+\begin{htmlonly}
+ \return{index of the current point}
+\end{htmlonly}
+\begin{code}
+
+ public boolean hasNextPoint();
+\end{code}
+ \begin{tabb}
+ Returns \texttt{true} if there is a next point.
+ This can be useful for testing if points are still available.
+ \end{tabb}
+\begin{htmlonly}
+ \return{\texttt{true} if a next point is available from the iterated point set}
+\end{htmlonly}
+\begin{code}
+
+ public int nextPoint (double[] p, int d);\begin{hide}
+}\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the \emph{first} \texttt{d} coordinates of the \emph{current}
+ point in \texttt{p}, advances to the next point, and
+ returns the index of the \emph{new} current point.
+ Even if the current coordinate index is 0, the point returned
+ starts from coordinate 0.
+ After obtaining the last point via this method, the current point
+ index (returned by the method) is equal to the number of points,
+ so it is no longer a valid point index.
+ An exception will then be raised if we attempt to generate additional
+ points or coordinates.
+
+ Specialized implementations of this method often allow for increased
+ efficiency, e.g., for cycle-based point sets where the cycles
+ (but not the points)
+ are stored explicitly or for digital nets
+ by allowing non-incremental point enumerations via Gray-code counters.
+ \end{tabb}
+\begin{htmlonly}
+ \param{p}{array to be filled with the coordinates,
+ starting from array index 0}
+ \param{d}{number of coordinates to return}
+ \return{index of the new current point}
+ \exception{NoSuchElementException}{if there are not enough coordinates
+ available in the current point for filling \texttt{p}}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/hups/PointSetRandomization.java b/source/umontreal/iro/lecuyer/hups/PointSetRandomization.java
new file mode 100644
index 0000000..e023033
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PointSetRandomization.java
@@ -0,0 +1,92 @@
+
+
+/*
+ * Interface: PointSetRandomization
+ * Description: Used to randomize a PointSet
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This interface is used to randomize a
+ * {@link umontreal.iro.lecuyer.hups.PointSet PointSet}. One can
+ * implement method {@link #randomize(PointSet) randomize} in any way. This
+ * method must use an internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}. This
+ * stream can be set in the constructor, but the methods
+ * {@link #getStream getStream} and {@link #setStream(RandomStream) setStream} must be
+ * implemented.
+ *
+ * <P>
+ * The method {@link #randomize(PointSet) randomize} must be implemented using
+ * combinations of the randomization methods from the point set such
+ * as
+ * {@link umontreal.iro.lecuyer.hups.PointSet#addRandomShift addRandomShift},
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet#leftMatrixScramble leftMatrixScramble},
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet#stripedMatrixScramble stripedMatrixScramble},
+ * ...
+ *
+ * <P>
+ * If more than one {@link PointSetRandomization} is applied to the
+ * same point set, the randomizations will concatenate if they are of
+ * different types, but only the last of each type will remain.
+ *
+ */
+public interface PointSetRandomization {
+
+
+
+ /**
+ * This method must randomize <TT>p</TT>.
+ *
+ * @param p Point set to randomize
+ *
+ *
+ */
+ public void randomize (PointSet p);
+
+
+ /**
+ * Sets the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} to
+ * <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ *
+ */
+ public void setStream (RandomStream stream);
+
+
+ /**
+ * Returns the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}.
+ *
+ * @return stream used in the randomization
+ *
+ */
+ public RandomStream getStream();
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/PointSetRandomization.tex b/source/umontreal/iro/lecuyer/hups/PointSetRandomization.tex
new file mode 100644
index 0000000..1c0a5b8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/PointSetRandomization.tex
@@ -0,0 +1,99 @@
+\defmodule{PointSetRandomization}
+
+This interface is used to randomize a
+\externalclass{umontreal.iro.lecuyer.hups}{PointSet}. One can
+implement method \method{randomize}{PointSet} in any way. This
+method must use an internal
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}. This
+stream can be set in the constructor, but the methods
+\method{getStream}{} and \method{setStream}{RandomStream} must be
+implemented.
+
+The method \method{randomize}{PointSet} must be implemented using
+combinations of the randomization methods from the point set such
+as
+\externalmethod{umontreal.iro.lecuyer.hups}{PointSet}{addRandomShift}{},
+\externalmethod{umontreal.iro.lecuyer.hups}{DigitalNet}{leftMatrixScramble}{},
+\externalmethod{umontreal.iro.lecuyer.hups}{DigitalNet}{striped\-Matrix\-Scramble}{},
+\hspace{1pt}\ldots
+
+If more than one \class{PointSetRandomization} is applied to the
+same point set, the randomizations will concatenate if they are of
+different types, but only the last of each type will remain.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Interface: PointSetRandomization
+ * Description: Used to randomize a PointSet
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+\begin{hide}
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+\end{hide}
+public interface PointSetRandomization \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void randomize (PointSet p);
+\end{code}
+\begin{tabb}
+ This method must randomize \texttt{p}.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{Point set to randomize}
+\end{htmlonly}
+\begin{code}
+
+ public void setStream (RandomStream stream);
+\end{code}
+\begin{tabb}
+ Sets the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} to
+ \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStream getStream();
+\end{code}
+\begin{tabb}
+ Returns the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+\end{tabb}
+\begin{htmlonly}
+ \return{stream used in the randomization}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/RQMCPointSet.java b/source/umontreal/iro/lecuyer/hups/RQMCPointSet.java
new file mode 100644
index 0000000..4b17316
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RQMCPointSet.java
@@ -0,0 +1,116 @@
+
+
+/*
+ * Class: RQMCPointSet
+ * Description: randomized quasi-Monte Carlo simulations
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+/**
+ * This class is used for <SPAN CLASS="textit">randomized quasi-Monte Carlo</SPAN> (RQMC) simulations.
+ * The idea is to randomize a point set so that:
+ *
+ * <UL>
+ * <LI>it retains its high uniformity when taken as a set and
+ * </LI>
+ * <LI>each individual point is a random vector with the uniform
+ * distribution over <SPAN CLASS="MATH">(0, 1)<SUP>s</SUP></SPAN>.
+ * </LI>
+ * </UL>
+ * A RQMC point set is one that satisfies these two conditions. One simple
+ * randomization that satisfies these conditions for an arbirary point set <SPAN CLASS="MATH"><I>P</I><SUB>n</SUB></SPAN>
+ * is a random shift modulo 1:
+ * Generate a single point
+ * <SPAN CLASS="MATH"><B>U</B></SPAN> uniformly over <SPAN CLASS="MATH">(0, 1)<SUP>s</SUP></SPAN> and add it
+ * to each point of <SPAN CLASS="MATH"><I>P</I><SUB>n</SUB></SPAN>, modulo 1, coordinate-wise.
+ * Another one is a random digital shift in base <SPAN CLASS="MATH"><I>b</I></SPAN>: generate again
+ * <SPAN CLASS="MATH"><B>U</B></SPAN> uniformly over
+ * <SPAN CLASS="MATH">(0, 1)<SUP>s</SUP></SPAN>, expand each of its coordinates in base <SPAN CLASS="MATH"><I>b</I></SPAN>, and add the
+ * digits, modulo <SPAN CLASS="MATH"><I>b</I></SPAN>, to the corresponding digits of each point of <SPAN CLASS="MATH"><I>P</I><SUB>n</SUB></SPAN>.
+ *
+ */
+public class RQMCPointSet {
+ private PointSet set;
+ private PointSetRandomization rand;
+
+
+
+ /**
+ * Constructor with the point set <TT>set</TT> and the randomization <TT>rand</TT>.
+ *
+ * @param set the point set
+ *
+ * @param rand the randomization
+ *
+ */
+ public RQMCPointSet (PointSet set, PointSetRandomization rand) {
+ this.rand = rand;
+ this.set = set;
+ }
+
+
+
+ /**
+ * Randomizes the point set. The randomization and the point set
+ * are those of this object.
+ *
+ */
+ public void randomize() {
+ rand.randomize(set);
+ }
+
+
+
+ /**
+ * Returns a new point set iterator for the point set associated to this object.
+ *
+ * @return point set iterator for the point set
+ *
+ */
+ public PointSetIterator iterator() {
+ return set.iterator();
+ }
+
+
+ /**
+ * Returns the point set associated to this object.
+ *
+ * @return the point set associated to this object
+ *
+ */
+ public PointSet getPointSet() {
+ return set;
+ }
+
+
+ /**
+ * Returns the randomization associated to this object.
+ *
+ * @return the randomization associated to this object
+ *
+ */
+ public PointSetRandomization getRandomization() {
+ return rand;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/RQMCPointSet.tex b/source/umontreal/iro/lecuyer/hups/RQMCPointSet.tex
new file mode 100644
index 0000000..b556c39
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RQMCPointSet.tex
@@ -0,0 +1,126 @@
+\defmodule{RQMCPointSet}
+
+This class is used for \emph{randomized quasi-Monte Carlo} (RQMC) simulations
+\cite{vLEC00b,vLEC02a,vOWE97a,vOWE97b}.
+ The idea is to randomize a point set so that:
+\begin{itemize}
+\item it retains its high uniformity when taken as a set and
+\item each individual point is a random vector with the uniform
+distribution over $(0, 1)^s$.
+\end{itemize}
+ A RQMC point set is one that satisfies these two conditions. One simple
+randomization that satisfies these conditions for an arbirary point set $P_n$
+ is a random shift modulo 1 \cite{vCRA76a,vLEC00b,vSLO94a}:
+Generate a single point $\mathbf{U}$ uniformly over $(0, 1)^s$ and add it
+ to each point of $P_n$, modulo 1, coordinate-wise.
+Another one is a random digital shift in base $b$
+ \cite{vLEC99a,vLEC02a,mMAT99a}: generate again $\mathbf{U}$ uniformly over
+ $(0, 1)^s$, expand each of its coordinates in base $b$, and add the
+digits, modulo $b$, to the corresponding digits of each point of $P_n$.
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RQMCPointSet
+ * Description: randomized quasi-Monte Carlo simulations
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+
+public class RQMCPointSet \begin{hide} {
+ private PointSet set;
+ private PointSetRandomization rand;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+\begin{code}
+
+ public RQMCPointSet (PointSet set, PointSetRandomization rand) \begin{hide} {
+ this.rand = rand;
+ this.set = set;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Constructor with the point set \texttt{set} and the randomization \texttt{rand}.
+\end{tabb}
+\begin{htmlonly}
+ \param{set}{the point set}
+ \param{rand}{the randomization}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void randomize() \begin{hide} {
+ rand.randomize(set);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Randomizes the point set. The randomization and the point set
+ are those of this object.
+\end{tabb}
+\begin{code}
+
+ public PointSetIterator iterator()\begin{hide} {
+ return set.iterator();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a new point set iterator for the point set associated to this object.
+\end{tabb}
+\begin{htmlonly}
+ \return{point set iterator for the point set}
+\end{htmlonly}
+\begin{code}
+
+ public PointSet getPointSet()\begin{hide} {
+ return set;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the point set associated to this object.
+\end{tabb}
+\begin{htmlonly}
+ \return{the point set associated to this object}
+\end{htmlonly}
+\begin{code}
+
+ public PointSetRandomization getRandomization()\begin{hide} {
+ return rand;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the randomization associated to this object.
+\end{tabb}
+\begin{htmlonly}
+ \return{the randomization associated to this object}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/RadicalInverse.java b/source/umontreal/iro/lecuyer/hups/RadicalInverse.java
new file mode 100644
index 0000000..433a2c5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RadicalInverse.java
@@ -0,0 +1,691 @@
+
+
+/*
+ * Class: RadicalInverse
+ * Description: Implements radical inverses of integers in an arbitrary basis
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+
+/**
+ * This class implements basic methods for working with radical
+ * inverses of integers in an arbitrary basis <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * These methods are used in classes that implement
+ * point sets and sequences based on the van der Corput sequence
+ * (the Hammersley nets and the Halton sequence, for example).
+ *
+ * <P>
+ * We recall that for a <SPAN CLASS="MATH"><I>k</I></SPAN>-digit integer <SPAN CLASS="MATH"><I>i</I></SPAN> whose digital
+ * <SPAN CLASS="MATH"><I>b</I></SPAN>-ary expansion is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>i</I> = <I>a</I><SUB>0</SUB> + <I>a</I><SUB>1</SUB><I>b</I> + ... + <I>a</I><SUB>k-1</SUB><I>b</I><SUP>k-1</SUP>,
+ * </DIV><P></P>
+ * the <SPAN CLASS="textit">radical inverse</SPAN> in base <SPAN CLASS="MATH"><I>b</I></SPAN> is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>ψ</I><SUB>b</SUB>(<I>i</I>) = <I>a</I><SUB>0</SUB><I>b</I><SUP>-1</SUP> + <I>a</I><SUB>1</SUB><I>b</I><SUP>-2</SUP> + <SUP> ... </SUP> + <I>a</I><SUB>k-1</SUB><I>b</I><SUP>-k</SUP>.
+ * </DIV><P></P>
+ * The <SPAN CLASS="textit">van der Corput sequence in base <SPAN CLASS="MATH"><I>b</I></SPAN></SPAN> is the sequence
+ *
+ * <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(0), <I>ψ</I><SUB>b</SUB>(1), <I>ψ</I><SUB>b</SUB>(2),...</SPAN>
+ *
+ * <P>
+ * Note that <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN> cannot always be represented exactly
+ * as a floating-point number on the computer (e.g., if <SPAN CLASS="MATH"><I>b</I></SPAN> is not
+ * a power of two). For an exact representation, one can use the integer
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>b</I><SUP>k</SUP><I>ψ</I><SUB>b</SUB>(<I>i</I>) = <I>a</I><SUB>k-1</SUB> + <SUP> ... </SUP> + <I>a</I><SUB>1</SUB><I>b</I><SUP>k-2</SUP> + <I>a</I><SUB>0</SUB><I>b</I><SUP>k-1</SUP>,
+ * </DIV><P></P>
+ * which we called the <SPAN CLASS="textit">integer radical inverse</SPAN> representation.
+ * This representation is simply a mirror image of the digits of the
+ * usual <SPAN CLASS="MATH"><I>b</I></SPAN>-ary representation of <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ *
+ * <P>
+ * It is common practice to permute locally the values of the
+ * van der Corput sequence. One way of doing this is to apply a
+ * permutation to the digits of <SPAN CLASS="MATH"><I>i</I></SPAN> before computing <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN>.
+ * That is, for a permutation <SPAN CLASS="MATH"><I>π</I></SPAN> of the digits
+ * <SPAN CLASS="MATH">{0,..., <I>b</I> - 1}</SPAN>,
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>ψ</I><SUB>b</SUB>(<I>i</I>) = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>-r-1</SUP>
+ * </DIV><P></P>
+ * is replaced by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>π</I>(<I>a</I><SUB>r</SUB>)<I>b</I><SUP>-r-1</SUP>.
+ * </DIV><P></P>
+ * Applying such a permutation only changes the order in which the
+ * values of <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN> are enumerated. For every integer <SPAN CLASS="MATH"><I>k</I></SPAN>, the first
+ * <SPAN CLASS="MATH"><I>b</I><SUP>k</SUP></SPAN> values that are enumerated remain the same (they are the values
+ * of <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,..., <I>b</I><SUP>k</SUP> - 1</SPAN>), but they are enumerated in a
+ * different order. Often, different permutations <SPAN CLASS="MATH"><I>π</I></SPAN> will be applied
+ * for different coordinates of a point set.
+ *
+ * <P>
+ * The permutation <SPAN CLASS="MATH"><I>π</I></SPAN> can be deterministic or random. One (deterministic)
+ * possibility implemented here is the Faure permutation <SPAN CLASS="MATH"><I>σ</I><SUB>b</SUB></SPAN> of
+ *
+ * <SPAN CLASS="MATH">{0,..., <I>b</I> - 1}</SPAN> defined as follows.
+ * For <SPAN CLASS="MATH"><I>b</I> = 2</SPAN>, take
+ * <SPAN CLASS="MATH"><I>σ</I> = <I>I</I></SPAN>, the identical permutation. For <SPAN CLASS="textit">even</SPAN>
+ * <SPAN CLASS="MATH"><I>b</I> = 2<I>c</I> > 2</SPAN>, take
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>σ</I>[<I>i</I>]</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>2<I>τ</I>[<I>i</I>] <I>i</I> = 0, 1,…, <I>c</I> - 1</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>σ</I>[<I>i</I> + <I>c</I>]</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>2<I>τ</I>[<I>i</I>] + 1 <I>i</I> = 0, 1,…, <I>c</I> - 1</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * where <SPAN CLASS="MATH"><I>τ</I>[<I>i</I>]</SPAN> is the Faure
+ * permutation for base <SPAN CLASS="MATH"><I>c</I></SPAN>. For <SPAN CLASS="textit">odd</SPAN> <SPAN CLASS="MATH"><I>b</I> = 2<I>c</I> + 1</SPAN>,
+ * take
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>σ</I>[<I>c</I>]</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>c</I></TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>σ</I>[<I>i</I>]</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>τ</I>[<I>i</I>], if 0 <= <I>τ</I>[<I>i</I>] < <I>c</I></TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>σ</I>[<I>i</I>]</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>τ</I>[<I>i</I>] + 1, if <I>c</I> <= <I>τ</I>[<I>i</I>] < 2<I>c</I></TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * for
+ * <SPAN CLASS="MATH">0 <= <I>i</I> < <I>c</I></SPAN>, and take
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>σ</I>[<I>i</I>]</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>τ</I>[<I>i</I> - 1], if 0 <= <I>τ</I>[<I>i</I> - 1] < <I>c</I></TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>σ</I>[<I>i</I>]</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>τ</I>[<I>i</I> - 1] + 1, if <I>c</I> <= <I>τ</I>[<I>i</I> - 1] < 2<I>c</I></TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * for
+ * <SPAN CLASS="MATH"><I>c</I> < <I>i</I> <= 2<I>c</I></SPAN>, and where <SPAN CLASS="MATH"><I>τ</I>[<I>i</I>]</SPAN> is the Faure
+ * permutation for base <SPAN CLASS="MATH"><I>c</I></SPAN>. The Faure permutations give very small
+ * discrepancies (amongst the best known today) for small bases.
+ *
+ */
+public class RadicalInverse {
+ private static final int NP = 168; // First NP primes in table below.
+ private static final int PLIM = 1000; // NP primes < PLIM
+
+ // The first NP prime numbers
+ private static final int[] PRIMES = {
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
+ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139,
+ 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223,
+ 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293,
+ 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
+ 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
+ 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569,
+ 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647,
+ 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743,
+ 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
+ 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
+ 947, 953, 967, 971, 977, 983, 991, 997 };
+
+ // Multiplicative factors for permutations as proposed by Faure & Lemieux (2008).
+ // The index corresponds to the coordinate.
+ private static final int[] FAURE_LEMIEUX_FACTORS = {
+ 1, 1, 3, 3, 4, 9, 7, 5, 9, 18, 18, 8, 13, 31, 9, 19, 36, 33, 21, 44, 43,
+ 61, 60, 56, 26, 71, 32, 77, 26, 95, 92, 47, 29, 61, 57, 69, 115, 63, 92,
+ 31, 104, 126, 50, 80, 55, 152, 114, 80, 83, 97, 95, 150, 148, 55, 80, 192,
+ 71, 76, 82, 109, 105, 173, 58, 143, 56, 177, 203, 239, 196, 143, 278, 227,
+ 87, 274, 264, 84, 226, 163, 231, 177, 95, 116, 165, 131, 156, 105, 188,
+ 142, 105, 125, 269, 292, 215, 182, 294, 152, 148, 144, 382, 194, 346, 323,
+ 220, 174, 133, 324, 215, 246, 159, 337, 254, 423, 484, 239, 440, 362, 464,
+ 376, 398, 174, 149, 418, 306, 282, 434, 196, 458, 313, 512, 450, 161, 315,
+ 441, 549, 555, 431, 295, 557, 172, 343, 472, 604, 297, 524, 251, 514, 385,
+ 531, 663, 674, 255, 519, 324, 391, 394, 533, 253, 717, 651, 399, 596, 676,
+ 425, 261, 404, 691, 604, 274, 627, 777, 269, 217, 599, 447, 581, 640, 666,
+ 595, 669, 686, 305, 460, 599, 335, 258, 649, 771, 619, 666, 669, 707, 737,
+ 854, 925, 818, 424, 493, 463, 535, 782, 476, 451, 520, 886, 340, 793, 390,
+ 381, 274, 500, 581, 345, 363, 1024, 514, 773, 932, 556, 954, 793, 294,
+ 863, 393, 827, 527, 1007, 622, 549, 613, 799, 408, 856, 601, 1072, 938,
+ 322, 1142, 873, 629, 1071, 1063, 1205, 596, 973, 984, 875, 918, 1133,
+ 1223, 933, 1110, 1228, 1017, 701, 480, 678, 1172, 689, 1138, 1022, 682,
+ 613, 635, 984, 526, 1311, 459, 1348, 477, 716, 1075, 682, 1245, 401, 774,
+ 1026, 499, 1314, 743, 693, 1282, 1003, 1181, 1079, 765, 815, 1350, 1144,
+ 1449, 718, 805, 1203, 1173, 737, 562, 579, 701, 1104, 1105, 1379, 827,
+ 1256, 759, 540, 1284, 1188, 776, 853, 1140, 445, 1265, 802, 932, 632,
+ 1504, 856, 1229, 1619, 774, 1229, 1300, 1563, 1551, 1265, 905, 1333, 493,
+ 913, 1397, 1250, 612, 1251, 1765, 1303, 595, 981, 671, 1403, 820, 1404,
+ 1661, 973, 1340, 1015, 1649, 855, 1834, 1621, 1704, 893, 1033, 721, 1737,
+ 1507, 1851, 1006, 994, 923, 872, 1860
+ };
+
+ private static final int NRILIM = 1000; // For nextRadicalInverse
+ private int b; // Base
+ private double invb; // 1/b
+ private double logb; // natural log(b)
+ private int JMAX; // b^JMAX = 2^32
+ private int co; // Counter for nextRadicalInverse
+ private double xx; // Current value of x
+ private long ix; // xx = RadicalInverse (ix)
+/*
+ // For Struckmeier's algorithm
+ private static final int PARTITION_MAX = 54; // Size of partitionL, bi
+ private int partM; // Effective size of partitionL, bi
+ private double[] bi; // bi[i] = (b + 1)/b^i - 1
+ private double[] partitionL; // L[i] = 1 - 1/b^i
+ // Boundaries of Struckmeier partitions Lkp of [0, 1]
+*/
+
+
+
+
+ /**
+ * Initializes the base of this object to <SPAN CLASS="MATH"><I>b</I></SPAN>
+ * and its first value of <SPAN CLASS="MATH"><I>x</I></SPAN> to <TT>x0</TT>.
+ *
+ * @param b Base
+ *
+ * @param x0 Initial value of x
+ *
+ */
+ public RadicalInverse (int b, double x0) {
+ co = 0;
+ this.b = b;
+ invb = 1.0 / b;
+ logb = Math.log (b);
+ JMAX = (int) (32.0 * 0.69314718055994530941 / logb);
+ xx = x0;
+ ix = computeI (x0);
+// initStruckmeier (b);
+ }
+
+ private long computeI (double x) {
+ // Compute i such that x = RadicalInverse (i).
+ int[] digits = new int[JMAX]; // Digits of x
+ int j;
+ for (j = 0; (j < JMAX) && (x > 0.5e-15); j++) {
+ x *= b;
+ digits[j] = (int) x;
+ x -= digits[j];
+ }
+ long i = 0;
+ for (j = JMAX - 1; j >= 0; j--) {
+ i = i * b + digits[j];
+ }
+ return i;
+ }
+
+ /**
+ * Provides an elementary method for obtaining the first <SPAN CLASS="MATH"><I>n</I></SPAN> prime
+ * numbers larger than 1.
+ * Creates and returns an array that contains
+ * these numbers. This is useful for determining the prime bases for
+ * the different coordinates of the Halton sequence and Hammersley nets.
+ *
+ * @param n number of prime numbers to return
+ *
+ * @return an array with the first <TT>n</TT> prime numbers
+ *
+ */
+ public static int[] getPrimes (int n) {
+ // Allocates an array of size n filled with the first n prime
+ // numbers. n must be positive (n > 0). Routine may fail if not enough
+ // memory for the array is available. The first prime number is 2.
+ int i;
+ boolean moreTests;
+ int[] prime = new int[n];
+
+ int n1 = Math.min (NP, n);
+ for (i = 0; i < n1; i++)
+ prime[i] = PRIMES[i];
+ if (NP < n) {
+ i = NP;
+ for (int candidate = PLIM + 1; i < n; candidate += 2) {
+ prime[i] = candidate;
+ for (int j = 1; (moreTests = prime[j] <= candidate / prime[j])
+ && ((candidate % prime[j]) > 0); j++);
+ if (! moreTests)
+ i++;
+ }
+ }
+ return prime;
+ }
+
+
+ /**
+ * Computes the radical inverse of <SPAN CLASS="MATH"><I>i</I></SPAN> in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * If
+ * <SPAN CLASS="MATH"><I>i</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>r</SUP></SPAN>, the method computes and returns
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>x</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>-r-1</SUP>.
+ * </DIV><P></P>
+ *
+ * @param b base used for the operation
+ *
+ * @param i the value for which the radical inverse will be computed
+ *
+ * @return the radical inverse of <TT>i</TT> in base <TT>b</TT>
+ *
+ */
+ public static double radicalInverse (int b, long i) {
+ double digit, radical, inverse;
+ digit = radical = 1.0 / (double) b;
+ for (inverse = 0.0; i > 0; i /= b) {
+ inverse += digit * (double) (i % b);
+ digit *= radical;
+ }
+ return inverse;
+ }
+
+
+ /**
+ * Computes the radical inverse of <SPAN CLASS="MATH"><I>x</I></SPAN> in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * If <SPAN CLASS="MATH"><I>x</I></SPAN> has more decimals in base <SPAN CLASS="MATH"><I>b</I></SPAN> than <SPAN CLASS="MATH">log<SUB>b</SUB></SPAN>(<TT>Long.MAX_VALUE</TT>),
+ * it is truncated to its minimum precision in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * If
+ * <SPAN CLASS="MATH"><I>x</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>-r-1</SUP></SPAN>, the method computes and returns
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>i</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>r</SUP>.
+ * </DIV><P></P>
+ *
+ * @param b base used for the operation
+ *
+ * @param x the value for which the radical inverse will be computed
+ *
+ * @return the radical inverse of <TT>x</TT> in base <TT>b</TT>
+ *
+ */
+ public static int radicalInverseInteger (int b, double x) {
+ int digit = 1;
+ int inverse = 0;
+ int precision = Integer.MAX_VALUE / (2 * b * b);
+ while (x > 0 && inverse < precision) {
+ int p = digit * b;
+ double y = Math.floor(x * p);
+ inverse += digit * (int)y;
+ x -= y / (double)p;
+ digit *= b;
+ }
+ return inverse;
+ }
+
+
+ /**
+ * Computes the radical inverse of <SPAN CLASS="MATH"><I>x</I></SPAN> in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * If <SPAN CLASS="MATH"><I>x</I></SPAN> has more decimals in base <SPAN CLASS="MATH"><I>b</I></SPAN> than <SPAN CLASS="MATH">log<SUB>b</SUB></SPAN>(<TT>Long.MAX_VALUE</TT>),
+ * it is truncated to its minimum precision in base <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * If
+ * <SPAN CLASS="MATH"><I>x</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>-r-1</SUP></SPAN>, the method computes and returns
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>i</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>r</SUP>.
+ * </DIV><P></P>
+ *
+ * @param b base used for the operation
+ *
+ * @param x the value for which the radical inverse will be computed
+ *
+ * @return the radical inverse of <TT>x</TT> in base <TT>b</TT>
+ *
+ */
+ public static long radicalInverseLong (int b, double x) {
+ long digit = 1;
+ long inverse = 0;
+ long precision = Long.MAX_VALUE / (b * b * b);
+ while (x > 0 && inverse < precision) {
+ long p = digit * b;
+ double y = Math.floor(x * p);
+ inverse += digit * (long)y;
+ x -= y / (double)p;
+ digit *= b;
+ }
+ return inverse;
+ }
+
+
+ /**
+ * A fast method that incrementally computes the radical inverse <SPAN CLASS="MATH"><I>x</I><SUB>i+1</SUB></SPAN>
+ * in base <SPAN CLASS="MATH"><I>b</I></SPAN> from <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> = <TT>x</TT> = <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN>, using addition with <EM>rigthward carry</EM>.
+ * The parameter <TT>invb</TT> is equal to <SPAN CLASS="MATH">1/<I>b</I></SPAN>.
+ * Using long incremental streams (i.e., calling this method several times
+ * in a row) cause increasing inaccuracy in <SPAN CLASS="MATH"><I>x</I></SPAN>. Thus the user should
+ * recompute the radical inverse directly by calling
+ * {@link #radicalInverse radicalInverse} every once in a while (i.e. in every few
+ * thousand calls).
+ *
+ * @param invb <SPAN CLASS="MATH">1/<I>b</I></SPAN> where <SPAN CLASS="MATH"><I>b</I></SPAN> is the base
+ *
+ * @param x the inverse <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN>
+ *
+ * @return the radical inverse <SPAN CLASS="MATH"><I>x</I><SUB>i+1</SUB></SPAN>
+ *
+ */
+ public static double nextRadicalInverse (double invb, double x) {
+ // Calculates the next radical inverse from x in base b.
+ // Repeated application causes a loss of accuracy.
+ // Note that x can be any number from [0,1).
+
+ final double ALMOST_ONE = 1.0 - 1e-10;
+ double nextInverse = x + invb;
+ if (nextInverse < ALMOST_ONE)
+ return nextInverse;
+ else {
+ double digit1 = invb;
+ double digit2 = invb * invb;
+ while (x + digit2 >= ALMOST_ONE) {
+ digit1 = digit2;
+ digit2 *= invb;
+ }
+ return x + (digit1 - 1.0) + digit2;
+ }
+ }
+
+
+
+ /**
+ * A fast method that incrementally computes the radical inverse <SPAN CLASS="MATH"><I>x</I><SUB>i+1</SUB></SPAN>
+ * in base <SPAN CLASS="MATH"><I>b</I></SPAN> from <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN> = <SPAN CLASS="MATH"><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN>,
+ * using addition with <EM>rigthward carry</EM> as described in
+ * Wang and Hickernell.
+ * Since using long incremental streams (i.e., calling this method several
+ * times in a row) cause increasing inaccuracy in <SPAN CLASS="MATH"><I>x</I></SPAN>, the method
+ * recomputes the radical inverse directly from <SPAN CLASS="MATH"><I>i</I></SPAN> by calling
+ * {@link #radicalInverse radicalInverse} once in every 1000 calls.
+ *
+ * @return the radical inverse <SPAN CLASS="MATH"><I>x</I><SUB>i+1</SUB></SPAN>
+ *
+ *
+ */
+ public double nextRadicalInverse () {
+ // Calculates the next radical inverse from xx in base b.
+ // Repeated application causes a loss of accuracy.
+ // For each NRILIM calls, a direct calculation via radicalInverse
+ // is inserted.
+
+ co++;
+ if (co >= NRILIM) {
+ co = 0;
+ ix += NRILIM;
+ xx = radicalInverse (b, ix);
+ return xx;
+ }
+ final double ALMOST_ONE = 1.0 - 1e-10;
+ double nextInverse = xx + invb;
+ if (nextInverse < ALMOST_ONE) {
+ xx = nextInverse;
+ return xx;
+ } else {
+ double digit1 = invb;
+ double digit2 = invb * invb;
+ while (xx + digit2 >= ALMOST_ONE) {
+ digit1 = digit2;
+ digit2 *= invb;
+ }
+ xx += (digit1 - 1.0) + digit2;
+ return xx;
+ }
+ }
+
+
+/*
+ private void initStruckmeier (int b) {
+ bi = new double[1 + PARTITION_MAX];
+ partitionL = new double[1 + PARTITION_MAX];
+ logb = Math.log (b);
+ partitionL[0] = 0.0;
+ bi[0] = 1.0;
+ int i = 0;
+ while ((i < PARTITION_MAX) && (partitionL[i] < 1.0)) {
+ ++i;
+ bi[i] = bi[i - 1] / b;
+ partitionL[i] = 1.0 - bi[i];
+ }
+ partM = i - 1;
+
+ for (i = 0; i <= partM + 1; ++i)
+ bi[i] = (b + 1) * bi[i] - 1.0;
+ }
+
+ public double nextRadicalInverse (double x) {
+ int k;
+ if (x < partitionL[partM]) {
+ k = 1;
+ // Find k such: partitionL[k-1] <= x < partitionL[k]
+ while (x >= partitionL[k])
+ ++k;
+
+ } else { // x >= partitionL [partM]
+ k = 1 + (int)(-Math.log(1.0 - x) / logb);
+ }
+ return x + bi[k];
+ } */
+
+
+ /**
+ * Given the <SPAN CLASS="MATH"><I>k</I></SPAN> <SPAN CLASS="MATH"><I>b</I></SPAN>-ary digits of <SPAN CLASS="MATH"><I>i</I></SPAN> in <TT>bdigits</TT>, returns the
+ * <SPAN CLASS="MATH"><I>k</I></SPAN> digits of the integer radical inverse of <SPAN CLASS="MATH"><I>i</I></SPAN> in <TT>idigits</TT>.
+ * This simply reverses the order of the digits.
+ *
+ * @param k number of digits in arrays
+ *
+ * @param bdigits digits in original order
+ *
+ * @param idigits digits in reverse order
+ *
+ *
+ */
+ public static void reverseDigits (int k, int bdigits[], int idigits[]) {
+ for (int l = 0; l < k; l++)
+ idigits[l] = bdigits[k-l];
+ }
+
+
+ /**
+ * Computes the integer radical inverse of <SPAN CLASS="MATH"><I>i</I></SPAN> in base <SPAN CLASS="MATH"><I>b</I></SPAN>,
+ * equal to
+ * <SPAN CLASS="MATH"><I>b</I><SUP>k</SUP><I>ψ</I><SUB>b</SUB>(<I>i</I>)</SPAN> if <SPAN CLASS="MATH"><I>i</I></SPAN> has <SPAN CLASS="MATH"><I>k</I></SPAN> <SPAN CLASS="MATH"><I>b</I></SPAN>-ary digits.
+ *
+ * @param b base used for the operation
+ *
+ * @param i the value for which the integer radical inverse will be computed
+ *
+ * @return the integer radical inverse of <TT>i</TT> in base <TT>b</TT>
+ *
+ */
+ public static int integerRadicalInverse (int b, int i) {
+ // Simply flips digits of i in base b.
+ int inverse;
+ for (inverse = 0; i > 0; i /= b)
+ inverse = inverse * b + (i % b);
+ return inverse;
+ }
+
+
+ /**
+ * Given the <SPAN CLASS="MATH"><I>k</I></SPAN> digits of the integer radical inverse of <SPAN CLASS="MATH"><I>i</I></SPAN> in <TT>bdigits</TT>,
+ * in base <SPAN CLASS="MATH"><I>b</I></SPAN>, this method replaces them by the digits of the integer
+ * radical inverse of <SPAN CLASS="MATH"><I>i</I> + 1</SPAN> and returns their number.
+ * The array must be large enough to hold this new number of digits.
+ *
+ * @param b base
+ *
+ * @param k initial number of digits in arrays
+ *
+ * @param idigits digits of integer radical inverse
+ *
+ * @return new number of digits in arrays
+ *
+ */
+ public static int nextRadicalInverseDigits (int b, int k, int idigits[]) {
+ int l;
+ for (l = k-1; l >= 0; l--)
+ if (idigits[l] == b-1)
+ idigits[l] = 0;
+ else {
+ idigits[l]++;
+ return k;
+ }
+ if (l == 0) {
+ idigits[k] = 1;
+ return ++k;
+ }
+ return 0;
+ }
+
+
+ /**
+ * Computes the permutations as proposed in <SPAN CLASS="MATH"><I>σ</I><SUB>b</SUB></SPAN> of the set
+ *
+ * <SPAN CLASS="MATH">{0,…, <I>b</I> - 1}</SPAN> and puts it in array <TT>pi</TT>.
+ *
+ * @param coordinate the coordinate
+ *
+ * @param pi an array of size at least <TT>b</TT>,
+ * to be filled with the permutation
+ *
+ *
+ */
+ public static void getFaureLemieuxPermutation (int coordinate, int[] pi) {
+ int f = FAURE_LEMIEUX_FACTORS[coordinate];
+ int b = PRIMES[coordinate];
+ for (int k = 0; k < pi.length; k++)
+ pi[k] = f * k % b;
+ }
+
+
+ /**
+ * Computes the Faure permutation <SPAN CLASS="MATH"><I>σ</I><SUB>b</SUB></SPAN> of the set
+ *
+ * <SPAN CLASS="MATH">{0,…, <I>b</I> - 1}</SPAN> and puts it in array <TT>pi</TT>.
+ * See the description in the introduction above.
+ *
+ * @param b the base
+ *
+ * @param pi an array of size at least <TT>b</TT>,
+ * to be filled with the permutation
+ *
+ *
+ */
+ public static void getFaurePermutation (int b, int[] pi) {
+ // This is a recursive implementation.
+ // Perhaps not the most efficient...
+ int i;
+ if (b == 2) {
+ pi[0] = 0;
+ pi[1] = 1;
+ }
+ else if ((b & 1) != 0) {
+ // b is odd.
+ b--;
+ getFaurePermutation (b, pi);
+ for (i = 0; i < b; i++)
+ if (pi[i] >= b / 2)
+ pi[i]++;
+ for (i = b; i > b / 2; i--)
+ pi[i] = pi[i - 1];
+ pi[b / 2] = b / 2;
+ }
+ else {
+ b /= 2;
+ getFaurePermutation (b, pi);
+ for (i = 0; i < b; i++) {
+ pi[i] *= 2;
+ pi[i + b] = pi[i] + 1;
+ }
+ }
+ }
+
+
+ /**
+ * Computes the radical inverse of <SPAN CLASS="MATH"><I>i</I></SPAN> in base <SPAN CLASS="MATH"><I>b</I></SPAN>, where the digits
+ * are permuted using the permutation <SPAN CLASS="MATH"><I>π</I></SPAN>.
+ * If
+ * <SPAN CLASS="MATH"><I>i</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>a</I><SUB>r</SUB><I>b</I><SUP>r</SUP></SPAN>, the method will compute and return
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>x</I> = ∑<SUB>r=0</SUB><SUP>k-1</SUP><I>π</I>[<I>a</I><SUB>r</SUB>]<I>b</I><SUP>-r-1</SUP>.
+ * </DIV><P></P>
+ *
+ * @param b base <SPAN CLASS="MATH"><I>b</I></SPAN> used for the operation
+ *
+ * @param pi an array of length at least <TT>b</TT> containing the permutation
+ * used during the computation
+ *
+ * @param i the value for which the radical inverse will be computed
+ *
+ * @return the radical inverse of <TT>i</TT> in base <TT>b</TT>
+ *
+ */
+ public static double permutedRadicalInverse (int b, int[] pi, long i) {
+ double digit, radical, inverse;
+ digit = radical = 1.0 / (double) b;
+ for (inverse = 0.0; i > 0; i /= b) {
+ inverse += digit * (double) pi[(int)(i % b)];
+ digit *= radical;
+ }
+ return inverse;
+ }
+
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/hups/RadicalInverse.tex b/source/umontreal/iro/lecuyer/hups/RadicalInverse.tex
new file mode 100644
index 0000000..2c94f71
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RadicalInverse.tex
@@ -0,0 +1,643 @@
+\defmodule{RadicalInverse}
+
+This class implements basic methods for working with radical
+inverses of integers in an arbitrary basis $b$.
+%, for applying permutations to the digits, etc.
+These methods are used in classes that implement
+point sets and sequences based on the van der Corput sequence
+(the Hammersley nets and the Halton sequence, for example).
+
+We recall that for a $k$-digit integer $i$ whose digital
+$b$-ary expansion is
+\[
+ i = a_0 + a_1 b + \dots + a_{k-1} b^{k-1},
+\]
+the \emph{radical inverse} in base $b$ is
+\eq
+ \psi_b(i) = a_0 b^{-1} + a_1 b^{-2} + \cdots + a_{k-1} b^{-k}.
+\endeq
+The \emph{van der Corput sequence in base $b$} is the sequence
+$\psi_b(0), \psi_b(1), \psi_b(2), \dots$
+
+Note that $\psi_b(i)$ cannot always be represented exactly
+as a floating-point number on the computer (e.g., if $b$ is not
+a power of two). For an exact representation, one can use the integer
+\[
+ b^k \psi_b(i) = a_{k-1} + \cdots + a_1 b^{k-2} + a_0 b^{k-1},
+\]
+which we called the \emph{integer radical inverse} representation.
+This representation is simply a mirror image of the digits of the
+usual $b$-ary representation of $i$.
+
+It is common practice to permute locally the values of the
+van der Corput sequence. One way of doing this is to apply a
+permutation to the digits of $i$ before computing $\psi_b(i)$.
+That is, for a permutation $\pi$ of the digits $\{0,\dots,b-1\}$,
+\[
+ \psi_{b}(i) = \sum_{r=0}^{k-1} a_r b^{-r-1}
+\]
+is replaced by
+\[
+ \sum_{r=0}^{k-1} \pi(a_r) b^{-r-1}.
+\]
+Applying such a permutation only changes the order in which the
+values of $\psi_b(i)$ are enumerated. For every integer $k$, the first
+$b^k$ values that are enumerated remain the same (they are the values
+of $\psi_b(i)$ for $i=0,\dots,b^k-1$), but they are enumerated in a
+different order. Often, different permutations $\pi$ will be applied
+for different coordinates of a point set.
+
+The permutation $\pi$ can be deterministic or random. One (deterministic)
+ possibility implemented here is the Faure permutation $\sigma_b$ of
+ $\{0,\dots,b-1\}$ defined as follows \cite{rFAU92a}.
+%\pierre{Which one? Definition and reference?}
+%\richard{Voici la d\'efinition et la r\'ef\'erence.}
+For $b=2$, take $\sigma = I$, the identical permutation. For \textit{even}
+ $b=2c > 2$, take
+\begin{eqnarray}
+ \sigma[i] &=& 2\tau[i]\phantom{{} + 1} \qquad i = 0, 1, \ldots, c-1 \\
+ \sigma[i+c] &=& 2\tau[i] + 1 \qquad i = 0, 1, \ldots, c-1
+\end{eqnarray}
+ where $\tau[i]$ is the Faure
+permutation for base $c$. For \textit{odd} $b=2c+1$,
+ take
+\begin{eqnarray}
+ \sigma[c] &=& c \\
+ \sigma[i] &=& \tau[i],\phantom{{} + 1} \qquad \mbox{ if } 0 \le \tau[i] < c\\
+ \sigma[i] &=& \tau[i] + 1, \qquad \mbox{ if } c \le \tau[i] < 2c
+\end{eqnarray}
+for $0 \le i < c$, and take
+\begin{eqnarray}
+ \sigma[i] &=& \tau[i-1],\phantom{{} + 1} \qquad
+ \mbox{ if } 0 \le \tau[i-1] < c\\
+ \sigma[i] &=& \tau[i-1]+1, \qquad \mbox{ if } c \le \tau[i-1] < 2c
+\end{eqnarray}
+for $c < i \le 2c$, and where $\tau[i]$ is the Faure
+permutation for base $c$. The Faure permutations give very small
+discrepancies (amongst the best known today) for small bases.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RadicalInverse
+ * Description: Implements radical inverses of integers in an arbitrary basis
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+
+
+public class RadicalInverse \begin{hide} {
+ private static final int NP = 168; // First NP primes in table below.
+ private static final int PLIM = 1000; // NP primes < PLIM
+
+ // The first NP prime numbers
+ private static final int[] PRIMES = {
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
+ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139,
+ 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223,
+ 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293,
+ 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
+ 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
+ 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569,
+ 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647,
+ 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743,
+ 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
+ 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
+ 947, 953, 967, 971, 977, 983, 991, 997 };
+
+ // Multiplicative factors for permutations as proposed by Faure & Lemieux (2008).
+ // The index corresponds to the coordinate.
+ private static final int[] FAURE_LEMIEUX_FACTORS = {
+ 1, 1, 3, 3, 4, 9, 7, 5, 9, 18, 18, 8, 13, 31, 9, 19, 36, 33, 21, 44, 43,
+ 61, 60, 56, 26, 71, 32, 77, 26, 95, 92, 47, 29, 61, 57, 69, 115, 63, 92,
+ 31, 104, 126, 50, 80, 55, 152, 114, 80, 83, 97, 95, 150, 148, 55, 80, 192,
+ 71, 76, 82, 109, 105, 173, 58, 143, 56, 177, 203, 239, 196, 143, 278, 227,
+ 87, 274, 264, 84, 226, 163, 231, 177, 95, 116, 165, 131, 156, 105, 188,
+ 142, 105, 125, 269, 292, 215, 182, 294, 152, 148, 144, 382, 194, 346, 323,
+ 220, 174, 133, 324, 215, 246, 159, 337, 254, 423, 484, 239, 440, 362, 464,
+ 376, 398, 174, 149, 418, 306, 282, 434, 196, 458, 313, 512, 450, 161, 315,
+ 441, 549, 555, 431, 295, 557, 172, 343, 472, 604, 297, 524, 251, 514, 385,
+ 531, 663, 674, 255, 519, 324, 391, 394, 533, 253, 717, 651, 399, 596, 676,
+ 425, 261, 404, 691, 604, 274, 627, 777, 269, 217, 599, 447, 581, 640, 666,
+ 595, 669, 686, 305, 460, 599, 335, 258, 649, 771, 619, 666, 669, 707, 737,
+ 854, 925, 818, 424, 493, 463, 535, 782, 476, 451, 520, 886, 340, 793, 390,
+ 381, 274, 500, 581, 345, 363, 1024, 514, 773, 932, 556, 954, 793, 294,
+ 863, 393, 827, 527, 1007, 622, 549, 613, 799, 408, 856, 601, 1072, 938,
+ 322, 1142, 873, 629, 1071, 1063, 1205, 596, 973, 984, 875, 918, 1133,
+ 1223, 933, 1110, 1228, 1017, 701, 480, 678, 1172, 689, 1138, 1022, 682,
+ 613, 635, 984, 526, 1311, 459, 1348, 477, 716, 1075, 682, 1245, 401, 774,
+ 1026, 499, 1314, 743, 693, 1282, 1003, 1181, 1079, 765, 815, 1350, 1144,
+ 1449, 718, 805, 1203, 1173, 737, 562, 579, 701, 1104, 1105, 1379, 827,
+ 1256, 759, 540, 1284, 1188, 776, 853, 1140, 445, 1265, 802, 932, 632,
+ 1504, 856, 1229, 1619, 774, 1229, 1300, 1563, 1551, 1265, 905, 1333, 493,
+ 913, 1397, 1250, 612, 1251, 1765, 1303, 595, 981, 671, 1403, 820, 1404,
+ 1661, 973, 1340, 1015, 1649, 855, 1834, 1621, 1704, 893, 1033, 721, 1737,
+ 1507, 1851, 1006, 994, 923, 872, 1860
+ };
+
+ private static final int NRILIM = 1000; // For nextRadicalInverse
+ private int b; // Base
+ private double invb; // 1/b
+ private double logb; // natural log(b)
+ private int JMAX; // b^JMAX = 2^32
+ private int co; // Counter for nextRadicalInverse
+ private double xx; // Current value of x
+ private long ix; // xx = RadicalInverse (ix)
+/*
+ // For Struckmeier's algorithm
+ private static final int PARTITION_MAX = 54; // Size of partitionL, bi
+ private int partM; // Effective size of partitionL, bi
+ private double[] bi; // bi[i] = (b + 1)/b^i - 1
+ private double[] partitionL; // L[i] = 1 - 1/b^i
+ // Boundaries of Struckmeier partitions Lkp of [0, 1]
+*/
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public RadicalInverse (int b, double x0)\begin{hide} {
+ co = 0;
+ this.b = b;
+ invb = 1.0 / b;
+ logb = Math.log (b);
+ JMAX = (int) (32.0 * 0.69314718055994530941 / logb);
+ xx = x0;
+ ix = computeI (x0);
+// initStruckmeier (b);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Initializes the base of this object to $b$
+ and its first value of $x$ to \texttt{x0}.
+% Subsequent values will be calculated directly from the current $x$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{Base}
+ \param{x0}{Initial value of x}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+ private long computeI (double x) {
+ // Compute i such that x = RadicalInverse (i).
+ int[] digits = new int[JMAX]; // Digits of x
+ int j;
+ for (j = 0; (j < JMAX) && (x > 0.5e-15); j++) {
+ x *= b;
+ digits[j] = (int) x;
+ x -= digits[j];
+ }
+ long i = 0;
+ for (j = JMAX - 1; j >= 0; j--) {
+ i = i * b + digits[j];
+ }
+ return i;
+ }\end{hide}
+
+ public static int[] getPrimes (int n) \begin{hide} {
+ // Allocates an array of size n filled with the first n prime
+ // numbers. n must be positive (n > 0). Routine may fail if not enough
+ // memory for the array is available. The first prime number is 2.
+ int i;
+ boolean moreTests;
+ int[] prime = new int[n];
+
+ int n1 = Math.min (NP, n);
+ for (i = 0; i < n1; i++)
+ prime[i] = PRIMES[i];
+ if (NP < n) {
+ i = NP;
+ for (int candidate = PLIM + 1; i < n; candidate += 2) {
+ prime[i] = candidate;
+ for (int j = 1; (moreTests = prime[j] <= candidate / prime[j])
+ && ((candidate % prime[j]) > 0); j++);
+ if (! moreTests)
+ i++;
+ }
+ }
+ return prime;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Provides an elementary method for obtaining the first $n$ prime
+ numbers larger than 1.
+ Creates and returns an array that contains
+ these numbers. This is useful for determining the prime bases for
+ the different coordinates of the Halton sequence and Hammersley nets.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{number of prime numbers to return}
+ \return{an array with the first \texttt{n} prime numbers}
+\end{htmlonly}
+\begin{code}
+
+ public static double radicalInverse (int b, long i) \begin{hide} {
+ double digit, radical, inverse;
+ digit = radical = 1.0 / (double) b;
+ for (inverse = 0.0; i > 0; i /= b) {
+ inverse += digit * (double) (i % b);
+ digit *= radical;
+ }
+ return inverse;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the radical inverse of $i$ in base $b$.
+ If $i=\sum_{r=0}^{k-1} a_r b^r$, the method computes and returns
+ \[
+ x = \sum_{r=0}^{k-1} a_r b^{-r-1}.
+ \]
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{base used for the operation}
+ \param{i}{the value for which the radical inverse will be computed}
+ \return{the radical inverse of \texttt{i} in base \texttt{b}}
+\end{htmlonly}
+\begin{code}
+
+ public static int radicalInverseInteger (int b, double x) \begin{hide} {
+ int digit = 1;
+ int inverse = 0;
+ int precision = Integer.MAX_VALUE / (2 * b * b);
+ while (x > 0 && inverse < precision) {
+ int p = digit * b;
+ double y = Math.floor(x * p);
+ inverse += digit * (int)y;
+ x -= y / (double)p;
+ digit *= b;
+ }
+ return inverse;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+Computes the radical inverse of $x$ in base $b$.
+If $x$ has more decimals in base $b$ than $\log_b$(\texttt{Long.MAX\_VALUE}),
+it is truncated to its minimum precision in base $b$.
+ If $x=\sum_{r=0}^{k-1} a_r b^{-r-1}$, the method computes and returns
+ \[
+ i = \sum_{r=0}^{k-1} a_r b^r.
+ \]
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{base used for the operation}
+ \param{x}{the value for which the radical inverse will be computed}
+ \return{the radical inverse of \texttt{x} in base \texttt{b}}
+\end{htmlonly}
+\begin{code}
+
+ public static long radicalInverseLong (int b, double x) \begin{hide} {
+ long digit = 1;
+ long inverse = 0;
+ long precision = Long.MAX_VALUE / (b * b * b);
+ while (x > 0 && inverse < precision) {
+ long p = digit * b;
+ double y = Math.floor(x * p);
+ inverse += digit * (long)y;
+ x -= y / (double)p;
+ digit *= b;
+ }
+ return inverse;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the radical inverse of $x$ in base $b$.
+If $x$ has more decimals in base $b$ than $\log_b$(\texttt{Long.MAX\_VALUE}),
+it is truncated to its minimum precision in base $b$.
+ If $x=\sum_{r=0}^{k-1} a_r b^{-r-1}$, the method computes and returns
+ \[
+ i = \sum_{r=0}^{k-1} a_r b^r.
+ \]
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{base used for the operation}
+ \param{x}{the value for which the radical inverse will be computed}
+ \return{the radical inverse of \texttt{x} in base \texttt{b}}
+\end{htmlonly}
+\begin{code}
+
+ public static double nextRadicalInverse (double invb, double x) \begin{hide} {
+ // Calculates the next radical inverse from x in base b.
+ // Repeated application causes a loss of accuracy.
+ // Note that x can be any number from [0,1).
+
+ final double ALMOST_ONE = 1.0 - 1e-10;
+ double nextInverse = x + invb;
+ if (nextInverse < ALMOST_ONE)
+ return nextInverse;
+ else {
+ double digit1 = invb;
+ double digit2 = invb * invb;
+ while (x + digit2 >= ALMOST_ONE) {
+ digit1 = digit2;
+ digit2 *= invb;
+ }
+ return x + (digit1 - 1.0) + digit2;
+ }
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ A fast method that incrementally computes the radical inverse $x_{i+1}$
+ in base $b$ from $x_i$ = \texttt{x} = $\psi_b(i)$, %\pierre{???}
+ % \richard{J'ai mis une r\'ef\'erence et une br\`eve description.}
+ using addition with {\em rigthward carry}.
+ The parameter \texttt{invb} is equal to $1/b$.
+ Using long incremental streams (i.e., calling this method several times
+ in a row) cause increasing inaccuracy in $x$. Thus the user should
+ recompute the radical inverse directly by calling
+ \method{radicalInverse}{} every once in a while (i.e. in every few
+ thousand calls).
+ \end{tabb}
+\begin{htmlonly}
+ \param{invb}{$1/b$ where $b$ is the base}
+ \param{x}{the inverse $x_i$}
+ \return{the radical inverse $x_{i+1}$}
+\end{htmlonly}
+\begin{code}
+
+ public double nextRadicalInverse () \begin{hide} {
+ // Calculates the next radical inverse from xx in base b.
+ // Repeated application causes a loss of accuracy.
+ // For each NRILIM calls, a direct calculation via radicalInverse
+ // is inserted.
+
+ co++;
+ if (co >= NRILIM) {
+ co = 0;
+ ix += NRILIM;
+ xx = radicalInverse (b, ix);
+ return xx;
+ }
+ final double ALMOST_ONE = 1.0 - 1e-10;
+ double nextInverse = xx + invb;
+ if (nextInverse < ALMOST_ONE) {
+ xx = nextInverse;
+ return xx;
+ } else {
+ double digit1 = invb;
+ double digit2 = invb * invb;
+ while (xx + digit2 >= ALMOST_ONE) {
+ digit1 = digit2;
+ digit2 *= invb;
+ }
+ xx += (digit1 - 1.0) + digit2;
+ return xx;
+ }
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ A fast method that incrementally computes the radical inverse $x_{i+1}$
+ in base $b$ from $x_i$ = $\psi_b(i)$,
+ using addition with {\em rigthward carry} as described in
+ \latex{\cite{vWAN99a}}\html{Wang and Hickernell}.
+ Since using long incremental streams (i.e., calling this method several
+ times in a row) cause increasing inaccuracy in $x$, the method
+ recomputes the radical inverse directly from $i$ by calling
+ \method{radicalInverse}{} once in every 1000 calls.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the radical inverse $x_{i+1}$}
+\end{htmlonly}
+\begin{hide}
+\begin{code}
+/*
+ private void initStruckmeier (int b) {
+ bi = new double[1 + PARTITION_MAX];
+ partitionL = new double[1 + PARTITION_MAX];
+ logb = Math.log (b);
+ partitionL[0] = 0.0;
+ bi[0] = 1.0;
+ int i = 0;
+ while ((i < PARTITION_MAX) && (partitionL[i] < 1.0)) {
+ ++i;
+ bi[i] = bi[i - 1] / b;
+ partitionL[i] = 1.0 - bi[i];
+ }
+ partM = i - 1;
+
+ for (i = 0; i <= partM + 1; ++i)
+ bi[i] = (b + 1) * bi[i] - 1.0;
+ }
+
+ public double nextRadicalInverse (double x) {
+ int k;
+ if (x < partitionL[partM]) {
+ k = 1;
+ // Find k such: partitionL[k-1] <= x < partitionL[k]
+ while (x >= partitionL[k])
+ ++k;
+
+ } else { // x >= partitionL [partM]
+ k = 1 + (int)(-Math.log(1.0 - x) / logb);
+ }
+ return x + bi[k];
+ } */
+\end{code}
+ \begin{tabb}
+ A fast method that incrementally computes the radical inverse $x_{i+1}$
+ in base $b$ from $x_i =$ \texttt{ x},
+ using the Struckmeier's algorithm described in \cite{rSTR95a}. It uses
+ a small precomputed table of values $L_k = 1 - b^{-k}$. The method returns
+ the next radical inverse $x_{i+1} = x_i + (b + 1 - b^k) / b^k$, where
+ $L_{k-1} \le x < L_k$.
+ \richard{This method can work only if it is reprogrammed with integers.
+ With floating-point numbers,
+ unavoidable accumulating rounding errors will sooner or later lead to
+ choosing the wrong interval, after which, all subsequent x's will be
+ completely wrong.}
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{base}
+ \param{x}{the inverse $x_i$}
+ \return{the radical inverse $x_{i+1}$}
+\end{htmlonly}
+\end{hide}
+\begin{code}
+
+ public static void reverseDigits (int k, int bdigits[], int idigits[])\begin{hide} {
+ for (int l = 0; l < k; l++)
+ idigits[l] = bdigits[k-l];
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Given the $k$ $b$-ary digits of $i$ in \texttt{bdigits}, returns the
+ $k$ digits of the integer radical inverse of $i$ in \texttt{idigits}.
+ This simply reverses the order of the digits.
+ \end{tabb}
+\begin{htmlonly}
+ \param{k}{number of digits in arrays}
+ \param{bdigits}{digits in original order}
+ \param{idigits}{digits in reverse order}
+\end{htmlonly}
+\begin{code}
+
+ public static int integerRadicalInverse (int b, int i) \begin{hide} {
+ // Simply flips digits of i in base b.
+ int inverse;
+ for (inverse = 0; i > 0; i /= b)
+ inverse = inverse * b + (i % b);
+ return inverse;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the integer radical inverse of $i$ in base $b$,
+ equal to $b^k \psi_b(i)$ if $i$ has $k$ $b$-ary digits.
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{base used for the operation}
+ \param{i}{the value for which the integer radical inverse will be computed}
+ \return{the integer radical inverse of \texttt{i} in base \texttt{b}}
+\end{htmlonly}
+\begin{code}
+
+ public static int nextRadicalInverseDigits (int b, int k, int idigits[])\begin{hide} {
+ int l;
+ for (l = k-1; l >= 0; l--)
+ if (idigits[l] == b-1)
+ idigits[l] = 0;
+ else {
+ idigits[l]++;
+ return k;
+ }
+ if (l == 0) {
+ idigits[k] = 1;
+ return ++k;
+ }
+ return 0;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Given the $k$ digits of the integer radical inverse of $i$ in \texttt{bdigits},
+ in base $b$, this method replaces them by the digits of the integer
+ radical inverse of $i+1$ and returns their number.
+ The array must be large enough to hold this new number of digits.
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{base}
+ \param{k}{initial number of digits in arrays}
+ \param{idigits}{digits of integer radical inverse}
+ \return{new number of digits in arrays}
+\end{htmlonly}
+\begin{code}
+
+ public static void getFaureLemieuxPermutation (int coordinate, int[] pi) \begin{hide} {
+ int f = FAURE_LEMIEUX_FACTORS[coordinate];
+ int b = PRIMES[coordinate];
+ for (int k = 0; k < pi.length; k++)
+ pi[k] = f * k % b;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the permutations as proposed in \cite{vFAU09a} $\sigma_b$ of the set
+ $\{0, \ldots, b - 1\}$ and puts it in array \texttt{pi}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{coordinate}{the coordinate}
+ \param{pi}{an array of size at least \texttt{b},
+ to be filled with the permutation}
+\end{htmlonly}
+\begin{code}
+
+ public static void getFaurePermutation (int b, int[] pi) \begin{hide} {
+ // This is a recursive implementation.
+ // Perhaps not the most efficient...
+ int i;
+ if (b == 2) {
+ pi[0] = 0;
+ pi[1] = 1;
+ }
+ else if ((b & 1) != 0) {
+ // b is odd.
+ b--;
+ getFaurePermutation (b, pi);
+ for (i = 0; i < b; i++)
+ if (pi[i] >= b / 2)
+ pi[i]++;
+ for (i = b; i > b / 2; i--)
+ pi[i] = pi[i - 1];
+ pi[b / 2] = b / 2;
+ }
+ else {
+ b /= 2;
+ getFaurePermutation (b, pi);
+ for (i = 0; i < b; i++) {
+ pi[i] *= 2;
+ pi[i + b] = pi[i] + 1;
+ }
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the Faure permutation \cite{rFAU92a} $\sigma_b$ of the set
+ $\{0, \ldots, b - 1\}$ and puts it in array \texttt{pi}.
+ % \pierre{Definition and reference?}
+ % \richard{Corrig\'e.}
+ See the description in the introduction above.
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{the base}
+ \param{pi}{an array of size at least \texttt{b},
+ to be filled with the permutation}
+\end{htmlonly}
+\begin{code}
+
+ public static double permutedRadicalInverse (int b, int[] pi, long i)\begin{hide} {
+ double digit, radical, inverse;
+ digit = radical = 1.0 / (double) b;
+ for (inverse = 0.0; i > 0; i /= b) {
+ inverse += digit * (double) pi[(int)(i % b)];
+ digit *= radical;
+ }
+ return inverse;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the radical inverse of $i$ in base $b$, where the digits
+ are permuted using the permutation $\pi$.
+ If $i=\sum_{r=0}^{k-1} a_r b^r$, the method will compute and return
+ \[
+ x = \sum_{r=0}^{k-1} \pi[a_r] b^{-r-1}.
+ \]
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{base $b$ used for the operation}
+ \param{pi}{an array of length at least \texttt{b} containing the permutation
+ used during the computation}
+ \param{i}{the value for which the radical inverse will be computed}
+ \return{the radical inverse of \texttt{i} in base \texttt{b}}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/hups/RandShiftedPointSet.java b/source/umontreal/iro/lecuyer/hups/RandShiftedPointSet.java
new file mode 100644
index 0000000..3f93fcb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RandShiftedPointSet.java
@@ -0,0 +1,192 @@
+
+
+/*
+ * Class: RandShiftedPointSet
+ * Description: Point set to which a random shift modulo 1 is applied
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.*;
+
+
+/**
+ * This container class embodies a point set to which a random shift
+ * modulo 1 is applied (i.e., a single uniform random point is added
+ * to all points, modulo 1, to randomize the inner point set).
+ * When calling {@link #addRandomShift addRandomShift}, a new random shift will be generated.
+ * This shift is represented by a vector of <SPAN CLASS="MATH"><I>d</I></SPAN> uniforms over <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ * where <SPAN CLASS="MATH"><I>d</I></SPAN> is the current dimension of the shift.
+ *
+ */
+public class RandShiftedPointSet extends ContainerPointSet {
+
+ protected double[] shift; // The random shift.
+ protected int dimShift = 0; // Current dimension of the shift.
+ protected int capacityShift = 0; // Number of array elements;
+ // always >= dimShift.
+ protected RandomStream shiftStream; // Used to generate random shifts.
+
+
+
+ /**
+ * Constructs a structure to contain a randomly shifted version of <TT>P</TT>.
+ * The random shifts will be generated in up to <TT>dimShift</TT> dimensions,
+ * using stream <TT>stream</TT>.
+ *
+ * @param P point set being randomized
+ *
+ * @param dimShift dimension of the initial shift
+ *
+ * @param stream stream used for generating random shifts
+ *
+ */
+ public RandShiftedPointSet (PointSet P, int dimShift, RandomStream stream) {
+ init (P);
+ if (dimShift <= 0) {
+ throw new IllegalArgumentException (
+ "Cannot construct RandShiftedPointSet with dimShift <= 0");
+ }
+ shiftStream = stream;
+ shift = new double [dimShift];
+ capacityShift = this.dimShift = dimShift;
+ }
+
+
+ /**
+ * Returns the number of dimensions of the current random shift.
+ *
+ */
+ public int getShiftDimension() {
+ return dimShift;
+ }
+
+
+ /**
+ * Changes the stream used for the random shifts to <TT>stream</TT>, then
+ * refreshes the shift for coordinates <TT>d1</TT> to <TT>d2-1</TT>.
+ *
+ */
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (stream != shiftStream)
+ shiftStream = stream;
+ addRandomShift (d1, d2);
+ }
+
+
+ /**
+ * Changes the stream used for the random shifts to <TT>stream</TT>, then
+ * refreshes all coordinates of the random shift, up to its current dimension.
+ *
+ *
+ */
+ public void addRandomShift (RandomStream stream) {
+ if (stream != shiftStream)
+ shiftStream = stream;
+ addRandomShift (0, dimShift);
+ }
+
+
+ /**
+ * Refreshes the random shift (generates new uniform values for the
+ * random shift coordinates) for coordinates <TT>d1</TT> to <TT>d2-1</TT>.
+ *
+ */
+ @Deprecated
+ public void addRandomShift (int d1, int d2) {
+ if (d1 < 0 || d1 > d2)
+ throw new IllegalArgumentException ("illegal parameter d1 or d2");
+ if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ double[] temp = new double[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = shift[i];
+ shift = temp;
+ }
+ dimShift = d2;
+ for (int i = d1; i < d2; i++)
+ shift[i] = shiftStream.nextDouble();
+
+ // Just for testing, to see the single uniform random point
+ // for(int k = 0; k < d2; k++)
+ // System.out.println ("shift " + k + " = " + shift[k]);
+ // System.out.println();
+
+ }
+
+
+ @Deprecated
+ public void addRandomShift() {
+ addRandomShift (0, dimShift);
+ }
+
+
+ public String toString() {
+ return "RandShiftedPointSet of: {" + PrintfFormat.NEWLINE
+ + P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+ public PointSetIterator iterator() {
+ return new RandShiftedPointSetIterator();
+ }
+
+ // ***************************************************************
+
+ private class RandShiftedPointSetIterator
+ extends ContainerPointSetIterator {
+
+ public double getCoordinate (int i, int j) {
+ int d1 = innerIterator.getCurCoordIndex();
+ if (dimShift <= d1)
+ // Must extend randomization.
+ addRandomShift (dimShift, 1 + d1);
+ double u = P.getCoordinate(i, j) + shift[j];
+ if (u >= 1.0)
+ u -= 1.0;
+ if (u > 0.0)
+ return u;
+ return EpsilonHalf; // avoid u = 0
+ }
+
+ public double nextCoordinate() {
+ int d1 = innerIterator.getCurCoordIndex();
+ if (dimShift <= d1)
+ addRandomShift (dimShift,1 + d1 );
+ double u = shift [d1];
+ u += innerIterator.nextCoordinate();
+ if (u >= 1.0)
+ u -= 1.0;
+ if (u > 0.0)
+ return u;
+ return EpsilonHalf; // avoid u = 0
+ }
+
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/RandShiftedPointSet.tex b/source/umontreal/iro/lecuyer/hups/RandShiftedPointSet.tex
new file mode 100644
index 0000000..dfa8bb2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RandShiftedPointSet.tex
@@ -0,0 +1,220 @@
+\defmodule{RandShiftedPointSet}
+
+\adam{Cette classe reprogramme un \texttt{addRandomShift} explicitement sur
+l'ensemble $P$, alors
+que le \texttt{ContainerPointSet} applique le \texttt{addRandomShift} sur le
+contenu $P$, ce qui est beaucoup plus propre et plus g\'en\'eral.
+Faut-il \'eliminer cette classe? Est-elle vraiment n\'ecessaire?}
+%
+This container class embodies a point set to which a random shift
+modulo 1 is applied (i.e., a single uniform random point is added
+to all points, modulo 1, to randomize the inner point set).
+When calling \method{addRandomShift}{}, a new random shift will be generated.
+This shift is represented by a vector of $d$ uniforms over $(0,1)$,
+where $d$ is the current dimension of the shift.
+
+% The shift is executed \emph{after} all other randomizations in the
+% randomization list, if any.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandShiftedPointSet
+ * Description: Point set to which a random shift modulo 1 is applied
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.*;
+\end{hide}
+
+public class RandShiftedPointSet extends ContainerPointSet \begin{hide} {
+
+ protected double[] shift; // The random shift.
+ protected int dimShift = 0; // Current dimension of the shift.
+ protected int capacityShift = 0; // Number of array elements;
+ // always >= dimShift.
+ protected RandomStream shiftStream; // Used to generate random shifts.
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public RandShiftedPointSet (PointSet P, int dimShift, RandomStream stream)\begin{hide} {
+ init (P);
+ if (dimShift <= 0) {
+ throw new IllegalArgumentException (
+ "Cannot construct RandShiftedPointSet with dimShift <= 0");
+ }
+ shiftStream = stream;
+ shift = new double [dimShift];
+ capacityShift = this.dimShift = dimShift;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a structure to contain a randomly shifted version of \texttt{P}.
+ The random shifts will be generated in up to \texttt{dimShift} dimensions,
+ using stream \texttt{stream}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set being randomized}
+ \param{dimShift}{dimension of the initial shift}
+ \param{stream}{stream used for generating random shifts}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public int getShiftDimension()\begin{hide} {
+ return dimShift;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the number of dimensions of the current random shift.
+ \end{tabb}
+\begin{code}
+
+ public void addRandomShift (int d1, int d2, RandomStream stream) \begin{hide} {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (stream != shiftStream)
+ shiftStream = stream;
+ addRandomShift (d1, d2);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Changes the stream used for the random shifts to \texttt{stream}, then
+ refreshes the shift for coordinates \texttt{d1} to \texttt{d2-1}.
+\richard{Il y a 4 m\'ethodes \texttt{addRandomShift}. Peut-\^etre faudrait-il
+en \'eliminer 2, comme dans \texttt{PointSet}.}
+ \end{tabb}
+\begin{code}
+
+ public void addRandomShift (RandomStream stream) \begin{hide} {
+ if (stream != shiftStream)
+ shiftStream = stream;
+ addRandomShift (0, dimShift);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Changes the stream used for the random shifts to \texttt{stream}, then
+ refreshes all coordinates of the random shift, up to its current dimension.
+ \end{tabb}
+\begin{hide}
+\begin{code}
+
+ @Deprecated
+ public void addRandomShift (int d1, int d2)\begin{hide} {
+ if (d1 < 0 || d1 > d2)
+ throw new IllegalArgumentException ("illegal parameter d1 or d2");
+ if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ double[] temp = new double[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = shift[i];
+ shift = temp;
+ }
+ dimShift = d2;
+ for (int i = d1; i < d2; i++)
+ shift[i] = shiftStream.nextDouble();
+
+ // Just for testing, to see the single uniform random point
+ // for(int k = 0; k < d2; k++)
+ // System.out.println ("shift " + k + " = " + shift[k]);
+ // System.out.println();
+
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Refreshes the random shift (generates new uniform values for the
+ random shift coordinates) for coordinates \texttt{d1} to \texttt{d2-1}.
+ \end{tabb}
+\begin{code}
+
+ @Deprecated
+ public void addRandomShift() \begin{hide} {
+ addRandomShift (0, dimShift);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Refreshes all coordinates of the random shift, up to its current dimension.
+ \end{tabb}
+\end{hide}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ return "RandShiftedPointSet of: {" + PrintfFormat.NEWLINE
+ + P.toString() + PrintfFormat.NEWLINE + "}";
+ }
+
+ public PointSetIterator iterator() {
+ return new RandShiftedPointSetIterator();
+ }
+
+ // ***************************************************************
+
+ private class RandShiftedPointSetIterator
+ extends ContainerPointSetIterator {
+
+ public double getCoordinate (int i, int j) {
+ int d1 = innerIterator.getCurCoordIndex();
+ if (dimShift <= d1)
+ // Must extend randomization.
+ addRandomShift (dimShift, 1 + d1);
+ double u = P.getCoordinate(i, j) + shift[j];
+ if (u >= 1.0)
+ u -= 1.0;
+ if (u > 0.0)
+ return u;
+ return EpsilonHalf; // avoid u = 0
+ }
+
+ public double nextCoordinate() {
+ int d1 = innerIterator.getCurCoordIndex();
+ if (dimShift <= d1)
+ addRandomShift (dimShift,1 + d1 );
+ double u = shift [d1];
+ u += innerIterator.nextCoordinate();
+ if (u >= 1.0)
+ u -= 1.0;
+ if (u > 0.0)
+ return u;
+ return EpsilonHalf; // avoid u = 0
+ }
+
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/RandomShift.java b/source/umontreal/iro/lecuyer/hups/RandomShift.java
new file mode 100644
index 0000000..cdbaec9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RandomShift.java
@@ -0,0 +1,110 @@
+
+
+/*
+ * Class: RandomShift
+ * Description: Applies a random shift on a point set
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+ import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This class implements a
+ * {@link umontreal.iro.lecuyer.hups.PointSetRandomization PointSetRandomization}.
+ * The {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} is
+ * stored internally. The method {@link #randomize(PointSet) randomize} simply
+ * calls
+ * {@link umontreal.iro.lecuyer.hups.PointSet#addRandomShift(RandomStream) addRandomShift}<TT>(stream)</TT>.
+ *
+ * <P>
+ * This class can be used as a base class to implement a specific
+ * randomization by overriding method {@link #randomize(PointSet) randomize}.
+ *
+ */
+public class RandomShift implements PointSetRandomization {
+ protected RandomStream stream;
+
+
+
+ /**
+ * Empty constructor.
+ *
+ */
+ public RandomShift() {
+ }
+
+
+
+ /**
+ * Sets the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} to <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ */
+ public RandomShift (RandomStream stream) {
+ this.stream = stream;
+ }
+
+
+
+ /**
+ * This method calls
+ * {@link umontreal.iro.lecuyer.hups.PointSet#addRandomShift(RandomStream) addRandomShift} <TT>(stream)</TT>.
+ *
+ * @param p Point set to randomize
+ *
+ *
+ */
+ public void randomize (PointSet p) {
+ p.addRandomShift(stream);
+ }
+
+
+ /**
+ * Sets the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} to
+ * <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ *
+ */
+ public void setStream (RandomStream stream) {
+ this.stream = stream;
+ }
+
+
+ /**
+ * Returns the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}.
+ *
+ * @return stream used in the randomization
+ *
+ */
+ public RandomStream getStream() {
+ return stream;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/RandomShift.tex b/source/umontreal/iro/lecuyer/hups/RandomShift.tex
new file mode 100644
index 0000000..ced8981
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RandomShift.tex
@@ -0,0 +1,122 @@
+\defmodule{RandomShift}
+
+This class implements a
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetRandomization}.
+The \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} is
+stored internally. The method \method{randomize}{PointSet} simply
+calls
+\externalmethod{umontreal.iro.lecuyer.hups}{PointSet}{addRandomShift}{RandomStream}\texttt{(stream)}.
+
+This class can be used as a base class to implement a specific
+randomization by overriding method \method{randomize}{PointSet}.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomShift
+ * Description: Applies a random shift on a point set
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+\begin{hide}
+ import umontreal.iro.lecuyer.rng.RandomStream;
+
+\end{hide}
+public class RandomShift implements PointSetRandomization\begin{hide} {
+ protected RandomStream stream;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public RandomShift() \begin{hide} {
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Empty constructor.
+\end{tabb}
+\begin{code}
+
+ public RandomShift (RandomStream stream) \begin{hide} {
+ this.stream = stream;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Sets the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} to \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void randomize (PointSet p) \begin{hide} {
+ p.addRandomShift(stream);
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ This method calls
+ \externalmethod{umontreal.iro.lecuyer.hups}{PointSet}
+ {addRandomShift}{RandomStream}~\texttt{(stream)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{Point set to randomize}
+\end{htmlonly}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ this.stream = stream;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Sets the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} to
+ \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ return stream;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Returns the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+\end{tabb}
+\begin{htmlonly}
+ \return{stream used in the randomization}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/RandomStart.java b/source/umontreal/iro/lecuyer/hups/RandomStart.java
new file mode 100644
index 0000000..31131f8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RandomStart.java
@@ -0,0 +1,121 @@
+
+
+/*
+ * Class: RandomStart
+ * Description: Randomizes a sequence with a random starting point
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import java.lang.IllegalArgumentException;
+
+
+/**
+ * This class implements a
+ * {@link umontreal.iro.lecuyer.hups.PointSetRandomization PointSetRandomization}
+ * that randomizes a sequence with a random starting point.
+ * The point set must be an instance of
+ * {@link umontreal.iro.lecuyer.hups.HaltonSequence HaltonSequence} or an
+ * {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ * For now, only the Halton sequence is allowed, but there may be others
+ * later.
+ *
+ */
+public class RandomStart implements PointSetRandomization {
+
+ protected RandomStream stream;
+
+
+
+ /**
+ * Empty constructor.
+ *
+ */
+ public RandomStart() {
+ }
+
+
+
+ /**
+ * Sets internal variable <TT>stream</TT> to the given
+ * <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ */
+ public RandomStart (RandomStream stream) {
+ this.stream = stream;
+ }
+
+
+
+ /**
+ * This method calls
+ * {@link umontreal.iro.lecuyer.hups.HaltonSequence#init(double[]) init}.
+ * If <TT>p</TT> is not a
+ * {@link umontreal.iro.lecuyer.hups.HaltonSequence HaltonSequence}, an
+ * {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ *
+ * @param p Point set to randomize
+ *
+ *
+ */
+ public void randomize (PointSet p) {
+ if (p instanceof HaltonSequence) {
+ double[] x0 = new double[p.getDimension()];
+ stream.nextArrayOfDouble(x0, 0, x0.length);
+ ((HaltonSequence)p).setStart (x0);
+ } else {
+ throw new IllegalArgumentException("RandomStart" +
+ " can only randomize a HaltonSequence");
+ }
+ }
+
+
+
+ /**
+ * Sets the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} to
+ * <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ *
+ */
+ public void setStream (RandomStream stream) {
+ this.stream = stream;
+ }
+
+
+ /**
+ * Returns the internal
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}.
+ *
+ * @return stream used in the randomization
+ *
+ */
+ public RandomStream getStream() {
+ return stream;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/RandomStart.tex b/source/umontreal/iro/lecuyer/hups/RandomStart.tex
new file mode 100644
index 0000000..02a1aa1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/RandomStart.tex
@@ -0,0 +1,134 @@
+\defmodule{RandomStart}
+
+This class implements a
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetRandomization}
+that randomizes a sequence with a random starting point.
+The point set must be an instance of
+\externalclass{umontreal.iro.lecuyer.hups}{HaltonSequence} or an
+\externalclass{java.lang}{IllegalArgumentException} is thrown.
+For now, only the Halton sequence is allowed, but there may be others
+later.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomStart
+ * Description: Randomizes a sequence with a random starting point
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+\begin{hide}
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import java.lang.IllegalArgumentException;
+\end{hide}
+
+public class RandomStart implements PointSetRandomization \begin{hide} {
+
+ protected RandomStream stream;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public RandomStart() \begin{hide} {
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Empty constructor.
+\end{tabb}
+\begin{code}
+
+ public RandomStart (RandomStream stream) \begin{hide} {
+ this.stream = stream;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Sets internal variable \texttt{stream} to the given
+ \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void randomize (PointSet p) \begin{hide} {
+ if (p instanceof HaltonSequence) {
+ double[] x0 = new double[p.getDimension()];
+ stream.nextArrayOfDouble(x0, 0, x0.length);
+ ((HaltonSequence)p).setStart (x0);
+ } else {
+ throw new IllegalArgumentException("RandomStart" +
+ " can only randomize a HaltonSequence");
+ }
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ This method calls
+ \externalmethod{umontreal.iro.lecuyer.hups}{HaltonSequence}{init}{double[]}.
+ If \texttt{p} is not a
+ \externalclass{umontreal.iro.lecuyer.hups}{HaltonSequence}, an
+\externalclass{java.lang}{IllegalArgumentException} is thrown.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{Point set to randomize}
+\end{htmlonly}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ this.stream = stream;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Sets the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} to
+ \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ return stream;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Returns the internal
+ \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+\end{tabb}
+\begin{htmlonly}
+ \return{stream used in the randomization}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/Rank1Lattice.java b/source/umontreal/iro/lecuyer/hups/Rank1Lattice.java
new file mode 100644
index 0000000..6e8e5f7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/Rank1Lattice.java
@@ -0,0 +1,249 @@
+
+
+/*
+ * Class: Rank1Lattice
+ * Description: Rank-1 lattice
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This class implements point sets specified by integration
+ * lattices of rank 1. They are defined as follows.
+ * One selects an arbitrary positive integer <SPAN CLASS="MATH"><I>n</I></SPAN> and a <SPAN CLASS="MATH"><I>s</I></SPAN>-dimensional
+ * integer vector
+ * <SPAN CLASS="MATH">(<I>a</I><SUB>0</SUB>,..., <I>a</I><SUB>s-1</SUB>)</SPAN>.
+ * [Usually, <SPAN CLASS="MATH"><I>a</I><SUB>0</SUB> = 1</SPAN> and
+ * <SPAN CLASS="MATH">0 <= <I>a</I><SUB>j</SUB> < <I>n</I></SPAN> for each <SPAN CLASS="MATH"><I>j</I></SPAN>;
+ * when the <SPAN CLASS="MATH"><I>a</I><SUB>j</SUB></SPAN> are outside the interval <SPAN CLASS="MATH">[0, <I>n</I>)</SPAN>, then we replace <SPAN CLASS="MATH"><I>a</I><SUB>j</SUB></SPAN> by
+ * (
+ * <SPAN CLASS="MATH"><I>a</I><SUB>j</SUB> mod <I>n</I></SPAN>) in all calculations.] The points are defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>u</B><SUB>i</SUB> = (<I>i</I>/<I>n</I>)(<I>a</I><SUB>0</SUB>, <I>a</I><SUB>1</SUB>,…, <I>a</I><SUB>s-1</SUB>) mod 1
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,..., <I>n</I> - 1</SPAN>.
+ * These <SPAN CLASS="MATH"><I>n</I></SPAN> points are distinct provided that <SPAN CLASS="MATH"><I>n</I></SPAN> and the <SPAN CLASS="MATH"><I>a</I><SUB>j</SUB></SPAN>'s have
+ * no common factor.
+ *
+ */
+public class Rank1Lattice extends PointSet {
+
+ protected int[] genAs; // Lattice generator: a[i]
+ protected double[] v; // Lattice vector: v[i] = a[i]/n
+ protected double normFactor; // 1/n.
+ protected double[] shift; // Random shift, initially null.
+
+ private void initN (int n) {
+ numPoints = n;
+ normFactor = 1.0 / (double) n;
+ for (int j = 0; j < dim; j++) {
+ int amod = (genAs[j] % n) + (genAs[j] < 0 ? n : 0);
+ v[j] = normFactor * amod;
+ }
+ }
+
+
+
+ /**
+ * Instantiates a {@link Rank1Lattice} with <SPAN CLASS="MATH"><I>n</I></SPAN> points and lattice
+ * vector <SPAN CLASS="MATH"><I>a</I></SPAN> of dimension <SPAN CLASS="MATH"><I>s</I></SPAN>.
+ *
+ * @param n there are n points
+ *
+ * @param a the lattice vector
+ *
+ * @param s dimension of the lattice vector a
+ *
+ */
+ public Rank1Lattice (int n, int[] a, int s) {
+ dim = s;
+ v = new double[s];
+ genAs = new int[s];
+ for (int j = 0; j < s; j++) {
+ genAs[j] = a[j];
+ }
+ initN (n);
+ }
+
+
+ /**
+ * Resets the number of points of the lattice to <SPAN CLASS="MATH"><I>n</I></SPAN>. The dimension <SPAN CLASS="MATH"><I>s</I></SPAN> and
+ * the <SPAN CLASS="MATH"><I>a</I><SUB>j</SUB></SPAN> are unchanged.
+ *
+ */
+ public void setNumPoints (int n) {
+ initN(n);
+ }
+
+
+ /**
+ * Returns the generator <SPAN CLASS="MATH"><I>a</I><SUB>j</SUB></SPAN> of the lattice. (The original ones before they are
+ * reset to
+ * <SPAN CLASS="MATH"><I>a</I><SUB>j</SUB> mod <I>n</I></SPAN>). Its components
+ * are returned as <TT>a[<SPAN CLASS="MATH"><I>j</I></SPAN>]</TT>, for
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…,(<I>s</I> - 1)</SPAN>.
+ *
+ */
+ public int[] getAs() {
+ return genAs;
+ }
+
+
+ /**
+ * Adds a random shift to all the points of the point set,
+ * using stream <TT>stream</TT> to generate the random numbers.
+ * For each coordinate <SPAN CLASS="MATH"><I>j</I></SPAN> from <TT>d1</TT> to <TT>d2-1</TT>,
+ * the shift <SPAN CLASS="MATH"><I>d</I><SUB>j</SUB></SPAN> is generated uniformly over <SPAN CLASS="MATH">[0, 1)</SPAN> and added modulo <SPAN CLASS="MATH">1</SPAN> to
+ * all the coordinates of all the points.
+ *
+ * @param d1 lower dimension of shift
+ *
+ * @param d2 upper dimension of shift is d2 - 1
+ *
+ * @param stream random number stream used to generate uniforms
+ *
+ *
+ */
+ public void addRandomShift (int d1, int d2, RandomStream stream) {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (shift == null) {
+ shift = new double[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ double[] temp = new double[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = shift[i];
+ shift = temp;
+ }
+ dimShift = d2;
+ for (int i = d1; i < d2; i++)
+ shift[i] = stream.nextDouble ();
+ shiftStream = stream;
+ }
+
+
+ /**
+ * Clears the random shift.
+ *
+ */
+ public void clearRandomShift() {
+ super.clearRandomShift();
+ shift = null;
+ }
+
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Rank1Lattice:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+
+ public double getCoordinate (int i, int j) {
+ double x = (v[j] * i) % 1.0;
+ if (shift != null) {
+ if (j >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, j + 1, shiftStream);
+ x += shift[j];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ return x;
+ }
+
+
+ // Recursive method that computes a^e mod m.
+ protected long modPower (long a, int e, int m) {
+ // If parameters a and m == numPoints could be omitted, then
+ // the routine would run much faster due to reduced stack usage.
+ // Note that a can be larger than m, e.g. in lattice sequences !
+
+ if (e == 0)
+ return 1;
+ else if (e == 1)
+ return a % m;
+ else if ((e & 1) == 1)
+ return (a * modPower(a, e - 1, m)) % m;
+ else {
+ long p = modPower(a, e / 2, m);
+ return (p * p) % m;
+ }
+ }
+
+ protected double radicalInverse (int base, int i) {
+ double digit, radical, inverse;
+ digit = radical = 1.0 / (double) base;
+ for (inverse = 0.0; i > 0; i /= base) {
+ inverse += digit * (double) (i % base);
+ digit *= radical;
+ }
+ return inverse;
+ }
+
+ public PointSetIterator iterator() {
+ return new Rank1LatticeIterator();
+ }
+
+// ************************************************************************
+
+ protected class Rank1LatticeIterator extends PointSet.DefaultPointSetIterator
+ {
+ public double nextCoordinate() {
+ // I tried with long's and with double's. The double version is
+ // 4.5 times faster than the long version.
+ if (curPointIndex >= numPoints || curCoordIndex >= dim)
+ outOfBounds();
+// return (curPointIndex * v[curCoordIndex++]) % 1.0;
+ double x = (curPointIndex * v[curCoordIndex]) % 1.0;
+ if (shift != null) {
+ if (curCoordIndex >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, curCoordIndex + 1, shiftStream);
+ x += shift[curCoordIndex];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ curCoordIndex++;
+ return x;
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/Rank1Lattice.tex b/source/umontreal/iro/lecuyer/hups/Rank1Lattice.tex
new file mode 100644
index 0000000..aba5a7e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/Rank1Lattice.tex
@@ -0,0 +1,250 @@
+\defmodule{Rank1Lattice}
+
+This class implements point sets specified by integration
+lattices of rank 1. They are defined as follows \cite{vSLO94a}.
+One selects an arbitrary positive integer $n$ and a $s$-dimensional
+integer vector $(a_0,\dots,a_{s-1})$.
+[Usually, $a_0=1$ and $0 \le a_j < n$ for each $j$;
+when the $a_j$ are outside the interval $[0,n)$, then we replace $a_j$ by
+($a_j \bmod n$) in all calculations.] The points are defined by
+\eq
+ \mathbf{u}_i = (i/n)(a_0, a_1, \ldots, a_{s-1}) \bmod 1
+\endeq
+for $i=0,\dots,n-1$.
+These $n$ points are distinct provided that $n$ and the $a_j$'s have
+no common factor.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Rank1Lattice
+ * Description: Rank-1 lattice
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+public class Rank1Lattice extends PointSet \begin{hide} {
+
+ protected int[] genAs; // Lattice generator: a[i]
+ protected double[] v; // Lattice vector: v[i] = a[i]/n
+ protected double normFactor; // 1/n.
+ protected double[] shift; // Random shift, initially null.
+
+ private void initN (int n) {
+ numPoints = n;
+ normFactor = 1.0 / (double) n;
+ for (int j = 0; j < dim; j++) {
+ int amod = (genAs[j] % n) + (genAs[j] < 0 ? n : 0);
+ v[j] = normFactor * amod;
+ }
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+\begin{code}
+
+ public Rank1Lattice (int n, int[] a, int s) \begin{hide} {
+ dim = s;
+ v = new double[s];
+ genAs = new int[s];
+ for (int j = 0; j < s; j++) {
+ genAs[j] = a[j];
+ }
+ initN (n);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Instantiates a \class{Rank1Lattice}{} with $n$ points and lattice
+ vector $a$ of dimension $s$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{there are n points}
+ \param{a}{the lattice vector}
+ \param{s}{dimension of the lattice vector a}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void setNumPoints (int n) \begin{hide} {
+ initN(n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Resets the number of points of the lattice to $n$. The dimension $s$ and
+ the $a_j$ are unchanged.
+\end{tabb}
+\begin{code}
+
+ public int[] getAs() \begin{hide} {
+ return genAs;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the generator $a_j$ of the lattice. (The original ones before they are
+reset to $a_j \bmod n$). Its components
+ are returned as \texttt{a[$j$]}, for $j = 0, 1, \ldots, (s-1)$.
+\end{tabb}
+\begin{code}
+
+ public void addRandomShift (int d1, int d2, RandomStream stream) \begin{hide} {
+ if (null == stream)
+ throw new IllegalArgumentException (
+ PrintfFormat.NEWLINE +
+ " Calling addRandomShift with null stream");
+ if (0 == d2)
+ d2 = Math.max (1, dim);
+ if (shift == null) {
+ shift = new double[d2];
+ capacityShift = d2;
+ } else if (d2 > capacityShift) {
+ int d3 = Math.max (4, capacityShift);
+ while (d2 > d3)
+ d3 *= 2;
+ double[] temp = new double[d3];
+ capacityShift = d3;
+ for (int i = 0; i < d1; i++)
+ temp[i] = shift[i];
+ shift = temp;
+ }
+ dimShift = d2;
+ for (int i = d1; i < d2; i++)
+ shift[i] = stream.nextDouble ();
+ shiftStream = stream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds a random shift to all the points of the point set,
+ using stream \texttt{stream} to generate the random numbers.
+ For each coordinate $j$ from \texttt{d1} to \texttt{d2-1},
+ the shift $d_{j}$ is generated uniformly over $[0, 1)$ and added modulo $1$ to
+ all the coordinates of all the points.
+% After adding a digital shift, all iterators must be reconstructed or
+% reset to zero.
+\end{tabb}
+\begin{htmlonly}
+ \param{d1}{lower dimension of shift}
+ \param{d2}{upper dimension of shift is d2 - 1}
+ \param{stream}{random number stream used to generate uniforms}
+\end{htmlonly}
+\begin{code}
+
+ public void clearRandomShift() \begin{hide} {
+ super.clearRandomShift();
+ shift = null;
+ }\end{hide}
+\end{code}
+\begin{tabb} Clears the random shift.
+\end{tabb}
+\begin{code}
+ \begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Rank1Lattice:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+
+ public double getCoordinate (int i, int j) {
+ double x = (v[j] * i) % 1.0;
+ if (shift != null) {
+ if (j >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, j + 1, shiftStream);
+ x += shift[j];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ return x;
+ }
+
+
+ // Recursive method that computes a^e mod m.
+ protected long modPower (long a, int e, int m) {
+ // If parameters a and m == numPoints could be omitted, then
+ // the routine would run much faster due to reduced stack usage.
+ // Note that a can be larger than m, e.g. in lattice sequences !
+
+ if (e == 0)
+ return 1;
+ else if (e == 1)
+ return a % m;
+ else if ((e & 1) == 1)
+ return (a * modPower(a, e - 1, m)) % m;
+ else {
+ long p = modPower(a, e / 2, m);
+ return (p * p) % m;
+ }
+ }
+
+ protected double radicalInverse (int base, int i) {
+ double digit, radical, inverse;
+ digit = radical = 1.0 / (double) base;
+ for (inverse = 0.0; i > 0; i /= base) {
+ inverse += digit * (double) (i % base);
+ digit *= radical;
+ }
+ return inverse;
+ }
+
+ public PointSetIterator iterator() {
+ return new Rank1LatticeIterator();
+ }
+
+// ************************************************************************
+
+ protected class Rank1LatticeIterator extends PointSet.DefaultPointSetIterator
+ {
+ public double nextCoordinate() {
+ // I tried with long's and with double's. The double version is
+ // 4.5 times faster than the long version.
+ if (curPointIndex >= numPoints || curCoordIndex >= dim)
+ outOfBounds();
+// return (curPointIndex * v[curCoordIndex++]) % 1.0;
+ double x = (curPointIndex * v[curCoordIndex]) % 1.0;
+ if (shift != null) {
+ if (curCoordIndex >= dimShift) // Extend the shift.
+ addRandomShift (dimShift, curCoordIndex + 1, shiftStream);
+ x += shift[curCoordIndex];
+ if (x >= 1.0)
+ x -= 1.0;
+ if (x <= 0.0)
+ x = EpsilonHalf; // avoid x = 0
+ }
+ curCoordIndex++;
+ return x;
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/SMScrambleShift.java b/source/umontreal/iro/lecuyer/hups/SMScrambleShift.java
new file mode 100644
index 0000000..a9c4705
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/SMScrambleShift.java
@@ -0,0 +1,92 @@
+
+
+/*
+ * Class: SMScrambleShift
+ * Description: Performs a striped matrix scrambling with a digital shift
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import java.lang.IllegalArgumentException;
+
+
+/**
+ * This class implements a
+ * {@link umontreal.iro.lecuyer.hups.PointSetRandomization PointSetRandomization}
+ * that performs a striped matrix scrambling and adds a random
+ * digital shift. Point set must be a
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet DigitalNet} or an
+ * {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ *
+ */
+public class SMScrambleShift extends RandomShift {
+
+
+
+ /**
+ * Empty constructor.
+ *
+ */
+ public SMScrambleShift() {
+ }
+
+
+
+ /**
+ * Sets internal variable <TT>stream</TT> to the given
+ * <TT>stream</TT>.
+ *
+ * @param stream stream to use in the randomization
+ *
+ */
+ public SMScrambleShift (RandomStream stream) {
+ super(stream);
+ }
+
+
+
+ /**
+ * This method calls
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet#stripedMatrixScramble(RandomStream) stripedMatrixScramble},
+ * then
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet#addRandomShift(RandomStream) addRandomShift}.
+ * If <TT>p</TT> is not a
+ * {@link umontreal.iro.lecuyer.hups.DigitalNet DigitalNet}, an
+ * {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ *
+ * @param p Point set to randomize
+ *
+ *
+ */
+ public void randomize (PointSet p) {
+ if(p instanceof DigitalNet){
+ ((DigitalNet)p).stripedMatrixScramble (stream);
+ ((DigitalNet)p).addRandomShift (stream);
+ }else{
+ throw new IllegalArgumentException("SMScrambleShift"+
+ " can only randomize a DigitalNet");
+ }
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/hups/SMScrambleShift.tex b/source/umontreal/iro/lecuyer/hups/SMScrambleShift.tex
new file mode 100644
index 0000000..408e83e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/SMScrambleShift.tex
@@ -0,0 +1,105 @@
+\defmodule{SMScrambleShift}
+
+This class implements a
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetRandomization}
+that performs a striped matrix scrambling and adds a random
+digital shift. Point set must be a
+\externalclass{umontreal.iro.lecuyer.hups}{DigitalNet} or an
+\externalclass{java.lang}{IllegalArgumentException} is thrown.
+
+\bigskip\hrule\bigskip
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: SMScrambleShift
+ * Description: Performs a striped matrix scrambling with a digital shift
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;
+\begin{hide}
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import java.lang.IllegalArgumentException;
+\end{hide}
+
+public class SMScrambleShift extends RandomShift \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public SMScrambleShift() \begin{hide} {
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Empty constructor.
+\end{tabb}
+\begin{code}
+
+ public SMScrambleShift (RandomStream stream) \begin{hide} {
+ super(stream);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Sets internal variable \texttt{stream} to the given
+ \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{stream to use in the randomization}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public void randomize (PointSet p) \begin{hide} {
+ if(p instanceof DigitalNet){
+ ((DigitalNet)p).stripedMatrixScramble (stream);
+ ((DigitalNet)p).addRandomShift (stream);
+ }else{
+ throw new IllegalArgumentException("SMScrambleShift"+
+ " can only randomize a DigitalNet");
+ }
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ This method calls
+ \externalmethod{umontreal.iro.lecuyer.hups}{DigitalNet}{stripedMatrixScramble}{RandomStream},
+ then
+ \externalmethod{umontreal.iro.lecuyer.hups}{DigitalNet}{addRandomShift}{RandomStream}.
+ If \texttt{p} is not a
+ \externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}, an
+\externalclass{java.lang}{IllegalArgumentException} is thrown.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{Point set to randomize}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/SobolSequence.java b/source/umontreal/iro/lecuyer/hups/SobolSequence.java
new file mode 100644
index 0000000..ad283e9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/SobolSequence.java
@@ -0,0 +1,924 @@
+
+
+/*
+ * Class: SobolSequence
+ * Description: Sobol sequences
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.*;
+import java.net.MalformedURLException;
+
+
+/**
+ * This class implements digital nets or digital sequences in base 2 formed by
+ * the first <SPAN CLASS="MATH"><I>n</I> = 2<SUP>k</SUP></SPAN> points of a Sobol' sequence.
+ * Values of <SPAN CLASS="MATH"><I>n</I></SPAN> up to <SPAN CLASS="MATH">2<SUP>30</SUP></SPAN> are allowed.
+ * <P>
+ * In Sobol's proposal, the generator matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> are upper triangular
+ * matrices defined by a set of <SPAN CLASS="textit">direction numbers</SPAN>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>v</I><SUB>j, c</SUB> = <I>m</I><SUB>j, c</SUB>2<SUP>-c</SUP> = ∑<SUB>l=1</SUB><SUP>c</SUP><I>v</I><SUB>j, c, l</SUB>2<SUP>-l</SUP>,
+ * </DIV><P></P>
+ * where each <SPAN CLASS="MATH"><I>m</I><SUB>j, c</SUB></SPAN> is an <SPAN CLASS="textit">odd</SPAN> integer smaller than <SPAN CLASS="MATH">2<SUP>c</SUP></SPAN>,
+ * for
+ * <SPAN CLASS="MATH"><I>c</I> = 1,..., <I>k</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>j</I> = 0,..., <I>s</I> - 1</SPAN>.
+ * The digit <SPAN CLASS="MATH"><I>v</I><SUB>j, c, l</SUB></SPAN> is the element <SPAN CLASS="MATH">(<I>l</I>, <I>c</I>)</SPAN> of
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>,
+ * so <SPAN CLASS="MATH"><I>v</I><SUB>j, c</SUB></SPAN> represents column <SPAN CLASS="MATH"><I>c</I></SPAN> of
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN>.
+ * One can also write
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>m</I><SUB>j, c</SUB> = ∑<SUB>l=1</SUB><SUP>c</SUP><I>v</I><SUB>j, c, l</SUB>2<SUP>c-l</SUP>,
+ * </DIV><P></P>
+ * so column <SPAN CLASS="MATH"><I>c</I></SPAN> of
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> contains the <SPAN CLASS="MATH"><I>c</I></SPAN> digits of the binary expansion
+ * of <SPAN CLASS="MATH"><I>m</I><SUB>j, c</SUB></SPAN>, from the most to the least significant,
+ * followed by <SPAN CLASS="MATH"><I>w</I> - <I>c</I></SPAN> zeros, where <SPAN CLASS="MATH"><I>w</I></SPAN> is the number of output digits.
+ * Since each <SPAN CLASS="MATH"><I>m</I><SUB>j, c</SUB></SPAN> is odd, the first <SPAN CLASS="MATH"><I>k</I></SPAN> rows of each
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> form
+ * a non-singular upper triangular matrix whose diagonal elements
+ * are all ones.
+ *
+ * <P>
+ * For each dimension <SPAN CLASS="MATH"><I>j</I></SPAN>, the integers <SPAN CLASS="MATH"><I>m</I><SUB>j, c</SUB></SPAN> are defined by
+ * selecting a primitive polynomial over
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2</SUB></SPAN> of degree <SPAN CLASS="MATH"><I>c</I><SUB>j</SUB></SPAN>,
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I><SUB>j</SUB>(<I>z</I>) = <I>z</I><SUP>c<SUB>j</SUB></SUP> + <I>a</I><SUB>j, 1</SUB><I>z</I><SUP>c<SUB>j</SUB>-1</SUP> + <SUP> ... </SUP> + <I>a</I><SUB>j, c<SUB>j</SUB></SUB>,
+ * </DIV><P></P>
+ * and the first <SPAN CLASS="MATH"><I>c</I><SUB>j</SUB></SPAN> integers
+ * <SPAN CLASS="MATH"><I>m</I><SUB>j, 0</SUB>,..., <I>m</I><SUB>j, c<SUB>j</SUB>-1</SUB></SPAN>.
+ * Then the following integers
+ * <SPAN CLASS="MATH"><I>m</I><SUB>j, c<SUB>j</SUB></SUB>, <I>m</I><SUB>j, c<SUB>j</SUB>+1</SUB>,...</SPAN> are
+ * determined by the recurrence
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>m</I><SUB>j, c</SUB> = 2<I>a</I><SUB>j, 1</SUB><I>m</I><SUB>j, c-1</SUB>⊕<SUP> ... </SUP>⊕2<SUP>c<SUB>j</SUB>-1</SUP><I>a</I><SUB>j, c<SUB>j</SUB>-1</SUB><I>m</I><SUB>j, c-c<SUB>j</SUB>+1</SUB>⊕2<SUP>c<SUB>j</SUB></SUP><I>m</I><SUB>j, c-c<SUB>j</SUB></SUB>⊕<I>m</I><SUB>j, c-c<SUB>j</SUB></SUB>
+ * </DIV><P></P>
+ * for <SPAN CLASS="MATH"><I>c</I> >= <I>c</I><SUB>j</SUB></SPAN>, or equivalently,
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>v</I><SUB>j, c, l</SUB> = <I>a</I><SUB>j, 1</SUB><I>v</I><SUB>j, c-1, l</SUB>⊕<SUP> ... </SUP>⊕<I>a</I><SUB>j, c<SUB>j</SUB>-1</SUB><I>v</I><SUB>j, c-c<SUB>j</SUB>+1, l</SUB>⊕<I>v</I><SUB>j, c-c<SUB>j</SUB>, l</SUB>⊕<I>v</I><SUB>j, c-c<SUB>j</SUB>, l+c<SUB>j</SUB></SUB>
+ * </DIV><P></P>
+ * for <SPAN CLASS="MATH"><I>l</I> >= 0</SPAN>, where <SPAN CLASS="MATH">⊕</SPAN> means bitwise exclusive or
+ * (i.e., bitwise addition modulo 2).
+ * Sobol' has shown that with this construction, if the
+ * primitive polynomials <SPAN CLASS="MATH"><I>f</I><SUB>j</SUB>(<I>z</I>)</SPAN> are all distinct, one obtains
+ * a <SPAN CLASS="MATH">(<I>t</I>, <I>s</I>)</SPAN>-sequence whose <SPAN CLASS="MATH"><I>t</I></SPAN>-value does not exceed
+ *
+ * <SPAN CLASS="MATH"><I>c</I><SUB>0</SUB> + <SUP> ... </SUP> + <I>c</I><SUB>s-1</SUB> + 1 - <I>s</I></SPAN>.
+ * He then suggested to list the set of all primitive polynomials over
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2</SUB></SPAN> by increasing order of degree, starting with
+ * <SPAN CLASS="MATH"><I>f</I><SUB>0</SUB>(<I>z</I>)≡1</SPAN>
+ * (whose corresponding matrix
+ * <SPAN CLASS="MATH"><B>C</B><SUB>0</SUB></SPAN> is the identity), and take <SPAN CLASS="MATH"><I>f</I><SUB>j</SUB>(<I>z</I>)</SPAN>
+ * as the <SPAN CLASS="MATH">(<I>j</I> + 1)</SPAN>th polynomial in the list, for <SPAN CLASS="MATH"><I>j</I> >= 0</SPAN>.
+ *
+ * <P>
+ * This list of primitive polynomials, as well as default choices
+ * for the direction numbers, are stored in precomputed tables.
+ * The ordered list of primitive polynomials was taken from Florent Chabaud's web site,
+ * at <TT><A NAME="tex2html1"
+ * HREF="http://fchabaud.free.fr/">http://fchabaud.free.fr/</A></TT>.
+ * Each polynomial <SPAN CLASS="MATH"><I>f</I><SUB>j</SUB>(<I>z</I>)</SPAN> is stored in the form of the integer
+ *
+ * <SPAN CLASS="MATH">2<SUP>c<SUB>j</SUB></SUP> + <I>a</I><SUB>j, 1</SUB>2<SUP>c<SUB>j</SUB>-1</SUP> + <SUP> ... </SUP> + <I>a</I><SUB>j, c<SUB>j</SUB></SUB></SPAN>, whose binary
+ * representation gives the polynomial coefficients.
+ *
+ * <P>
+ * For the set of direction numbers, there are several possibilities
+ * based on different selection criteria.
+ * The original values proposed by Sobol' and implemented in the code of
+ * Bratley and Fox for <SPAN CLASS="MATH"><I>j</I> <= 40</SPAN>
+ * were selected in terms of his properties <SPAN CLASS="MATH"><I>A</I></SPAN> and <SPAN CLASS="MATH"><I>A'</I></SPAN>,
+ * which are equivalent to <SPAN CLASS="MATH"><I>s</I></SPAN>-distribution with one and
+ * two bits of accuracy, respectively.
+ * The default direction numbers used here have been taken from
+ * Lemieux et al.
+ * For <SPAN CLASS="MATH"><I>j</I> <= 40</SPAN>, they are the same as in
+ * Bratley and Fox.
+ * Several files of parameters for Sobol sequences are given on F. Kuo's
+ * Web site at <TT><A NAME="tex2html2"
+ * HREF="http://web.maths.unsw.edu.au/~fkuo/sobol/index.html">http://web.maths.unsw.edu.au/~fkuo/sobol/index.html</A></TT>.
+ *
+ */
+public class SobolSequence extends DigitalSequenceBase2 {
+
+ // Maximal dimension for primitive polynomials included in this file
+ protected static final int MAXDIM = 360;
+ protected static final int MAXDEGREE = 18; // Of primitive polynomial
+ private String filename = null;
+
+
+
+ /**
+ * Constructs a new digital net with <SPAN CLASS="MATH"><I>n</I> = 2<SUP>k</SUP></SPAN> points and <SPAN CLASS="MATH"><I>w</I></SPAN>
+ * output digits, in dimension <TT>dim</TT>, formed by taking the first
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> points of the Sobol' sequence.
+ * The predefined generator matrices
+ * <SPAN CLASS="MATH"><B>C</B><SUB>j</SUB></SPAN> are <SPAN CLASS="MATH"><I>w</I>×<I>k</I></SPAN>. Restrictions:
+ * <SPAN CLASS="MATH">0 <= <I>k</I> <= 30</SPAN>, <SPAN CLASS="MATH"><I>k</I> <= <I>w</I></SPAN> and <TT>dim</TT> <SPAN CLASS="MATH"> <= 360</SPAN>.
+ *
+ * @param k there will be 2^k points
+ *
+ * @param w number of output digits
+ *
+ * @param dim dimension of the point set
+ *
+ *
+ */
+ public SobolSequence (int k, int w, int dim) {
+ init (k, w, w, dim);
+ }
+
+ private void init (int k, int r, int w, int dim) {
+ if (filename == null)
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for SobolSequence must be > 0 and <= " + MAXDIM);
+ else
+ if (dim < 1)
+ throw new IllegalArgumentException
+ ("Dimension for SobolSequence must be > 0");
+
+ if (r < k || w < r || w > MAXBITS || k >= MAXBITS)
+ throw new IllegalArgumentException
+ ("One must have k < 31 and k <= r <= w <= 31 for SobolSequence");
+ numCols = k;
+ numRows = r; // Not used!
+ outDigits = w;
+ numPoints = (1 << k);
+ this.dim = dim;
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+ genMat = new int[dim * numCols];
+ initGenMat();
+ }
+
+
+
+ /**
+ * Constructs a Sobol point set with <SPAN CLASS="textit">at least</SPAN> <TT>n</TT> points and 31
+ * output digits, in dimension <TT>dim</TT>. Equivalent to
+ * <TT>SobolSequence (k, 31, dim)</TT> with
+ * <SPAN CLASS="MATH"><I>k</I> = ceil(log<SUB>2</SUB><I>n</I>)</SPAN>.
+ *
+ * @param dim dimension of the point set
+ *
+ * @param n minimal number of points
+ *
+ *
+ */
+ public SobolSequence (int n, int dim) {
+ numCols = MAXBITS; // Defined in PointSet.
+ while (((n >> numCols) & 1) == 0)
+ numCols--;
+ if (1 << numCols != n)
+ numCols++;
+ init (numCols, MAXBITS, MAXBITS, dim);
+ }
+
+
+ /**
+ * Constructs a new digital net using the direction numbers provided in file
+ * <TT>filename</TT>. The net has <SPAN CLASS="MATH"><I>n</I> = 2<SUP>k</SUP></SPAN> points, <SPAN CLASS="MATH"><I>w</I></SPAN> output digits and
+ * dimension <TT>dim</TT>. The file can be either on the user's host, or
+ * somewhere on the Internet: in that case, the full <SPAN CLASS="textbf">url</SPAN> address must
+ * be given using either the <SPAN CLASS="textit">http</SPAN> or <SPAN CLASS="textit">ftp</SPAN> protocol. For
+ * example:
+ *
+ * <P>
+ * <TT>net = new SobolSequence(
+ * <BR> "http://web.maths.unsw.edu.au/˜fkuo/sobol/joe-kuo-6.16900", k, w, dim);</TT>
+ *
+ * <P>
+ * The file must have the following format
+ * (the first line is treated as a comment by the program and discarded):
+ * <DIV ALIGN="CENTER">
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER">dim</TD>
+ * <TD ALIGN="LEFT"><SPAN CLASS="MATH"><I>s</I></SPAN></TD>
+ * <TD ALIGN="LEFT"><SPAN CLASS="MATH"><I>a</I></SPAN></TD>
+ * <TD ALIGN="LEFT"><SPAN CLASS="MATH"><I>m</I><SUB>i</SUB></SPAN></TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">2</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * <TD ALIGN="LEFT">0</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">3</TD>
+ * <TD ALIGN="LEFT">2</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * <TD ALIGN="LEFT">1 3</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">4</TD>
+ * <TD ALIGN="LEFT">3</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * <TD ALIGN="LEFT">1 3 1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">5</TD>
+ * <TD ALIGN="LEFT">3</TD>
+ * <TD ALIGN="LEFT">2</TD>
+ * <TD ALIGN="LEFT">1 1 1</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">6</TD>
+ * <TD ALIGN="LEFT">4</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * <TD ALIGN="LEFT">1 1 3 3</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER">7</TD>
+ * <TD ALIGN="LEFT">4</TD>
+ * <TD ALIGN="LEFT">4</TD>
+ * <TD ALIGN="LEFT">1 3 5 13</TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER"> </TD>
+ * <TD ALIGN="LEFT">⋮</TD>
+ * <TD ALIGN="LEFT">⋮</TD>
+ * <TD ALIGN="LEFT"> </TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ * dim is the
+ * dimension, <SPAN CLASS="MATH"><I>s</I></SPAN> the degree of the polynomial, the binary representation of <SPAN CLASS="MATH"><I>a</I></SPAN>
+ * gives the inner coefficients of the polynomial
+ * (the first and the last coefficients are always 1), and <SPAN CLASS="MATH"><I>m</I><SUB>i</SUB></SPAN> are the
+ * direction numbers. Thus if
+ * <SPAN CLASS="MATH"><I>a</I> = (<I>a</I><SUB>1</SUB><I>a</I><SUB>2</SUB>…<I>a</I><SUB>s-1</SUB>)<SUB>2</SUB></SPAN> for a given <SPAN CLASS="MATH"><I>s</I></SPAN>,
+ * then the polynomial is
+ * <SPAN CLASS="MATH"><I>x</I><SUP>s</SUP> + <I>a</I><SUB>1</SUB><I>x</I><SUP>s-1</SUP> + <I>a</I><SUB>2</SUB><I>x</I><SUP>s-2</SUP> + <SUP> ... </SUP> + <I>a</I><SUB>s-1</SUB><I>x</I> + 1</SPAN>.
+ * For example, if <SPAN CLASS="MATH"><I>s</I> = 4</SPAN> and
+ * <SPAN CLASS="MATH"><I>a</I> = 4 = 100<SUB>2</SUB></SPAN>, then the
+ * polynomial is
+ * <SPAN CLASS="MATH"><I>x</I><SUP>4</SUP> + <I>x</I><SUP>3</SUP> + 1</SPAN>.
+ *
+ * @param k number of points is <SPAN CLASS="MATH">2<SUP>k</SUP></SPAN>
+ *
+ * @param w number of output digits
+ *
+ * @param dim dimension of the point set
+ *
+ * @param filename file containing the direction numbers
+ *
+ *
+ */
+ public SobolSequence (String filename, int k, int w, int dim) {
+
+ poly_from_file = new int[dim];
+ for (int i = 0; i < dim; i++)
+ poly_from_file[i] = 0;
+
+ minit_from_file = new int[dim][MAXDEGREE];
+ for (int i = 0; i < dim; i++) {
+ for (int j = 0; j < MAXDEGREE; j++) {
+ minit_from_file[i][j] = 0;
+ }
+ }
+
+ // Dimension d = 1
+ int d = 1;
+ poly_from_file[d - 1] = 1;
+
+ // Read the direction number file up to a certain number of dimension dim
+ try {
+ // If filename can be found starting from the program's directory,
+ // it will be used; otherwise, the filename in the Jar archive will
+ // be used.
+ int prev_d = 1;
+
+ BufferedReader reader;
+
+ if (filename.startsWith("http") || filename.startsWith("ftp"))
+ reader = DigitalNetFromFile.openURL(filename);
+ else
+ reader = new BufferedReader(new FileReader(filename));
+
+ // First line of file is a comment; discard it
+ String line = reader.readLine();
+ String[] tokens;
+
+ while ((line = reader.readLine()) != null) {
+ tokens = line.split("[\t ]+");
+
+ // Direction number lines from dimension 2 and up
+ if (tokens.length < 4) {
+ System.err.println("\nBad direction number file format!\n");
+ System.exit(1);
+ }
+
+ // Parse dim d, polynomial degree s and coefficients a
+ d = Integer.parseInt(tokens[0]);
+
+ int s = Integer.parseInt(tokens[1]);
+ int a = Integer.parseInt(tokens[2]);
+
+ if (s + 3 != tokens.length) {
+ System.err.println("\nBad direction number file format!\n");
+ System.exit(1);
+ }
+
+ if (d != prev_d + 1) {
+ System.err.println("Dimension in file shall be in ");
+ System.err.println("increasing order, one per line!");
+ System.exit(1);
+ }
+ prev_d = d;
+
+ // If d in the file exceeds dim, stop reading!
+ if (d > dim)
+ break;
+
+ poly_from_file[d - 1] = (1 << s) ^ (a << 1) ^ 1;
+
+ // Parse the s direction numbers
+ for (int i = 0; i < s; i++)
+ minit_from_file[d - 2][i] = Integer.parseInt(tokens[i + 3]);
+ } // end while
+
+ } catch (MalformedURLException e) {
+ System.err.println (" Invalid URL address: " + filename);
+ System.exit(1);
+
+ } catch (IOException e) {
+ System.err.println("Error: " + e);
+ System.exit(1);
+ }
+
+ if (dim > d) {
+ System.err.printf("\n\nNot enough dimension in file: %s", filename);
+ System.exit(1);
+ }
+
+ this.filename = filename;
+
+ init(k, w, w, dim);
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Sobol sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+
+ public void extendSequence (int k) {
+ int start, degree, nextCol;
+ int i, j, c;
+ int oldNumCols = numCols;
+ int[] oldGenMat = genMat; // Save old generating matrix.
+
+ numCols = k;
+ numPoints = (1 << k);
+ genMat = new int[dim * numCols];
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-c-1));
+
+ // the other dimensions j > 0.
+ for (j = 1; j < dim; j++) {
+ // find the degree of primitive polynomial f_j
+ for (degree = MAXDEGREE; ((poly[j] >> degree) & 1) == 0; degree--)
+ ;
+ // Get initial direction numbers m_{j,0},..., m_{j,degree-1}.
+ start = j * numCols;
+ for (c = 0; (c < degree && c < numCols); c++)
+ genMat[start+c] = minit[j-1][c] << (outDigits-c-1);
+
+ // Compute the following ones via the recursion.
+ for (c = degree; c < numCols; c++) {
+ if (c < oldNumCols)
+ genMat[start+c] = oldGenMat[j*oldNumCols + c];
+ else {
+ nextCol = genMat[start+c-degree] >> degree;
+ for (i = 0; i < degree; i++)
+ if (((poly[j] >> i) & 1) == 1)
+ nextCol ^= genMat[start+c-degree+i];
+ genMat[start+c] = nextCol;
+ }
+ }
+ }
+ }
+
+
+ // Initializes the generator matrices for a sequence.
+ private void initGenMat() {
+ int start, degree, nextCol;
+ int i, j, c;
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-c-1));
+
+ // the other dimensions j > 0.
+ for (j = 1; j < dim; j++) {
+ // if a direction number file was provided, use it
+ int polynomial = (filename != null ? poly_from_file[j] : poly[j]);
+ // find the degree of primitive polynomial f_j
+ for (degree = MAXDEGREE; ((polynomial >> degree) & 1) == 0; degree--)
+ ;
+ // Get initial direction numbers m_{j,0},..., m_{j,degree-1}.
+ start = j * numCols;
+ for (c = 0; (c < degree && c < numCols); c++) {
+ int m_i = (filename != null ?
+ minit_from_file[j-1][c] : minit[j-1][c]);
+ genMat[start+c] = m_i << (outDigits-c-1);
+ }
+
+ // Compute the following ones via the recursion.
+ for (c = degree; c < numCols; c++) {
+ nextCol = genMat[start+c-degree] >> degree;
+ for (i = 0; i < degree; i++)
+ if (((polynomial >> i) & 1) == 1)
+ nextCol ^= genMat[start+c-degree+i];
+ genMat[start+c] = nextCol;
+ }
+ }
+ }
+
+/*
+ // Initializes the generator matrices for a net.
+ protected void initGenMatNet() {
+ int start, degree, nextCol;
+ int i, j, c;
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-numCols+c));
+
+ // the second dimension, j = 1.
+ for (c = 0; c < numCols; c++)
+ genMat[numCols+c] = (1 << (outDigits-c-1));
+
+ // the other dimensions j > 1.
+ for (j = 2; j < dim; j++) {
+ // find the degree of primitive polynomial f_j
+ for (degree = MAXDEGREE; ((poly[j-1] >> degree) & 1) == 0; degree--);
+ // Get initial direction numbers m_{j,0},..., m_{j,degree-1}.
+ start = j * numCols;
+ for (c = 0; (c < degree && c < numCols); c++)
+ genMat[start+c] = minit[j-2][c] << (outDigits-c-1);
+
+ // Compute the following ones via the recursion.
+ for (c = degree; c < numCols; c++) {
+ nextCol = genMat[start+c-degree] >> degree;
+ for (i = 0; i < degree; i++)
+ if (((poly[j-1] >> i) & 1) == 1)
+ nextCol ^= genMat[start+c-degree+i];
+ genMat[start+c] = nextCol;
+ }
+ }
+ }
+*/
+
+ // *******************************************************
+ // The ordered list of first MAXDIM primitive polynomials.
+
+ protected int[] poly_from_file;
+
+ protected static final int[] poly = {
+ 1, 3, 7, 11, 13, 19, 25, 37, 59,
+ 47, 61, 55, 41, 67, 97, 91, 109, 103,
+ 115, 131, 193, 137, 145, 143, 241, 157, 185,
+ 167, 229, 171, 213, 191, 253, 203, 211, 239,
+ 247, 285, 369, 299, 425, 301, 361, 333, 357,
+ 351, 501, 355, 397, 391, 451, 463, 487, 529,
+ 545, 539, 865, 557, 721, 563, 817, 601, 617,
+ 607, 1001, 623, 985, 631, 953, 637, 761, 647,
+ 901, 661, 677, 675, 789, 687, 981, 695, 949,
+ 701, 757, 719, 973, 731, 877, 787, 803, 799,
+ 995, 827, 883, 847, 971, 859, 875, 895, 1019,
+ 911, 967, 1033, 1153, 1051, 1729, 1063, 1825, 1069,
+ 1441, 1125, 1329, 1135, 1969, 1163, 1673, 1221, 1305,
+ 1239, 1881, 1255, 1849, 1267, 1657, 1279, 2041, 1293,
+ 1413, 1315, 1573, 1341, 1509, 1347, 1557, 1367, 1877,
+ 1387, 1717, 1423, 1933, 1431, 1869, 1479, 1821, 1527,
+ 1917, 1531, 1789, 1555, 1603, 1591, 1891, 1615, 1939,
+ 1627, 1747, 1663, 2035, 1759, 2011, 1815, 1863, 2053,
+ 2561, 2071, 3713, 2091, 3393, 2093, 2881, 2119, 3617,
+ 2147, 3169, 2149, 2657, 2161, 2273, 2171, 3553, 2189,
+ 2833, 2197, 2705, 2207, 3985, 2217, 2385, 2225, 2257,
+ 2255, 3889, 2279, 3697, 2283, 3441, 2293, 2801, 2317,
+ 2825, 2323, 3209, 2341, 2633, 2345, 2377, 2363, 3529,
+ 2365, 3017, 2373, 2601, 2395, 3497, 2419, 3305, 2421,
+ 2793, 2431, 4073, 2435, 3097, 2447, 3865, 2475, 3417,
+ 2477, 2905, 2489, 2521, 2503, 3641, 2533, 2681, 2551,
+ 3833, 2567, 3589, 2579, 3205, 2581, 2693, 2669, 2917,
+ 2687, 4069, 2717, 2965, 2727, 3669, 2731, 3413, 2739,
+ 3285, 2741, 2773, 2783, 4021, 2799, 3957, 2811, 3573,
+ 2819, 3085, 2867, 3277, 2879, 4045, 2891, 3373, 2911,
+ 4013, 2927, 3949, 2941, 3053, 2951, 3613, 2955, 3357,
+ 2963, 3229, 2991, 3933, 2999, 3805, 3005, 3037, 3035,
+ 3517, 3047, 3709, 3083, 3331, 3103, 3971, 3159, 3747,
+ 3179, 3427, 3187, 3299, 3223, 3731, 3227, 3475, 3251,
+ 3283, 3263, 4051, 3271, 3635, 3319, 3827, 3343, 3851,
+ 3367, 3659, 3399, 3627, 3439, 3947, 3487, 3995, 3515,
+ 3547, 3543, 3771, 3559, 3707, 3623, 3655, 3679, 4007,
+ 3743, 3991, 3791, 3895, 4179, 6465, 4201, 4801, 4219,
+ 7105, 4221, 6081, 4249, 4897, 4305, 4449, 4331, 6881,
+ 4359, 7185, 4383, 7953, 4387, 6289, 4411, 7057, 4431
+ };
+
+
+ // The direction numbers. For j > 0 and c < c_j,
+ // minit[j-1][c] contains the integer m_{j,c}.
+ // The values for j=0 are not stored, since C_0 is the identity matrix.
+
+ protected int minit_from_file[][];
+
+ protected static final int minit[][] = {
+ {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 3, 7, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 3, 9, 9, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 7, 13, 3, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 5, 11, 27, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 5, 1, 15, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 7, 3, 29, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 7, 7, 21, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 1, 9, 23, 37, 0, 0, 0, 0, 0, 0},
+ {1, 3, 3, 5, 19, 33, 0, 0, 0, 0, 0, 0},
+ {1, 1, 3, 13, 11, 7, 0, 0, 0, 0, 0, 0},
+ {1, 1, 7, 13, 25, 5, 0, 0, 0, 0, 0, 0},
+ {1, 3, 5, 11, 7, 11, 0, 0, 0, 0, 0, 0},
+ {1, 1, 1, 3, 13, 39, 0, 0, 0, 0, 0, 0},
+ {1, 3, 1, 15, 17, 63, 13, 0, 0, 0, 0, 0},
+ {1, 1, 5, 5, 1, 27, 33, 0, 0, 0, 0, 0},
+ {1, 3, 3, 3, 25, 17, 115, 0, 0, 0, 0, 0},
+ {1, 1, 3, 15, 29, 15, 41, 0, 0, 0, 0, 0},
+ {1, 3, 1, 7, 3, 23, 79, 0, 0, 0, 0, 0},
+ {1, 3, 7, 9, 31, 29, 17, 0, 0, 0, 0, 0},
+ {1, 1, 5, 13, 11, 3, 29, 0, 0, 0, 0, 0},
+ {1, 3, 1, 9, 5, 21, 119, 0, 0, 0, 0, 0},
+ {1, 1, 3, 1, 23, 13, 75, 0, 0, 0, 0, 0},
+ {1, 3, 3, 11, 27, 31, 73, 0, 0, 0, 0, 0},
+ {1, 1, 7, 7, 19, 25, 105, 0, 0, 0, 0, 0},
+ {1, 3, 5, 5, 21, 9, 7, 0, 0, 0, 0, 0},
+ {1, 1, 1, 15, 5, 49, 59, 0, 0, 0, 0, 0},
+ {1, 1, 1, 1, 1, 33, 65, 0, 0, 0, 0, 0},
+ {1, 3, 5, 15, 17, 19, 21, 0, 0, 0, 0, 0},
+ {1, 1, 7, 11, 13, 29, 3, 0, 0, 0, 0, 0},
+ {1, 3, 7, 5, 7, 11, 113, 0, 0, 0, 0, 0},
+ {1, 1, 5, 3, 15, 19, 61, 0, 0, 0, 0, 0},
+ {1, 3, 1, 1, 9, 27, 89, 7, 0, 0, 0, 0},
+ {1, 1, 3, 7, 31, 15, 45, 23, 0, 0, 0, 0},
+ {1, 3, 3, 9, 9, 25, 107, 39, 0, 0, 0, 0},
+ {1, 1, 3, 13, 7, 35, 61, 91, 0, 0, 0, 0},
+ {1, 1, 7, 11, 5, 35, 55, 75, 0, 0, 0, 0},
+ {1, 3, 5, 5, 11, 23, 29, 139, 0, 0, 0, 0},
+ {1, 1, 1, 7, 11, 15, 17, 81, 0, 0, 0, 0},
+ {1, 1, 7, 9, 5, 57, 79, 103, 0, 0, 0, 0},
+ {1, 1, 7, 13, 19, 5, 5, 185, 0, 0, 0, 0},
+ {1, 3, 1, 3, 13, 57, 97, 131, 0, 0, 0, 0},
+ {1, 1, 5, 5, 21, 25, 125, 197, 0, 0, 0, 0},
+ {1, 3, 3, 9, 31, 11, 103, 201, 0, 0, 0, 0},
+ {1, 1, 5, 3, 7, 25, 51, 121, 0, 0, 0, 0},
+ {1, 3, 7, 15, 19, 53, 73, 189, 0, 0, 0, 0},
+ {1, 1, 1, 15, 19, 55, 27, 183, 0, 0, 0, 0},
+ {1, 1, 7, 13, 3, 29, 109, 69, 0, 0, 0, 0},
+ {1, 1, 5, 15, 15, 23, 15, 1, 57, 0, 0, 0},
+ {1, 3, 1, 3, 23, 55, 43, 143, 397, 0, 0, 0},
+ {1, 1, 3, 11, 29, 9, 35, 131, 411, 0, 0, 0},
+ {1, 3, 1, 7, 27, 39, 103, 199, 277, 0, 0, 0},
+ {1, 3, 7, 3, 19, 55, 127, 67, 449, 0, 0, 0},
+ {1, 3, 7, 3, 5, 29, 45, 85, 3, 0, 0, 0},
+ {1, 3, 5, 5, 13, 23, 75, 245, 453, 0, 0, 0},
+ {1, 3, 1, 15, 21, 47, 3, 77, 165, 0, 0, 0},
+ {1, 1, 7, 9, 15, 5, 117, 73, 473, 0, 0, 0},
+ {1, 3, 1, 9, 1, 21, 13, 173, 313, 0, 0, 0},
+ {1, 1, 7, 3, 11, 45, 63, 77, 49, 0, 0, 0},
+ {1, 1, 1, 1, 1, 25, 123, 39, 259, 0, 0, 0},
+ {1, 1, 1, 5, 23, 11, 59, 11, 203, 0, 0, 0},
+ {1, 3, 3, 15, 21, 1, 73, 71, 421, 0, 0, 0},
+ {1, 1, 5, 11, 15, 31, 115, 95, 217, 0, 0, 0},
+ {1, 1, 3, 3, 7, 53, 37, 43, 439, 0, 0, 0},
+ {1, 1, 1, 1, 27, 53, 69, 159, 321, 0, 0, 0},
+ {1, 1, 5, 15, 29, 17, 19, 43, 449, 0, 0, 0},
+ {1, 1, 3, 9, 1, 55, 121, 205, 255, 0, 0, 0},
+ {1, 1, 3, 11, 9, 47, 107, 11, 417, 0, 0, 0},
+ {1, 1, 1, 5, 17, 25, 21, 83, 95, 0, 0, 0},
+ {1, 3, 5, 13, 31, 25, 61, 157, 407, 0, 0, 0},
+ {1, 1, 7, 9, 25, 33, 41, 35, 17, 0, 0, 0},
+ {1, 3, 7, 15, 13, 39, 61, 187, 461, 0, 0, 0},
+ {1, 3, 7, 13, 5, 57, 23, 177, 435, 0, 0, 0},
+ {1, 1, 3, 15, 11, 27, 115, 5, 337, 0, 0, 0},
+ {1, 3, 7, 3, 15, 63, 61, 171, 339, 0, 0, 0},
+ {1, 3, 3, 13, 15, 61, 59, 47, 1, 0, 0, 0},
+ {1, 1, 5, 15, 13, 5, 39, 83, 329, 0, 0, 0},
+ {1, 1, 5, 5, 5, 27, 25, 39, 301, 0, 0, 0},
+ {1, 1, 5, 11, 31, 41, 35, 233, 27, 0, 0, 0},
+ {1, 3, 5, 15, 7, 37, 119, 171, 419, 0, 0, 0},
+ {1, 3, 5, 5, 3, 29, 21, 189, 417, 0, 0, 0},
+ {1, 1, 1, 1, 21, 41, 117, 119, 351, 0, 0, 0},
+ {1, 1, 3, 1, 7, 27, 87, 19, 213, 0, 0, 0},
+ {1, 1, 1, 1, 17, 7, 97, 217, 477, 0, 0, 0},
+ {1, 1, 7, 1, 29, 61, 103, 231, 269, 0, 0, 0},
+ {1, 1, 7, 13, 9, 27, 107, 207, 311, 0, 0, 0},
+ {1, 1, 7, 5, 25, 21, 107, 179, 423, 0, 0, 0},
+ {1, 3, 5, 11, 7, 1, 17, 245, 281, 0, 0, 0},
+ {1, 3, 5, 9, 1, 5, 53, 59, 125, 0, 0, 0},
+ {1, 1, 7, 1, 31, 57, 71, 245, 125, 0, 0, 0},
+ {1, 1, 7, 5, 5, 57, 53, 253, 441, 0, 0, 0},
+ {1, 3, 1, 13, 19, 35, 119, 235, 381, 0, 0, 0},
+ {1, 3, 1, 7, 19, 59, 115, 33, 361, 0, 0, 0},
+ {1, 1, 3, 5, 13, 1, 49, 143, 501, 0, 0, 0},
+ {1, 1, 3, 5, 1, 63, 101, 85, 189, 0, 0, 0},
+ {1, 1, 5, 11, 27, 63, 13, 131, 5, 0, 0, 0},
+ {1, 1, 5, 7, 15, 45, 75, 59, 455, 585, 0, 0},
+ {1, 3, 1, 3, 7, 7, 111, 23, 119, 959, 0, 0},
+ {1, 3, 3, 9, 11, 41, 109, 163, 161, 879, 0, 0},
+ {1, 3, 5, 1, 21, 41, 121, 183, 315, 219, 0, 0},
+ {1, 1, 3, 9, 15, 3, 9, 223, 441, 929, 0, 0},
+ {1, 1, 7, 9, 3, 5, 93, 57, 253, 457, 0, 0},
+ {1, 1, 7, 13, 15, 29, 83, 21, 35, 45, 0, 0},
+ {1, 1, 3, 7, 13, 61, 119, 219, 85, 505, 0, 0},
+ {1, 1, 3, 3, 17, 13, 35, 197, 291, 109, 0, 0},
+ {1, 1, 3, 3, 5, 1, 113, 103, 217, 253, 0, 0},
+ {1, 1, 7, 1, 15, 39, 63, 223, 17, 9, 0, 0},
+ {1, 3, 7, 1, 17, 29, 67, 103, 495, 383, 0, 0},
+ {1, 3, 3, 15, 31, 59, 75, 165, 51, 913, 0, 0},
+ {1, 3, 7, 9, 5, 27, 79, 219, 233, 37, 0, 0},
+ {1, 3, 5, 15, 1, 11, 15, 211, 417, 811, 0, 0},
+ {1, 3, 5, 3, 29, 27, 39, 137, 407, 231, 0, 0},
+ {1, 1, 3, 5, 29, 43, 125, 135, 109, 67, 0, 0},
+ {1, 1, 1, 5, 11, 39, 107, 159, 323, 381, 0, 0},
+ {1, 1, 1, 1, 9, 11, 33, 55, 169, 253, 0, 0},
+ {1, 3, 5, 5, 11, 53, 63, 101, 251, 897, 0, 0},
+ {1, 3, 7, 1, 25, 15, 83, 119, 53, 157, 0, 0},
+ {1, 3, 5, 13, 5, 5, 3, 195, 111, 451, 0, 0},
+ {1, 3, 1, 15, 11, 1, 19, 11, 307, 777, 0, 0},
+ {1, 3, 7, 11, 5, 5, 17, 231, 345, 981, 0, 0},
+ {1, 1, 3, 3, 1, 33, 83, 201, 57, 475, 0, 0},
+ {1, 3, 7, 7, 17, 13, 35, 175, 499, 809, 0, 0},
+ {1, 1, 5, 3, 3, 17, 103, 119, 499, 865, 0, 0},
+ {1, 1, 1, 11, 27, 25, 37, 121, 401, 11, 0, 0},
+ {1, 1, 1, 11, 9, 25, 25, 241, 403, 3, 0, 0},
+ {1, 1, 1, 1, 11, 1, 39, 163, 231, 573, 0, 0},
+ {1, 1, 1, 13, 13, 21, 75, 185, 99, 545, 0, 0},
+ {1, 1, 1, 15, 3, 63, 69, 11, 173, 315, 0, 0},
+ {1, 3, 5, 15, 11, 3, 95, 49, 123, 765, 0, 0},
+ {1, 1, 1, 15, 3, 63, 77, 31, 425, 711, 0, 0},
+ {1, 1, 7, 15, 1, 37, 119, 145, 489, 583, 0, 0},
+ {1, 3, 5, 15, 3, 49, 117, 211, 165, 323, 0, 0},
+ {1, 3, 7, 1, 27, 63, 77, 201, 225, 803, 0, 0},
+ {1, 1, 1, 11, 23, 35, 67, 21, 469, 357, 0, 0},
+ {1, 1, 7, 7, 9, 7, 25, 237, 237, 571, 0, 0},
+ {1, 1, 3, 15, 29, 5, 107, 109, 241, 47, 0, 0},
+ {1, 3, 5, 11, 27, 63, 29, 13, 203, 675, 0, 0},
+ {1, 1, 3, 9, 9, 11, 103, 179, 449, 263, 0, 0},
+ {1, 3, 5, 11, 29, 63, 53, 151, 259, 223, 0, 0},
+ {1, 1, 3, 7, 9, 25, 5, 197, 237, 163, 0, 0},
+ {1, 3, 7, 13, 5, 57, 67, 193, 147, 241, 0, 0},
+ {1, 1, 5, 15, 15, 33, 17, 67, 161, 341, 0, 0},
+ {1, 1, 3, 13, 17, 43, 21, 197, 441, 985, 0, 0},
+ {1, 3, 1, 5, 15, 33, 33, 193, 305, 829, 0, 0},
+ {1, 1, 1, 13, 19, 27, 71, 187, 477, 239, 0, 0},
+ {1, 1, 1, 9, 9, 17, 41, 177, 229, 983, 0, 0},
+ {1, 3, 5, 9, 15, 45, 97, 205, 43, 767, 0, 0},
+ {1, 1, 1, 9, 31, 31, 77, 159, 395, 809, 0, 0},
+ {1, 3, 3, 3, 29, 19, 73, 123, 165, 307, 0, 0},
+ {1, 3, 1, 7, 5, 11, 77, 227, 355, 403, 0, 0},
+ {1, 3, 5, 5, 25, 31, 1, 215, 451, 195, 0, 0},
+ {1, 3, 7, 15, 29, 37, 101, 241, 17, 633, 0, 0},
+ {1, 1, 5, 1, 11, 3, 107, 137, 489, 5, 0, 0},
+ {1, 1, 1, 7, 19, 19, 75, 85, 471, 355, 0, 0},
+ {1, 1, 3, 3, 9, 13, 113, 167, 13, 27, 0, 0},
+ {1, 3, 5, 11, 21, 3, 89, 205, 377, 307, 0, 0},
+ {1, 1, 1, 9, 31, 61, 65, 9, 391, 141, 867, 0},
+ {1, 1, 1, 9, 19, 19, 61, 227, 241, 55, 161, 0},
+ {1, 1, 1, 11, 1, 19, 7, 233, 463, 171, 1941, 0},
+ {1, 1, 5, 7, 25, 13, 103, 75, 19, 1021, 1063, 0},
+ {1, 1, 1, 15, 17, 17, 79, 63, 391, 403, 1221, 0},
+ {1, 3, 3, 11, 29, 25, 29, 107, 335, 475, 963, 0},
+ {1, 3, 5, 1, 31, 33, 49, 43, 155, 9, 1285, 0},
+ {1, 1, 5, 5, 15, 47, 39, 161, 357, 863, 1039, 0},
+ {1, 3, 7, 15, 1, 39, 47, 109, 427, 393, 1103, 0},
+ {1, 1, 1, 9, 9, 29, 121, 233, 157, 99, 701, 0},
+ {1, 1, 1, 7, 1, 29, 75, 121, 439, 109, 993, 0},
+ {1, 1, 1, 9, 5, 1, 39, 59, 89, 157, 1865, 0},
+ {1, 1, 5, 1, 3, 37, 89, 93, 143, 533, 175, 0},
+ {1, 1, 3, 5, 7, 33, 35, 173, 159, 135, 241, 0},
+ {1, 1, 1, 15, 17, 37, 79, 131, 43, 891, 229, 0},
+ {1, 1, 1, 1, 1, 35, 121, 177, 397, 1017, 583, 0},
+ {1, 1, 3, 15, 31, 21, 43, 67, 467, 923, 1473, 0},
+ {1, 1, 1, 7, 1, 33, 77, 111, 125, 771, 1975, 0},
+ {1, 3, 7, 13, 1, 51, 113, 139, 245, 573, 503, 0},
+ {1, 3, 1, 9, 21, 49, 15, 157, 49, 483, 291, 0},
+ {1, 1, 1, 1, 29, 35, 17, 65, 403, 485, 1603, 0},
+ {1, 1, 1, 7, 19, 1, 37, 129, 203, 321, 1809, 0},
+ {1, 3, 7, 15, 15, 9, 5, 77, 29, 485, 581, 0},
+ {1, 1, 3, 5, 15, 49, 97, 105, 309, 875, 1581, 0},
+ {1, 3, 5, 1, 5, 19, 63, 35, 165, 399, 1489, 0},
+ {1, 3, 5, 3, 23, 5, 79, 137, 115, 599, 1127, 0},
+ {1, 1, 7, 5, 3, 61, 27, 177, 257, 91, 841, 0},
+ {1, 1, 3, 5, 9, 31, 91, 209, 409, 661, 159, 0},
+ {1, 3, 1, 15, 23, 39, 23, 195, 245, 203, 947, 0},
+ {1, 1, 3, 1, 15, 59, 67, 95, 155, 461, 147, 0},
+ {1, 3, 7, 5, 23, 25, 87, 11, 51, 449, 1631, 0},
+ {1, 1, 1, 1, 17, 57, 7, 197, 409, 609, 135, 0},
+ {1, 1, 1, 9, 1, 61, 115, 113, 495, 895, 1595, 0},
+ {1, 3, 7, 15, 9, 47, 121, 211, 379, 985, 1755, 0},
+ {1, 3, 1, 3, 7, 57, 27, 231, 339, 325, 1023, 0},
+ {1, 1, 1, 1, 19, 63, 63, 239, 31, 643, 373, 0},
+ {1, 3, 1, 11, 19, 9, 7, 171, 21, 691, 215, 0},
+ {1, 1, 5, 13, 11, 57, 39, 211, 241, 893, 555, 0},
+ {1, 1, 7, 5, 29, 21, 45, 59, 509, 223, 491, 0},
+ {1, 1, 7, 9, 15, 61, 97, 75, 127, 779, 839, 0},
+ {1, 1, 7, 15, 17, 33, 75, 237, 191, 925, 681, 0},
+ {1, 3, 5, 7, 27, 57, 123, 111, 101, 371, 1129, 0},
+ {1, 3, 5, 5, 29, 45, 59, 127, 229, 967, 2027, 0},
+ {1, 1, 1, 1, 17, 7, 23, 199, 241, 455, 135, 0},
+ {1, 1, 7, 15, 27, 29, 105, 171, 337, 503, 1817, 0},
+ {1, 1, 3, 7, 21, 35, 61, 71, 405, 647, 2045, 0},
+ {1, 1, 1, 1, 1, 15, 65, 167, 501, 79, 737, 0},
+ {1, 1, 5, 1, 3, 49, 27, 189, 341, 615, 1287, 0},
+ {1, 1, 1, 9, 1, 7, 31, 159, 503, 327, 1613, 0},
+ {1, 3, 3, 3, 3, 23, 99, 115, 323, 997, 987, 0},
+ {1, 1, 1, 9, 19, 33, 93, 247, 509, 453, 891, 0},
+ {1, 1, 3, 1, 13, 19, 35, 153, 161, 633, 445, 0},
+ {1, 3, 5, 15, 31, 5, 87, 197, 183, 783, 1823, 0},
+ {1, 1, 7, 5, 19, 63, 69, 221, 129, 231, 1195, 0},
+ {1, 1, 5, 5, 13, 23, 19, 231, 245, 917, 379, 0},
+ {1, 3, 1, 15, 19, 43, 27, 223, 171, 413, 125, 0},
+ {1, 1, 1, 9, 1, 59, 21, 15, 509, 207, 589, 0},
+ {1, 3, 5, 3, 19, 31, 113, 19, 23, 733, 499, 0},
+ {1, 1, 7, 1, 19, 51, 101, 165, 47, 925, 1093, 0},
+ {1, 3, 3, 9, 15, 21, 43, 243, 237, 461, 1361, 0},
+ {1, 1, 1, 9, 17, 15, 75, 75, 113, 715, 1419, 0},
+ {1, 1, 7, 13, 17, 1, 99, 15, 347, 721, 1405, 0},
+ {1, 1, 7, 15, 7, 27, 23, 183, 39, 59, 571, 0},
+ {1, 3, 5, 9, 7, 43, 35, 165, 463, 567, 859, 0},
+ {1, 3, 3, 11, 15, 19, 17, 129, 311, 343, 15, 0},
+ {1, 1, 1, 15, 31, 59, 63, 39, 347, 359, 105, 0},
+ {1, 1, 1, 15, 5, 43, 87, 241, 109, 61, 685, 0},
+ {1, 1, 7, 7, 9, 39, 121, 127, 369, 579, 853, 0},
+ {1, 1, 1, 1, 17, 15, 15, 95, 325, 627, 299, 0},
+ {1, 1, 3, 13, 31, 53, 85, 111, 289, 811, 1635, 0},
+ {1, 3, 7, 1, 19, 29, 75, 185, 153, 573, 653, 0},
+ {1, 3, 7, 1, 29, 31, 55, 91, 249, 247, 1015, 0},
+ {1, 3, 5, 7, 1, 49, 113, 139, 257, 127, 307, 0},
+ {1, 3, 5, 9, 15, 15, 123, 105, 105, 225, 1893, 0},
+ {1, 3, 3, 1, 15, 5, 105, 249, 73, 709, 1557, 0},
+ {1, 1, 1, 9, 17, 31, 113, 73, 65, 701, 1439, 0},
+ {1, 3, 5, 15, 13, 21, 117, 131, 243, 859, 323, 0},
+ {1, 1, 1, 9, 19, 15, 69, 149, 89, 681, 515, 0},
+ {1, 1, 1, 5, 29, 13, 21, 97, 301, 27, 967, 0},
+ {1, 1, 3, 3, 15, 45, 107, 227, 495, 769, 1935, 0},
+ {1, 1, 1, 11, 5, 27, 41, 173, 261, 703, 1349, 0},
+ {1, 3, 3, 3, 11, 35, 97, 43, 501, 563, 1331, 0},
+ {1, 1, 1, 7, 1, 17, 87, 17, 429, 245, 1941, 0},
+ {1, 1, 7, 15, 29, 13, 1, 175, 425, 233, 797, 0},
+ {1, 1, 3, 11, 21, 57, 49, 49, 163, 685, 701, 0},
+ {1, 3, 3, 7, 11, 45, 107, 111, 379, 703, 1403, 0},
+ {1, 1, 7, 3, 21, 7, 117, 49, 469, 37, 775, 0},
+ {1, 1, 5, 15, 31, 63, 101, 77, 507, 489, 1955, 0},
+ {1, 3, 3, 11, 19, 21, 101, 255, 203, 673, 665, 0},
+ {1, 3, 3, 15, 17, 47, 125, 187, 271, 899, 2003, 0},
+ {1, 1, 7, 7, 1, 35, 13, 235, 5, 337, 905, 0},
+ {1, 3, 1, 15, 1, 43, 1, 27, 37, 695, 1429, 0},
+ {1, 3, 1, 11, 21, 27, 93, 161, 299, 665, 495, 0},
+ {1, 3, 3, 15, 3, 1, 81, 111, 105, 547, 897, 0},
+ {1, 3, 5, 1, 3, 53, 97, 253, 401, 827, 1467, 0},
+ {1, 1, 1, 5, 19, 59, 105, 125, 271, 351, 719, 0},
+ {1, 3, 5, 13, 7, 11, 91, 41, 441, 759, 1827, 0},
+ {1, 3, 7, 11, 29, 61, 61, 23, 307, 863, 363, 0},
+ {1, 1, 7, 1, 15, 35, 29, 133, 415, 473, 1737, 0},
+ {1, 1, 1, 13, 7, 33, 35, 225, 117, 681, 1545, 0},
+ {1, 1, 1, 3, 5, 41, 83, 247, 13, 373, 1091, 0},
+ {1, 3, 1, 13, 25, 61, 71, 217, 233, 313, 547, 0},
+ {1, 3, 1, 7, 3, 29, 3, 49, 93, 465, 15, 0},
+ {1, 1, 1, 9, 17, 61, 99, 163, 129, 485, 1087, 0},
+ {1, 1, 1, 9, 9, 33, 31, 163, 145, 649, 253, 0},
+ {1, 1, 1, 1, 17, 63, 43, 235, 287, 111, 567, 0},
+ {1, 3, 5, 13, 29, 7, 11, 69, 153, 127, 449, 0},
+ {1, 1, 5, 9, 11, 21, 15, 189, 431, 493, 1219, 0},
+ {1, 1, 1, 15, 19, 5, 47, 91, 399, 293, 1743, 0},
+ {1, 3, 3, 11, 29, 53, 53, 225, 409, 303, 333, 0},
+ {1, 1, 1, 15, 31, 31, 21, 81, 147, 287, 1753, 0},
+ {1, 3, 5, 5, 5, 63, 35, 125, 41, 687, 1793, 0},
+ {1, 1, 1, 9, 19, 59, 107, 219, 455, 971, 297, 0},
+ {1, 1, 3, 5, 3, 51, 121, 31, 245, 105, 1311, 0},
+ {1, 3, 1, 5, 5, 57, 75, 107, 161, 431, 1693, 0},
+ {1, 3, 1, 3, 19, 53, 27, 31, 191, 565, 1015, 0},
+ {1, 3, 5, 13, 9, 41, 35, 249, 287, 49, 123, 0},
+ {1, 1, 5, 7, 27, 17, 21, 3, 151, 885, 1165, 0},
+ {1, 1, 7, 1, 15, 17, 65, 139, 427, 339, 1171, 0},
+ {1, 1, 1, 5, 23, 5, 9, 89, 321, 907, 391, 0},
+ {1, 1, 7, 9, 15, 1, 77, 71, 87, 701, 917, 0},
+ {1, 1, 7, 1, 17, 37, 115, 127, 469, 779, 1543, 0},
+ {1, 3, 7, 3, 5, 61, 15, 37, 301, 951, 1437, 0},
+ {1, 1, 1, 13, 9, 51, 127, 145, 229, 55, 1567, 0},
+ {1, 3, 7, 15, 19, 47, 53, 153, 295, 47, 1337, 0},
+ {1, 3, 3, 5, 11, 31, 29, 133, 327, 287, 507, 0},
+ {1, 1, 7, 7, 25, 31, 37, 199, 25, 927, 1317, 0},
+ {1, 1, 7, 9, 3, 39, 127, 167, 345, 467, 759, 0},
+ {1, 1, 1, 1, 31, 21, 15, 101, 293, 787, 1025, 0},
+ {1, 1, 5, 3, 11, 41, 105, 109, 149, 837, 1813, 0},
+ {1, 1, 3, 5, 29, 13, 19, 97, 309, 901, 753, 0},
+ {1, 1, 7, 1, 19, 17, 31, 39, 173, 361, 1177, 0},
+ {1, 3, 3, 3, 3, 41, 81, 7, 341, 491, 43, 0},
+ {1, 1, 7, 7, 31, 35, 29, 77, 11, 335, 1275, 0},
+ {1, 3, 3, 15, 17, 45, 19, 63, 151, 849, 129, 0},
+ {1, 1, 7, 5, 7, 13, 47, 73, 79, 31, 499, 0},
+ {1, 3, 1, 11, 1, 41, 59, 151, 247, 115, 1295, 0},
+ {1, 1, 1, 9, 31, 37, 73, 23, 295, 483, 179, 0},
+ {1, 3, 1, 15, 13, 63, 81, 27, 169, 825, 2037, 0},
+ {1, 3, 5, 15, 7, 11, 73, 1, 451, 101, 2039, 0},
+ {1, 3, 5, 3, 13, 53, 31, 137, 173, 319, 1521, 0},
+ {1, 3, 1, 3, 29, 1, 73, 227, 377, 337, 1189, 0},
+ {1, 3, 3, 13, 27, 9, 31, 101, 229, 165, 1983, 0},
+ {1, 3, 1, 13, 13, 19, 19, 111, 319, 421, 223, 0},
+ {1, 1, 7, 15, 25, 37, 61, 55, 359, 255, 1955, 0},
+ {1, 1, 5, 13, 17, 43, 49, 215, 383, 915, 51, 0},
+ {1, 1, 3, 1, 3, 7, 13, 119, 155, 585, 967, 0},
+ {1, 3, 1, 13, 1, 63, 125, 21, 103, 287, 457, 0},
+ {1, 1, 7, 1, 31, 17, 125, 137, 345, 379, 1925, 0},
+ {1, 1, 3, 5, 5, 25, 119, 153, 455, 271, 2023, 0},
+ {1, 1, 7, 9, 9, 37, 115, 47, 5, 255, 917, 0},
+ {1, 3, 5, 3, 31, 21, 75, 203, 489, 593, 1, 0},
+ {1, 3, 7, 15, 19, 63, 123, 153, 135, 977, 1875, 0},
+ {1, 1, 1, 1, 5, 59, 31, 25, 127, 209, 745, 0},
+ {1, 1, 1, 1, 19, 45, 67, 159, 301, 199, 535, 0},
+ {1, 1, 7, 1, 31, 17, 19, 225, 369, 125, 421, 0},
+ {1, 3, 3, 11, 7, 59, 115, 197, 459, 469, 1055, 0},
+ {1, 3, 1, 3, 27, 45, 35, 131, 349, 101, 411, 0},
+ {1, 3, 7, 11, 9, 3, 67, 145, 299, 253, 1339, 0},
+ {1, 3, 3, 11, 9, 37, 123, 229, 273, 269, 515, 0},
+ {1, 3, 7, 15, 11, 25, 75, 5, 367, 217, 951, 0},
+ {1, 1, 3, 7, 9, 23, 63, 237, 385, 159, 1273, 0},
+ {1, 1, 5, 11, 23, 5, 55, 193, 109, 865, 663, 0},
+ {1, 1, 7, 15, 1, 57, 17, 141, 51, 217, 1259, 0},
+ {1, 1, 3, 3, 15, 7, 89, 233, 71, 329, 203, 0},
+ {1, 3, 7, 11, 11, 1, 19, 155, 89, 437, 573, 0},
+ {1, 3, 1, 9, 27, 61, 47, 109, 161, 913, 1681, 0},
+ {1, 1, 7, 15, 1, 33, 19, 15, 23, 913, 989, 0},
+ {1, 3, 1, 1, 25, 39, 119, 193, 13, 571, 157, 0},
+ {1, 1, 7, 13, 9, 55, 59, 147, 361, 935, 515, 0},
+ {1, 1, 1, 9, 7, 59, 67, 117, 71, 855, 1493, 0},
+ {1, 3, 1, 3, 13, 19, 57, 141, 305, 275, 1079, 0},
+ {1, 1, 1, 9, 17, 61, 33, 7, 43, 931, 781, 0},
+ {1, 1, 3, 1, 11, 17, 21, 97, 295, 277, 1721, 0},
+ {1, 3, 1, 13, 15, 43, 11, 241, 147, 391, 1641, 0},
+ {1, 1, 1, 1, 1, 19, 37, 21, 255, 263, 1571, 0},
+ {1, 1, 3, 3, 23, 59, 89, 17, 475, 303, 757, 543},
+ {1, 3, 3, 9, 11, 55, 35, 159, 139, 203, 1531, 1825},
+ {1, 1, 5, 3, 17, 53, 51, 241, 269, 949, 1373, 325},
+ {1, 3, 7, 7, 5, 29, 91, 149, 239, 193, 1951, 2675},
+ {1, 3, 5, 1, 27, 33, 69, 11, 51, 371, 833, 2685},
+ {1, 1, 1, 15, 1, 17, 35, 57, 171, 1007, 449, 367},
+ {1, 1, 1, 7, 25, 61, 73, 219, 379, 53, 589, 4065},
+ {1, 3, 5, 13, 21, 29, 45, 19, 163, 169, 147, 597},
+ {1, 1, 5, 11, 21, 27, 7, 17, 237, 591, 255, 1235},
+ {1, 1, 7, 7, 17, 41, 69, 237, 397, 173, 1229, 2341},
+ {1, 1, 3, 1, 1, 33, 125, 47, 11, 783, 1323, 2469},
+ {1, 3, 1, 11, 3, 39, 35, 133, 153, 55, 1171, 3165},
+ {1, 1, 5, 11, 27, 23, 103, 245, 375, 753, 477, 2165},
+ {1, 3, 1, 15, 15, 49, 127, 223, 387, 771, 1719, 1465},
+ {1, 1, 1, 9, 11, 9, 17, 185, 239, 899, 1273, 3961},
+ {1, 1, 3, 13, 11, 51, 73, 81, 389, 647, 1767, 1215},
+ {1, 3, 5, 15, 19, 9, 69, 35, 349, 977, 1603, 1435},
+ {1, 1, 1, 1, 19, 59, 123, 37, 41, 961, 181, 1275},
+ {1, 1, 1, 1, 31, 29, 37, 71, 205, 947, 115, 3017},
+ {1, 1, 7, 15, 5, 37, 101, 169, 221, 245, 687, 195},
+ {1, 1, 1, 1, 19, 9, 125, 157, 119, 283, 1721, 743},
+ {1, 1, 7, 3, 1, 7, 61, 71, 119, 257, 1227, 2893},
+ {1, 3, 3, 3, 25, 41, 25, 225, 31, 57, 925, 2139}
+ };
+}
+
diff --git a/source/umontreal/iro/lecuyer/hups/SobolSequence.tex b/source/umontreal/iro/lecuyer/hups/SobolSequence.tex
new file mode 100644
index 0000000..9ef2838
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/SobolSequence.tex
@@ -0,0 +1,869 @@
+\defmodule {SobolSequence}
+
+This class implements digital nets or digital sequences in base 2 formed by
+ the first $n = 2^k$ points of a Sobol' sequence \cite{rSOB67a,rSOB76b}.
+Values of $n$ up to $2^{30}$ are allowed. %, in a maximum of 360 dimensions.
+
+In Sobol's proposal, the generator matrices $\mathbf{C}_j$ are upper triangular
+matrices defined by a set of \emph{direction numbers}
+\[
+ v_{j,c} = m_{j,c} 2^{-c} = \sum_{l=1}^c v_{j,c,l} 2^{-l},
+\]
+where each $m_{j,c}$ is an \emph{odd} integer smaller than $2^c$,
+for $c=1,\dots,k$ and $j=0,\dots,s-1$.
+The digit $v_{j,c,l}$ is the element $(l,c)$ of $\mathbf{C}_j$,
+so $v_{j,c}$ represents column $c$ of $\mathbf{C}_j$.
+One can also write
+\[
+ m_{j,c} = \sum_{l=1}^c v_{j,c,l} 2^{c-l},
+\]
+so column $c$ of $\mathbf{C}_j$ contains the $c$ digits of the binary expansion
+of $m_{j,c}$, from the most to the least significant,
+followed by $w-c$ zeros, where $w$ is the number of output digits.
+Since each $m_{j,c}$ is odd, the first $k$ rows of each $\mathbf{C}_j$ form
+a non-singular upper triangular matrix whose diagonal elements
+are all ones.
+
+For each dimension $j$, the integers $m_{j,c}$ are defined by
+selecting a primitive polynomial over
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_2$ of degree $c_j$,
+\[
+ f_j(z) = z^{c_j} + a_{j,1}z^{c_j-1} + \cdots + a_{j,c_j},
+\]
+and the first $c_j$ integers $m_{j,0},\dots,m_{j,c_j-1}$.
+Then the following integers $m_{j,c_j}, m_{j, c_j+1}, \dots$ are
+determined by the recurrence
+\[
+ m_{j,c} = 2 a_{j,1} m_{j,c-1}
+ \oplus\cdots\oplus 2^{c_j-1} a_{j,c_j-1}m_{j,c-c_j+1}
+ \oplus 2^{c_j} m_{j,c-c_j}\oplus m_{j,c-c_j}
+\]
+for $c\ge c_j$, or equivalently,
+$$
+ v_{j,c,l} = a_{j,1} v_{j,c-1,l}
+ \oplus\cdots\oplus a_{j,c_j-1} v_{j,c-c_j+1,l}
+ \oplus v_{j,c-c_j,l}\oplus v_{j,c-c_j,l+c_j}
+$$
+for $l\ge 0$, where $\oplus$ means bitwise exclusive or
+(i.e., bitwise addition modulo 2).
+Sobol' has shown \cite{rSOB67a} that with this construction, if the
+primitive polynomials $f_j(z)$ are all distinct, one obtains
+a $(t,s)$-sequence whose $t$-value does not exceed
+$c_0 + \cdots + c_{s-1} + 1 - s$.
+He then suggested to list the set of all primitive polynomials over
+$\latex{\mathbb{F}}\html{\mathbf{F}}_2$ by increasing order of degree, starting with $f_0(z) \equiv 1$
+(whose corresponding matrix $\mathbf{C}_0$ is the identity), and take $f_j(z)$
+as the $(j+1)$th polynomial in the list, for $j\ge 0$.
+
+This list of primitive polynomials, as well as default choices
+for the direction numbers, are stored in precomputed tables.
+The ordered list of primitive polynomials \latex{is the same as in
+\cite{iLEM04a} and} was taken from Florent Chabaud's web site,
+at \url{http://fchabaud.free.fr/}.
+Each polynomial $f_j(z)$ is stored in the form of the integer
+$2^{c_j} + a_{j,1}2^{c_j-1} + \cdots + a_{j,c_j}$, whose binary
+representation gives the polynomial coefficients.
+
+For the set of direction numbers, there are several possibilities
+based on different selection criteria.
+The original values proposed by Sobol' and implemented in the code of
+Bratley and Fox \cite{rBRA88c} for $j\le 40$
+were selected in terms of his properties $A$ and $A'$,
+which are equivalent to $s$-distribution with one and
+two bits of accuracy, respectively.
+The default direction numbers used here have been taken from
+\latex{\cite{iLEM04a}}\html{Lemieux et al}.
+For $j\le 40$, they are the same as in
+ \latex{\cite{rBRA88c}}\html{Bratley and Fox}.
+Several files of parameters for Sobol sequences are given on F. Kuo's
+Web site at \url{http://web.maths.unsw.edu.au/~fkuo/sobol/index.html}.
+%
+\pierre{We should eventually have other choices for the direction numbers.}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: SobolSequence
+ * Description: Sobol sequences
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.*;
+import java.net.MalformedURLException;
+\end{hide}
+
+public class SobolSequence extends DigitalSequenceBase2 \begin{hide} {
+
+ // Maximal dimension for primitive polynomials included in this file
+ protected static final int MAXDIM = 360;
+ protected static final int MAXDEGREE = 18; // Of primitive polynomial
+ private String filename = null;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public SobolSequence (int k, int w, int dim) \begin{hide} {
+ init (k, w, w, dim);
+ }
+
+ private void init (int k, int r, int w, int dim) {
+ if (filename == null)
+ if ((dim < 1) || (dim > MAXDIM))
+ throw new IllegalArgumentException
+ ("Dimension for SobolSequence must be > 0 and <= " + MAXDIM);
+ else
+ if (dim < 1)
+ throw new IllegalArgumentException
+ ("Dimension for SobolSequence must be > 0");
+
+ if (r < k || w < r || w > MAXBITS || k >= MAXBITS)
+ throw new IllegalArgumentException
+ ("One must have k < 31 and k <= r <= w <= 31 for SobolSequence");
+ numCols = k;
+ numRows = r; // Not used!
+ outDigits = w;
+ numPoints = (1 << k);
+ this.dim = dim;
+ normFactor = 1.0 / ((double) (1L << (outDigits)));
+ genMat = new int[dim * numCols];
+ initGenMat();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new digital net with $n = 2^k$ points and $w$
+ output digits, in dimension \texttt{dim}, formed by taking the first
+ $n$ points of the Sobol' sequence.
+ The predefined generator matrices $\mathbf{C}_j$ are $w\times k$. % (so, $r=w$).
+ Restrictions: $0\le k\le 30$, $k\le w$ and \texttt{dim} $ \le 360$.
+\end{tabb}
+\begin{htmlonly}
+ \param{k}{there will be 2^k points}
+% \param{r}{number of rows in the generator matrices}
+ \param{w}{number of output digits}
+ \param{dim}{dimension of the point set}
+\end{htmlonly}
+\begin{code}
+
+ public SobolSequence (int n, int dim) \begin{hide} {
+ numCols = MAXBITS; // Defined in PointSet.
+ while (((n >> numCols) & 1) == 0)
+ numCols--;
+ if (1 << numCols != n)
+ numCols++;
+ init (numCols, MAXBITS, MAXBITS, dim);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a Sobol point set with \emph{at least} \texttt{n} points and 31
+ output digits, in dimension \texttt{dim}. Equivalent to
+ \texttt{SobolSequence (k, 31, dim)} with $k = \lceil \log_2 n\rceil$.
+\end{tabb}
+\begin{htmlonly}
+ \param{dim}{dimension of the point set}
+ \param{n}{minimal number of points}
+\end{htmlonly}
+\begin{code}
+
+ public SobolSequence (String filename, int k, int w, int dim) \begin{hide} {
+
+ poly_from_file = new int[dim];
+ for (int i = 0; i < dim; i++)
+ poly_from_file[i] = 0;
+
+ minit_from_file = new int[dim][MAXDEGREE];
+ for (int i = 0; i < dim; i++) {
+ for (int j = 0; j < MAXDEGREE; j++) {
+ minit_from_file[i][j] = 0;
+ }
+ }
+
+ // Dimension d = 1
+ int d = 1;
+ poly_from_file[d - 1] = 1;
+
+ // Read the direction number file up to a certain number of dimension dim
+ try {
+ // If filename can be found starting from the program's directory,
+ // it will be used; otherwise, the filename in the Jar archive will
+ // be used.
+ int prev_d = 1;
+
+ BufferedReader reader;
+
+ if (filename.startsWith("http") || filename.startsWith("ftp"))
+ reader = DigitalNetFromFile.openURL(filename);
+ else
+ reader = new BufferedReader(new FileReader(filename));
+
+ // First line of file is a comment; discard it
+ String line = reader.readLine();
+ String[] tokens;
+
+ while ((line = reader.readLine()) != null) {
+ tokens = line.split("[\t ]+");
+
+ // Direction number lines from dimension 2 and up
+ if (tokens.length < 4) {
+ System.err.println("\nBad direction number file format!\n");
+ System.exit(1);
+ }
+
+ // Parse dim d, polynomial degree s and coefficients a
+ d = Integer.parseInt(tokens[0]);
+
+ int s = Integer.parseInt(tokens[1]);
+ int a = Integer.parseInt(tokens[2]);
+
+ if (s + 3 != tokens.length) {
+ System.err.println("\nBad direction number file format!\n");
+ System.exit(1);
+ }
+
+ if (d != prev_d + 1) {
+ System.err.println("Dimension in file shall be in ");
+ System.err.println("increasing order, one per line!");
+ System.exit(1);
+ }
+ prev_d = d;
+
+ // If d in the file exceeds dim, stop reading!
+ if (d > dim)
+ break;
+
+ poly_from_file[d - 1] = (1 << s) ^ (a << 1) ^ 1;
+
+ // Parse the s direction numbers
+ for (int i = 0; i < s; i++)
+ minit_from_file[d - 2][i] = Integer.parseInt(tokens[i + 3]);
+ } // end while
+
+ } catch (MalformedURLException e) {
+ System.err.println (" Invalid URL address: " + filename);
+ System.exit(1);
+
+ } catch (IOException e) {
+ System.err.println("Error: " + e);
+ System.exit(1);
+ }
+
+ if (dim > d) {
+ System.err.printf("\n\nNot enough dimension in file: %s", filename);
+ System.exit(1);
+ }
+
+ this.filename = filename;
+
+ init(k, w, w, dim);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new digital net using the direction numbers provided in file
+ \texttt{filename}. The net has $n = 2^k$ points, $w$ output digits and
+ dimension \texttt{dim}. The file can be either on the user's host, or
+ somewhere on the Internet: in that case, the full \textbf{url} address must
+ be given using either the \emph{http} or \emph{ftp} protocol. For
+ example:
+
+ \texttt{net = new SobolSequence(\\
+ "http://web.maths.unsw.edu.au/\~{}fkuo/sobol/joe-kuo-6.16900", k, w, dim);}
+
+ The file must have the following format
+(the first line is treated as a comment by the program and discarded):
+%
+\begin{center}
+\begin {tabular}{|c|l|l|l|}
+\hline
+dim & $s$ & $a$ & $m_i$ \\
+2 & 1 & 0 & 1 \\
+3 & 2 & 1 & 1 3 \\
+4 & 3 & 1 & 1 3 1 \\
+5 & 3 & 2 & 1 1 1 \\
+6 & 4 & 1 & 1 1 3 3 \\
+7 & 4 & 4 & 1 3 5 13 \\
+ &\vdots &\vdots & \\
+\hline
+\end {tabular}
+\end{center}
+%
+ dim is the
+dimension, $s$ the degree of the polynomial, the binary representation of $a$
+ gives the inner coefficients of the polynomial
+ (the first and the last coefficients are always 1), and $m_i$ are the
+direction numbers. Thus if $a = (a_1 a_2 \ldots a_{s-1})_2$ for a given $s$,
+then the polynomial is $x^s + a_1x^{s-1} + a_2x^{s-2} + \cdots + a_{s-1} x + 1$.
+For example, if $s=4$ and $a=4 = 100_2$, then the
+polynomial is $x^4 + x^3 +1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{k}{number of points is $2^k$}
+ \param{w}{number of output digits}
+ \param{dim}{dimension of the point set}
+ \param{filename}{file containing the direction numbers}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Sobol sequence:" +
+ PrintfFormat.NEWLINE);
+ sb.append (super.toString());
+ return sb.toString();
+ }
+
+
+ public void extendSequence (int k) {
+ int start, degree, nextCol;
+ int i, j, c;
+ int oldNumCols = numCols;
+ int[] oldGenMat = genMat; // Save old generating matrix.
+
+ numCols = k;
+ numPoints = (1 << k);
+ genMat = new int[dim * numCols];
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-c-1));
+
+ // the other dimensions j > 0.
+ for (j = 1; j < dim; j++) {
+ // find the degree of primitive polynomial f_j
+ for (degree = MAXDEGREE; ((poly[j] >> degree) & 1) == 0; degree--)
+ ;
+ // Get initial direction numbers m_{j,0},..., m_{j,degree-1}.
+ start = j * numCols;
+ for (c = 0; (c < degree && c < numCols); c++)
+ genMat[start+c] = minit[j-1][c] << (outDigits-c-1);
+
+ // Compute the following ones via the recursion.
+ for (c = degree; c < numCols; c++) {
+ if (c < oldNumCols)
+ genMat[start+c] = oldGenMat[j*oldNumCols + c];
+ else {
+ nextCol = genMat[start+c-degree] >> degree;
+ for (i = 0; i < degree; i++)
+ if (((poly[j] >> i) & 1) == 1)
+ nextCol ^= genMat[start+c-degree+i];
+ genMat[start+c] = nextCol;
+ }
+ }
+ }
+ }
+
+
+ // Initializes the generator matrices for a sequence.
+ private void initGenMat() {
+ int start, degree, nextCol;
+ int i, j, c;
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-c-1));
+
+ // the other dimensions j > 0.
+ for (j = 1; j < dim; j++) {
+ // if a direction number file was provided, use it
+ int polynomial = (filename != null ? poly_from_file[j] : poly[j]);
+ // find the degree of primitive polynomial f_j
+ for (degree = MAXDEGREE; ((polynomial >> degree) & 1) == 0; degree--)
+ ;
+ // Get initial direction numbers m_{j,0},..., m_{j,degree-1}.
+ start = j * numCols;
+ for (c = 0; (c < degree && c < numCols); c++) {
+ int m_i = (filename != null ?
+ minit_from_file[j-1][c] : minit[j-1][c]);
+ genMat[start+c] = m_i << (outDigits-c-1);
+ }
+
+ // Compute the following ones via the recursion.
+ for (c = degree; c < numCols; c++) {
+ nextCol = genMat[start+c-degree] >> degree;
+ for (i = 0; i < degree; i++)
+ if (((polynomial >> i) & 1) == 1)
+ nextCol ^= genMat[start+c-degree+i];
+ genMat[start+c] = nextCol;
+ }
+ }
+ }
+
+/*
+ // Initializes the generator matrices for a net.
+ protected void initGenMatNet() {
+ int start, degree, nextCol;
+ int i, j, c;
+
+ // the first dimension, j = 0.
+ for (c = 0; c < numCols; c++)
+ genMat[c] = (1 << (outDigits-numCols+c));
+
+ // the second dimension, j = 1.
+ for (c = 0; c < numCols; c++)
+ genMat[numCols+c] = (1 << (outDigits-c-1));
+
+ // the other dimensions j > 1.
+ for (j = 2; j < dim; j++) {
+ // find the degree of primitive polynomial f_j
+ for (degree = MAXDEGREE; ((poly[j-1] >> degree) & 1) == 0; degree--);
+ // Get initial direction numbers m_{j,0},..., m_{j,degree-1}.
+ start = j * numCols;
+ for (c = 0; (c < degree && c < numCols); c++)
+ genMat[start+c] = minit[j-2][c] << (outDigits-c-1);
+
+ // Compute the following ones via the recursion.
+ for (c = degree; c < numCols; c++) {
+ nextCol = genMat[start+c-degree] >> degree;
+ for (i = 0; i < degree; i++)
+ if (((poly[j-1] >> i) & 1) == 1)
+ nextCol ^= genMat[start+c-degree+i];
+ genMat[start+c] = nextCol;
+ }
+ }
+ }
+*/
+
+ // *******************************************************
+ // The ordered list of first MAXDIM primitive polynomials.
+
+ protected int[] poly_from_file;
+
+ protected static final int[] poly = {
+ 1, 3, 7, 11, 13, 19, 25, 37, 59,
+ 47, 61, 55, 41, 67, 97, 91, 109, 103,
+ 115, 131, 193, 137, 145, 143, 241, 157, 185,
+ 167, 229, 171, 213, 191, 253, 203, 211, 239,
+ 247, 285, 369, 299, 425, 301, 361, 333, 357,
+ 351, 501, 355, 397, 391, 451, 463, 487, 529,
+ 545, 539, 865, 557, 721, 563, 817, 601, 617,
+ 607, 1001, 623, 985, 631, 953, 637, 761, 647,
+ 901, 661, 677, 675, 789, 687, 981, 695, 949,
+ 701, 757, 719, 973, 731, 877, 787, 803, 799,
+ 995, 827, 883, 847, 971, 859, 875, 895, 1019,
+ 911, 967, 1033, 1153, 1051, 1729, 1063, 1825, 1069,
+ 1441, 1125, 1329, 1135, 1969, 1163, 1673, 1221, 1305,
+ 1239, 1881, 1255, 1849, 1267, 1657, 1279, 2041, 1293,
+ 1413, 1315, 1573, 1341, 1509, 1347, 1557, 1367, 1877,
+ 1387, 1717, 1423, 1933, 1431, 1869, 1479, 1821, 1527,
+ 1917, 1531, 1789, 1555, 1603, 1591, 1891, 1615, 1939,
+ 1627, 1747, 1663, 2035, 1759, 2011, 1815, 1863, 2053,
+ 2561, 2071, 3713, 2091, 3393, 2093, 2881, 2119, 3617,
+ 2147, 3169, 2149, 2657, 2161, 2273, 2171, 3553, 2189,
+ 2833, 2197, 2705, 2207, 3985, 2217, 2385, 2225, 2257,
+ 2255, 3889, 2279, 3697, 2283, 3441, 2293, 2801, 2317,
+ 2825, 2323, 3209, 2341, 2633, 2345, 2377, 2363, 3529,
+ 2365, 3017, 2373, 2601, 2395, 3497, 2419, 3305, 2421,
+ 2793, 2431, 4073, 2435, 3097, 2447, 3865, 2475, 3417,
+ 2477, 2905, 2489, 2521, 2503, 3641, 2533, 2681, 2551,
+ 3833, 2567, 3589, 2579, 3205, 2581, 2693, 2669, 2917,
+ 2687, 4069, 2717, 2965, 2727, 3669, 2731, 3413, 2739,
+ 3285, 2741, 2773, 2783, 4021, 2799, 3957, 2811, 3573,
+ 2819, 3085, 2867, 3277, 2879, 4045, 2891, 3373, 2911,
+ 4013, 2927, 3949, 2941, 3053, 2951, 3613, 2955, 3357,
+ 2963, 3229, 2991, 3933, 2999, 3805, 3005, 3037, 3035,
+ 3517, 3047, 3709, 3083, 3331, 3103, 3971, 3159, 3747,
+ 3179, 3427, 3187, 3299, 3223, 3731, 3227, 3475, 3251,
+ 3283, 3263, 4051, 3271, 3635, 3319, 3827, 3343, 3851,
+ 3367, 3659, 3399, 3627, 3439, 3947, 3487, 3995, 3515,
+ 3547, 3543, 3771, 3559, 3707, 3623, 3655, 3679, 4007,
+ 3743, 3991, 3791, 3895, 4179, 6465, 4201, 4801, 4219,
+ 7105, 4221, 6081, 4249, 4897, 4305, 4449, 4331, 6881,
+ 4359, 7185, 4383, 7953, 4387, 6289, 4411, 7057, 4431
+ };
+
+
+ // The direction numbers. For j > 0 and c < c_j,
+ // minit[j-1][c] contains the integer m_{j,c}.
+ // The values for j=0 are not stored, since C_0 is the identity matrix.
+
+ protected int minit_from_file[][];
+
+ protected static final int minit[][] = {
+ {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 3, 7, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 3, 9, 9, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 7, 13, 3, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 5, 11, 27, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 5, 1, 15, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 7, 3, 29, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 7, 7, 21, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 1, 9, 23, 37, 0, 0, 0, 0, 0, 0},
+ {1, 3, 3, 5, 19, 33, 0, 0, 0, 0, 0, 0},
+ {1, 1, 3, 13, 11, 7, 0, 0, 0, 0, 0, 0},
+ {1, 1, 7, 13, 25, 5, 0, 0, 0, 0, 0, 0},
+ {1, 3, 5, 11, 7, 11, 0, 0, 0, 0, 0, 0},
+ {1, 1, 1, 3, 13, 39, 0, 0, 0, 0, 0, 0},
+ {1, 3, 1, 15, 17, 63, 13, 0, 0, 0, 0, 0},
+ {1, 1, 5, 5, 1, 27, 33, 0, 0, 0, 0, 0},
+ {1, 3, 3, 3, 25, 17, 115, 0, 0, 0, 0, 0},
+ {1, 1, 3, 15, 29, 15, 41, 0, 0, 0, 0, 0},
+ {1, 3, 1, 7, 3, 23, 79, 0, 0, 0, 0, 0},
+ {1, 3, 7, 9, 31, 29, 17, 0, 0, 0, 0, 0},
+ {1, 1, 5, 13, 11, 3, 29, 0, 0, 0, 0, 0},
+ {1, 3, 1, 9, 5, 21, 119, 0, 0, 0, 0, 0},
+ {1, 1, 3, 1, 23, 13, 75, 0, 0, 0, 0, 0},
+ {1, 3, 3, 11, 27, 31, 73, 0, 0, 0, 0, 0},
+ {1, 1, 7, 7, 19, 25, 105, 0, 0, 0, 0, 0},
+ {1, 3, 5, 5, 21, 9, 7, 0, 0, 0, 0, 0},
+ {1, 1, 1, 15, 5, 49, 59, 0, 0, 0, 0, 0},
+ {1, 1, 1, 1, 1, 33, 65, 0, 0, 0, 0, 0},
+ {1, 3, 5, 15, 17, 19, 21, 0, 0, 0, 0, 0},
+ {1, 1, 7, 11, 13, 29, 3, 0, 0, 0, 0, 0},
+ {1, 3, 7, 5, 7, 11, 113, 0, 0, 0, 0, 0},
+ {1, 1, 5, 3, 15, 19, 61, 0, 0, 0, 0, 0},
+ {1, 3, 1, 1, 9, 27, 89, 7, 0, 0, 0, 0},
+ {1, 1, 3, 7, 31, 15, 45, 23, 0, 0, 0, 0},
+ {1, 3, 3, 9, 9, 25, 107, 39, 0, 0, 0, 0},
+ {1, 1, 3, 13, 7, 35, 61, 91, 0, 0, 0, 0},
+ {1, 1, 7, 11, 5, 35, 55, 75, 0, 0, 0, 0},
+ {1, 3, 5, 5, 11, 23, 29, 139, 0, 0, 0, 0},
+ {1, 1, 1, 7, 11, 15, 17, 81, 0, 0, 0, 0},
+ {1, 1, 7, 9, 5, 57, 79, 103, 0, 0, 0, 0},
+ {1, 1, 7, 13, 19, 5, 5, 185, 0, 0, 0, 0},
+ {1, 3, 1, 3, 13, 57, 97, 131, 0, 0, 0, 0},
+ {1, 1, 5, 5, 21, 25, 125, 197, 0, 0, 0, 0},
+ {1, 3, 3, 9, 31, 11, 103, 201, 0, 0, 0, 0},
+ {1, 1, 5, 3, 7, 25, 51, 121, 0, 0, 0, 0},
+ {1, 3, 7, 15, 19, 53, 73, 189, 0, 0, 0, 0},
+ {1, 1, 1, 15, 19, 55, 27, 183, 0, 0, 0, 0},
+ {1, 1, 7, 13, 3, 29, 109, 69, 0, 0, 0, 0},
+ {1, 1, 5, 15, 15, 23, 15, 1, 57, 0, 0, 0},
+ {1, 3, 1, 3, 23, 55, 43, 143, 397, 0, 0, 0},
+ {1, 1, 3, 11, 29, 9, 35, 131, 411, 0, 0, 0},
+ {1, 3, 1, 7, 27, 39, 103, 199, 277, 0, 0, 0},
+ {1, 3, 7, 3, 19, 55, 127, 67, 449, 0, 0, 0},
+ {1, 3, 7, 3, 5, 29, 45, 85, 3, 0, 0, 0},
+ {1, 3, 5, 5, 13, 23, 75, 245, 453, 0, 0, 0},
+ {1, 3, 1, 15, 21, 47, 3, 77, 165, 0, 0, 0},
+ {1, 1, 7, 9, 15, 5, 117, 73, 473, 0, 0, 0},
+ {1, 3, 1, 9, 1, 21, 13, 173, 313, 0, 0, 0},
+ {1, 1, 7, 3, 11, 45, 63, 77, 49, 0, 0, 0},
+ {1, 1, 1, 1, 1, 25, 123, 39, 259, 0, 0, 0},
+ {1, 1, 1, 5, 23, 11, 59, 11, 203, 0, 0, 0},
+ {1, 3, 3, 15, 21, 1, 73, 71, 421, 0, 0, 0},
+ {1, 1, 5, 11, 15, 31, 115, 95, 217, 0, 0, 0},
+ {1, 1, 3, 3, 7, 53, 37, 43, 439, 0, 0, 0},
+ {1, 1, 1, 1, 27, 53, 69, 159, 321, 0, 0, 0},
+ {1, 1, 5, 15, 29, 17, 19, 43, 449, 0, 0, 0},
+ {1, 1, 3, 9, 1, 55, 121, 205, 255, 0, 0, 0},
+ {1, 1, 3, 11, 9, 47, 107, 11, 417, 0, 0, 0},
+ {1, 1, 1, 5, 17, 25, 21, 83, 95, 0, 0, 0},
+ {1, 3, 5, 13, 31, 25, 61, 157, 407, 0, 0, 0},
+ {1, 1, 7, 9, 25, 33, 41, 35, 17, 0, 0, 0},
+ {1, 3, 7, 15, 13, 39, 61, 187, 461, 0, 0, 0},
+ {1, 3, 7, 13, 5, 57, 23, 177, 435, 0, 0, 0},
+ {1, 1, 3, 15, 11, 27, 115, 5, 337, 0, 0, 0},
+ {1, 3, 7, 3, 15, 63, 61, 171, 339, 0, 0, 0},
+ {1, 3, 3, 13, 15, 61, 59, 47, 1, 0, 0, 0},
+ {1, 1, 5, 15, 13, 5, 39, 83, 329, 0, 0, 0},
+ {1, 1, 5, 5, 5, 27, 25, 39, 301, 0, 0, 0},
+ {1, 1, 5, 11, 31, 41, 35, 233, 27, 0, 0, 0},
+ {1, 3, 5, 15, 7, 37, 119, 171, 419, 0, 0, 0},
+ {1, 3, 5, 5, 3, 29, 21, 189, 417, 0, 0, 0},
+ {1, 1, 1, 1, 21, 41, 117, 119, 351, 0, 0, 0},
+ {1, 1, 3, 1, 7, 27, 87, 19, 213, 0, 0, 0},
+ {1, 1, 1, 1, 17, 7, 97, 217, 477, 0, 0, 0},
+ {1, 1, 7, 1, 29, 61, 103, 231, 269, 0, 0, 0},
+ {1, 1, 7, 13, 9, 27, 107, 207, 311, 0, 0, 0},
+ {1, 1, 7, 5, 25, 21, 107, 179, 423, 0, 0, 0},
+ {1, 3, 5, 11, 7, 1, 17, 245, 281, 0, 0, 0},
+ {1, 3, 5, 9, 1, 5, 53, 59, 125, 0, 0, 0},
+ {1, 1, 7, 1, 31, 57, 71, 245, 125, 0, 0, 0},
+ {1, 1, 7, 5, 5, 57, 53, 253, 441, 0, 0, 0},
+ {1, 3, 1, 13, 19, 35, 119, 235, 381, 0, 0, 0},
+ {1, 3, 1, 7, 19, 59, 115, 33, 361, 0, 0, 0},
+ {1, 1, 3, 5, 13, 1, 49, 143, 501, 0, 0, 0},
+ {1, 1, 3, 5, 1, 63, 101, 85, 189, 0, 0, 0},
+ {1, 1, 5, 11, 27, 63, 13, 131, 5, 0, 0, 0},
+ {1, 1, 5, 7, 15, 45, 75, 59, 455, 585, 0, 0},
+ {1, 3, 1, 3, 7, 7, 111, 23, 119, 959, 0, 0},
+ {1, 3, 3, 9, 11, 41, 109, 163, 161, 879, 0, 0},
+ {1, 3, 5, 1, 21, 41, 121, 183, 315, 219, 0, 0},
+ {1, 1, 3, 9, 15, 3, 9, 223, 441, 929, 0, 0},
+ {1, 1, 7, 9, 3, 5, 93, 57, 253, 457, 0, 0},
+ {1, 1, 7, 13, 15, 29, 83, 21, 35, 45, 0, 0},
+ {1, 1, 3, 7, 13, 61, 119, 219, 85, 505, 0, 0},
+ {1, 1, 3, 3, 17, 13, 35, 197, 291, 109, 0, 0},
+ {1, 1, 3, 3, 5, 1, 113, 103, 217, 253, 0, 0},
+ {1, 1, 7, 1, 15, 39, 63, 223, 17, 9, 0, 0},
+ {1, 3, 7, 1, 17, 29, 67, 103, 495, 383, 0, 0},
+ {1, 3, 3, 15, 31, 59, 75, 165, 51, 913, 0, 0},
+ {1, 3, 7, 9, 5, 27, 79, 219, 233, 37, 0, 0},
+ {1, 3, 5, 15, 1, 11, 15, 211, 417, 811, 0, 0},
+ {1, 3, 5, 3, 29, 27, 39, 137, 407, 231, 0, 0},
+ {1, 1, 3, 5, 29, 43, 125, 135, 109, 67, 0, 0},
+ {1, 1, 1, 5, 11, 39, 107, 159, 323, 381, 0, 0},
+ {1, 1, 1, 1, 9, 11, 33, 55, 169, 253, 0, 0},
+ {1, 3, 5, 5, 11, 53, 63, 101, 251, 897, 0, 0},
+ {1, 3, 7, 1, 25, 15, 83, 119, 53, 157, 0, 0},
+ {1, 3, 5, 13, 5, 5, 3, 195, 111, 451, 0, 0},
+ {1, 3, 1, 15, 11, 1, 19, 11, 307, 777, 0, 0},
+ {1, 3, 7, 11, 5, 5, 17, 231, 345, 981, 0, 0},
+ {1, 1, 3, 3, 1, 33, 83, 201, 57, 475, 0, 0},
+ {1, 3, 7, 7, 17, 13, 35, 175, 499, 809, 0, 0},
+ {1, 1, 5, 3, 3, 17, 103, 119, 499, 865, 0, 0},
+ {1, 1, 1, 11, 27, 25, 37, 121, 401, 11, 0, 0},
+ {1, 1, 1, 11, 9, 25, 25, 241, 403, 3, 0, 0},
+ {1, 1, 1, 1, 11, 1, 39, 163, 231, 573, 0, 0},
+ {1, 1, 1, 13, 13, 21, 75, 185, 99, 545, 0, 0},
+ {1, 1, 1, 15, 3, 63, 69, 11, 173, 315, 0, 0},
+ {1, 3, 5, 15, 11, 3, 95, 49, 123, 765, 0, 0},
+ {1, 1, 1, 15, 3, 63, 77, 31, 425, 711, 0, 0},
+ {1, 1, 7, 15, 1, 37, 119, 145, 489, 583, 0, 0},
+ {1, 3, 5, 15, 3, 49, 117, 211, 165, 323, 0, 0},
+ {1, 3, 7, 1, 27, 63, 77, 201, 225, 803, 0, 0},
+ {1, 1, 1, 11, 23, 35, 67, 21, 469, 357, 0, 0},
+ {1, 1, 7, 7, 9, 7, 25, 237, 237, 571, 0, 0},
+ {1, 1, 3, 15, 29, 5, 107, 109, 241, 47, 0, 0},
+ {1, 3, 5, 11, 27, 63, 29, 13, 203, 675, 0, 0},
+ {1, 1, 3, 9, 9, 11, 103, 179, 449, 263, 0, 0},
+ {1, 3, 5, 11, 29, 63, 53, 151, 259, 223, 0, 0},
+ {1, 1, 3, 7, 9, 25, 5, 197, 237, 163, 0, 0},
+ {1, 3, 7, 13, 5, 57, 67, 193, 147, 241, 0, 0},
+ {1, 1, 5, 15, 15, 33, 17, 67, 161, 341, 0, 0},
+ {1, 1, 3, 13, 17, 43, 21, 197, 441, 985, 0, 0},
+ {1, 3, 1, 5, 15, 33, 33, 193, 305, 829, 0, 0},
+ {1, 1, 1, 13, 19, 27, 71, 187, 477, 239, 0, 0},
+ {1, 1, 1, 9, 9, 17, 41, 177, 229, 983, 0, 0},
+ {1, 3, 5, 9, 15, 45, 97, 205, 43, 767, 0, 0},
+ {1, 1, 1, 9, 31, 31, 77, 159, 395, 809, 0, 0},
+ {1, 3, 3, 3, 29, 19, 73, 123, 165, 307, 0, 0},
+ {1, 3, 1, 7, 5, 11, 77, 227, 355, 403, 0, 0},
+ {1, 3, 5, 5, 25, 31, 1, 215, 451, 195, 0, 0},
+ {1, 3, 7, 15, 29, 37, 101, 241, 17, 633, 0, 0},
+ {1, 1, 5, 1, 11, 3, 107, 137, 489, 5, 0, 0},
+ {1, 1, 1, 7, 19, 19, 75, 85, 471, 355, 0, 0},
+ {1, 1, 3, 3, 9, 13, 113, 167, 13, 27, 0, 0},
+ {1, 3, 5, 11, 21, 3, 89, 205, 377, 307, 0, 0},
+ {1, 1, 1, 9, 31, 61, 65, 9, 391, 141, 867, 0},
+ {1, 1, 1, 9, 19, 19, 61, 227, 241, 55, 161, 0},
+ {1, 1, 1, 11, 1, 19, 7, 233, 463, 171, 1941, 0},
+ {1, 1, 5, 7, 25, 13, 103, 75, 19, 1021, 1063, 0},
+ {1, 1, 1, 15, 17, 17, 79, 63, 391, 403, 1221, 0},
+ {1, 3, 3, 11, 29, 25, 29, 107, 335, 475, 963, 0},
+ {1, 3, 5, 1, 31, 33, 49, 43, 155, 9, 1285, 0},
+ {1, 1, 5, 5, 15, 47, 39, 161, 357, 863, 1039, 0},
+ {1, 3, 7, 15, 1, 39, 47, 109, 427, 393, 1103, 0},
+ {1, 1, 1, 9, 9, 29, 121, 233, 157, 99, 701, 0},
+ {1, 1, 1, 7, 1, 29, 75, 121, 439, 109, 993, 0},
+ {1, 1, 1, 9, 5, 1, 39, 59, 89, 157, 1865, 0},
+ {1, 1, 5, 1, 3, 37, 89, 93, 143, 533, 175, 0},
+ {1, 1, 3, 5, 7, 33, 35, 173, 159, 135, 241, 0},
+ {1, 1, 1, 15, 17, 37, 79, 131, 43, 891, 229, 0},
+ {1, 1, 1, 1, 1, 35, 121, 177, 397, 1017, 583, 0},
+ {1, 1, 3, 15, 31, 21, 43, 67, 467, 923, 1473, 0},
+ {1, 1, 1, 7, 1, 33, 77, 111, 125, 771, 1975, 0},
+ {1, 3, 7, 13, 1, 51, 113, 139, 245, 573, 503, 0},
+ {1, 3, 1, 9, 21, 49, 15, 157, 49, 483, 291, 0},
+ {1, 1, 1, 1, 29, 35, 17, 65, 403, 485, 1603, 0},
+ {1, 1, 1, 7, 19, 1, 37, 129, 203, 321, 1809, 0},
+ {1, 3, 7, 15, 15, 9, 5, 77, 29, 485, 581, 0},
+ {1, 1, 3, 5, 15, 49, 97, 105, 309, 875, 1581, 0},
+ {1, 3, 5, 1, 5, 19, 63, 35, 165, 399, 1489, 0},
+ {1, 3, 5, 3, 23, 5, 79, 137, 115, 599, 1127, 0},
+ {1, 1, 7, 5, 3, 61, 27, 177, 257, 91, 841, 0},
+ {1, 1, 3, 5, 9, 31, 91, 209, 409, 661, 159, 0},
+ {1, 3, 1, 15, 23, 39, 23, 195, 245, 203, 947, 0},
+ {1, 1, 3, 1, 15, 59, 67, 95, 155, 461, 147, 0},
+ {1, 3, 7, 5, 23, 25, 87, 11, 51, 449, 1631, 0},
+ {1, 1, 1, 1, 17, 57, 7, 197, 409, 609, 135, 0},
+ {1, 1, 1, 9, 1, 61, 115, 113, 495, 895, 1595, 0},
+ {1, 3, 7, 15, 9, 47, 121, 211, 379, 985, 1755, 0},
+ {1, 3, 1, 3, 7, 57, 27, 231, 339, 325, 1023, 0},
+ {1, 1, 1, 1, 19, 63, 63, 239, 31, 643, 373, 0},
+ {1, 3, 1, 11, 19, 9, 7, 171, 21, 691, 215, 0},
+ {1, 1, 5, 13, 11, 57, 39, 211, 241, 893, 555, 0},
+ {1, 1, 7, 5, 29, 21, 45, 59, 509, 223, 491, 0},
+ {1, 1, 7, 9, 15, 61, 97, 75, 127, 779, 839, 0},
+ {1, 1, 7, 15, 17, 33, 75, 237, 191, 925, 681, 0},
+ {1, 3, 5, 7, 27, 57, 123, 111, 101, 371, 1129, 0},
+ {1, 3, 5, 5, 29, 45, 59, 127, 229, 967, 2027, 0},
+ {1, 1, 1, 1, 17, 7, 23, 199, 241, 455, 135, 0},
+ {1, 1, 7, 15, 27, 29, 105, 171, 337, 503, 1817, 0},
+ {1, 1, 3, 7, 21, 35, 61, 71, 405, 647, 2045, 0},
+ {1, 1, 1, 1, 1, 15, 65, 167, 501, 79, 737, 0},
+ {1, 1, 5, 1, 3, 49, 27, 189, 341, 615, 1287, 0},
+ {1, 1, 1, 9, 1, 7, 31, 159, 503, 327, 1613, 0},
+ {1, 3, 3, 3, 3, 23, 99, 115, 323, 997, 987, 0},
+ {1, 1, 1, 9, 19, 33, 93, 247, 509, 453, 891, 0},
+ {1, 1, 3, 1, 13, 19, 35, 153, 161, 633, 445, 0},
+ {1, 3, 5, 15, 31, 5, 87, 197, 183, 783, 1823, 0},
+ {1, 1, 7, 5, 19, 63, 69, 221, 129, 231, 1195, 0},
+ {1, 1, 5, 5, 13, 23, 19, 231, 245, 917, 379, 0},
+ {1, 3, 1, 15, 19, 43, 27, 223, 171, 413, 125, 0},
+ {1, 1, 1, 9, 1, 59, 21, 15, 509, 207, 589, 0},
+ {1, 3, 5, 3, 19, 31, 113, 19, 23, 733, 499, 0},
+ {1, 1, 7, 1, 19, 51, 101, 165, 47, 925, 1093, 0},
+ {1, 3, 3, 9, 15, 21, 43, 243, 237, 461, 1361, 0},
+ {1, 1, 1, 9, 17, 15, 75, 75, 113, 715, 1419, 0},
+ {1, 1, 7, 13, 17, 1, 99, 15, 347, 721, 1405, 0},
+ {1, 1, 7, 15, 7, 27, 23, 183, 39, 59, 571, 0},
+ {1, 3, 5, 9, 7, 43, 35, 165, 463, 567, 859, 0},
+ {1, 3, 3, 11, 15, 19, 17, 129, 311, 343, 15, 0},
+ {1, 1, 1, 15, 31, 59, 63, 39, 347, 359, 105, 0},
+ {1, 1, 1, 15, 5, 43, 87, 241, 109, 61, 685, 0},
+ {1, 1, 7, 7, 9, 39, 121, 127, 369, 579, 853, 0},
+ {1, 1, 1, 1, 17, 15, 15, 95, 325, 627, 299, 0},
+ {1, 1, 3, 13, 31, 53, 85, 111, 289, 811, 1635, 0},
+ {1, 3, 7, 1, 19, 29, 75, 185, 153, 573, 653, 0},
+ {1, 3, 7, 1, 29, 31, 55, 91, 249, 247, 1015, 0},
+ {1, 3, 5, 7, 1, 49, 113, 139, 257, 127, 307, 0},
+ {1, 3, 5, 9, 15, 15, 123, 105, 105, 225, 1893, 0},
+ {1, 3, 3, 1, 15, 5, 105, 249, 73, 709, 1557, 0},
+ {1, 1, 1, 9, 17, 31, 113, 73, 65, 701, 1439, 0},
+ {1, 3, 5, 15, 13, 21, 117, 131, 243, 859, 323, 0},
+ {1, 1, 1, 9, 19, 15, 69, 149, 89, 681, 515, 0},
+ {1, 1, 1, 5, 29, 13, 21, 97, 301, 27, 967, 0},
+ {1, 1, 3, 3, 15, 45, 107, 227, 495, 769, 1935, 0},
+ {1, 1, 1, 11, 5, 27, 41, 173, 261, 703, 1349, 0},
+ {1, 3, 3, 3, 11, 35, 97, 43, 501, 563, 1331, 0},
+ {1, 1, 1, 7, 1, 17, 87, 17, 429, 245, 1941, 0},
+ {1, 1, 7, 15, 29, 13, 1, 175, 425, 233, 797, 0},
+ {1, 1, 3, 11, 21, 57, 49, 49, 163, 685, 701, 0},
+ {1, 3, 3, 7, 11, 45, 107, 111, 379, 703, 1403, 0},
+ {1, 1, 7, 3, 21, 7, 117, 49, 469, 37, 775, 0},
+ {1, 1, 5, 15, 31, 63, 101, 77, 507, 489, 1955, 0},
+ {1, 3, 3, 11, 19, 21, 101, 255, 203, 673, 665, 0},
+ {1, 3, 3, 15, 17, 47, 125, 187, 271, 899, 2003, 0},
+ {1, 1, 7, 7, 1, 35, 13, 235, 5, 337, 905, 0},
+ {1, 3, 1, 15, 1, 43, 1, 27, 37, 695, 1429, 0},
+ {1, 3, 1, 11, 21, 27, 93, 161, 299, 665, 495, 0},
+ {1, 3, 3, 15, 3, 1, 81, 111, 105, 547, 897, 0},
+ {1, 3, 5, 1, 3, 53, 97, 253, 401, 827, 1467, 0},
+ {1, 1, 1, 5, 19, 59, 105, 125, 271, 351, 719, 0},
+ {1, 3, 5, 13, 7, 11, 91, 41, 441, 759, 1827, 0},
+ {1, 3, 7, 11, 29, 61, 61, 23, 307, 863, 363, 0},
+ {1, 1, 7, 1, 15, 35, 29, 133, 415, 473, 1737, 0},
+ {1, 1, 1, 13, 7, 33, 35, 225, 117, 681, 1545, 0},
+ {1, 1, 1, 3, 5, 41, 83, 247, 13, 373, 1091, 0},
+ {1, 3, 1, 13, 25, 61, 71, 217, 233, 313, 547, 0},
+ {1, 3, 1, 7, 3, 29, 3, 49, 93, 465, 15, 0},
+ {1, 1, 1, 9, 17, 61, 99, 163, 129, 485, 1087, 0},
+ {1, 1, 1, 9, 9, 33, 31, 163, 145, 649, 253, 0},
+ {1, 1, 1, 1, 17, 63, 43, 235, 287, 111, 567, 0},
+ {1, 3, 5, 13, 29, 7, 11, 69, 153, 127, 449, 0},
+ {1, 1, 5, 9, 11, 21, 15, 189, 431, 493, 1219, 0},
+ {1, 1, 1, 15, 19, 5, 47, 91, 399, 293, 1743, 0},
+ {1, 3, 3, 11, 29, 53, 53, 225, 409, 303, 333, 0},
+ {1, 1, 1, 15, 31, 31, 21, 81, 147, 287, 1753, 0},
+ {1, 3, 5, 5, 5, 63, 35, 125, 41, 687, 1793, 0},
+ {1, 1, 1, 9, 19, 59, 107, 219, 455, 971, 297, 0},
+ {1, 1, 3, 5, 3, 51, 121, 31, 245, 105, 1311, 0},
+ {1, 3, 1, 5, 5, 57, 75, 107, 161, 431, 1693, 0},
+ {1, 3, 1, 3, 19, 53, 27, 31, 191, 565, 1015, 0},
+ {1, 3, 5, 13, 9, 41, 35, 249, 287, 49, 123, 0},
+ {1, 1, 5, 7, 27, 17, 21, 3, 151, 885, 1165, 0},
+ {1, 1, 7, 1, 15, 17, 65, 139, 427, 339, 1171, 0},
+ {1, 1, 1, 5, 23, 5, 9, 89, 321, 907, 391, 0},
+ {1, 1, 7, 9, 15, 1, 77, 71, 87, 701, 917, 0},
+ {1, 1, 7, 1, 17, 37, 115, 127, 469, 779, 1543, 0},
+ {1, 3, 7, 3, 5, 61, 15, 37, 301, 951, 1437, 0},
+ {1, 1, 1, 13, 9, 51, 127, 145, 229, 55, 1567, 0},
+ {1, 3, 7, 15, 19, 47, 53, 153, 295, 47, 1337, 0},
+ {1, 3, 3, 5, 11, 31, 29, 133, 327, 287, 507, 0},
+ {1, 1, 7, 7, 25, 31, 37, 199, 25, 927, 1317, 0},
+ {1, 1, 7, 9, 3, 39, 127, 167, 345, 467, 759, 0},
+ {1, 1, 1, 1, 31, 21, 15, 101, 293, 787, 1025, 0},
+ {1, 1, 5, 3, 11, 41, 105, 109, 149, 837, 1813, 0},
+ {1, 1, 3, 5, 29, 13, 19, 97, 309, 901, 753, 0},
+ {1, 1, 7, 1, 19, 17, 31, 39, 173, 361, 1177, 0},
+ {1, 3, 3, 3, 3, 41, 81, 7, 341, 491, 43, 0},
+ {1, 1, 7, 7, 31, 35, 29, 77, 11, 335, 1275, 0},
+ {1, 3, 3, 15, 17, 45, 19, 63, 151, 849, 129, 0},
+ {1, 1, 7, 5, 7, 13, 47, 73, 79, 31, 499, 0},
+ {1, 3, 1, 11, 1, 41, 59, 151, 247, 115, 1295, 0},
+ {1, 1, 1, 9, 31, 37, 73, 23, 295, 483, 179, 0},
+ {1, 3, 1, 15, 13, 63, 81, 27, 169, 825, 2037, 0},
+ {1, 3, 5, 15, 7, 11, 73, 1, 451, 101, 2039, 0},
+ {1, 3, 5, 3, 13, 53, 31, 137, 173, 319, 1521, 0},
+ {1, 3, 1, 3, 29, 1, 73, 227, 377, 337, 1189, 0},
+ {1, 3, 3, 13, 27, 9, 31, 101, 229, 165, 1983, 0},
+ {1, 3, 1, 13, 13, 19, 19, 111, 319, 421, 223, 0},
+ {1, 1, 7, 15, 25, 37, 61, 55, 359, 255, 1955, 0},
+ {1, 1, 5, 13, 17, 43, 49, 215, 383, 915, 51, 0},
+ {1, 1, 3, 1, 3, 7, 13, 119, 155, 585, 967, 0},
+ {1, 3, 1, 13, 1, 63, 125, 21, 103, 287, 457, 0},
+ {1, 1, 7, 1, 31, 17, 125, 137, 345, 379, 1925, 0},
+ {1, 1, 3, 5, 5, 25, 119, 153, 455, 271, 2023, 0},
+ {1, 1, 7, 9, 9, 37, 115, 47, 5, 255, 917, 0},
+ {1, 3, 5, 3, 31, 21, 75, 203, 489, 593, 1, 0},
+ {1, 3, 7, 15, 19, 63, 123, 153, 135, 977, 1875, 0},
+ {1, 1, 1, 1, 5, 59, 31, 25, 127, 209, 745, 0},
+ {1, 1, 1, 1, 19, 45, 67, 159, 301, 199, 535, 0},
+ {1, 1, 7, 1, 31, 17, 19, 225, 369, 125, 421, 0},
+ {1, 3, 3, 11, 7, 59, 115, 197, 459, 469, 1055, 0},
+ {1, 3, 1, 3, 27, 45, 35, 131, 349, 101, 411, 0},
+ {1, 3, 7, 11, 9, 3, 67, 145, 299, 253, 1339, 0},
+ {1, 3, 3, 11, 9, 37, 123, 229, 273, 269, 515, 0},
+ {1, 3, 7, 15, 11, 25, 75, 5, 367, 217, 951, 0},
+ {1, 1, 3, 7, 9, 23, 63, 237, 385, 159, 1273, 0},
+ {1, 1, 5, 11, 23, 5, 55, 193, 109, 865, 663, 0},
+ {1, 1, 7, 15, 1, 57, 17, 141, 51, 217, 1259, 0},
+ {1, 1, 3, 3, 15, 7, 89, 233, 71, 329, 203, 0},
+ {1, 3, 7, 11, 11, 1, 19, 155, 89, 437, 573, 0},
+ {1, 3, 1, 9, 27, 61, 47, 109, 161, 913, 1681, 0},
+ {1, 1, 7, 15, 1, 33, 19, 15, 23, 913, 989, 0},
+ {1, 3, 1, 1, 25, 39, 119, 193, 13, 571, 157, 0},
+ {1, 1, 7, 13, 9, 55, 59, 147, 361, 935, 515, 0},
+ {1, 1, 1, 9, 7, 59, 67, 117, 71, 855, 1493, 0},
+ {1, 3, 1, 3, 13, 19, 57, 141, 305, 275, 1079, 0},
+ {1, 1, 1, 9, 17, 61, 33, 7, 43, 931, 781, 0},
+ {1, 1, 3, 1, 11, 17, 21, 97, 295, 277, 1721, 0},
+ {1, 3, 1, 13, 15, 43, 11, 241, 147, 391, 1641, 0},
+ {1, 1, 1, 1, 1, 19, 37, 21, 255, 263, 1571, 0},
+ {1, 1, 3, 3, 23, 59, 89, 17, 475, 303, 757, 543},
+ {1, 3, 3, 9, 11, 55, 35, 159, 139, 203, 1531, 1825},
+ {1, 1, 5, 3, 17, 53, 51, 241, 269, 949, 1373, 325},
+ {1, 3, 7, 7, 5, 29, 91, 149, 239, 193, 1951, 2675},
+ {1, 3, 5, 1, 27, 33, 69, 11, 51, 371, 833, 2685},
+ {1, 1, 1, 15, 1, 17, 35, 57, 171, 1007, 449, 367},
+ {1, 1, 1, 7, 25, 61, 73, 219, 379, 53, 589, 4065},
+ {1, 3, 5, 13, 21, 29, 45, 19, 163, 169, 147, 597},
+ {1, 1, 5, 11, 21, 27, 7, 17, 237, 591, 255, 1235},
+ {1, 1, 7, 7, 17, 41, 69, 237, 397, 173, 1229, 2341},
+ {1, 1, 3, 1, 1, 33, 125, 47, 11, 783, 1323, 2469},
+ {1, 3, 1, 11, 3, 39, 35, 133, 153, 55, 1171, 3165},
+ {1, 1, 5, 11, 27, 23, 103, 245, 375, 753, 477, 2165},
+ {1, 3, 1, 15, 15, 49, 127, 223, 387, 771, 1719, 1465},
+ {1, 1, 1, 9, 11, 9, 17, 185, 239, 899, 1273, 3961},
+ {1, 1, 3, 13, 11, 51, 73, 81, 389, 647, 1767, 1215},
+ {1, 3, 5, 15, 19, 9, 69, 35, 349, 977, 1603, 1435},
+ {1, 1, 1, 1, 19, 59, 123, 37, 41, 961, 181, 1275},
+ {1, 1, 1, 1, 31, 29, 37, 71, 205, 947, 115, 3017},
+ {1, 1, 7, 15, 5, 37, 101, 169, 221, 245, 687, 195},
+ {1, 1, 1, 1, 19, 9, 125, 157, 119, 283, 1721, 743},
+ {1, 1, 7, 3, 1, 7, 61, 71, 119, 257, 1227, 2893},
+ {1, 3, 3, 3, 25, 41, 25, 225, 31, 57, 925, 2139}
+ };
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/hups/SubsetOfPointSet.java b/source/umontreal/iro/lecuyer/hups/SubsetOfPointSet.java
new file mode 100644
index 0000000..56838c7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/SubsetOfPointSet.java
@@ -0,0 +1,390 @@
+
+
+/*
+ * Class: SubsetOfPointSet
+ * Description: Subset of a point set
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.hups;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+ /*Attention: No array index range tests neither for the dimension
+ nor for the number of points is performed. This is left to JAVA. */
+
+
+/**
+ * This container class permits one to select a subset of a point set.
+ * This is done by selecting a range or providing an array of either
+ * point or coordinate indices. A typical application of a range selection
+ * is to make the number of points or the dimension finite. It is also
+ * possible to provide, for example, a random permutation in the selection of
+ * components. It is possible also to take <SPAN CLASS="textit">projections</SPAN> of coordinates
+ * for selected dimensions.
+ *
+ * <P>
+ * Selecting a new subset of points or coordinates overwrites the previous
+ * selection. The specification of a subset with respect to the points
+ * is independent from selecting a subset with respect to the coordinates.
+ * The number of points and the dimension are adapted
+ * to the current selection and all indices still start from 0,
+ * i.e., the subset works just like an ordinary point set.
+ *
+ * <P>
+ * When the points or coordinates ranges are changed, existing iterators
+ * become invalid. They should be reconstructed or reset to avoid
+ * inconsistencies.
+ *
+ */
+public class SubsetOfPointSet extends PointSet {
+ protected PointSet P; // Source points
+ protected int i_from, i_to, i_index[]; // Limits or lookup for row
+ protected int j_from, j_to, j_index[]; // Limits or lookup for column
+
+
+
+
+ /**
+ * Constructs a new {@link PointSet} object, initially identical to <TT>P</TT>,
+ * and from which a subset of the points and/or a subset of the coordinates
+ * is to be extracted.
+ *
+ * @param P point set for which a subset is constructed
+ *
+ */
+ public SubsetOfPointSet (PointSet P) {
+ this.P = P;
+ numPoints = P.getNumPoints();
+ dim = P.getDimension();
+ i_from = 0;
+ i_to = P.getNumPoints();
+ j_from = 0;
+ j_to = P.getDimension();
+ }
+
+
+ /**
+ * Selects the points numbered from ``<TT>from</TT>'' to ``<TT>to - 1</TT>'' from the
+ * original point set.
+ *
+ * @param from index of the point, in the contained point set,
+ * corresponding to the point 0 of this point set
+ *
+ * @param to index of the last point taken from the contained point set
+ *
+ *
+ */
+ public void selectPointsRange (int from, int to) {
+ if (0 > from || from >= to || to > P.getNumPoints())
+ throw new IllegalArgumentException ("Invalid range for points");
+
+ i_index = null;
+ i_from = from;
+ i_to = to;
+ numPoints = to - from;
+ }
+
+
+ /**
+ * Selects the <TT>numPoints</TT> points whose numbers are provided in the array
+ * <TT>pointIndices</TT>.
+ *
+ * @param pointIndices array of point indices to be selected
+ *
+ * @param numPoints number of points in the subset of point set
+ *
+ *
+ */
+ public void selectPoints (int[] pointIndices, int numPoints) {
+ if (numPoints > P.getNumPoints() || numPoints > pointIndices.length)
+ throw new IllegalArgumentException ("Number of indices too large");
+
+ i_index = pointIndices;
+ this.numPoints = numPoints;
+ }
+
+
+ /**
+ * Selects the coordinates from ``<TT>from</TT>'' to ``<TT>to - 1</TT>'' from the
+ * original point set.
+ *
+ * @param from index of the coordinate, in the contained point set,
+ * corresponding to the coordinate 0 of each point of this point set
+ *
+ * @param to index of the last coordinate taken for each point
+ * from the contained point set
+ *
+ *
+ */
+ public void selectCoordinatesRange (int from, int to) {
+ if (0 > from || from >= to || to > P.getDimension())
+ throw new IllegalArgumentException("Invalid column range");
+
+ j_index = null;
+ j_from = from;
+ j_to = to;
+ dim = to - from;
+ }
+
+
+ /**
+ * Selects the <TT>numCoord</TT> coordinates whose numbers are provided in
+ * the array <TT>coordIndices</TT>.
+ *
+ * @param coordIndices array of coordinate indices to be selected
+ *
+ * @param numCoord number of coordinatess for each point in the subset of point set
+ *
+ *
+ */
+ public void selectCoordinates (int[] coordIndices, int numCoord) {
+ if (numCoord > P.getDimension() || numCoord > coordIndices.length)
+ throw new IllegalArgumentException ("Number of indices too large");
+
+ j_index = coordIndices;
+ this.dim = numCoord;
+ }
+
+
+ public double getCoordinate (int i, int j) {
+ int access_i, access_j;
+
+ // if no range check done: left to JAVA array index check
+
+ if (i_index == null) {
+ if (i < 0 || i >= numPoints)
+ throw new IllegalArgumentException ("Row out of range");
+
+ access_i = i + i_from;
+ } else
+ access_i = i_index[i];
+
+ if (j_index == null) {
+ if (j < 0 || j > dim)
+ throw new IllegalArgumentException("Column out of range");
+
+ access_j = j + j_from;
+ } else
+ access_j = j_index[j];
+
+ return P.getCoordinate (access_i, access_j);
+ }
+
+ public PointSetIterator iterator() {
+ return new SubsetIterator();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Subset of point set" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Inner point set information {" + PrintfFormat.NEWLINE);
+ sb.append (P.toString());
+ sb.append (PrintfFormat.NEWLINE + "}" + PrintfFormat.NEWLINE);
+
+ if (i_index == null)
+ sb.append ("Points range from " + i_from + " to " + i_to + "." +
+ PrintfFormat.NEWLINE);
+ else {
+ sb.append ("Point indices: [");
+ boolean first = true;
+ for (int i = 0; i < numPoints; i++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (i_index[i]);
+ }
+ sb.append ("]" + PrintfFormat.NEWLINE);
+ }
+
+ if (j_index == null)
+ sb.append ("Coordinates range from " + j_from + " to " + j_to + ".");
+ else {
+ sb.append ("Coordinate indices: [");
+ boolean first = true;
+ for (int i = 0; i < dim; i++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (j_index[i]);
+ }
+ sb.append ("]");
+ }
+
+ return sb.toString();
+ }
+
+
+ // ***********************************************************
+
+ private class SubsetIterator extends DefaultPointSetIterator {
+
+ private PointSetIterator innerIterator;
+/*
+ private int i_from;
+ private int i_to;
+ private int j_from;
+ private int j_to;
+ private int[] i_index;
+ private int[] j_index;
+*/
+
+ SubsetIterator() {
+ // Since one can change range after construction, we
+ // must save the current one.
+ //this.i_from = SubsetOfPointSet.this.i_from;
+ //this.i_to = SubsetOfPointSet.this.i_to;
+ //this.j_from = SubsetOfPointSet.this.j_from;
+ //this.j_to = SubsetOfPointSet.this.j_to;
+
+ // Also recopy indices in case one has set the indices,
+ // kept the array and modified it after construction.
+ //if (SubsetOfPointSet.this.i_index == null)
+ // this.i_index = null;
+ //else {
+ // this.i_index = new int[SubsetOfPointSet.this.i_index.length];
+ // System.arraycopy (SubsetOfPointSet.this.i_index, 0,
+ // this.i_index, 0, numPoints);
+ //}
+
+ //if (SubsetOfPointSet.this.j_index == null)
+ // this.j_index = null;
+ //else {
+ // this.j_index = new int[SubsetOfPointSet.this.j_index.length];
+ // System.arraycopy (SubsetOfPointSet.this.j_index, 0, this.j_index, 0, dim);
+ //}
+
+ // Create the inner iterator and set its state according to the subset.
+ innerIterator = P.iterator();
+ if (i_index == null) {
+ if (i_from != 0)
+ innerIterator.setCurPointIndex (i_from);
+ }
+ else {
+ if (i_index[0] != 0)
+ innerIterator.setCurPointIndex (i_index[0]);
+ }
+
+ if (j_index == null) {
+ if (j_from != 0)
+ innerIterator.setCurCoordIndex (j_from);
+ }
+ else {
+ if (j_index[0] != 0)
+ innerIterator.setCurCoordIndex (j_index[0]);
+ }
+ }
+
+ public void setCurCoordIndex (int j) {
+ if (j_index == null)
+ innerIterator.setCurCoordIndex (j + j_from);
+ else
+ innerIterator.setCurCoordIndex (j_index[j]);
+ curCoordIndex = j;
+ }
+
+ public void resetCurCoordIndex() {
+ if (j_index == null) {
+ if (j_from == 0)
+ innerIterator.resetCurCoordIndex();
+ else
+ innerIterator.setCurCoordIndex (j_from);
+ }
+ else {
+ if (j_index[0] == 0)
+ innerIterator.resetCurCoordIndex();
+ else
+ innerIterator.setCurCoordIndex (j_index[0]);
+ }
+ curCoordIndex = 0;
+ }
+
+ public double nextCoordinate() {
+ if ((curPointIndex >= numPoints) || (curCoordIndex >= dim))
+ outOfBounds();
+ // The inner iterator could throw an exception.
+ // If that happens, e must not alter the current coordinate.
+ double coord = 0.0;
+
+ if (j_index == null)
+ coord = innerIterator.nextCoordinate();
+ else {
+ int currentIndex = j_index[curCoordIndex];
+ int futureIndex = (curCoordIndex+1) == dim ?
+ currentIndex+1 : j_index[curCoordIndex+1];
+ coord = innerIterator.nextCoordinate();
+ if (futureIndex != (currentIndex+1))
+ innerIterator.setCurCoordIndex (futureIndex);
+ }
+ curCoordIndex++;
+ return coord;
+ }
+
+ public void nextCoordinates (double[] p, int d) {
+ if (curPointIndex >= numPoints || curCoordIndex + d > dim)
+ outOfBounds();
+ if (j_index != null) {
+ super.nextCoordinates (p, d);
+ return;
+ }
+ innerIterator.nextCoordinates (p, d);
+ curCoordIndex += d;
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i_index == null)
+ innerIterator.setCurPointIndex (i + i_from);
+ else
+ innerIterator.setCurPointIndex (i_index[i]);
+ curPointIndex = i;
+ resetCurCoordIndex();
+ }
+
+ public void resetCurPointIndex() {
+ if (i_index == null) {
+ if (i_from == 0)
+ innerIterator.resetCurPointIndex();
+ else
+ innerIterator.setCurPointIndex (i_from);
+ }
+ else {
+ if (i_index[0] == 0)
+ innerIterator.resetCurPointIndex();
+ else
+ innerIterator.setCurPointIndex (i_index[0]);
+ }
+ curPointIndex = 0;
+ resetCurCoordIndex();
+ }
+
+ public int resetToNextPoint() {
+ if (i_index == null)
+ innerIterator.resetToNextPoint();
+ else if (curPointIndex < (numPoints-1))
+ innerIterator.setCurPointIndex (i_index[curPointIndex + 1]);
+ curPointIndex++;
+ resetCurCoordIndex();
+ return curPointIndex;
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/hups/SubsetOfPointSet.tex b/source/umontreal/iro/lecuyer/hups/SubsetOfPointSet.tex
new file mode 100644
index 0000000..6f9c0b2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/SubsetOfPointSet.tex
@@ -0,0 +1,412 @@
+\defmodule{SubsetOfPointSet}
+
+This container class permits one to select a subset of a point set.
+This is done by selecting a range or providing an array of either
+point or coordinate indices. A typical application of a range selection
+is to make the number of points or the dimension finite. It is also
+possible to provide, for example, a random permutation in the selection of
+components. It is possible also to take \emph{projections} of coordinates
+for selected dimensions.
+
+Selecting a new subset of points or coordinates overwrites the previous
+selection. The specification of a subset with respect to the points
+is independent from selecting a subset with respect to the coordinates.
+The number of points and the dimension are adapted
+to the current selection and all indices still start from 0,
+i.e., the subset works just like an ordinary point set.
+
+%\begin{itemize}
+%\item Creation first, extraction later via methods.
+%\item Extraction is always with respect to the original \texttt{P}
+%passed to the constructor.
+%\end{itemize}
+
+%When creating an iterator, the selected ranges are saved in it.
+%Any modification to the range from the \texttt{PointSet} will not
+%affect the past iterators but will influence the future ones.
+When the points or coordinates ranges are changed, existing iterators
+become invalid. They should be reconstructed or reset to avoid
+inconsistencies.
+
+\hpierre{This class does not extend \class{ContainerPointSet}{},
+ because its iterators must maintain their own indices in addition
+ to having inner iterators.}
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: SubsetOfPointSet
+ * Description: Subset of a point set
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.hups;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+ /*Attention: No array index range tests neither for the dimension
+ nor for the number of points is performed. This is left to JAVA. */
+\end{hide}
+
+public class SubsetOfPointSet extends PointSet \begin{hide} {
+ protected PointSet P; // Source points
+ protected int i_from, i_to, i_index[]; // Limits or lookup for row
+ protected int j_from, j_to, j_index[]; // Limits or lookup for column
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+\begin{code}
+
+ public SubsetOfPointSet (PointSet P) \begin{hide} {
+ this.P = P;
+ numPoints = P.getNumPoints();
+ dim = P.getDimension();
+ i_from = 0;
+ i_to = P.getNumPoints();
+ j_from = 0;
+ j_to = P.getDimension();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a new \class{PointSet}{} object, initially identical to \texttt{P},
+ and from which a subset of the points and/or a subset of the coordinates
+ is to be extracted.
+% The point set \texttt{P} in unaffected.
+ \end{tabb}
+\begin{htmlonly}
+ \param{P}{point set for which a subset is constructed}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public void selectPointsRange (int from, int to) \begin{hide} {
+ if (0 > from || from >= to || to > P.getNumPoints())
+ throw new IllegalArgumentException ("Invalid range for points");
+
+ i_index = null;
+ i_from = from;
+ i_to = to;
+ numPoints = to - from;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Selects the points numbered from ``\texttt{from}'' to ``\texttt{to - 1}'' from the
+ original point set.
+ \end{tabb}
+\begin{htmlonly}
+ \param{from}{index of the point, in the contained point set,
+ corresponding to the point 0 of this point set}
+ \param{to}{index of the last point taken from the contained point set}
+\end{htmlonly}
+\begin{code}
+
+ public void selectPoints (int[] pointIndices, int numPoints) \begin{hide} {
+ if (numPoints > P.getNumPoints() || numPoints > pointIndices.length)
+ throw new IllegalArgumentException ("Number of indices too large");
+
+ i_index = pointIndices;
+ this.numPoints = numPoints;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Selects the \texttt{numPoints} points whose numbers are provided in the array
+ \texttt{pointIndices}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{pointIndices}{array of point indices to be selected}
+ \param{numPoints}{number of points in the subset of point set}
+\end{htmlonly}
+\begin{code}
+
+ public void selectCoordinatesRange (int from, int to) \begin{hide} {
+ if (0 > from || from >= to || to > P.getDimension())
+ throw new IllegalArgumentException("Invalid column range");
+
+ j_index = null;
+ j_from = from;
+ j_to = to;
+ dim = to - from;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Selects the coordinates from ``\texttt{from}'' to ``\texttt{to - 1}'' from the
+ original point set.
+ \end{tabb}
+\begin{htmlonly}
+ \param{from}{index of the coordinate, in the contained point set,
+ corresponding to the coordinate 0 of each point of this point set}
+ \param{to}{index of the last coordinate taken for each point
+ from the contained point set}
+\end{htmlonly}
+\begin{code}
+
+ public void selectCoordinates (int[] coordIndices, int numCoord) \begin{hide} {
+ if (numCoord > P.getDimension() || numCoord > coordIndices.length)
+ throw new IllegalArgumentException ("Number of indices too large");
+
+ j_index = coordIndices;
+ this.dim = numCoord;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Selects the \texttt{numCoord} coordinates whose numbers are provided in
+ the array \texttt{coordIndices}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{coordIndices}{array of coordinate indices to be selected}
+ \param{numCoord}{number of coordinatess for each point in the subset of point set}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double getCoordinate (int i, int j) {
+ int access_i, access_j;
+
+ // if no range check done: left to JAVA array index check
+
+ if (i_index == null) {
+ if (i < 0 || i >= numPoints)
+ throw new IllegalArgumentException ("Row out of range");
+
+ access_i = i + i_from;
+ } else
+ access_i = i_index[i];
+
+ if (j_index == null) {
+ if (j < 0 || j > dim)
+ throw new IllegalArgumentException("Column out of range");
+
+ access_j = j + j_from;
+ } else
+ access_j = j_index[j];
+
+ return P.getCoordinate (access_i, access_j);
+ }
+
+ public PointSetIterator iterator() {
+ return new SubsetIterator();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Subset of point set" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Inner point set information {" + PrintfFormat.NEWLINE);
+ sb.append (P.toString());
+ sb.append (PrintfFormat.NEWLINE + "}" + PrintfFormat.NEWLINE);
+
+ if (i_index == null)
+ sb.append ("Points range from " + i_from + " to " + i_to + "." +
+ PrintfFormat.NEWLINE);
+ else {
+ sb.append ("Point indices: [");
+ boolean first = true;
+ for (int i = 0; i < numPoints; i++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (i_index[i]);
+ }
+ sb.append ("]" + PrintfFormat.NEWLINE);
+ }
+
+ if (j_index == null)
+ sb.append ("Coordinates range from " + j_from + " to " + j_to + ".");
+ else {
+ sb.append ("Coordinate indices: [");
+ boolean first = true;
+ for (int i = 0; i < dim; i++) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (j_index[i]);
+ }
+ sb.append ("]");
+ }
+
+ return sb.toString();
+ }
+
+
+ // ***********************************************************
+
+ private class SubsetIterator extends DefaultPointSetIterator {
+
+ private PointSetIterator innerIterator;
+/*
+ private int i_from;
+ private int i_to;
+ private int j_from;
+ private int j_to;
+ private int[] i_index;
+ private int[] j_index;
+*/
+
+ SubsetIterator() {
+ // Since one can change range after construction, we
+ // must save the current one.
+ //this.i_from = SubsetOfPointSet.this.i_from;
+ //this.i_to = SubsetOfPointSet.this.i_to;
+ //this.j_from = SubsetOfPointSet.this.j_from;
+ //this.j_to = SubsetOfPointSet.this.j_to;
+
+ // Also recopy indices in case one has set the indices,
+ // kept the array and modified it after construction.
+ //if (SubsetOfPointSet.this.i_index == null)
+ // this.i_index = null;
+ //else {
+ // this.i_index = new int[SubsetOfPointSet.this.i_index.length];
+ // System.arraycopy (SubsetOfPointSet.this.i_index, 0,
+ // this.i_index, 0, numPoints);
+ //}
+
+ //if (SubsetOfPointSet.this.j_index == null)
+ // this.j_index = null;
+ //else {
+ // this.j_index = new int[SubsetOfPointSet.this.j_index.length];
+ // System.arraycopy (SubsetOfPointSet.this.j_index, 0, this.j_index, 0, dim);
+ //}
+
+ // Create the inner iterator and set its state according to the subset.
+ innerIterator = P.iterator();
+ if (i_index == null) {
+ if (i_from != 0)
+ innerIterator.setCurPointIndex (i_from);
+ }
+ else {
+ if (i_index[0] != 0)
+ innerIterator.setCurPointIndex (i_index[0]);
+ }
+
+ if (j_index == null) {
+ if (j_from != 0)
+ innerIterator.setCurCoordIndex (j_from);
+ }
+ else {
+ if (j_index[0] != 0)
+ innerIterator.setCurCoordIndex (j_index[0]);
+ }
+ }
+
+ public void setCurCoordIndex (int j) {
+ if (j_index == null)
+ innerIterator.setCurCoordIndex (j + j_from);
+ else
+ innerIterator.setCurCoordIndex (j_index[j]);
+ curCoordIndex = j;
+ }
+
+ public void resetCurCoordIndex() {
+ if (j_index == null) {
+ if (j_from == 0)
+ innerIterator.resetCurCoordIndex();
+ else
+ innerIterator.setCurCoordIndex (j_from);
+ }
+ else {
+ if (j_index[0] == 0)
+ innerIterator.resetCurCoordIndex();
+ else
+ innerIterator.setCurCoordIndex (j_index[0]);
+ }
+ curCoordIndex = 0;
+ }
+
+ public double nextCoordinate() {
+ if ((curPointIndex >= numPoints) || (curCoordIndex >= dim))
+ outOfBounds();
+ // The inner iterator could throw an exception.
+ // If that happens, e must not alter the current coordinate.
+ double coord = 0.0;
+
+ if (j_index == null)
+ coord = innerIterator.nextCoordinate();
+ else {
+ int currentIndex = j_index[curCoordIndex];
+ int futureIndex = (curCoordIndex+1) == dim ?
+ currentIndex+1 : j_index[curCoordIndex+1];
+ coord = innerIterator.nextCoordinate();
+ if (futureIndex != (currentIndex+1))
+ innerIterator.setCurCoordIndex (futureIndex);
+ }
+ curCoordIndex++;
+ return coord;
+ }
+
+ public void nextCoordinates (double[] p, int d) {
+ if (curPointIndex >= numPoints || curCoordIndex + d > dim)
+ outOfBounds();
+ if (j_index != null) {
+ super.nextCoordinates (p, d);
+ return;
+ }
+ innerIterator.nextCoordinates (p, d);
+ curCoordIndex += d;
+ }
+
+ public void setCurPointIndex (int i) {
+ if (i_index == null)
+ innerIterator.setCurPointIndex (i + i_from);
+ else
+ innerIterator.setCurPointIndex (i_index[i]);
+ curPointIndex = i;
+ resetCurCoordIndex();
+ }
+
+ public void resetCurPointIndex() {
+ if (i_index == null) {
+ if (i_from == 0)
+ innerIterator.resetCurPointIndex();
+ else
+ innerIterator.setCurPointIndex (i_from);
+ }
+ else {
+ if (i_index[0] == 0)
+ innerIterator.resetCurPointIndex();
+ else
+ innerIterator.setCurPointIndex (i_index[0]);
+ }
+ curPointIndex = 0;
+ resetCurCoordIndex();
+ }
+
+ public int resetToNextPoint() {
+ if (i_index == null)
+ innerIterator.resetToNextPoint();
+ else if (curPointIndex < (numPoints-1))
+ innerIterator.setCurPointIndex (i_index[curPointIndex + 1]);
+ curPointIndex++;
+ resetCurCoordIndex();
+ return curPointIndex;
+ }
+ }
+}\end{hide}
+\end{code}
+
+\pierre{Code \`a reviser.}
diff --git a/source/umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat b/source/umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat
new file mode 100644
index 0000000..66b8046
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/dataLFSR/j1_k11.dat
@@ -0,0 +1,20 @@
+#-------------------------------------------------------#
+# Equidistributed LFSR generator with one composent
+# Criteria : DELTA(k,k,k/2) <= 1
+#-------------------------------------------------------#
+# Format :
+#
+# Number Of Component (only at the beginning of the file)
+#
+# Comments (value of the criteria)
+# s
+# Non-zeros coefficients of the polynomial
+#-------------------------------------------------------#
+
+
+1
+
+# DELTA = 1
+5
+11 5 3 1 0
+
diff --git a/source/umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat b/source/umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat
new file mode 100644
index 0000000..0332d79
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/dataLFSR/j2_k17.dat
@@ -0,0 +1,45 @@
+#-------------------------------------------------------#
+# Equidistributed LFSR Generator with 2 composents
+# Criteria : DELTA(k,k,k/2) <= 1
+#-------------------------------------------------------#
+# Format :
+#
+# Number Of Component (only at the beginning of the file
+#
+# Comments (value of the criteria)
+# s1 s2
+# Non-zeros coefficients of the first polynomial
+# Non-zeros coefficients of the second polynomial
+#-------------------------------------------------------#
+
+2
+
+#DELTA = 1
+4 2
+7 1 0
+10 4 3 1 0
+
+#DELTA = 1
+1 4
+7 1 0
+10 4 3 1 0
+
+#DELTA = 1
+5 7
+6 1 0
+11 4 2 1 0
+
+#DELTA = 1
+2 3
+6 1 0
+11 5 3 2 0
+
+#DELTA = 1
+2 8
+6 1 0
+11 2 0
+
+#DELTA = 1
+1 4
+6 1 0
+11 4 2 1 0
diff --git a/source/umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat b/source/umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat
new file mode 100644
index 0000000..209d9d1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/dataLFSR/j2_k19.dat
@@ -0,0 +1,35 @@
+#-------------------------------------------------------#
+# Equidistributed LFSR Generator with 2 composents
+# Criteria : DELTA(k,k,k/2) <= 1
+#-------------------------------------------------------#
+# Format :
+#
+# Number Of Component (only at the beginning of the file
+#
+# Comments (value of the criteria)
+# s1 s2
+# Non-zeros coefficients of the first polynomial
+# Non-zeros coefficients of the second polynomial
+#-------------------------------------------------------#
+
+2
+
+#DELTA = 1
+5 4
+9 4 0
+10 4 3 1 0
+
+#DELTA = 1
+2 4
+9 4 0
+10 3 0
+
+#DELTA = 1
+1 5
+6 1 0
+13 6 4 1 0
+
+#DELTA = 1
+2 4
+6 1 0
+13 6 4 1 0
diff --git a/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.dat b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.dat
new file mode 100644
index 0000000..24d3b22
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.dat
@@ -0,0 +1,318 @@
+ 536870912 268435456 134217728 67108864 33554432 16777216 8388608 4194304 2097152 1048576 524288 262144 131072 65536 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1
+536870912 805306368 671088640 1006632960 570425344 855638016 713031680 1069547520 538968064 808452096 673710080 1010565120 572653568 858980352 715816960 1073725440 536879104 805318656 671098880 1006648320 570434048 855651072 713042560 1069563840 538976288 808464432 673720360 1010580540 572662306 858993459
+805306368 603979776 469762048 1023410176 788529152 457179136 851443712 630194176 523239424 1063518208 786169856 413990912 807337984 608124928 472825856 1025019904 791687168 459547648 853285888 632110336 522137344 1065311040 787411648 411612560 805318448 603986788 469775068 1023419821 788537119 457195391
+939524096 822083584 587202560 251658240 371195904 761266176 467664896 1053032448 900464640 576454656 228098048 319913984 774078464 476254208 954347520 834990080 596468224 255776256 377044480 769027136 462215872 1044198848 897214856 584335640 229376056 324796465 781746211 470908943 939753494 822284333
+939524096 822083584 721420288 385875968 656408576 257949696 534773760 1053032448 883687424 693895168 330858496 643137536 212828160 408883200 951726080 827650048 730722816 390063616 660327936 261299264 522369216 1046574016 887256456 700774680 328069688 641721905 207343147 412294231 939702503 822178767
+1006632960 943718400 817889280 566231040 130023424 193200128 319553536 643563520 217841664 431505408 925941760 711311360 348897280 768836608 534973440 1069685760 1002454016 868497472 600584384 127672768 188483520 305941380 607965964 205121116 406310140 879484344 684981040 363329125 789309643 504861075
+1006632960 943718400 884998144 700448768 398458880 801374208 466354176 870055936 670826496 196624384 322224128 577355776 81248256 229360640 521653248 1047256064 953678848 904657984 735574208 397145536 790096832 439341956 807133964 611829276 149670972 295147580 590295096 102638709 205261035 473715095
+1006632960 943718400 884998144 767557632 532676608 1065615360 990642176 907804672 742129664 473448448 1010089984 946716672 887078912 771736576 536595456 1068751872 992214016 906214464 738408640 469905856 1006642112 943720388 885002188 767564764 532689916 1065624508 990644024 907808369 742136035 473461191
+1040187392 1007681536 942669824 812646400 586153984 133169152 232816640 466714624 934510592 796360704 486506496 1005519872 903777280 732797952 357252096 679902208 287078432 540569696 39903456 80855520 162759648 325551041 617579395 194971399 388927023 744332415 413908190 861404605 649067355 256897687
+1040187392 1007681536 942669824 846200832 653262848 233832448 468746240 905019392 703823872 334987264 637501440 166691840 298781696 562961408 51166208 68779008 171112480 343273568 721150176 401065440 802099168 496902081 993836931 912883495 750975567 428208319 822829407 571885247 104631679 241803006
+1040187392 1007681536 942669824 846200832 619708416 166723584 334528512 669089792 230916096 429359104 826245120 545227776 50301952 100604928 201243648 402488320 771422240 469102688 905666784 736511456 365694944 696819649 285327235 571702055 104265327 242052351 483057119 998588318 923466524 773190200
+1040187392 1007681536 976224256 879755264 686817280 300941312 602963968 98664448 163807232 327647232 621772800 169837568 339708928 713006080 385825792 770636800 432929824 832306272 557317344 41909728 117342176 268206017 502857635 972127079 871527151 670327295 234406910 467764220 935560185 798458834
+1040187392 1007681536 976224256 913309696 753926144 435159040 870350848 666992640 261324800 489127936 944734208 814711808 555715584 36641792 105823232 211647488 455769120 877984864 716799200 358808032 684061664 328983489 623330179 139331367 277614159 588815519 70335775 174258750 382070908 797696216
+1040187392 1007681536 976224256 913309696 787480576 502267904 971014144 835813376 597917696 89620480 180322304 359597056 718179328 396205056 757808128 441908224 884833312 695893088 284458208 568883680 63992800 127951809 223396739 413238023 861110799 616005695 158269567 315524319 664604063 255467327
+1056964608 1040449536 1007419392 941359104 809238528 544997376 33292288 49811456 82849792 148926464 281079808 562425856 51376128 102490176 204718272 409174464 834864064 579213248 84684736 169365377 338726659 694488583 332270607 664537119 255590463 511180862 1039138876 988024888 885796912 681340960
+1056964608 1040449536 1007419392 941359104 826015744 595329024 133955584 268177408 536621056 1073508352 1056763904 1023275008 956297216 838594624 603189440 132379072 264496064 528730048 1057198016 1040654209 1007566595 941391367 826080271 595458079 134213695 268431486 536867068 1073738232 1056957425 1023395811
+1056964608 1040449536 1007419392 958136320 859570176 662437888 251396096 486281216 956051456 838365184 586215424 82178048 147845120 295690304 591380672 108761536 234038208 468076480 936153024 798564225 523386627 1063546439 1053609167 1016695263 959648767 862337022 667713532 245170104 473825072 947912224
+1056964608 1040449536 1007419392 958136320 842792960 628883456 184287232 368578560 720384000 350253056 683732992 293728256 587460608 100921408 201580736 419680704 855876544 654788544 252612544 504958849 1026690819 996412999 936123599 781986271 490492927 981252030 905801532 738119288 385719537 771701154
+1056964608 1040449536 1024196608 974913536 893124608 712769536 368836608 737939456 385626112 754741248 436006912 872280064 654307328 234610752 468963520 937927104 785339328 496674752 993091520 912699329 768434115 446345159 875651023 694595551 315449343 614379518 155017213 326807546 670130165 249737194
+1056964608 1040449536 1024196608 974913536 893124608 729546752 402391040 788271104 486289408 955805696 837873664 602009600 113504256 210235456 420470976 857461184 657695680 241387456 499290048 998580161 906899395 739790791 405835727 811929567 550379519 26755070 53510141 124055547 265150454 513523628
+1056964608 1040449536 1024196608 974913536 876347392 679215104 284950528 553127936 32518144 65040384 130084864 243396608 470020096 939782208 805560512 554160576 34583488 68908992 154337216 308408257 616812483 143101895 286199759 588910559 103813119 224399294 465575740 914370105 738217011 419727398
+1056964608 1040449536 1024196608 991690752 909901824 763101184 452722688 888672256 686829568 300183552 583856128 77197312 137621504 275243072 567263424 44011968 88024000 192829376 385658816 754798529 436117443 855457671 637431567 184344095 368950335 721119359 351453439 702906878 315560957 614344699
+1056964608 1040449536 1024196608 991690752 909901824 746323968 419168256 838340608 586166272 81817600 146862080 293990400 588247040 119533632 239071424 478142912 973063104 872384448 671031232 268320705 536641475 1073545095 1056829199 1039912479 1005816895 921110655 751698175 412877311 826016766 578291645
+1065353216 1057030144 1040384000 1007091712 940507136 807337984 540999680 16711680 25035264 41682432 74976768 141565440 274742784 549551616 25427456 50789380 101513228 202960924 405856316 820035708 557941500 42141180 84281848 168563184 337125856 682705344 300122496 600244481 126812163 253624323
+1065353216 1057030144 1040384000 1007091712 940507136 815726592 566165504 67043328 125698560 251463168 502992384 1006050816 938425856 794787328 507510272 1023343620 964557324 855307804 636808764 191421564 374388988 748712444 423748088 839107056 612795360 151848896 303763328 607592193 141508099 274628099
+1065353216 1057030144 1040384000 1007091712 940507136 815726592 557776896 50266112 92144128 184354304 368774656 729161216 376192512 744062464 414449152 828832772 575535628 77264412 162917436 334223996 668382972 254635516 509270520 1018474992 963208160 852740032 623349632 172891905 345848835 691697667
+1065353216 1057030144 1040384000 1007091712 948895744 832503808 599719936 125763584 251593216 503252480 998182400 914300416 746536448 419397120 830471680 587136516 92077580 175701020 351336508 694218876 314630908 629261820 193170424 394729456 797913056 522149824 1052753792 1031700229 989658639 897186847
+1065353216 1057030144 1040384000 1007091712 948895744 824115200 582942720 92209152 184484352 360580608 721161728 368647680 737361408 401047040 793771520 505413124 1019214860 973077020 864024124 645917820 218094332 427734524 855403512 628742128 175353312 342251968 684503424 286875909 573816847 73892383
+1065353216 1057030144 1040384000 1015480320 965672960 857669632 650051584 234815488 461308416 922682880 763301376 452861440 897334784 720993792 359923200 711457796 340785164 681504796 289202236 578338940 82871036 157288444 306188280 612376052 159398380 318796252 637591996 209764733 427852031 864092671
+1065353216 1057030144 1040384000 1015480320 965672960 866058240 666828800 268369920 528417280 1056835072 1039928832 997727744 921714176 769687040 465632768 922876932 772012044 470217244 940369468 806997628 540253948 15155196 21987320 43974132 87882732 175764956 343140796 677826941 281846015 563692031
+1065353216 1057030144 1040384000 1015480320 957284352 840892416 608108544 142540800 276693504 545064448 16453120 24583680 49167872 98401792 188415488 376765444 745077260 407959068 807464508 549575804 25410300 59144188 118287864 244898804 489732076 979398620 876732348 671268729 277184243 554368995
+1065353216 1057030144 1040384000 1015480320 957284352 840892416 616497152 159318016 310247936 612173312 142282240 284564992 569196032 56327680 104267264 208534532 408680972 808973340 544139324 14537340 20621052 49631228 99327480 190266356 380598252 769650140 457169340 914272633 746414323 419087331
+1065353216 1057030144 1048772608 1023868928 982450176 891224064 717160448 360644608 712966656 352257536 704581120 327097856 645873152 218070528 436207104 872348676 662501900 251261980 502524476 1004983420 927771388 781800956 489925628 979785212 885828604 689526268 313764860 635852285 189574143 370693631
+1065353216 1057030144 1048772608 1023868928 982450176 899612672 725549056 385810432 771686912 469632512 930876928 788078080 502480384 1005026816 936377856 790560260 507313676 1014562332 955317820 836894332 600047356 117964284 244317180 488568828 977071612 880335868 686863868 291531261 583062527 83994111
+1065353216 1057030144 1048772608 1023868928 974061568 882835456 700383232 327090176 645792256 209454592 410586624 821239296 568737280 55410176 110886400 221773316 443481100 878508572 674821692 284290172 560126716 46511612 84634108 169268220 346859004 693718012 313759740 635842041 189618675 379302371
+1065353216 1057030144 1048772608 1023868928 974061568 882835456 691994624 310312960 620626432 159122944 318311936 628235776 182795776 365592064 731184640 388561924 785512972 488830492 986050108 889970300 714587900 355368444 710801916 347795964 703980028 334218236 659982332 237833721 475666931 951333859
+1065353216 1057030144 1048772608 1032257536 990838784 916389888 759103488 452919296 897450496 712771072 343477760 678567424 275004928 541687296 9633280 27655684 55246348 110427676 229243964 466811516 933623548 801893884 521591804 1043117560 1012427248 942724064 811640768 549539717 17014031 42350623
+1065353216 1057030144 1048772608 1032257536 990838784 908001280 750714880 427753472 855507456 637339136 192548352 376708608 745029120 416316928 832699904 591592452 100988940 210301468 428991548 857983100 650547452 227353596 454707196 917868024 761994224 441858016 875392448 685431685 297056015 602434591
+1065353216 1057030144 1048772608 1032257536 999227392 924778496 784269312 503250944 998179328 922682880 771689984 469704192 939474432 796884480 520093184 1031732228 981268492 880406556 687071292 292012668 584025340 94308860 188682748 377364984 754729968 427395040 854790080 635903873 198131459 396262915
+1065353216 1057030144 1048772608 1032257536 999227392 924778496 775880704 486473728 973013504 872351232 671026688 259923456 519847424 1031306752 980483584 878771716 675347980 268565532 545520188 17233532 42790140 85580284 179549180 367486968 735038960 396270560 792475584 511274881 1022614787 963098627
+1065353216 1057030144 1048772608 1032257536 999227392 933167104 801046528 528416768 1048510976 1014892032 956042752 838344192 602947072 123829760 247725568 486996996 982317068 890892316 716431932 359122556 709856508 345906172 691812348 318336504 628349424 182957024 365913536 731892097 398430467 796794883
+1069547520 1065369600 1057013760 1040302080 1006878720 944226304 814727168 555728896 41926656 83869760 167755968 331317696 658441152 243140544 482086848 959995840 842072000 610385856 142819264 285638529 575471363 77184583 158547087 312899935 621605567 169452927 343083775 681989630 290253820 584685496
+1069547520 1065369600 1057013760 1040302080 1006878720 944226304 814727168 559923200 50315264 100646976 201310400 398426560 792658880 507398080 1014812608 955899840 833880000 594001856 110051264 220102529 440205059 876215815 674495567 271055007 537915711 6283903 12567807 20941310 37688316 75392952
+1069547520 1065369600 1057013760 1040302080 1011073024 952614912 831504384 593477632 113229824 226476096 448774336 897548736 717161408 360581056 721162176 368598976 737214400 396492736 792985472 516423489 1032846979 991952199 914356879 754971935 432007743 859821183 645916927 213881342 427762684 851331000
+1069547520 1065369600 1057013760 1040302080 1011073024 952614912 831504384 589283328 104841216 209698880 415219904 830439872 582943680 92161984 180146112 360308672 716439488 354942912 705691520 337624897 679427715 280919367 557644495 37352863 70511423 141022847 282062079 564124158 54522876 109045688
+1069547520 1065369600 1057013760 1040302080 1011073024 948420608 827310080 580894720 88064000 171933760 343867584 687751616 301777856 603572160 133418944 266837952 533675968 1071546240 1069350720 1060765377 1043594627 1013447495 948958927 828370335 582998847 88061503 171912255 343808062 687616060 301473848
+1069547520 1065369600 1057013760 1040302080 1011073024 948420608 827310080 585089024 96452608 188710976 377422016 754860480 435995584 871991232 666046400 254156736 504119232 1008238464 942735168 811712193 545471875 21396295 46986895 98168095 196336191 392672319 781133887 488525886 977068092 876199992
+1069547520 1065369600 1057013760 1044496384 1019461632 965197824 860864512 652197888 234864640 469745728 935313600 796885440 520029120 1040074688 1002229696 930717632 783499200 489045888 973881152 874020481 674299203 270645895 537081167 4614879 9229823 22653887 45291391 90599103 181198207 362396415
+1069547520 1065369600 1057013760 1044496384 1019461632 969392128 865058816 656392192 243253248 486506560 968818880 859718080 645710784 217696192 435408832 870834112 663748544 253755264 511704896 1019215553 964689347 855636935 633337807 192933791 381673343 759168767 444595647 889174847 700397119 327036031
+1069547520 1065369600 1057013760 1044496384 1015267328 961003520 852475904 635420672 201310208 398426176 796852416 515768768 1027343296 980961216 884002752 694280128 310640576 621281216 168820608 337624833 679427587 285096967 565983247 58208287 116400191 232783935 461373567 926957823 784384511 495027199
+1069547520 1065369600 1057013760 1044496384 1015267328 961003520 852475904 631226368 192921600 381648960 763297984 452854208 901514176 729286592 384831424 769679296 461438912 927055808 780353408 491159361 982318723 895073607 716389007 359019871 718023359 366499135 733014655 388093119 776202623 478663359
+1069547520 1065369600 1057013760 1044496384 1015267328 961003520 848281600 622837760 176144384 348094528 691994816 306069952 607962048 137988032 271781824 539369408 4997056 9994176 24182720 48365441 96730947 197656199 395312463 794819295 520091071 1035971391 993990207 910044287 742135999 406335807
+1069547520 1065369600 1057013760 1044496384 1015267328 956809216 839892992 606060544 142589952 281002048 562020544 46121408 88064960 176146368 352309184 704634816 335544256 671072192 264191872 528367361 1052524035 1031306311 984676559 895594975 713237439 356910975 718016191 366484799 732969535 387986559
+1069547520 1065369600 1057013760 1044496384 1015267328 956809216 839892992 610254848 150978560 297779264 595574976 117424576 230671296 461342656 918491072 759062464 444399552 888799168 703856512 329776961 655359619 241171719 482343503 964670623 851388735 624841343 171762879 339315071 678630079 279323967
+1069547520 1065369600 1057013760 1044496384 1015267328 956809216 844087296 618643456 167755776 331333696 658489536 239043008 473891776 943589312 809242560 540549056 7356352 10502080 20987840 46169985 92340035 188858055 377699727 755399455 441251391 882486399 691214591 304492991 604775295 135792319
+1069547520 1065369600 1061208064 1048690688 1027850240 981975040 894418944 719306752 369082368 733986880 390054080 780124608 486523840 973047744 868159424 658382784 243023808 481853376 959512448 845266689 616775235 159808647 323811663 647607007 225650175 447089599 889984895 706244351 338746879 677493695
+1069547520 1065369600 1061208064 1048690688 1027850240 981975040 890224640 710918144 348110848 692043840 310362304 616530368 155124672 306071488 612159424 150593472 301203392 598196160 122634112 245268289 494730883 989461831 905181839 732411231 391064319 786306559 503065535 1006131071 938536703 799120831
+1069547520 1065369600 1061208064 1048690688 1027850240 981975040 890224640 706723840 339722240 679460928 285196480 570393024 62849984 121505728 243011520 486023104 972046272 870350720 662765376 251772609 503528835 1007041351 944518863 819490271 565238783 56735743 113455103 226910207 453836799 911851455
+1069547520 1065369600 1061208064 1048690688 1027850240 986169344 902807552 731889664 394248192 784302144 490668224 981336512 888931264 699942848 321966016 639754176 205782976 415760256 835714880 601865921 134167939 268335943 532477647 1060744671 1047731135 1017542463 957148735 844733503 615725183 153497791
+1069547520 1065369600 1061208064 1048690688 1023655936 977780736 886030336 702529536 335527936 671055936 268370112 532562368 1060946880 1048152000 1022562240 971399104 864878528 660193216 250822592 501628801 1003241283 932740807 795934159 518110175 1032009727 994471871 915201855 756678207 439614527 879212671
+1069547520 1065369600 1061208064 1048690688 1023655936 977780736 881836032 689946624 306167808 608141376 138346688 272499136 540804032 7866304 15732672 31481792 58785728 121765760 247725824 495435329 990854275 907950343 742142479 406332447 808454271 543183103 8446463 16876543 37947327 80088895
+1069547520 1065369600 1061208064 1048690688 1023655936 973586432 873447424 677363712 281001984 557826112 37732544 75481536 150979520 301975488 599773120 125820864 251658176 499105728 998195136 918454209 758972291 439992071 879967823 681983199 290208255 580400063 87041919 174067455 343940607 687897535
+1069547520 1065369600 1061208064 1048690688 1023655936 973586432 877641728 685752320 301973504 599769152 125812928 247431616 494863296 985532352 897322944 716709824 359677888 719339456 360726464 721452929 369164035 738328071 407108687 814200991 558838079 48112191 96207999 188221631 376443199 752886399
+1069547520 1065369600 1061208064 1052884992 1032044544 994557952 919584768 765444096 457162752 914325568 754909376 436093376 872203200 666486720 255053760 505929664 1011875776 954187648 834617088 595492417 113048707 226097415 452194831 904389727 730843391 387961279 780133183 482313790 960433276 842946744
+1069547520 1065369600 1061208064 1052884992 1032044544 994557952 915390464 757055488 444579840 889159744 704577728 331219392 658244544 238553024 472911808 945840064 813760448 557973376 42204992 88604353 177208707 354401095 708785871 339619231 675027775 272135807 544271551 10606974 21230268 46654840
+1069547520 1065369600 1061208064 1052884992 1032044544 990363648 907001856 744472576 419414016 834650176 595574976 117424576 234865600 469731264 939462592 805183424 536625088 1073233792 1068515136 1063272129 1052786115 1031830407 989918991 906079839 742595839 415660543 831321023 593078142 116592380 233184696
+1069547520 1065369600 1061208064 1052884992 1032044544 990363648 911196160 748666880 423608320 847233088 620740800 163545536 322896832 645793728 213651392 427319232 850460608 627179456 180617152 361234369 722468739 375373639 754925199 436108639 872217343 666482175 255011775 510007166 1015819964 957881656
+1069547520 1065369600 1061208064 1052884992 1036238848 998752256 927973376 782221312 494911488 985645120 893370560 713015744 352306112 704612288 335482816 666771392 259801024 519585664 1034960704 996179585 918617347 763476551 453194895 910567775 751571647 429417791 858835519 643929150 209922172 419844344
+1069547520 1065369600 1061208064 1052884992 1036238848 1002946560 932167680 794804224 515883008 1031766080 985596096 893272512 712819648 351913920 703844288 329752512 655310784 241057664 482098944 959987265 846216323 614496519 151056975 306291871 616761727 163975871 332146047 668486398 267441660 530705400
+1069547520 1065369600 1061208064 1052884992 1036238848 1002946560 932167680 790609920 507494400 1010794560 943653056 809386432 545047488 12158912 24317888 44457920 84737984 173653952 351485888 698761153 319569795 639123271 204488399 408976799 813759359 553776831 33811775 71801470 147797244 299772408
+1069547520 1065369600 1061208064 1052884992 1036238848 1002946560 936361984 798998528 528465920 1052737600 1027539136 981336512 888931264 699942848 326160320 652337088 230948800 461881280 927940480 786333505 503119555 1002028423 926104335 774256159 474754175 949491967 825242047 572547966 67159740 138530104
+1071644672 1069551616 1065365504 1056993280 1040248832 1006759936 939782144 805826560 537915392 4190208 6283272 10469400 18841656 35586168 69075192 136053240 270009336 540022776 6307832 12611568 25219040 50433984 100863872 201723648 403443200 808979457 542119947 10498071 20996135 41992263
+1071644672 1069551616 1065365504 1056993280 1040248832 1006759936 941879296 812118016 552595456 33550336 65003528 130011160 260026424 520056952 1040118008 1006498296 937161720 798488568 521142264 1044377584 1012916200 952086488 830427064 587108216 98373360 194645473 389286851 778569607 483401479 964705799
+1071644672 1069551616 1065365504 1056993280 1040248832 1006759936 941879296 812118016 550498304 29356032 56614920 113233944 226471992 452948088 905900280 735961592 398181368 794269688 514801656 1031696368 989650920 903458776 731074488 388403064 776806136 481967609 961833979 849922039 628203495 182661063
+1071644672 1069551616 1065365504 1056993280 1040248832 1006759936 941879296 810020864 546304000 20967424 39837704 79679512 159363128 316629112 631161080 186483192 372966392 745936888 416038904 834170864 592502760 109162456 218324912 438746976 877493960 683339153 292936491 585872983 98004135 196004167
+1071644672 1069551616 1065365504 1056993280 1040248832 1008857088 946073600 820506624 569372672 65007616 130019336 260042776 520089656 1038086264 1000337656 924840440 773846008 473954296 945815544 817885176 562024440 48205816 94310384 188616672 377229248 752357249 430968587 861937175 650132527 228620383
+1071644672 1069551616 1065365504 1056993280 1040248832 1008857088 946073600 818409472 565178368 56619008 113242120 226488344 452980792 905961592 738181368 402625016 803156984 530479096 1060962296 1046081528 1018417144 963092472 854540280 637431800 201117688 400138225 798179307 520515543 1043124143 1014599519
+1071644672 1069551616 1065365504 1056993280 1040248832 1008857088 943976448 814215168 554692608 35647488 71299080 140501016 278904888 557809784 39780600 79561208 157025272 314054648 626016248 176193528 354484208 708964320 344182728 690458512 307171104 614342209 154942603 309885207 617673263 161608799
+1071644672 1069551616 1065365504 1056993280 1042345984 1013051392 954462208 837283840 602927104 132116480 262139912 522186776 1042280504 1010819192 947896568 819954168 566166520 56498168 110903288 219709424 439418848 878837696 686030728 298315544 596626992 119512169 239024339 478048679 954004303 834266783
+1071644672 1069551616 1065365504 1056993280 1042345984 1013051392 952365056 830992384 588247040 102756352 203419656 406839320 811581496 549425272 23015672 43938296 85783544 171567096 343134200 686264304 298782696 599662544 125583272 249065304 498126520 994155889 914569955 755398087 437058447 876209951
+1071644672 1069551616 1065365504 1056993280 1042345984 1013051392 952365056 830992384 590344192 106950656 211808264 423616536 845135928 616534136 159330552 318661112 637322232 200902648 401805304 801509360 531369960 1064832976 1055920040 1035997008 1000345248 929045833 784349843 497050919 994101839 914461855
+1071644672 1069551616 1065365504 1056993280 1042345984 1010954240 948170752 824700928 575664128 79687680 159375368 316653592 633307192 192872568 383647992 767300088 458761208 915425272 755015672 438382584 878858224 686071784 300494800 600989600 128233288 256466585 512929083 1023756919 975865063 877992391
+1071644672 1069551616 1065365504 1056993280 1042345984 1010954240 948170752 822603776 571469824 71299072 142598152 283099160 566198328 58658936 117321976 232550904 465101816 928106488 782475256 493305848 986611696 899477480 723111888 370380712 740757328 407772833 815541579 555244183 34649383 67201607
+1071644672 1069551616 1065365504 1056993280 1042345984 1010954240 948170752 822603776 573566976 75493376 150986760 299876376 599752760 125767800 249442552 498885112 997774328 919709688 763584504 455524344 913145840 754647016 433455056 864812960 655880008 238014097 476024099 952044103 828245127 582752519
+1071644672 1069551616 1065365504 1056993280 1042345984 1010954240 950267904 826798080 581955584 92270592 184541192 369086488 738177080 402616440 803139832 532537848 1065079800 1056421880 1039101944 1006555128 937271288 800800760 527855600 1053614056 1035579344 997412769 921079619 768417415 463092999 926181895
+1071644672 1069551616 1065365504 1056993280 1042345984 1010954240 950267904 828895232 584052736 96464896 192929800 385863704 771731512 469721208 937345272 800952824 526066680 1050040312 1026338808 978935800 884125688 694509560 317374448 634748904 193654736 387309481 774614875 475487927 948878695 826108615
+1071644672 1069551616 1065365504 1059090432 1046540288 1019342848 964947968 858255360 642772992 211808256 423620616 845148184 616558648 159379576 316666104 631235064 188728312 377460728 752828408 431914992 861732832 649719744 225693576 449285912 900664888 727587953 381434083 762868167 451998599 904001287
+1071644672 1069551616 1065365504 1059090432 1046540288 1019342848 967045120 860352512 649064448 224391168 446689288 891281432 708821048 343900280 687800568 301859320 603718648 133695480 265293816 530587640 1061175280 1048604648 1025560536 977379256 881016696 690388721 307035627 614067167 154388407 308776815
+1071644672 1069551616 1065365504 1059090432 1046540288 1019342848 967045120 862449664 653258752 232779776 463466504 926933016 780124216 484413560 966734072 859726328 643613688 211392504 422789112 843481080 615317496 156893176 313786360 627572720 183500776 367001561 731905979 387972991 773848831 473955839
+1071644672 1069551616 1065365504 1059090432 1046540288 1021440000 971239424 868741120 663744512 253751296 507502600 1015005208 956268600 838795384 601755896 129774072 259552248 517007352 1031917560 992190448 912736232 753823696 433905576 867807064 661868208 249990505 502074075 1006241215 936647543 797452015
+1071644672 1069551616 1065365504 1059090432 1046540288 1021440000 971239424 868741120 665841664 257945600 515891208 1031782424 987725880 901709944 727585016 379331064 756564984 439388152 878776312 683806704 295964648 594022360 116400056 232796016 467689192 937475537 803302307 532862799 1067822743 1064000807
+1071644672 1069551616 1065365504 1059090432 1046540288 1021440000 971239424 870838272 667938816 262139904 524279816 1046462488 1019183160 962531448 851321080 626807288 177779704 355563512 711131128 350617584 703332320 332918720 665833352 260022032 517942816 1033784385 993822859 911806751 747774527 421811327
+1071644672 1069551616 1065365504 1059090432 1046540288 1021440000 969142272 866643968 659550208 245362688 488628232 977260568 878686264 683634808 291430648 582861304 89883640 177674232 353255416 706510840 339275768 678547440 283353064 566706128 61763496 123522897 247041707 491982167 981863079 889984335
+1071644672 1069551616 1065365504 1059090432 1046540288 1021440000 969142272 864546816 657453056 241168384 480239624 958386200 843034680 610230392 146723064 293450232 584807416 95873016 189648888 379293688 756490224 437137384 874270680 672698288 269557600 537018049 2387331 4770567 11634191 21175319
+1071644672 1069551616 1065365504 1059090432 1044443136 1017245696 962850816 851963904 630190080 188739584 377479176 754958360 436174904 870256760 664674552 255611384 509125624 1016154104 958570488 845492208 619335656 164929496 329854896 659709792 245677768 493448601 986897203 900052583 726367431 381090183
+1071644672 1069551616 1065365504 1059090432 1044443136 1017245696 960753664 847769600 623898624 176156672 350216200 698339352 320843832 641691768 207544568 415093240 830186488 584538104 93237240 188567544 377135088 752168936 430591960 863281080 652816248 231886585 461671923 923343855 772941791 470044599
+1071644672 1069551616 1065365504 1059090432 1044443136 1017245696 960753664 849866752 625995776 180350976 358604808 717213720 360689720 721379448 366924024 731750904 389764088 779528184 483221496 966438904 859135992 644526064 215310304 430620616 861237136 646631209 219520595 439041199 878078295 680313519
+1071644672 1069551616 1065365504 1059090432 1044443136 1015148544 958656512 845672448 619704320 167768064 333443080 664789016 253739064 507478136 1012859128 951976440 830211064 586684408 97533944 195067896 390135792 782368744 490995664 984084384 894422848 717196937 358550811 715000375 354157671 708315343
+1071644672 1069551616 1065365504 1059090432 1044443136 1015148544 958656512 843575296 615510016 159379456 316665864 633331736 190824504 381653112 763310328 450781688 901563384 727287800 378736632 757469176 443289592 886579192 697319416 320892912 641781736 209821657 419643323 841379703 609013487 146378207
+1071644672 1069551616 1067462656 1061187584 1050734592 1027731456 983822336 893906944 716173312 358608896 715124744 356511768 713027640 350220408 698347768 322957816 645919736 216004600 429916152 857731056 641716200 207593424 413089704 824078160 574410408 72981841 143866539 285631831 571263663 70882647
+1071644672 1069551616 1067462656 1061187584 1050734592 1027731456 981725184 891809792 711979008 352317440 702541832 331341848 662683704 251629688 501166328 1002332664 928826360 783910904 494079992 986062840 900481016 729317368 384892920 769781752 465817592 931631089 791613411 511582151 1023164303 972586775
+1071644672 1069551616 1067462656 1061187584 1050734592 1027731456 981725184 891809792 709881856 348123136 696250376 318758936 635420728 197103736 394211576 786330104 498922488 995751928 917766136 761786352 451923936 905940936 736038808 398335792 794574432 515407049 1030814099 987882287 902022743 732404911
+1071644672 1069551616 1067462656 1061187584 1050734592 1027731456 981725184 889712640 707784704 341831680 681570312 287301656 574603320 75464824 148832504 297669112 593245176 110655480 219217912 436334584 874762232 675778552 277811184 557719520 39600064 79196041 156290835 312581671 627260487 182876303
+1071644672 1069551616 1067462656 1061187584 1050734592 1029828608 985919488 898101248 724561920 377483264 752869384 431996952 863998008 652161144 230580472 459063800 916034552 756230136 436621304 875335672 679026672 282214368 564428736 57212808 114421528 226745905 455588971 913270999 754897319 433955663
+1071644672 1069551616 1067462656 1061187584 1050734592 1029828608 988016640 904392704 737144832 400551936 799006728 524275736 1048551480 1023365240 970891512 865948152 658154488 240474104 480952312 963997680 854249440 636854216 199966608 402026272 804048448 534355073 1066608907 1059475991 1045214255 1016690783
+1071644672 1069551616 1067462656 1061187584 1050734592 1029828608 988016640 902295552 730853376 390066176 780132360 484429848 966762552 859783288 645828856 215822840 431645688 863295480 652853240 231960568 461819896 925732856 777723896 481701880 963399672 853053425 632365035 188886999 377769895 757632847
+1071644672 1069551616 1067462656 1061187584 1048637440 1025634304 979628032 885518336 699396096 327151616 654303240 232767512 463441976 926883960 780030200 486318584 972637176 871536632 669331448 264921080 529842168 1057587184 1041428456 1007017936 940289952 808935241 542031515 12418367 22739583 43382007
+1071644672 1069551616 1067462656 1061187584 1048637440 1025634304 979628032 885518336 697298944 322957312 643817480 211795992 423596088 847192184 620646648 165458424 328823800 655550456 235266040 470527984 943149024 810459072 547172224 18501376 34901512 67701785 137500731 272904319 547905783 24166887
+1071644672 1069551616 1067462656 1061187584 1048637440 1025634304 977530880 883421184 695201792 316665856 633331720 190828568 381657144 763314296 452890872 903688696 731542520 389347320 776597496 481550320 960999400 848252880 622763936 169684808 339365528 678726969 281614971 563225855 50616823 103330791
+1071644672 1069551616 1067462656 1061187584 1048637440 1023537152 973336576 875032576 676327424 281014272 559935496 44036120 88076344 174059640 346026232 692056568 310375416 620754936 167772152 335540216 671076336 266313704 530530256 1058959264 1042075456 1010405001 944966931 816187951 560727127 49809583
+1071644672 1069551616 1067462656 1061187584 1048637440 1023537152 973336576 872935424 674230272 274722816 549449736 25161752 48230456 96460920 190824696 381653496 761213944 448690168 897384440 721027056 368312296 736624600 399507384 799010680 522178296 1044352505 1012861939 951982063 828129247 582512575
+1071644672 1069551616 1067462656 1061187584 1048637440 1023537152 973336576 872935424 672133120 270528512 538963976 4190232 8384568 16769144 31441144 60785144 119473144 236849144 471601144 941101048 806359024 538976232 4210640 8417184 18927424 37850753 75697411 151390727 302777351 605554695
+1071644672 1069551616 1067462656 1061187584 1048637440 1023537152 975433728 877129728 680521728 287305728 572518408 69197848 136298552 270504056 541012216 6185464 10273784 20551672 39010296 78020592 156041184 314175432 626249624 176656184 353308280 706612465 337381867 674763743 273692607 547389303
+1071644672 1069551616 1067462656 1061187584 1048637440 1023537152 975433728 879226880 684716032 297791488 593489928 113238040 224378936 448757880 897515768 721293816 368849912 737699832 399560696 799121392 526598112 1053192136 1032638360 991530808 909315704 744885489 416025059 834147271 594556815 113278743
+1071644672 1069551616 1067462656 1063284736 1052831744 1034022912 996405248 919072768 764407808 455077888 910155784 744472600 415207480 828321912 580808952 87876088 175756280 351512568 700932088 330215416 662523896 251301880 502603768 1005203440 936660960 797482953 521224083 1042448175 1013247583 952749239
+1071644672 1069551616 1067462656 1063284736 1052831744 1034022912 996405248 921169920 770699264 469757952 937418760 798998552 524259384 1046421624 1019101432 964461048 855184376 634533880 195325944 390651888 783400936 490958800 981913504 892178248 710610576 347475241 694946395 316150967 630204783 184566495
+1071644672 1069551616 1067462656 1063284736 1052831744 1031925760 990113792 906489856 741339136 408940544 817885192 559935512 46133304 92270712 184545528 366998008 731902968 387971064 775946232 476049392 952094688 828350408 582958992 90079016 178060880 354020521 708036955 340230847 682558847 291375871
+1071644672 1069551616 1067462656 1063284736 1052831744 1031925760 992210944 912781312 751824896 432009216 864022536 654303256 232767544 465539192 928985336 782131704 490521592 981043192 888344568 702947320 334249976 668495856 261148648 522293200 1044582304 1015422793 957103771 842558775 611371623 148997319
+1071644672 1069551616 1067462656 1063284736 1052831744 1031925760 992210944 910684160 747630592 421523456 843051016 612360216 148881464 297762936 593428728 111022584 219952120 437807096 875614200 677482488 283316208 566628328 61607896 125308856 250613616 499125993 1000345051 926944183 782239599 490737375
+1071644672 1069551616 1067462656 1063284736 1054928896 1036120064 1000599552 929558528 785379328 497020928 994045960 912257048 750776376 425717880 851439864 627040760 178242552 354387960 708775928 343805944 687607792 301473768 600850392 127954872 255905656 511807225 1025707515 977669111 881592295 691539919
+1071644672 1069551616 1067462656 1063284736 1054928896 1036120064 1000599552 927461376 781185024 490729472 981463048 887091224 700444728 327147640 654295288 234852856 467612664 935225336 796708856 517574648 1033048056 990253040 906760160 741875648 410009472 820018945 568393227 63044631 126089255 252174415
+1071644672 1069551616 1067462656 1063284736 1054928896 1036120064 1000599552 927461376 783282176 494923776 987754504 899674136 725610552 377479288 754958584 434078200 868156408 662575096 249315320 496529392 993054688 912363456 750980992 430313224 862719504 653790249 233834579 467669159 935342415 796947095
+1071644672 1069551616 1067462656 1063284736 1054928896 1036120064 998502400 923267072 772796416 473952256 945811464 817881112 562020408 50303096 98513144 194929144 389858296 777623544 479412216 956727288 839712752 605679592 137613272 273129392 548355936 25067201 48037251 96074503 190051855 380103703
+1071644672 1069551616 1067462656 1063284736 1054928896 1036120064 998502400 925364224 776990720 480243712 958394376 843046936 612352056 148865144 295633144 591270392 108803064 215513080 431030264 859963384 648282104 224919536 451936224 903868360 731893656 390041393 780078699 486411479 970721711 867697503
+1071644672 1069551616 1067462656 1063284736 1054928896 1038217216 1002696704 933752832 795865088 517992448 1035984904 998227992 922718264 771698808 469659896 939323896 802812920 529786872 1059573752 1045401584 1019158496 962473920 849108864 624475904 175209992 348322833 698738731 323735639 649568431 225395031
+1071644672 1069551616 1067462656 1063284736 1054928896 1038217216 1002696704 931655680 789573632 507506688 1012916232 952090648 830443576 585048184 96354552 190616056 379139064 756185080 438632440 879357944 684974064 296202216 594497496 115253176 230506352 458915553 915729867 757713823 441685815 883375727
+1071644672 1069551616 1067462656 1063284736 1054928896 1038217216 1004793856 937947136 802156544 530575360 1061150728 1046466584 1019191352 964640888 855539960 635245048 196752376 391407608 782815224 493981688 987959280 902176744 730611672 387481528 772861808 471981793 946060747 818375575 563009319 54373967
+1072693248 1071645696 1069550592 1065360384 1056979968 1040219136 1006697472 939654144 806616064 540539904 8387584 15726593 30404611 59760647 118472719 236946463 473893951 947788927 821837055 569933311 66125823 132250622 265548796 532145144 1065337840 1055885281 1036980163 999169927 924598030 774404636
+1072693248 1071645696 1069550592 1065360384 1056979968 1040219136 1006697472 939654144 806616064 539491328 6290432 11532289 22016003 42983431 84918287 169837599 339676223 678303871 281817343 563635711 53530623 107060222 215167996 430334968 860668912 646547425 218304451 436607878 873214733 672686619
+1072693248 1071645696 1069550592 1065360384 1056979968 1040219136 1006697472 940702720 807664640 541588480 10484736 19920897 38793219 77587463 155175951 309303327 617558079 160325759 319602943 638158335 202575871 406199294 813446140 552101881 30461939 60923878 122896332 245792665 491585330 982122085
+1072693248 1071645696 1069550592 1065360384 1056979968 1040219136 1006697472 940702720 807664640 542637056 12581888 24115201 47181827 94364679 188730383 376412191 751775807 429810815 859622655 644455935 215171071 430341118 861729788 648669177 223596531 447193062 894386124 713980824 354218801 708437602
+1072693248 1071645696 1069550592 1065360384 1056979968 1040219136 1007746048 942799872 812907520 552074240 31456256 62913537 125828099 251657223 503315471 1006631967 938474559 803207295 532672767 1065346559 1055903743 1038064639 1001337855 927884287 782025727 489260031 978519038 884344829 694947835 316152823
+1072693248 1071645696 1069550592 1065360384 1056979968 1040219136 1007746048 942799872 811858944 551025664 29359104 58719233 117439491 234880007 469761039 938473503 803205183 531620991 1063243007 1051696639 1029652479 984513535 894235647 714728447 354665471 709330942 344920061 689839098 305935349 610822122
+1072693248 1071645696 1069550592 1065360384 1056979968 1040219136 1007746048 941751296 809761792 546831360 20970496 41942017 83885059 166721543 332394511 663740447 253739071 507479167 1013910783 953032191 831275007 587758591 101774335 204597246 410243068 821533688 570373105 65955811 130863047 260677518
+1072693248 1071645696 1069550592 1065360384 1056979968 1041267712 1009843200 946994176 821296128 569900032 66059264 132119553 264240131 527432711 1053817871 1032846367 990903359 908064895 741339391 407889407 814731263 555719679 37696511 74343422 147637244 294225912 587403248 102112225 204223426 408446852
+1072693248 1071645696 1069550592 1065360384 1056979968 1041267712 1009843200 945945600 818150400 563608576 53476352 106953729 213908483 427816967 855633935 636477471 198164543 395280511 790561023 506332671 1011617791 948445183 823148543 572555263 71368703 142736382 286520316 574089209 74436594 147824612
+1072693248 1071645696 1069550592 1065360384 1056979968 1041267712 1008794624 943848448 813956096 555219968 36699136 72349697 143650819 287301639 573554703 73367583 145686591 291373183 581697791 88605183 177210367 355468286 710935548 349176825 698352626 324010980 648020936 222299025 445645602 891291204
+1072693248 1071645696 1069550592 1065360384 1056979968 1041267712 1008794624 943848448 813956096 554171392 34601984 68155393 135262211 270524423 540000271 6258719 11468863 22938751 45878527 91757055 183514111 367027198 734053372 394363897 788726770 504760292 1010569160 946346897 817902371 562062918
+1072693248 1071645696 1069550592 1065360384 1056979968 1041267712 1008794624 943848448 815004672 557317120 40893440 80738305 160428035 320856071 640663567 206537759 412027967 824055935 574370047 74998271 149996543 299992062 601031676 129370105 259788786 519576549 1038103498 1002464149 932234026 791773780
+1072693248 1071645696 1069550592 1065360384 1058028544 1043364864 1014037504 954334208 834927616 596114432 119536640 238025729 475003907 950008839 826276879 578812959 82836543 165674111 331349247 661650943 248512511 497023998 992998396 912253944 749716465 425690083 851379143 629016462 183242524 366485048
+1072693248 1071645696 1069550592 1065360384 1058028544 1043364864 1014037504 954334208 835976192 599260160 125828096 250608641 500169731 1000340487 925891599 778041375 482340927 963633279 853524735 633308671 191827967 383655934 767311868 459833336 918618096 763493345 453243842 907535236 742376201 409962003
+1072693248 1071645696 1069550592 1065360384 1058028544 1043364864 1014037504 955382784 837024768 600308736 127925248 254802945 508558339 1017116679 959442959 845145119 616549439 158309503 316620031 632191487 189592575 378135550 755221500 436700152 874447856 674104289 274465730 548930437 24118026 49284628
+1072693248 1071645696 1069550592 1065360384 1058028544 1043364864 1012988928 953285632 832830464 591920128 111148032 222296065 443543555 887087111 700432399 326075423 651103295 228464767 455880959 911761919 749782015 425822207 851644414 629545980 186397688 372794352 745587681 418481091 838009734 603325196
+1072693248 1071645696 1069550592 1065360384 1058028544 1043364864 1012988928 953285632 832830464 592968704 113245184 226490369 451932163 903864327 733986831 394232863 787418175 500046975 1000094975 925399551 776008703 478275583 957599742 841457660 609173496 144604145 289207267 577364934 79938444 159876889
+1072693248 1071645696 1069550592 1065360384 1058028544 1042316288 1010891776 949091328 825490432 578288640 82836480 164624385 329248771 658497543 243253263 485457951 970915903 867041407 659292415 243795455 486543359 974134270 874525693 676357114 280019957 561088490 48435156 96869288 193737553 386426530
+1072693248 1071645696 1069550592 1065360384 1058028544 1042316288 1010891776 949091328 824441856 576191488 78642176 156235777 312471555 624943111 175095823 350192671 700386367 325982335 651964671 229139967 457232383 915513342 758333437 442925050 886898676 699005929 323220434 647489445 222285643 444570262
+1072693248 1071645696 1069550592 1065360384 1058028544 1042316288 1010891776 948042752 822344704 570948608 68156416 135264257 270528515 541058055 8375311 16751647 32455743 63863935 127728895 254409215 507769855 1015538686 957334525 840927226 608112629 142483435 283918295 567836591 61931359 123861694
+1072693248 1071645696 1069550592 1065360384 1058028544 1042316288 1011940352 950139904 826539008 579337216 84933632 169868289 339737603 679476231 285211663 570424351 66059327 132118655 264237311 528475647 1055903743 1038064639 1001337855 928933886 783077372 492412920 983777265 893811683 713880519 354019214
+1072693248 1071645696 1069550592 1065360384 1058028544 1042316288 1011940352 950139904 827587584 582482944 91225088 182451201 364903427 729807879 384826383 769652767 464515135 929031295 783273215 491757055 982466559 891191295 708640767 342491134 683933693 294125562 588251125 101710826 203420629 405792682
+1072693248 1071645696 1069550592 1065360384 1058028544 1042316288 1011940352 951188480 829684736 585628672 97516544 195034113 390069251 780138503 486535183 973070367 872398911 670007423 266273023 531497471 1061946367 1050149887 1025508351 978323454 882905084 693115896 313537520 628122592 183550913 368150402
+1072693248 1071645696 1069550592 1065360384 1058028544 1042316288 1011940352 951188480 828636160 583531520 93322240 186645505 373292035 746584071 418377743 835707935 597675071 120559743 241119487 481190399 961332223 848922623 623054847 173415422 347878397 695756795 316723190 633446381 194199515 388398006
+1072693248 1071645696 1069550592 1066408960 1060125696 1046510592 1019280384 965868544 857996288 643300352 212859904 424672257 849345539 623901703 174062607 347076639 694153279 314564735 628080895 182419967 363791359 727582718 381423613 762847226 451952628 903904232 734065617 395436962 790872901 509051531
+1072693248 1071645696 1069550592 1066408960 1060125696 1046510592 1019280384 965868544 857996288 642251776 210762752 420477953 840956931 608173063 142605327 284162079 568324159 62907519 125816063 250583551 501167103 1001284606 928826365 782862331 490934262 981868525 889995227 705199031 335606638 672261852
+1072693248 1071645696 1069550592 1066408960 1060125696 1046510592 1019280384 964819968 856947712 640154624 206568448 413137921 825228291 575666183 76541967 153084959 305122367 609196159 143601919 286156287 571265023 67739647 135479294 270957565 542962682 12183540 24367080 49781712 99562401 198076227
+1072693248 1071645696 1069550592 1066408960 1060125696 1046510592 1019280384 964819968 856947712 641203200 208665600 417332225 833616899 593491975 112193551 224388127 447728703 894409855 715078911 356417023 712835071 350878719 701756414 329769980 660587512 247432177 493814754 986579909 898368394 722993940
+1072693248 1071645696 1069550592 1066408960 1060125696 1046510592 1020328960 966917120 860093440 647494656 222297088 443545601 886042627 697294855 319799311 638551071 203361343 405675135 810302719 545816063 17891327 36831230 74711036 150470648 301989873 603978723 134214598 267379597 533709595 1066369590
+1072693248 1071645696 1069550592 1066408960 1060125696 1046510592 1020328960 967965696 863239168 652737536 232782848 465565697 931131395 787473415 501206031 1002412063 930033727 785278079 495766783 991533567 909325311 745957375 418172927 836344831 597898239 122054655 244109310 488217597 976434171 880175095
+1072693248 1071645696 1069550592 1066408960 1060125696 1046510592 1020328960 967965696 862190592 650640384 228588544 457177089 913305603 751821831 429902863 858758175 642726975 211713151 422378751 844758527 614727679 156762111 313524222 625999869 178257914 355467252 710934505 348127186 695205797 315620171
+1072693248 1071645696 1069550592 1066408960 1060125696 1047559168 1022426112 971111424 868482048 664271872 254802944 509605889 1019211779 963633159 853524495 633308191 192875583 385752191 771505407 469268991 938537983 803334143 533975039 1067950078 1062158333 1049525243 1025307638 975824876 877907928 682073009
+1072693248 1071645696 1069550592 1066408960 1060125696 1047559168 1022426112 972160000 871627776 670563328 267385856 533723137 1066397699 1059054599 1043319823 1012897823 952053823 830365823 586989823 99190271 197332991 395714558 792477692 510163960 1019278321 964814818 855887813 639082379 204422935 407796270
+1072693248 1071645696 1069550592 1066408960 1060125696 1047559168 1021377536 970062848 867433472 661126144 249560064 499121153 998243331 922745863 771750927 468712479 937425983 801111167 527432959 1053818367 1032847359 991951871 909112319 743433215 413123582 826247164 578752504 82714608 164380640 327712704
+1072693248 1071645696 1069550592 1066408960 1060125696 1047559168 1021377536 970062848 867433472 662174720 251657216 503315457 1006631939 938474503 803208207 531627039 1063255103 1051719807 1029697791 984606207 895471615 717201407 359612415 719223806 364704764 728360953 381931506 763861989 453981130 907961236
+1072693248 1071645696 1069550592 1066408960 1060125696 1047559168 1021377536 970062848 866384896 660077568 247462912 494926849 988806147 902822919 731905039 390068255 779087935 484434047 968868095 863994367 653198335 231606271 463212542 926424060 779105273 485516274 971031525 869369802 664997780 257302312
+1072693248 1071645696 1069550592 1066408960 1059077120 1045462016 1018231808 963771392 853801984 634911744 196082688 391116801 782233603 489676807 978305039 882869279 691997759 310254719 619461887 165181951 329315327 659678206 245613564 491227128 982454257 890117090 706491333 338191243 676381463 280068654
+1072693248 1071645696 1069550592 1066408960 1059077120 1045462016 1018231808 963771392 854850560 635960320 198179840 395311105 789573635 505405447 1010810895 947879967 822018111 569245823 64749823 129500671 257953791 515906558 1031812093 989882363 907071479 741448686 409154524 818309048 563924848 54106849
+1072693248 1071645696 1069550592 1066408960 1059077120 1045462016 1018231808 962722816 852753408 631766016 189791232 379582465 758116355 442491911 883936271 694130719 314519615 629040255 184339711 367630847 735261695 396781567 794611711 514431998 1027814397 981886971 890032119 705273839 336805854 674659260
+1072693248 1071645696 1069550592 1066408960 1059077120 1045462016 1018231808 962722816 851704832 629668864 185596928 371193857 742387715 411034631 822070287 569351199 64961599 128875647 257752319 515505663 1029963775 987233279 900723710 726657020 379572217 759143411 444543975 888039374 702336925 329882426
+1072693248 1071645696 1069550592 1066408960 1059077120 1045462016 1017183232 960625664 848559104 624425984 176159744 351271937 701496323 329251847 658504719 243268639 485489727 969931903 866123007 657456639 241172479 482343934 964686845 854582267 635421686 197101549 393154523 786308023 497824622 994600668
+1072693248 1071645696 1069550592 1066408960 1059077120 1045462016 1017183232 960625664 847510528 621280256 169868288 338689025 677379075 279968775 558889999 44038175 87027775 174055551 348111103 696222207 317654015 635307006 195822588 390595576 780141552 486541281 974131138 874520453 675299083 276856342
+1072693248 1071645696 1069550592 1066408960 1059077120 1045462016 1017183232 961674240 850656256 628620288 184548352 369097729 737147907 400553991 800059407 526378015 1051708479 1028626559 982462719 890135039 706528255 338266111 675483647 277225470 554450940 34110456 68219889 137487331 276022214 552044429
+1072693248 1071645696 1069550592 1066408960 1059077120 1044413440 1015086080 956431360 840170496 607648768 141556736 282065921 563084291 51379207 101710863 202374175 403700799 806354047 538967295 4193791 8388607 15727614 31454205 61858811 122668023 245336047 490672095 981343166 888943485 703095547
+1072693248 1071645696 1069550592 1066408960 1059077120 1044413440 1015086080 956431360 840170496 606600192 139459584 277871617 554695683 35650567 70253583 140508191 279968831 559937663 45084927 89122303 178245631 355442686 709836797 345931770 690814965 307888107 614727638 156761004 313520985 627040946
+1072693248 1071645696 1069550592 1066408960 1059077120 1044413440 1016134656 959577088 845413376 618134528 163576832 326105089 652210179 230678535 460308495 919569439 765398079 456006783 912014591 749239807 423690239 848428030 623113212 173533176 347066353 693084130 311377861 622754698 170717973 340386347
+1072693248 1071645696 1069550592 1066408960 1059077120 1044413440 1016134656 958528512 844364800 614988800 157285376 314570753 628092931 181396487 361745423 723490847 372191295 744383615 415026431 829005311 584269823 94796799 190641151 380232702 759415805 445088763 890176502 706610156 339477464 677905328
+1072693248 1071645696 1069550592 1066408960 1059077120 1044413440 1016134656 958528512 843316224 612891648 153091072 306182145 612364291 149939207 299879439 598711327 123681855 246316159 491584767 982120959 889451519 705161215 336580606 672111612 270480377 540959731 7128038 13206476 25363352 50725680
+1072693248 1071645696 1070599168 1067457536 1062222848 1050704896 1028717568 984742912 895745024 717749248 361757696 723516417 373292035 746584071 418377743 836756511 599772223 125802623 250556671 501113343 1001178111 928614399 782438398 491133949 983315450 891839477 708887530 344033237 689115051 304488278
+1072693248 1071645696 1070599168 1067457536 1062222848 1050704896 1028717568 984742912 895745024 718797824 363854848 726662145 379583491 758118407 441446415 881845279 689949759 306158719 611269887 148798975 296550399 592051198 109310973 219670523 439341046 877633516 681525209 289307571 578614118 83485389
+1072693248 1071645696 1070599168 1067457536 1062222848 1050704896 1027668992 982645760 892599296 711457792 350223360 699398145 324005891 646964231 220187663 439326751 878653503 683566207 292343039 583638527 93536255 187072510 375193597 749337594 424932341 850912234 629130197 184518571 369037143 738074287
+1072693248 1071645696 1070599168 1067457536 1062222848 1050704896 1027668992 982645760 892599296 712506368 352320512 704641025 334491651 668984327 264227855 528455711 1056911423 1040081023 1005371647 937001471 800261119 526779391 1053557758 1032325116 990908409 908074995 743456743 414219214 828437404 583131960
+1072693248 1071645696 1070599168 1067457536 1062222848 1050704896 1027668992 981597184 889453568 705166336 336591872 672135169 270528515 541057031 7323663 13598751 26148927 52297855 104595711 208142847 415237119 830473215 587203582 101713917 203427834 406854645 813708267 554723287 36753327 73505631
+1072693248 1071645696 1070599168 1067457536 1062222848 1050704896 1027668992 981597184 889453568 706214912 338689024 677378049 281014275 560979975 47169551 94339103 187629631 375260287 750521599 427302399 853557247 634421246 196149245 392297467 785642486 497543148 996134873 917478323 761213798 447637197
+1072693248 1071645696 1070599168 1067457536 1062222848 1051753472 1029766144 985791488 898890752 724040704 375389184 749729793 424669187 849339399 623889423 174037023 348074047 696149119 317508863 633969151 193147903 387344383 774688766 475635709 950222843 827751415 582808559 91874270 183747517 367494010
+1072693248 1071645696 1070599168 1067457536 1062222848 1051753472 1029766144 985791488 897842176 721943552 370146304 739244033 404746243 809493511 545246223 15703071 31407167 61765759 123531519 246015487 492031999 984062975 894383102 715024381 355258363 709467126 344142829 689333210 305972148 610895720
+1072693248 1071645696 1070599168 1067457536 1062222848 1051753472 1029766144 986840064 899939328 726137856 378534912 756021249 438300675 875552775 677363727 279937055 559874111 44957823 88867071 176686591 352325631 704651262 335560701 671120378 269546484 540141544 6541264 13082528 27213632 53378688
+1072693248 1071645696 1070599168 1067457536 1062222848 1051753472 1029766144 986840064 900987904 729283584 385874944 771749889 468709379 937418759 800047119 525304863 1050610751 1026431103 979120383 883451391 692113407 311533567 623067134 172392444 345833464 690618353 307494883 616038343 158334863 315620127
+1072693248 1071645696 1070599168 1067457536 1062222848 1051753472 1030814720 987888640 903085056 733477888 394263552 787479553 500169731 1000339463 926937103 780132383 485474367 969900159 866058495 658375167 243008511 486015999 972030974 870320124 666898425 261103602 523255780 1046510537 1020326802 966911781
+1072693248 1071645696 1070599168 1067457536 1062222848 1051753472 1030814720 987888640 903085056 732429312 392166400 784333825 493878275 986707975 899674127 724557855 374325311 748651647 423562495 846077439 618414079 162037758 324075517 648150010 222557173 446162922 893374420 713005992 351220561 702441122
+1072693248 1071645696 1070599168 1067457536 1061174272 1049656320 1026620416 980548608 887356416 700972032 329251840 658503681 242216963 483386375 965725199 856659999 639578175 204366975 408734975 817470975 560152575 46563327 94175231 188349438 375649277 752347130 430952436 861904873 650067922 225345444
+1072693248 1071645696 1070599168 1067457536 1061174272 1049656320 1026620416 980548608 888404992 703069184 332397568 664795137 255848451 510649351 1020251151 966761503 858733631 643725439 212660479 425320959 849593343 626493439 179245055 357441534 713834492 353926136 706802673 340912099 682872774 292002700
+1072693248 1071645696 1070599168 1067457536 1061174272 1049656320 1025571840 977402880 881064960 688389120 304086016 607124481 140508163 281017351 560987151 48233503 95419455 190838911 380629247 761258495 448775167 897550335 721358846 367927292 735854584 397967345 795934690 519176132 1038352265 1004011282
+1072693248 1071645696 1070599168 1067457536 1061174272 1049656320 1025571840 977402880 882113536 691534848 309328896 618658817 162528259 324008967 646970383 219150367 438300735 875552895 676315391 278888959 557777919 41812990 82576381 164103162 328205300 656410601 240127954 480254885 960508746 847274644
+1072693248 1071645696 1070599168 1067457536 1061174272 1049656320 1025571840 978451456 884210688 694680576 315620352 630193153 185596931 371193863 741339151 407888927 814730303 555719807 37698815 74349055 148698111 297396222 593743869 113745914 227491828 453934056 907867088 741992353 410242882 821533316
+1072693248 1071645696 1070599168 1067457536 1061174272 1049656320 1025571840 978451456 883162112 693632000 314571776 629144577 184548355 368048135 735047695 396353567 792707135 510624895 1021250815 967711231 860632063 647521279 220251134 441549820 884147192 694551536 315360224 630719425 186647426 374343428
+1072693248 1071645696 1070599168 1067457536 1061174272 1048607744 1023474688 974257152 874773504 676854784 281017344 560987137 47184899 93322247 186645519 373292063 746585151 419429503 838860031 602930687 132120575 264240126 527430652 1054861305 1034932210 995072997 915354570 755917716 438092584 876184144
+1072693248 1071645696 1070599168 1067457536 1061174272 1048607744 1023474688 974257152 875822080 678951936 284163072 567278593 60816387 120585223 241171471 481294367 962588735 850387071 625983743 178225663 356451327 711853054 349963260 698877945 324014067 648027111 223359950 446719901 893439802 714185332
+1072693248 1071645696 1070599168 1067457536 1061174272 1048607744 1023474688 973208576 873724928 674757632 275774464 550501377 27261955 53475335 106950671 213902367 426757183 852465791 631189759 188638719 376229887 752459775 430129151 860258303 646774782 219806716 439612409 880273394 686804964 299868105
+1072693248 1071645696 1070599168 1067457536 1061174272 1048607744 1023474688 973208576 872676352 672660480 272628736 544209921 13630467 26212359 52424719 103800863 206553151 413107327 825167103 575543807 77345791 154691583 308334591 617717759 162742271 326532094 653063165 232383483 464765943 930580462
+1072693248 1071645696 1070599168 1067457536 1061174272 1048607744 1024523264 976354304 880016384 686291968 298843136 596637697 118484995 235921415 470794255 940539935 807338047 540934271 7078143 14156287 28312575 57672702 115344381 231737339 464523255 929045487 785396703 497050559 994100095 914457342
+1072693248 1071645696 1070599168 1067457536 1061174272 1048607744 1024523264 976354304 878967808 685243392 297794560 595589121 117436419 234872839 468697103 937395231 801049663 528357503 1055666431 1036542463 998294527 923895807 775098366 477503485 955006971 835222518 595653612 117565401 236179379 472358759
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1035009024 996277248 918813696 764935168 456129536 912259073 749727747 424666119 848284687 621778975 168767551 337536127 675073279 275356159 549663743 26634238 54317053 107585530 214122485 428244971 856489942 638189484 201588568 403176113
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1035009024 997325824 920910848 769129472 464518144 927987713 781185027 487579655 974110735 873432095 673123391 271456383 542912767 11035135 22070271 45188094 91423741 182847482 365694965 730340331 385889239 772827055 472960862 945920700
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1035009024 997325824 920910848 768080896 462420992 924841985 774893571 474996743 948944911 824149023 574557247 74325119 148651263 296254975 591462399 109181951 219411454 438821884 877642744 680495089 286199778 572398533 71054218 141059861
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1035009024 997325824 921959424 771226624 469761024 938473473 802156547 529522695 1059045391 1044348959 1013907519 953025663 831261951 588782079 103822335 207644670 416337916 832674809 591606770 109471716 218943433 436837267 872624934 671508045
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1033960448 995228672 917765120 762838016 451935232 903871489 734002179 393214983 785382415 497024031 993000511 912260223 749731071 424672767 849346559 623901695 174060542 348121085 695193594 316645365 633290730 191790036 382530472 764012369
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1033960448 995228672 916716544 759692288 446692352 892337153 710933507 347077639 694156303 314570783 629141567 184542335 368037119 735025663 395260927 790521854 506253308 1012506616 950222832 827752416 581763008 88734592 176419585 352839171
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1033960448 994180096 914619392 755497984 438303744 876608513 679476227 284162055 568324111 62907423 124767295 249534591 499069183 997089791 920437759 767132670 460522492 922093560 770445296 466099168 932197313 790651778 508609284 1018267144
+1072693248 1071645696 1070599168 1068506112 1063271424 1053850624 1033960448 994180096 915667968 757595136 441449472 882899969 692059139 309327879 617607183 160423967 320847935 641696895 209652991 419305983 837563391 601384958 127979517 255958011 512963575 1026975727 981258207 887726015 701710207 330726143
+1072693248 1071645696 1070599168 1068506112 1063271424 1052802048 1031863296 989985792 907279360 741866496 409992192 819985409 565181443 56622087 113245199 226491423 452983871 905967743 738193663 402645503 804242431 533693438 1067385852 1061028857 1048314866 1022887909 970985418 869276565 664810283 256926294
+1072693248 1071645696 1070599168 1068506112 1063271424 1052802048 1031863296 989985792 906230784 738720768 404749312 808451073 542112771 10484743 19921935 39843871 79687743 158326911 315605247 631211519 187633663 374218751 748437502 423133180 846266360 619839473 166985699 333970375 667939727 262136606
+1072693248 1071645696 1070599168 1068506112 1063271424 1052802048 1031863296 991034368 908327936 743963648 415235072 829422593 584055811 94369799 187691023 375383071 749718591 424646783 849293567 623796735 172803071 344557566 689115133 305537018 612122613 150502379 299955158 599909292 127124312 255296176
+1072693248 1071645696 1070599168 1068506112 1063271424 1052802048 1031863296 991034368 909376512 746060800 418380800 835714049 596638723 119535623 239071247 477093919 953139263 831489151 588188927 101587455 202126335 404251646 808502268 544311289 15929330 32906212 66859976 132670353 264291107 529629767
+1072693248 1071645696 1070599168 1068506112 1063271424 1052802048 1032911872 993131520 913570816 753400832 434109440 867170305 660598787 247456775 494914575 988781599 902773823 730757247 387772671 775546367 477351935 955751422 838808573 602826747 130863094 260676589 520303578 1040606132 1007469416 940147409
+1072693248 1071645696 1070599168 1068506112 1063271424 1052802048 1032911872 992082944 911473664 750255104 427817984 854587393 635432963 197124103 394248207 787447839 501153855 1002307711 930873599 788005375 501220351 1003488255 933233662 793774077 514854907 1029709815 986726382 900758493 728822714 384951157
+1072693248 1071645696 1070599168 1068506112 1063271424 1052802048 1032911872 992082944 911473664 749206528 425720832 851441665 629141507 184541191 369082383 738164767 402587711 804127871 534514943 1067982335 1062223871 1050704894 1027666941 981591035 889439222 706185196 338628568 676207537 277623651 555246278
+1072693248 1071645696 1070599168 1068506112 1064320000 1054899200 1037106176 1001520128 930348032 786955264 501218304 1001389057 927988739 781188103 488635407 977271839 880802879 686816383 299891967 598736383 122683391 245365758 490730493 980412411 887082999 700424174 327106525 654213051 233635703 466221806
+1072693248 1071645696 1070599168 1068506112 1064320000 1054899200 1037106176 1001520128 930348032 788003840 503315456 1006631937 938474499 802159623 530578447 1061157919 1048575039 1022359679 970977535 868213247 661636095 249529343 499057662 997066749 920391675 765991927 458241006 916480989 760267707 446792567
+1072693248 1071645696 1070599168 1068506112 1064320000 1054899200 1037106176 1001520128 929299456 784858112 495975424 990903297 907017219 739245063 404749327 808450079 542109759 9429119 17809663 34571775 68095999 136190974 271332348 542663672 11584497 23167971 46334918 93718412 187436825 373825075
+1072693248 1071645696 1070599168 1068506112 1064320000 1054899200 1037106176 1000471552 927202304 780663808 487586816 975174657 875559939 677378055 281014287 562029599 49269823 98540671 196033791 391019007 782038015 490334206 980668412 887595001 701448178 328104933 655160266 235530133 471060266 942120532
+1072693248 1071645696 1070599168 1068506112 1064320000 1054899200 1036057600 998374400 923008000 773323776 473955328 947910657 821030915 568321031 61852687 122656799 245313599 489579647 979160319 884578815 694367231 314992638 631033852 187277304 373506032 747012064 420282305 840563586 606335749 139978251
+1072693248 1071645696 1070599168 1068506112 1064320000 1054899200 1036057600 999422976 925105152 776469504 480246784 960493569 846196739 617603079 160415759 320832543 641666111 208542847 416038143 832076287 590410751 108127231 216253439 432505855 865010687 656278527 237765631 474482687 948965374 824188924
+1072693248 1071645696 1070599168 1068506112 1064320000 1055947776 1038154752 1003617280 934542336 795343872 516946944 1033893889 992997379 912253959 750767119 427793439 854539327 634288255 194834687 389670399 778293247 482844671 966737918 858685436 642580472 210370544 419692513 840433603 607125383 139459343
+1072693248 1071645696 1070599168 1068506112 1064320000 1055947776 1038154752 1002568704 932445184 791149568 508558336 1016068097 957345795 839901191 606060559 138379295 275710015 551421055 28052735 55056895 109065215 219179007 438358014 877764605 682835963 291930103 584908782 96075741 191102906 383253364
+1072693248 1071645696 1070599168 1068506112 1064320000 1055947776 1039203328 1004665856 935590912 797441024 521141248 1042283521 1009777667 944765959 815791119 556791839 39841855 78636159 157273343 314546687 628044799 182346750 364692476 728336377 381882354 763764709 453787595 906525591 739308334 405922396
+1072693248 1071645696 1070599168 1068506112 1064320000 1055947776 1039203328 1005714432 937688064 802683904 531627008 1063255041 1051720707 1029699591 985657359 896525343 719309887 364878975 729758975 384727551 769455103 465167359 929285119 784828414 495915005 991830010 909918196 746093544 417395664 834791329
+1072693248 1071645696 1070599168 1068506112 1064320000 1055947776 1039203328 1005714432 938736640 803732480 534772736 1068497921 1062206467 1050671111 1027600399 981458975 889176127 703562879 333384959 666770943 259801087 518552574 1037104125 1000466427 928239607 782737391 492781535 984514494 894238588 713685753
+1072693248 1071645696 1070599168 1068506112 1064320000 1055947776 1039203328 1005714432 938736640 804781056 536869888 1073740801 1072692227 1071642631 1069543439 1065345055 1056948287 1040154751 1006567679 939393535 805045247 536347647 1072694270 1071646717 1070600187 1068507127 1064321007 1055948767 1039204287 1005715327
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040203520 1006665472 939589376 805437184 537657088 2096896 3669504 6814720 13105152 25686017 50847747 101171207 201818127 403636511 807273279 540804991 7868415 15736575 31472895 62945535 125890814 251781372 504086776 1008697584
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040203520 1006665472 940113664 807010048 540278528 7339776 14155264 27786240 55048192 110096641 220193539 440387335 880774927 687283743 300301375 600603007 126940159 254404351 508808447 1018140927 962540030 851338236 628934648 184127217
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040203520 1007189760 941162240 808582912 543424256 13631232 26738176 53476608 106953472 213907201 427814659 855105031 635943951 198146079 396292159 792584575 511427583 1022854911 971967999 870193919 666645758 259549437 519098875 1038197751
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040203520 1007189760 941162240 808582912 543948544 14679808 28835328 57670912 115342080 230684417 461369091 922213895 770161679 466057503 931590975 789440383 504614911 1009229567 944717311 815168255 556594430 38922493 77844987 156214263
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040727808 1008238336 943259392 813301504 552861440 31981312 63962880 127926016 255852288 511704833 1022885635 971505415 868744975 663748127 253230143 506460543 1012397055 951052031 827837695 581409023 88551679 176578815 352633087 705266175
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040727808 1007714048 941686528 809631488 545521408 17301248 34602752 68681216 136838144 273152001 546304003 18341895 36683791 72843295 145162303 290324863 580125695 85985279 172494847 344989439 689978623 306215166 612954364 152166648
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040727808 1007714048 941686528 809631488 546045696 18349824 36699904 72875520 145226752 289929217 579858435 85450759 170901519 341803295 683082559 292423551 584847359 95428607 190857215 381714175 763952383 454162686 908325116 743432696
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040727808 1007714048 942210816 811204352 549191424 24641280 49282816 98041344 195558400 391117057 781710083 489678599 979357455 884449055 694632255 314998655 629473279 185204735 370409471 741343231 408944639 817364990 560463868 46661369
+1073217536 1072693504 1071645440 1069549312 1065357056 1056972544 1040727808 1007714048 942210816 810680064 547618560 21495552 42991360 85458432 170392576 340785409 681046787 287827463 575130639 75995167 151466047 302932351 605340671 136415231 272830463 546185215 18104319 36208382 72940796 146405881
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041776384 1010335488 947453696 821690112 569638656 65535744 131071744 261619456 522714880 1044905729 1015545603 957349383 840432655 607123743 139981631 279439231 558354431 42442495 84360447 168720895 337441791 674883326 276024572 552573176
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041776384 1010335488 946929408 820641536 567541504 61341440 122683136 244842240 489160448 978320897 882375683 691009543 307752975 614981919 156222271 311920511 623841279 173940735 347881471 695238655 317259775 634519294 195296508 390593017
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041776384 1010335488 946929408 820117248 567017216 60292864 120585984 240647936 480771840 961543681 848821251 623900935 173536015 347072031 693619775 313497983 626471935 179202047 357879807 715759359 358300927 717126142 360510460 721020921
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041776384 1009811200 945880832 818020096 562298624 50855680 101711616 203423232 406846464 813168641 552071171 29876487 59228943 118458143 236392255 472784511 945569023 816871935 560526335 47835135 96194559 192389119 384253951 767983358
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041776384 1009811200 946405120 819068672 564395776 55049984 110100224 220200448 440400896 880802049 687338243 300410631 600297231 126328607 252133183 503742079 1007484159 941226239 809234687 545251839 16237567 32475135 64425983 128851967
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041252096 1008762624 943783680 814350080 555482880 37748480 74972672 149945344 299366400 598732801 123199491 246398983 492797967 985071647 896401727 719061631 364381695 728763135 383784191 768092415 462967038 925933821 778125818 482509557
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041252096 1008762624 943783680 813825792 554434304 35651328 70778368 141556736 282589184 565178369 56090627 112181511 223838991 447677983 895356223 716446335 359151103 718826239 363910399 728345087 383472638 766945277 459624442 919248885
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041252096 1008762624 944307968 814874368 556007168 38797056 77069824 154139648 307755008 614985985 155706115 311412487 622300943 170860319 341720639 683441279 292616703 585757695 97773567 195546879 390569470 780614653 487487226 974450164
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041252096 1009286912 945356544 816971520 560201472 47185664 93847040 187170048 374340352 748680961 423096067 846192391 618118927 162496287 324468287 648412543 222558975 445117695 890235391 706204415 338666750 676808956 279351801 558703347
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041252096 1009286912 945356544 816971520 560725760 48234240 95944192 191364352 382728960 765458177 456650499 913301255 752860943 431455775 862387519 650509183 227276543 455077119 910154239 746042111 418342142 836159996 598577913 123414003
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041252096 1009286912 945356544 817495808 561774336 50331392 100138496 199752960 399506176 799012609 523759363 1047518727 1020771343 967800863 861335871 648405887 223069951 446663935 892803583 711341055 348415998 696307452 319397113 638793971
+1073217536 1072693504 1071645440 1069549312 1065357056 1057496832 1041252096 1009286912 944832256 816447232 559152896 45088512 89652736 178781440 357563136 714601985 354937859 709875719 345485327 690970911 308199999 615875967 157485823 315495935 630991615 188241151 377006590 754012924 434284025 868567794
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043873536 1014005504 954269440 835321600 597425920 121634560 242745088 485490432 970981120 867696385 661126915 248512007 496499727 992475423 911209279 748152703 422563839 844603391 615464959 157187839 314375422 628750588 184283384 368566512
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043873536 1014005504 954793728 836370176 598998784 124780288 249036544 498073344 995622656 917503489 760740867 447739911 895479823 716693535 359645247 718766207 363266303 726008319 377750527 754976767 436735998 873471996 673202169 272662515
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043873536 1014529792 955842304 838467328 603717376 134217472 267910912 535821824 1071643648 1069545473 1064824835 1055383559 1037025295 1000308767 926875711 780009599 486277375 972030463 870319103 667420415 261098750 522197244 1044918520 1016095217
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043873536 1014529792 955842304 837943040 602668800 132120320 263716608 527433216 1054866432 1035466753 996667395 919593223 764920591 456099359 911674431 749082751 424423679 848847103 624476415 175210751 350945534 701890812 329515256 658505968
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043873536 1014529792 955318016 836894464 600047360 126877440 253230848 506461696 1012399104 951056641 827847427 581953287 90165007 180330271 360660799 721321855 368377855 736755455 400293119 801110527 528479230 1056958460 1040175097 1006608371
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043873536 1014529792 955318016 837418752 601095936 128974592 257425152 514850304 1029176320 984086785 893907715 713549319 352832527 705665311 337064767 673605503 272945151 545890303 18563071 37650431 74776574 149028860 298582009 597163762
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043873536 1014529792 955318016 837418752 601620224 130023168 259522304 519044608 1037564928 1000864001 927986435 781706759 489147407 977770527 881799231 689332607 304923647 609323007 144904191 289808127 579615998 84965884 169931769 340387570
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043349248 1013481216 953745152 834273024 595328768 116915968 233307648 466615296 932706304 791146497 508551171 1017102343 960463119 847184415 620102719 166463615 332927487 666379007 259540479 519080703 1038685438 1003628796 934039801 794337522
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043349248 1013481216 953745152 833748736 594280192 114818816 229113344 458226688 915929088 758116353 442490883 884457735 694649359 315032607 630065215 186388607 372253183 744506367 415794943 832113919 590485758 107229692 213934841 427869427
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043349248 1013481216 953220864 832700160 592183040 110624512 220724736 441449472 882898944 691532033 308798211 617072391 159878671 319757343 639514687 204763519 409527039 819578367 565414655 57611775 114699006 228873724 457223160 914970608
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043349248 1013481216 953220864 833224448 592707328 111673088 222821888 445643776 891287552 708833537 343925507 687851015 301960463 603921183 133576511 266628991 532733695 1065467135 1057716735 1041691391 1009116670 943966972 814192120 554642161
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043349248 1012956928 952696576 832175872 590610176 107478784 214433280 428866816 857733888 641201665 208661507 417323015 834121999 594502175 114738239 228952191 457904639 916333567 758401023 443059967 886644222 700070908 326399992 652275696
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043349248 1012956928 952696576 832175872 591134464 108527360 216530432 433061120 866122496 657978881 241691651 483383303 966242575 858219295 642172735 210079359 419634687 839269375 604272639 134803455 270130942 540261884 6781944 13563632
+1073217536 1072693504 1071645440 1069549312 1065881344 1058545408 1043349248 1012956928 952696576 831651584 589561600 105381632 210238976 420478208 840956672 608171521 142601219 284678407 569356815 64447519 128895039 257790079 515056127 1030111999 985957631 898173183 723128830 372515580 744506872 415271921
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042300672 1011384064 949550848 825360128 577502976 81788672 163577344 327154688 654309376 234876929 469229571 938459143 802652431 531039007 1061553727 1049365887 1024465919 975189759 877161983 681106431 288995327 577990398 82763260 165002232
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042300672 1011384064 949550848 825884416 578551552 83885824 167771648 335543296 671086592 267907073 535289859 1070579975 1067418127 1060570399 1047398975 1020532095 967322623 860903423 648589055 223960319 448444671 897413374 721084668 368427513
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042300672 1010859776 947977984 822214400 571211520 69205760 138411520 276823296 553646848 33552129 67104515 134209031 267894031 535263775 1070527807 1067314047 1060362239 1047506943 1021272063 968277759 862289151 650836478 228455420 456386552
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042300672 1010859776 947977984 822214400 570687232 68157184 136314368 272628992 545258240 16774913 33025795 66051591 132103439 264207135 527889983 1055780223 1037818879 1002420223 930574335 787406847 501071871 1001619198 929496316 785250808
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042300672 1010859776 948502272 823787264 573832960 74448640 148897280 297794816 595065600 116389377 232254467 464509191 929018383 784295199 494324287 988124287 901982463 730223103 386704383 773408511 473599487 947198718 820655357 568092922
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042824960 1011908352 950075136 826932992 580648704 87555840 175111936 350224128 700448512 326631169 653262595 232259335 464518927 928513567 783285311 492304767 984609791 895477503 717212927 360159743 720319487 366897150 733794300 393322233
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042824960 1011908352 950599424 827981568 582221568 90701568 181403392 362807040 725090048 376438273 752352259 430438663 860353295 646965023 219664191 439328383 878132479 682523135 291304447 582608895 91475967 183476222 366428157 732856058
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042824960 1012432640 951648000 829554432 585367296 96993024 193986304 387972608 775945216 478148609 956297219 838852615 603963407 134184991 267845695 535691647 1071383551 1069025279 1064833023 1056448255 1039678719 1005615614 937489404 801761016
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042824960 1012432640 951648000 830078720 586940160 100138752 200277760 400555520 801111040 527955969 1055387651 1037033735 1000325903 926910239 780078911 486416255 972308479 870874879 668007679 262797567 526119167 1052762622 1031259132 988776185
+1073217536 1072693504 1071645440 1069549312 1065881344 1058021120 1042824960 1012432640 951123712 829030144 584843008 95944448 191889152 383778304 767032320 460323073 920122115 766502663 459263759 918527775 763313983 452886143 905247999 736229887 399242239 799008767 524275711 1048551166 1022835965 971405818
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1046494976 1019772672 966328064 858914560 644087552 214957824 429391616 858783488 643825408 213384961 426770179 853016327 631766799 189792031 379060031 758120319 442499071 884997887 695729407 317192447 633860350 193978876 387433465 774866931
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1046494976 1019772672 966328064 858914560 644611840 216006400 431488768 862977792 651689728 229113601 458227459 915930887 758120207 441974303 883948607 693631359 313521151 627042303 179818495 359112447 718224638 362182908 723841272 373416433
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1046494976 1019772672 965803776 858390272 643563264 213909248 427818752 855113472 635961088 198180353 396360707 792721415 511701007 1023402271 973062975 872384127 670502143 267262207 534524159 1069048319 1064354815 1054967807 1036718079 999694335
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1046494976 1019772672 965803776 858390272 643038976 212860672 425721600 850919168 628096768 182451713 364903427 729806855 385347599 770170911 466075711 931627135 788988159 504234495 1007944703 942147583 810553343 547364607 21511422 43022845
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1046494976 1019248384 964755200 855768832 637796096 201850624 403701504 806878720 539491328 5240833 9957379 19914759 39829519 79659039 158793791 317063295 633602303 193462783 387449855 775423743 477629695 955259391 836776959 599812094
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1047019264 1020296960 966852352 859963136 646708992 220200704 439877120 879754240 685242368 296742913 593486083 112706055 225412111 450824223 901124415 727982719 382223871 764447487 455153151 910306047 746870014 419997948 839995641 606249459
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1047019264 1020821248 968425216 863108864 653000448 232259328 464518656 928513280 783284992 492304129 984083971 893902087 713538319 352810783 705621567 337501567 675003135 276264191 552528127 30789887 61579519 123159039 245793535 491587070
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1047019264 1020821248 968425216 863633152 654049024 234356480 468712960 937426176 801110784 527955713 1055387139 1036508167 999274511 924283167 774300223 474858623 949193215 824644351 575022335 76302847 152081407 304686846 609373437 145529339
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1047019264 1020821248 967900928 862060288 650903296 228065024 455605760 910687488 747633408 421524993 842525955 611310343 148354831 296185375 591846719 109951871 219903743 440331775 880139263 686536703 299331326 598662653 123059194 245593845
+1073217536 1072693504 1071645440 1070073600 1066929920 1060118272 1047019264 1020821248 967900928 862060288 650379008 227016448 453508608 906493184 738720512 403699201 806874371 540007175 6272783 12545823 24567359 48610687 96697087 193918207 387311871 774623743 475505406 950486269 826706171 579670519
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1048067840 1022394112 971046656 868876032 664010496 254279424 508558848 1016593408 959444992 844623873 615506179 157270791 314541839 629083679 184425535 368851071 737177855 400613631 801751551 530285567 1060571135 1047400190 1021058557 967851002
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1048067840 1022394112 971570944 869400320 665059072 256376576 512228864 1024457728 975173632 876605697 678945283 284148743 568297487 62853407 125182783 250365823 500207615 1000414975 927088127 780958719 487651070 975826173 877910522 682603252
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1048067840 1022394112 971570944 869924608 666107648 258473728 516423168 1032322048 990902272 908062977 741859843 409453831 818383631 562501407 51261247 102522495 205044991 410614015 821752319 570287103 66832126 134188284 267852024 535703793
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1048067840 1022918400 972619520 871497472 669777664 266338048 532151808 1063779584 1053817600 1033369345 992996867 911727879 749189903 424113695 847703103 621140351 168014847 336553983 673107967 272473855 544423166 15628796 31257336 62514673
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1048067840 1022918400 972619520 872021760 670826240 268435200 536346112 1072692480 1071643392 1069020929 1064300035 1054858247 1035974671 998207519 922673215 771604607 469467391 938934783 803603455 532940543 1065356542 1056971261 1040200698 1007183860
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1048067840 1022918400 972095232 870973184 668729088 264240896 528481792 1056963840 1040186112 1006630401 938994947 803723783 533181455 1065838879 1057411903 1040557695 1007373567 941005311 808268799 542271231 11324927 22125567 44775167 90074367
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1048067840 1022918400 972095232 870973184 668204800 263192320 526384640 1052769536 1031273216 988804609 903343363 732420615 390575119 781150239 488558655 977117311 879968511 686719231 299696383 599392511 125043199 249562111 499648254 999820541
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1047543552 1021869824 969998080 866778880 660340480 246939392 493354752 986185472 898629376 722992641 371719171 743438343 413134863 826269727 578273343 82280575 164036863 328073471 655622399 237502975 475005950 950536188 827854841 582492147
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1047543552 1021869824 969998080 866254592 658767616 243793664 487063296 974126848 873987840 673709569 273153027 545782023 17298191 34072351 68144959 136290175 272056319 543588351 12910591 25296895 51118078 102760189 205520122 411040244
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1047543552 1021345536 968949504 864681728 655621888 238026496 475528960 951057920 828374016 583006209 92270595 184541447 369083151 738166559 402591551 805183103 536624383 1073248511 1072754943 1071768063 1070318590 1067419389 1061096699 1047927286
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1047543552 1021345536 968949504 864157440 655097600 236977920 473431808 946339328 818412544 563083265 52424707 104325127 208125967 415727647 830931007 588120447 101975039 203425791 407375871 815276031 557334526 41451516 82903032 165806065
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1047543552 1021345536 968949504 864157440 654573312 235929344 471334656 942145024 810548224 547354625 20967427 41410567 82296847 164593951 328663871 656803711 239865855 479731455 959986943 846756351 619770878 166324220 332648441 665820915
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1047543552 1021345536 969473792 865206016 657194752 241172224 482344704 964165120 854064128 633862401 193458947 386917895 773311503 472881439 945238847 816736127 559206399 44146687 88293375 176586495 352648447 705296638 336851197 674226426
+1073217536 1072693504 1071645440 1070073600 1066929920 1060642560 1047543552 1021345536 969473792 865730304 657719040 242220800 484441856 968883712 864025600 653785345 233304835 466085639 932171535 790076959 506412095 1012824191 951906559 829546751 585875711 98009343 196018431 392036863 783549438 493356797
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045970688 1018724096 963706624 853671680 634126080 195034880 390069760 780139520 486537216 973074433 871883011 669500167 264734223 528944159 1057888575 1041511039 1009280255 945342975 817468415 561194751 49171966 98343932 196687865 393375731
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045970688 1018724096 963706624 854195968 634650368 196083456 392166912 783809536 493352960 986181633 898621699 722977287 371688719 742853407 411440703 822357375 570973183 68728831 137457663 274915071 550354430 26966781 54457850 108391157
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045970688 1018724096 964230912 855244544 636747520 200277760 400031232 799538176 525334528 1050669313 1027596803 981451783 888637711 703533599 332801343 665078399 256414975 513353983 1026707711 980197631 886653183 699564543 325911550 651298557
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045970688 1018199808 962658048 851574528 629931776 186121984 372243968 744488192 414710528 829421057 584576259 95410695 190297359 380594975 761189951 448638335 896752639 720287743 366833407 733666559 394115326 788230652 502719480 1004914672
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045970688 1018199808 962658048 852098816 630980352 188219136 376438272 752352512 430439168 860354049 646442243 219142919 438285839 876047647 678353471 282965119 565405951 57069823 114139647 227755007 455510014 911020029 747773690 421805300
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045446400 1017151232 961085184 848953088 624688896 176160512 351796992 703594240 333446912 666369793 258473731 516947719 1033895695 994049823 914358079 754974591 435683327 871366399 668990719 264239359 528478463 1056432638 1038599165 1003456250
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045446400 1017151232 961085184 848428800 623116032 173014784 345505536 690487040 306708224 613416705 153091843 306183687 612367375 150468639 300937279 601874559 130007295 259490047 518979839 1037435135 1001128191 927990271 782238718 490735612
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045446400 1017151232 960560896 847904512 622591744 171966208 343932672 687865600 301465344 602930689 131595267 262666503 524808975 1049093663 1023921215 973576319 872886527 671506943 269272063 538019839 2297854 5119996 9715705 18906866
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045446400 1017151232 960560896 847904512 622067456 170917632 341835520 683671296 293601024 587202049 100662275 200800519 401601295 803202847 532139839 1064279679 1054293247 1034844415 995422463 917102847 760463614 447185148 894370040 714998256
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045446400 1017675520 961609472 849477376 625737472 177733376 355467008 710409728 347077632 693630977 312995843 625467655 176669455 352814879 705630015 336993919 673987839 274233855 548991999 24242175 49008638 98017021 196033786 391543029
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045446400 1017675520 961609472 850001664 626261760 178781952 357564160 715128320 355990528 711981057 350220291 699916295 325566479 651132959 228524095 457048447 914097151 754452479 435163135 870326271 667435006 261127932 522779897 1046084083
+1073217536 1072693504 1071645440 1070073600 1066405632 1059593984 1045446400 1017675520 962133760 850525952 627834624 181927680 363331328 726138368 378010624 756021505 437777155 875554567 677367567 280993311 561986623 49707391 98890751 197256959 394513663 789027071 504312063 1008623871 942981374 811696381
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044397824 1015054080 956890880 840564480 607911680 142606080 284688128 568852224 63962880 127926017 255852291 511704839 1023409935 972553759 870841407 667416959 260568063 521136127 1041747967 1009229823 944193535 814120958 553975805 34209531
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044397824 1015054080 956890880 840040192 606863104 140508928 280493824 560987904 48234240 95944449 191889155 383254023 765983759 458225695 915927103 757588095 441434367 882344191 690422015 306577919 612631551 150996991 301993983 604511999
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044397824 1015054080 956366592 838991616 604765952 136314624 272629504 545259264 16252672 32505345 64486403 128972807 257421327 514842911 1029686079 985106303 896471039 718675711 363085055 725645823 378074110 756148221 438554619 877633271
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044397824 1015054080 956366592 838991616 604241664 135266048 270532352 541064960 8388352 16776705 33553411 67106823 133689359 266854431 533184575 1066369407 1058997247 1043728383 1013714943 953163519 832584958 591428093 109638650 219801333
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044397824 1015578368 957415168 841613056 610008832 146276096 292552448 584580608 95419392 190838785 381153283 761782279 449298447 898596895 722927679 371589247 743178495 412090879 824706047 575670015 78122238 156244476 312488952 624977649
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044397824 1015578368 957939456 842661632 612105984 150470400 300416768 600309248 126352384 252180737 504361731 1008723463 943180815 812620063 550974271 28206975 55889919 111255551 222511103 445546239 891092223 707918334 342619132 685762297
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044397824 1015578368 957939456 842137344 610533120 147324672 294125312 588250624 102759424 205519105 410514179 820504327 567267087 60792351 121584703 243169407 485814527 971629055 870040575 666863615 259461119 518922239 1037844479 1001422591
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044922112 1016626944 959512320 845807360 618397440 163577600 327155200 654310400 234354688 468709377 937419011 801096199 528450831 1056377631 1038489407 1003236991 932732415 791723007 509704191 1019932671 966123262 858504445 643267066 212267764
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044922112 1016626944 960036608 846331648 618921728 164626176 328728064 656931840 239597568 479195393 958390787 843040007 612338191 150934815 301345599 602691199 131640831 263805951 528136191 1056272383 1038278655 1002815230 931888637 790559482
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044922112 1016626944 960036608 846331648 619446016 165674752 330825216 661126144 248510464 497021185 993518083 913294599 752847375 431428639 862332991 650399871 227058175 454640383 909804799 746391807 418517247 837558526 600850940 128484344
+1073217536 1072693504 1071645440 1070073600 1066405632 1059069696 1044922112 1016102656 958988032 844758784 615776000 157810432 315096576 630193408 186645248 373290753 746581507 419421447 838318607 602895391 132048959 263573887 527147775 1054819839 1035373311 997004799 919743231 766268927 458796030 918116348
diff --git a/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser
new file mode 100644
index 0000000..fe13d07
Binary files /dev/null and b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedSequenceBase2.ser differ
diff --git a/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.dat b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.dat
new file mode 100644
index 0000000..fa5d72a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.dat
@@ -0,0 +1,3132 @@
+ 656877351 888468724 582476471 263458740 549200060
+ 59556748 159292459 927157496 208687340 256081932
+ 96151308 1001976844 778423 3670964 9788606
+ 58429315 190586891 999079995 663780135 927168756
+ 727174112 1001715712 654687163 876329912 536870923
+ 59 156 847 3040 14336
+ 644245094 189483851 277909648 706079253 81609949
+ 681322652 76809364 9797482 938126963 561862089
+ 599241511 603359715 335630556 41953820 4719836
+ 622435 86413 10806 939522855 159383395
+ 281542676 706641922 857978879 165934720 282853713
+ 706478122 81526788 10223616 1310720 163840
+ 922746880 650117120 162529280 615251968 731578368
+ 143310848 185184256 136621056 472619776 784894656
+ 364579248 614737068 187378587 134744202 196086537
+ 145492004 186691627 134251208 469809595 34980
+ 549129003 9095176 386513840 136619180 724275232
+ 134775488 196131248 145524908 186649376 134219776
+ 442128986 997948283 735325140 260263811 165628383
+ 999046028 537649140 930675636 980659429 994102519
+ 988090673 998257667 726360724 943195060 570425867
+ 872416120 702406498 999263235 823707441 55555075
+ 165634708 999031732 823848165 55376768 165541526
+ 999274423 537523721 930854779 727722848 939538432
+ 713031680 1028653056 276430848 620052480 854321152
+ 1063687424 832587680 1045106516 468108990 593634153
+ 455302564 588966884 831404242 508908875 570011537
+ 447011402 58925105 22147102 711520267 1027043335
+ 274395139 619589633 853889316 526625828 556899090
+ 450414731 61695349 22003374 711319143 1026803237
+ 469762048 301989888 897581056 37748736 556793856
+ 872939520 43384832 558432256 339001344 370679808
+ 924604416 51127296 563352320 340279424 371599712
+ 922817680 587236172 352338306 377498053 390075586
+ 934283841 55051268 565463810 341337217 372011844
+ 923021702 587340231 352404675 377528897 390090756
+ 444180378 582771426 386103319 820878768 768469037
+ 464323199 540534784 147816993 431574890 613378415
+ 440641348 585935378 231724032 321626113 957130649
+ 762735301 11922288 24841932 431584074 613353990
+ 440628151 585993112 231593821 321875383 956409753
+ 761948869 9300848 19861196 419525450 636044067
+ 874725940 276345168 1005962939 404842328 44797378
+ 390406220 719975565 573261292 995508545 418874694
+ 907865252 322234891 226350248 200248046 996057078
+ 201936327 719123621 922205246 327333406 761281382
+ 321461212 767251478 664019960 1027015197 482492772
+ 825457823 37181888 54617459 914535782 331364768
+ 1010287484 387092183 210512716 885098740 230022349
+ 551664607 58382349 737801095 851645666 472257767
+ 20236143 1039091209 58775360 44000576 1061407804
+ 1021417497 1044512992 686817112 210706479 885241161
+ 830590908 506889871 1048831907 677547034 209243052
+ 497930786 3673294 3045292 1006772076 389294580
+ 383778816 722468864 23986176 800129024 446226432
+ 541724672 113339904 589345024 1049786592 617306640
+ 489140078 601067953 120449688 753963338 584870081
+ 682250784 798181746 208257243 609395273 78189410
+ 888204725 603484858 1054489373 619387786 618105313
+ 734765746 19396379 798079435 208182949 72485714
+ 797691305 916845735 838620602 199186574 871209474
+ 180399414 699390035 885049866 141980244 1071890820
+ 754290912 39426818 782574121 57721156 205900573
+ 1016814417 548868435 198534677 618459513 1032097815
+ 360613782 912640615 151106594 1059022478 336624603
+ 915641088 836919848 198639940 476126250 158531351
+ 320625556 145603358 1026437737 689605644 886415366
+ 36494372 532079256 1003948847 1001007135 702276442
+ 935712093 179356063 823085178 411563162 1036670051
+ 969575398 613724341 283217954 64356554 411422760
+ 1036440567 972620198 923763234 446208822 837231233
+ 140942575 1042276777 558226066 174891183 561907690
+ 833626112 1065951744 676442144 744458530 242868624
+ 1028183211 688138321 933312349 663154153 456614081
+ 311292277 929221086 759197949 782450045 92727721
+ 135750339 265211988 744622621 895689816 744538378
+ 80893429 149626249 1041476033 333218294 489559448
+ 3497406 833481181 612483580 986031260 456496733
+ 288187994 1040877298 584034803 740892765 185962756
+ 223526700 504477666 872328211 961448611 568563773
+ 76405404 3404749 286335128 273398420 772987227
+ 869206788 677205034 534411943 844411645 750549094
+ 529798898 224203291 171361268 488019589 555966858
+ 1043606254 586996973 739629675 436829754 488020266
+ 726723408 241700456 542662744 340038724 212334364
+ 673599040 274420696 732093716 428903160 420497320
+ 84157788 651420100 465115563 825667870 901203511
+ 635535330 972134899 438394402 659477623 445925173
+ 214900149 53648802 575177259 1011138422 1009022596
+ 725405947 314572857 1062425461 509501038 391336827
+ 832380928 449565184 885234976 315693522 339882397
+ 688587545 43036721 435882755 351310000 770844939
+ 778175120 394854441 413831170 25864448 538487440
+ 570526377 35657898 2228618 139288 433201665
+ 351404576 233963554 744620034 713621504 581472256
+ 651725056 785426576 326118025 878191272 185246250
+ 427995892 662554007 646594092 381174886 120481560
+ 74051729 151793698 107256717 648226380 909901265
+ 386184892 82577126 461699916 832113327 556372916
+ 5036425 10885656 21140430 33858208 383827849
+ 128674962 909897723 386178314 607586154 187402588
+ 596950670 1023697982 657479363 640607768 75355119
+ 950536562 478684748 594879656 1033893912 552877714
+ 365048126 727978816 945631324 722799762 576284404
+ 816971172 990580781 403321524 827566804 595847602
+ 80582864 139450810 290621896 595712268 677167010
+ 194174546 592590670 857159012 826954460 598666038
+ 650764039 945042922 156761958 137639898 92668180
+ 870883690 588967179 967694184 122881776 820086398
+ 353761665 707504642 390955973 281173692 449332314
+ 180911562 768158772 447482560 826446334 591669534
+ 88266177 178575720 291259279 593963196 34277285
+ 980309206 193647793 697063138 538890853 2294166
+ 266141201 551639010 840271141 441287894 72842064
+ 152017920 532899264 677598108 595198821 273175823
+ 171751782 137898758 512746362 602753470 239851162
+ 718368930 454835701 167785381 134911118 569872
+ 178296170 143309076 168335032 134265376 515408416
+ 601251466 81094594 577461140 901911576 866038442
+ 250167938 710132232 568838144 418496512 178257920
+ 962528777 910580770 482684436 503777539 294831360
+ 862374375 783875012 793648388 1007619703 75428582
+ 628241995 839867444 966674752 540738649 2280853
+ 838404064 761024423 999752918 590241100 631043518
+ 850857079 1036566646 91330679 687836970 228496396
+ 924491823 197743074 129085518 831627852 862224077
+ 443737449 256238116 699634317 374281008 158998584
+ 184315655 244919344 343961456 180349880 853069138
+ 332063171 241684646 337820354 457944235 602676359
+ 41766323 50796238 46378311 63860524 510684848
+ 70096943 189955105 871054426 672109499 226399936
+ 26223586 1028902863 226811127 712928095 138244848
+ 760554632 893269568 1018726252 397550846 486706887
+ 709025900 691463814 897059424 771925038 48245314
+ 445661732 365996202 738733570 180388576 964722734
+ 537575554 167816200 144706240 723044396 895150218
+ 771787264 48236704 137232522 142794760 8924672
+ 557792 448710830 500318210 747110536 180912136
+ 883717684 96518021 652853286 466291355 693221929
+ 545287968 966891722 388157465 457902881 1043440021
+ 767486751 96647075 1015198876 817049866 59417384
+ 1020938648 805505024 537001984 943048725 356580925
+ 487641606 853040652 664254599 238742324 399224619
+ 780317982 208309733 119593013 994773809 1043440053
+ 191925899 850322866 184392687 549799844 1024443066
+ 576347035 382532166 16627116 182150555 475711155
+ 390430684 312432314 738311052 271793309 380782643
+ 1020390196 815275293 540233864 292597895 239456792
+ 478389643 808653086 557457846 40619436 129541521
+ 246531490 821638469 472330680 978942525 1012590597
+ 527160192 861192064 574201038 56565168 4568205
+ 48833925 95077866 255844538 189274428 503510557
+ 792005641 294530466 1024051984 853178650 1028795142
+ 806055692 538444458 3240220 35861445 17606958
+ 478325819 817042729 567019160 48393984 131186604
+ 243438122 247019859 334236546 995232528 1043571127
+ 592598110 477898738 153058420 620511864 746949233
+ 214503603 1035856176 135138758 748241599 948069951
+ 742923334 312806868 1040260755 351989255 286634848
+ 439053624 592972212 44821798 935742183 83151353
+ 626597915 911288994 458790296 581632030 848845844
+ 746096664 881612722 840751721 266897418 974806028
+ 212183505 575897462 674724232 907724773 335232225
+ 249625608 406679306 978524888 801158541 983070477
+ 82256637 246020036 111731374 885723697 756331345
+ 445791144 601390858 43522354 290858668 872585938
+ 626715393 912326453 458692903 580025402 842012457
+ 751523504 529968469 738202308 884017946 405909655
+ 966096851 159603416 391853503 534263005 567925083
+ 754632580 605300517 525481798 749929250 951300503
+ 831759277 730950479 90347043 911409212 381929330
+ 911329204 459292567 581683098 776501376 328445808
+ 627119239 911278881 459320868 581640838 848849327
+ 746103428 695461275 185665125 882556987 411067682
+ 814597320 726846282 153113138 530614223 894963566
+ 209244070 761266718 1027120870 391299851 447447865
+ 651951006 624563529 109126031 888852356 141706978
+ 447434656 600454415 44812218 658370173 830073561
+ 506161194 348721804 695606583 241751322 179316754
+ 214663084 1035206920 128477917 884028701 405904022
+ 838243744 239156523 647557629 109315051 875875314
+ 959983580 274061079 741334500 697854125 519626796
+ 801407083 1032334744 98786577 235292816 561958288
+ 53129930 574476836 624358698 801719509 484736473
+ 684748772 171719338 49758202 417197079 473387342
+ 18019128 349335546 395141072 779960799 694722130
+ 396886016 129368064 763902976 1016535552 155660960
+ 28993752 511258164 406772413 1014586941 818086413
+ 364179713 79841329 612514637 1052625555 865444452
+ 750404025 897325087 13157286 766393353 924271360
+ 2502080 872960 249090 193845809 157480687
+ 398125178 1010255391 626936230 324199179 1052680546
+ 309072384 955836536 347255868 689887255 68623492
+ 839086367 686760254 937300652 799580486 912291890
+ 440618598 514705466 441341682 997208827 976740979
+ 830559181 599564480 127290985 441278081 564514023
+ 9810200 30162181 48632332 479303825 809811482
+ 905778838 288509907 294354326 904273202 685148238
+ 511892875 1019447093 1029422419 848389736 149124034
+ 75622907 514412117 838304632 795238927 189042055
+ 350233733 1041189683 821493348 664394853 266715996
+ 615904510 191900479 315851641 380350181 46770454
+ 228659215 353544786 620226240 592839323 751958551
+ 725321884 32732368 664854252 971674948 853180199
+ 451890688 578720596 151863123 496369728 697389489
+ 413942949 795292328 501042196 988701630 900559718
+ 1070616254 701244513 114603604 138539404 209542937
+ 141317884 203248015 26321050 176657968 218566763
+ 56241545 111795400 212764462 80561423 527314520
+ 907359617 637959183 81864949 98710049 496099952
+ 905736082 642956579 966596129 876764806 71367633
+ 489218146 365729292 39358303 741407471 61139655
+ 454938621 155001242 992261357 932808702 172552665
+ 18346841 783374627 64477735 1006601684 591751391
+ 150606004 203880684 172514135 464868485 102383621
+ 321220262 718004594 271906953 1030163523 90709283
+ 809962407 769555943 574002618 62816205 714439625
+ 748602263 20585524 688964524 121002590 767876227
+ 330987021 229558592 410748813 549495135 188405791
+ 648946677 241779393 66328032 548119657 587937296
+ 940955596 545978295 826485363 262597177 715241358
+ 670573015 484653316 643097332 659630612 259723969
+ 751858688 866990868 58428619 910116020 575997042
+ 475285448 579838737 760270351 453174388 458387793
+ 55327821 649599534 60224591 915085418 221057308
+ 399365510 933492310 940281856 743438027 485437630
+ 243688565 200693579 219217838 316075994 65931378
+ 686615834 963251468 163270054 968959285 132142669
+ 1023263457 552970117 500163366 427042962 802378488
+ 832833865 71535195 584372086 737434988 766711887
+ 506790170 429138384 727282220 882489729 898910393
+ 584621682 737968413 802627968 625648063 339630316
+ 219386916 147490873 237129083 546249823 428785682
+ 330767928 171853856 428609633 56622102 432376961
+ 628244322 663333845 882296878 246011943 52435811
+ 421751719 757542659 39968673 17185574 737565217
+ 191429190 783489735 456716661 63504906 955322197
+ 763110984 834419200 516848886 915542075 501971769
+ 414675804 348937564 924271406 93098721 147133492
+ 67505386 141822881 794683072 1053397520 550830718
+ 551348437 340660560 768642316 442371800 577016474
+ 631343352 287044629 138843235 86323468 456242308
+ 473017979 285001218 911926929 772989070 729450397
+ 949023263 838435319 1019678231 611325633 828289690
+ 305743476 837362318 178769772 313970371 520645996
+ 159726523 361428685 358365775 356264974 328018023
+ 397257488 318300243 301935372 586187168 773909543
+ 466322204 881599939 723765882 191260927 563378315
+ 1058064083 783115123 1069757195 960277788 758172182
+ 963470421 447896095 593216185 677814077 768075241
+ 787587689 469193308 52065777 923886500 470871907
+ 80621474 317011682 612148093 731052569 223087456
+ 922392553 124947445 1046119470 529320144 181259499
+ 486204312 991757024 316524084 678463521 88223815
+ 988371371 767330896 1063921265 386056468 634106865
+ 286462716 408457278 60840986 122953194 904721355
+ 191202642 528445873 182691258 564712470 520312781
+ 912176517 988425102 767282513 161251968 763355379
+ 417552490 173124137 44095164 108311110 642286006
+ 429650217 153732742 526373863 917481003 593443060
+ 1005438373 830469598 846258151 921145841 247893446
+ 236865470 244522995 994783612 488328252 474134065
+ 855734733 897907309 646248272 878394362 276630577
+ 576157872 940671348 876229917 610018824 120153549
+ 1009435396 370789586 167588427 1018598137 201752751
+ 541553441 457912434 48367656 107251241 617714619
+ 504037401 71526778 530172533 995905573 201731255
+ 153849966 644452259 706523837 920272361 325840943
+ 443161727 355672683 1054813593 959143458 274914223
+ 944550361 304402635 383632512 499021034 668041299
+ 44741227 602422940 1016858917 244367575 210589715
+ 421862084 346157665 1021943298 720071286 130337258
+ 328323809 728657516 73819362 859050658 894984511
+ 64930876 7451091 106769661 270880898 427482070
+ 877379704 883648752 917472544 834487191 277137038
+ 531285002 640369520 245160438 1007649041 954989655
+ 239618264 840577422 695529096 428802325 142429674
+ 1022081891 942907810 82338885 132168141 386848784
+ 809658708 910401117 1014256372 846154802 763535
+ 597450647 882680733 992929754 211471068 305477316
+ 842208914 380680384 665426499 345719318 531390061
+ 347481025 529907392 112597641 379229007 664056174
+ 544800809 912876658 122864374 896969514 1047251048
+ 810079619 407547318 603581105 1069533852 556660984
+ 1023425018 579464998 345294685 871805936 8780998
+ 277086535 1048842039 832636316 59482647 430750143
+ 583793336 620097599 4127400 24019321 813758066
+ 588996007 240129824 67036815 148129377 932802502
+ 891626588 358079728 725137263 475588728 1058253326
+ 394347842 81504314 713970250 819766435 108196066
+ 293123092 953788472 1065536316 988552592 141893100
+ 337385369 714943202 482449126 856948899 154443444
+ 546524738 111835749 150012240 431828272 764542741
+ 902654258 420348090 877743984 19642815 221772628
+ 873361941 39675192 641943595 196611923 144459230
+ 80730111 988533597 197537390 811028052 718715793
+ 902849835 730995844 942194193 855837420 672689714
+ 518201709 214704226 1045800391 387214862 788125471
+ 931239516 242081036 469065990 917112226 216239443
+ 386546492 362912082 454753990 733281166 241413592
+ 345830477 1039296480 1051435268 356813124 297651891
+ 915790310 736880238 121792951 706250643 661474141
+ 387534218 123900171 541462730 826832834 379494347
+ 136979891 992232668 297538880 191325946 662548905
+ 387093969 431407981 482147282 55677113 778634996
+ 547302009 1055174950 723560962 890687792 314679927
+ 578059815 679912538 73949107 321566785 944622151
+ 1018401692 668972491 966277484 396685163 816993537
+ 323649050 327481376 285257598 279859191 314313943
+ 62100284 696886384 260544276 208728138 382434729
+ 4372584 712664232 1035481831 267010695 118024621
+ 988298373 390472271 953955997 264881390 346970449
+ 819096461 725271698 550175392 154839901 941029965
+ 123683570 604379230 33137754 453838581 728904797
+ 913022326 653640415 269373328 229128976 1032940224
+ 802779796 973136779 547446844 539932551 618394282
+ 828490642 187562043 278257956 70446241 1021196314
+ 760803129 419521014 244892256 895811782 61466290
+ 932072062 145771656 1050975336 416263608 828982277
+ 524814068 25968723 304017357 441580516 182564975
+ 953530314 846438237 890592409 998391720 872824248
+ 477895171 1003522818 883552321 497661296 361855261
+ 226112162 11975834 297731799 512761525 802184597
+ 857645642 183748260 212423763 907962622 41834381
+ 414422084 803784104 124072130 648255371 605053353
+ 725221646 46593485 172062699 737089676 66726345
+ 289612060 224134558 226532722 212841370 797956497
+ 444687868 30954365 1061700568 671922661 170106473
+ 195074660 292545458 1073460736 343505609 406609136
+ 776418530 955838422 544881623 318274348 480474979
+ 194848516 611484970 146240996 102294887 633394952
+ 466393566 750931781 365204387 362865420 975136158
+ 575114882 310682988 101622421 1049028485 909404619
+ 254983480 809987628 565819644 835850745 842701629
+ 1051157563 641273756 210518620 936252476 476803775
+ 443249639 1067042632 789652859 311494081 700536564
+ 1034755690 1062709334 538395647 278406461 2195510
+ 515948832 157533888 479306731 328483256 13086615
+ 772913135 903960924 211797001 566498547 1062301446
+ 151108157 814152490 660464090 514361194 439241364
+ 988667435 1016481266 956713221 165677376 587625772
+ 768020035 1072507034 491887572 414042023 1018874071
+ 918683110 551238969 96494970 739462027 946624458
+ 166805851 412218703 850653566 995683276 902052973
+ 1008333692 426477193 770233563 75758078 963022027
+ 680772809 515597381 709887861 880164108 474501568
+ 64896481 544541558 318055464 720162756 1065687861
+ 795412987 810241953 766569581 223401755 581920699
+ 814159787 127732602 554016083 430509424 790368514
+ 83581604 196627167 670753787 161542560 11719054
+ 402855123 772560980 647253904 733946336 255481405
+ 951113728 701554884 204963695 973976820 164225746
+ 342215120 938209158 239924532 40524626 751130487
+ 366017457 32833652 366016557 981951671 245277955
+ 365945469 464477927 673407977 689446257 148614055
+ 954762332 435805618 483965881 465289947 639333135
+ 594417879 1038326404 1051957077 1006870042 707398878
+ 499189708 265771963 191644127 559345688 89353086
+ 479786179 462047687 535284982 997938819 967080572
+ 765964602 215773756 663091634 1454589 433869961
+ 252215984 864276124 699507390 510928753 845699208
+ 82533290 644859793 395449633 553000223 913204810
+ 595500015 906371977 338363974 663088509 962696970
+ 542289106 805695025 747765213 286410971 308756199
+ 206704647 494074895 405522661 741577140 283639814
+ 817829586 184582494 543215044 580026357 784021307
+ 242815519 635236231 556953938 1020950922 1069075129
+ 268479581 1042607710 27313384 689584122 392590915
+ 655501717 843316254 737540922 831742174 946235431
+ 402979677 717425112 773912055 336636869 680155539
+ 6234929 519410495 490835249 39491861 614033442
+ 378372485 552499832 964593313 304421627 767126048
+ 564454552 85361058 321438558 983571625 206350949
+ 914138201 346283155 821604750 407463030 907463814
+ 473581440 460932061 778710871 453282844 301686909
+ 652003803 41148137 367255184 871011462 804057353
+ 651617182 1024622118 908007041 411963508 261876098
+ 619907068 395304792 711328421 466886025 902390512
+ 995718998 402462082 706339535 448390071 100711805
+ 9155798 396226887 1018308428 303414329 635088364
+ 47551008 193974973 653506493 141082997 1027468178
+ 931964604 940104767 695683061 1036945065 162309335
+ 588411723 406670909 1031571561 87584579 894086404
+ 437280259 687396544 139603041 160392127 598592259
+ 794887620 612292070 108500278 562959667 209269047
+ 560395115 964027342 38445764 657483141 723188366
+ 685970691 114707603 956960179 623423718 19734433
+ 6557594 658894041 214856900 677448257 791836064
+ 939112325 797280272 971947206 25322108 115055977
+ 170444422 420419583 537540163 178915412 206011591
+ 619905782 710376115 733471774 764654563 318534975
+ 347781802 635920774 207925923 415411658 135375068
+ 78014817 612822950 444320142 288376755 1030835351
+ 785382723 737441838 650895476 330921405 998646079
+ 276726425 462896353 23104877 850837842 119039177
+ 981158889 554132867 1015992 1056016441 640780674
+ 379965678 450161718 51240546 222644541 625637479
+ 309028524 937188651 272694570 1060600511 716936833
+ 292279118 524118085 312973139 985778191 857711143
+ 659601750 762824160 817502473 385463010 1039459873
+ 237120444 493377621 20364947 1025318427 158963800
+ 665467920 1027112571 807758322 751246186 34909282
+ 926069831 308967919 114184999 941157924 320727130
+ 494973117 6895936 794178348 814173512 83489335
+ 324914676 706468291 266915350 746035754 488194407
+ 110225636 366865525 1032291591 36538248 922885193
+ 390992644 964082179 1053226361 725507042 572507396
+ 533375019 523116607 2978134 61073835 99688472
+ 178551170 555496303 412073944 349465049 482209827
+ 539263034 721292675 235332176 1069494833 816935555
+ 606795380 840444087 820172603 422537218 531363209
+ 242121200 34972855 665919716 996785416 943704749
+ 772305392 648487711 693889412 778799446 268841495
+ 1020369919 813167526 570431521 364847150 742454102
+ 943067649 806969570 474118982 536340671 261097612
+ 431775509 529815646 647365945 119390764 249142645
+ 468127742 585099215 594557548 886786500 529612753
+ 453286750 603711595 511884956 499062012 961017822
+ 962025658 844375175 449288734 45189231 112986847
+ 212170761 569974359 952412630 1001260208 511854146
+ 525183616 973604691 690003179 580331138 409331293
+ 891933913 30177400 155321969 210836313 819201442
+ 689069600 742904437 807426084 147991312 672025501
+ 717999592 392762808 837257360 209743425 334260821
+ 884727436 661790640 903256427 365621670 1006167125
+ 361526500 507190613 176328247 247095540 24909206
+ 445357416 277103354 772962398 528991655 201085678
+ 238470099 241734002 280419355 130737156 893817390
+ 923807778 562726158 785416848 668921698 436588166
+ 452851387 316563851 806204734 746351368 393235609
+ 785055552 241084264 425072951 753966336 623429937
+ 593939233 2473378 182102442 156190465 16326253
+ 252215984 864276124 699507390 510928753 845699208
+ 82533290 644859793 395449633 553000223 913204810
+ 595500015 906371977 338363974 663088509 962696970
+ 542289106 805695025 747765213 286410971 308756199
+ 206704647 494074895 405522661 741577140 283639814
+ 817829586 184582494 543215044 580026357 784021307
+ 242815519 635236231 556953938 1020950922 1069075129
+ 268479581 1042607710 27313384 689584122 392590915
+ 655501717 843316254 737540922 831742174 946235431
+ 402979677 717425112 773912055 336636869 680155539
+ 6234929 519410495 490835249 39491861 614033442
+ 378372485 552499832 964593313 304421627 767126048
+ 564454552 85361058 321438558 983571625 206350949
+ 914138201 346283155 821604750 407463030 907463814
+ 473581440 460932061 778710871 453282844 301686909
+ 652003803 41148137 367255184 871011462 804057353
+ 651617182 1024622118 908007041 411963508 261876098
+ 619907068 395304792 711328421 466886025 902390512
+ 995718998 402462082 706339535 448390071 100711805
+ 9155798 396226887 1018308428 303414329 635088364
+ 47551008 193974973 653506493 141082997 1027468178
+ 931964604 940104767 695683061 1036945065 162309335
+ 588411723 406670909 1031571561 87584579 894086404
+ 437280259 687396544 139603041 160392127 598592259
+ 794887620 612292070 108500278 562959667 209269047
+ 560395115 964027342 38445764 657483141 723188366
+ 685970691 114707603 956960179 623423718 19734433
+ 6557594 658894041 214856900 677448257 791836064
+ 939112325 797280272 971947206 25322108 115055977
+ 170444422 420419583 537540163 178915412 206011591
+ 619905782 710376115 733471774 764654563 318534975
+ 347781802 635920774 207925923 415411658 135375068
+ 78014817 612822950 444320142 288376755 1030835351
+ 785382723 737441838 650895476 330921405 998646079
+ 276726425 462896353 23104877 850837842 119039177
+ 981158889 554132867 1015992 1056016441 640780674
+ 379965678 450161718 51240546 222644541 625637479
+ 309028524 937188651 272694570 1060600511 716936833
+ 292279118 524118085 312973139 985778191 857711143
+ 659601750 762824160 817502473 385463010 1039459873
+ 237120444 493377621 20364947 1025318427 158963800
+ 665467920 1027112571 807758322 751246186 34909282
+ 926069831 308967919 114184999 941157924 320727130
+ 494973117 6895936 794178348 814173512 83489335
+ 324914676 706468291 266915350 746035754 488194407
+ 110225636 366865525 1032291591 36538248 922885193
+ 390992644 964082179 1053226361 725507042 572507396
+ 533375019 523116607 2978134 61073835 99688472
+ 178551170 555496303 412073944 349465049 482209827
+ 539263034 721292675 235332176 1069494833 816935555
+ 606795380 840444087 820172603 422537218 531363209
+ 242121200 34972855 665919716 996785416 943704749
+ 772305392 648487711 693889412 778799446 268841495
+ 1020369919 813167526 570431521 364847150 742454102
+ 943067649 806969570 474118982 536340671 261097612
+ 431775509 529815646 647365945 119390764 249142645
+ 468127742 585099215 594557548 886786500 529612753
+ 453286750 603711595 511884956 499062012 961017822
+ 962025658 844375175 449288734 45189231 112986847
+ 212170761 569974359 952412630 1001260208 511854146
+ 525183616 973604691 690003179 580331138 409331293
+ 891933913 30177400 155321969 210836313 819201442
+ 689069600 742904437 807426084 147991312 672025501
+ 717999592 392762808 837257360 209743425 334260821
+ 884727436 661790640 903256427 365621670 1006167125
+ 361526500 507190613 176328247 247095540 24909206
+ 445357416 277103354 772962398 528991655 201085678
+ 238470099 241734002 280419355 130737156 893817390
+ 923807778 562726158 785416848 668921698 436588166
+ 452851387 316563851 806204734 746351368 393235609
+ 785055552 241084264 425072951 753966336 623429937
+ 593939233 2473378 182102442 156190465 16326253
+ 262529244 21592218 965345980 479690933 955324142
+ 984701520 914913434 767362328 560164314 759605511
+ 427765748 980270105 479798055 749749071 716749844
+ 836496875 381312216 377171650 448020792 46387677
+ 487063793 135657163 416039684 163639026 720205113
+ 346860479 729554461 817281695 717205541 511532180
+ 376175633 616684039 1039020136 449309046 680970117
+ 845058541 469090037 515523439 955419940 440938347
+ 672961251 10667570 518095991 909334329 829241778
+ 606901762 1069459369 548650699 281697813 956103993
+ 30363935 339364301 406101282 308865403 308458767
+ 418942750 157650893 113828949 580526693 311954692
+ 364960261 795741162 353093361 791175368 638975973
+ 875449682 460268540 208819369 769026742 62063738
+ 554723483 421653015 987995246 845866854 965220704
+ 469056260 490245958 36114055 305670606 338644522
+ 878545470 475565206 48226938 414340622 706377461
+ 70151508 255087336 718559375 1049633008 203894503
+ 22194629 514025952 432145520 1046434302 1027431730
+ 936773418 269288516 814523735 654625221 526729135
+ 599574790 445271092 691705236 66914308 778318502
+ 286800495 500910531 994126538 743211695 530404568
+ 485366627 583986333 238361012 227380574 197111153
+ 232160518 631034631 500786347 760595355 462904804
+ 609343217 595756379 496035633 662904978 853444175
+ 66462873 659644850 49887216 302388813 892555253
+ 504727252 815116672 261582344 701813672 831147871
+ 587556545 89067369 880251307 152316401 13238019
+ 334948561 408592509 1007217184 792449768 191126463
+ 811383543 942770774 303020505 566294452 956112349
+ 113325173 794493220 776004093 240664835 1059037919
+ 237208028 425015136 542550489 206169560 901095678
+ 930529500 268533774 435568201 56211401 584946987
+ 850669777 1037555385 406382893 559349944 243337945
+ 526282600 284666920 604604231 632557768 638093677
+ 480471062 81680586 875931933 156833367 713605973
+ 435549848 172576393 1059566601 565184765 65081289
+ 498722400 696833717 815163641 627911824 886543732
+ 231443739 959131646 170265570 928052224 50537919
+ 94664642 130452716 202049600 1044742660 1033074835
+ 259617812 538853407 524942603 117216169 867000385
+ 70906149 495615222 522584988 38485889 96039217
+ 762427013 186776316 589021278 256704543 989846818
+ 423913266 691312108 779409519 853887521 20948627
+ 200559587 984136703 873562964 808364496 835606385
+ 597329500 564444196 638065929 707059395 992277275
+ 895568835 341739178 510261100 427147115 391394275
+ 166587437 1014529382 265836956 339076334 501688585
+ 551530424 489904465 178761665 629476775 989569909
+ 938163115 836415723 994848082 1007507834 253469459
+ 759639615 456445967 338295772 24166946 558561593
+ 640820558 171868573 553347872 997823978 1010979259
+ 775041786 158407289 371496761 487641693 224299329
+ 624976168 512981491 513480971 115986803 81210722
+ 226253065 147376323 1009732681 730985558 441124721
+ 1068684617 902160231 362029366 888143725 862286015
+ 338017446 22627501 340663697 130200275 630657526
+ 42917116 324565575 640822375 540054672 214577500
+ 940181003 475917176 880471585 962583926 937093578
+ 1035690495 496052161 546267240 104388458 758338923
+ 690186498 28135325 207389149 406249420 500443339
+ 243999007 327631081 692702120 76531724 120264432
+ 305589566 23783510 936430524 129427675 891049503
+ 133329099 759064359 805090750 393841595 895712599
+ 773285067 682629906 68835152 219221812 116077787
+ 1009892323 978580199 641435137 169615059 526019736
+ 1022625483 698385936 898112413 249663226 205835709
+ 705738196 895436366 895609753 374375611 251816591
+ 1042250203 386676307 655220556 773069713 702212988
+ 544515493 107655149 827848985 664615786 135722191
+ 833601558 794394025 285800218 973859759 91912329
+ 1026345940 434460332 131921302 773348406 219581483
+ 980089391 391708352 926256276 896371039 310909560
+ 979432015 493272124 917208312 1036535788 877479787
+ 482841756 412688220 359703637 34285171 186257268
+ 291662887 419986916 406738964 854285218 978887784
+ 721071389 775547362 26134026 601673573 282823644
+ 255902629 1047867989 427420853 402574797 284342098
+ 774742246 897048753 361497418 669748823 974414367
+ 277589547 46696182 213200440 927844991 378135777
+ 336575427 403380791 215641103 181837838 578757601
+ 784192548 673773134 45159208 868796196 310076906
+ 744662427 993437519 869097864 562621057 1072525979
+ 636704565 912102235 608111056 878120738 891338751
+ 805808902 869612750 1014505505 659117686 974088279
+ 754787719 897980337 182399558 679511945 128098469
+ 807094275 396415877 578746434 486849617 898118574
+ 336686068 827665739 327435489 213490869 744345119
+ 351126316 316839271 473669547 495956288 483901865
+ 885031589 246728908 823364297 817872238 114109283
+ 337622035 334860404 542862728 747378116 1060520737
+ 356188920 1034281795 517074701 53604863 458834877
+ 299245345 900582690 510035110 1008046309 172239476
+ 895700147 996230079 315526785 355871837 865175953
+ 322169978 255164727 925435747 17166052 468880982
+ 808877545 845573888 937119811 465607577 348271961
+ 375131083 879975717 1073331089 281965555 938575881
+ 532571815 831387143 12359490 791982867 1062061038
+ 793055176 247746346 835895756 338309926 962055359
+ 364721213 439903542 384907542 1043466891 438408252
+ 567864191 178239774 504452214 116029571 773454840
+ 438046212 237086792 796386308 109030996 704730860
+ 616041338 203252400 851269131 967700176 756020841
+ 486425326 112893736 721884776 800731879 889828670
+ 292330843 703193591 529792957 441134166 212347904
+ 1013337600 769472966 887713227 1069789201 987271037
+ 149250704 129749605 779974660 986567573 355214121
+ 966028897 555556936 644268906 400985677 719810516
+ 703320954 972019833 6265291 873985659 335072144
+ 104675264 226534877 344861916 5418409 186327158
+ 61269771 570217842 738888304 471198224 117202514
+ 56280814 779834957 85159424 480845534 663669079
+ 292035683 509224027 563002106 763076643 150453627
+ 375622925 542634367 235294643 205062015 304392974
+ 918165794 184599786 116478343 1013262588 697725768
+ 870755451 335485401 165552150 541821384 434735878
+ 961331127 347816001 803069661 580162405 45882885
+ 57532846 20696969 623969114 780597408 348721781
+ 408161239 989907213 833135759 1047993019 536103030
+ 352099418 630284052 305653877 33580900 927848356
+ 106857490 384485313 909609131 495181596 102593572
+ 904975345 930782370 873097945 204690343 438357784
+ 273766669 961077042 459054082 35770365 756741021
+ 12192575 260936561 419490675 579106979 1031536535
+ 385239376 962691302 422224519 227324732 702684332
+ 449840748 725898452 796740095 225373181 878210059
+ 230206329 826618624 136042344 770070821 869773006
+ 614246019 129745703 347161517 875704024 191316168
+ 22767510 435932051 705196918 329836198 886331357
+ 1062930908 294608484 664584307 916959768 835689293
+ 98152703 859472534 1038105238 524920996 1049581464
+ 95707649 66279494 294063192 1051227778 201427905
+ 183090358 391138722 844074830 562816717 713088635
+ 706700435 626181952 381724870 296077687 617061176
+ 304224495 835818840 708619158 765735078 779514438
+ 734774823 668493217 787037007 969808915 259223203
+ 458934064 161801483 113652848 908211647 509348131
+ 628872557 888122575 607443542 959139811 473093206
+ 982623543 48271609 717670903 24960561 410662501
+ 165317207 664893006 627385859 452270420 684012315
+ 972507289 147024872 221868196 934843804 1049125314
+ 364586434 569161428 206703847 696740553 50639661
+ 176142456 654998800 225548049 930177702 743502067
+ 195958508 349290666 495481499 341680015 890580552
+ 544062158 297575095 362134692 254619238 259026483
+ 38275277 802572058 1043299060 233594690 183195525
+ 470520010 1072240657 800589057 98373243 513005037
+ 563380679 956665063 200194083 604216537 299918901
+ 349022688 199816425 162311926 220733230 819786706
+ 475520350 96821198 943062991 779004267 818890210
+ 138490440 1053940323 743136451 948863056 902162646
+ 884588603 371561322 265174506 950814478 336663982
+ 679748072 628076719 767998468 999841873 946753921
+ 644000945 653723894 825344835 279970870 111120260
+ 227900549 203925762 566041946 308056596 825159897
+ 745289515 1060897754 792634623 516596543 260556241
+ 247198052 1012852572 1003742273 637188376 131990134
+ 309017331 72223237 218821399 890004285 734862851
+ 957948328 841340935 18184806 1001698940 289856529
+ 431122012 436057750 915036855 1051221848 483124764
+ 345458668 780685836 296167875 926451431 185509360
+ 513013539 793408842 213516771 884073681 527518069
+ 291612934 203380115 325312921 713854166 297910902
+ 31438370 692451345 128410767 607933363 554340481
+ 791189236 180786627 134519167 547160630 419918953
+ 210125148 993469773 15704498 656605441 70498809
+ 980617649 799461895 1063908725 36924921 614025305
+ 77636487 19687441 186460255 153736208 729999838
+ 873850524 1059872258 599707904 4437397 568914753
+ 229029524 845922096 839944403 120116963 465309127
+ 528964659 116915461 85803649 870909545 223574265
+ 191216858 395413371 894414056 219452546 920990178
+ 682108783 892991971 321638621 778720083 113398251
+ 993155142 774757366 426281406 688790283 738341431
+ 300323659 782439120 637385110 1007954474 734907714
+ 1069894186 552918200 117937847 73433090 324617312
+ 798094521 443622858 29562514 708047059 619065729
+ 443419234 397390533 522060786 82415771 1016634721
+ 352255992 115744640 594938813 242636667 818530328
+ 88895909 767073865 555770904 979070416 830162594
+ 337622035 334860404 542862728 747378116 1060520737
+ 356188920 1034281795 517074701 53604863 458834877
+ 299245345 900582690 510035110 1008046309 172239476
+ 895700147 996230079 315526785 355871837 865175953
+ 322169978 255164727 925435747 17166052 468880982
+ 808877545 845573888 937119811 465607577 348271961
+ 375131083 879975717 1073331089 281965555 938575881
+ 532571815 831387143 12359490 791982867 1062061038
+ 793055176 247746346 835895756 338309926 962055359
+ 364721213 439903542 384907542 1043466891 438408252
+ 567864191 178239774 504452214 116029571 773454840
+ 438046212 237086792 796386308 109030996 704730860
+ 616041338 203252400 851269131 967700176 756020841
+ 486425326 112893736 721884776 800731879 889828670
+ 292330843 703193591 529792957 441134166 212347904
+ 1013337600 769472966 887713227 1069789201 987271037
+ 149250704 129749605 779974660 986567573 355214121
+ 966028897 555556936 644268906 400985677 719810516
+ 703320954 972019833 6265291 873985659 335072144
+ 104675264 226534877 344861916 5418409 186327158
+ 61269771 570217842 738888304 471198224 117202514
+ 56280814 779834957 85159424 480845534 663669079
+ 292035683 509224027 563002106 763076643 150453627
+ 375622925 542634367 235294643 205062015 304392974
+ 918165794 184599786 116478343 1013262588 697725768
+ 870755451 335485401 165552150 541821384 434735878
+ 961331127 347816001 803069661 580162405 45882885
+ 57532846 20696969 623969114 780597408 348721781
+ 408161239 989907213 833135759 1047993019 536103030
+ 352099418 630284052 305653877 33580900 927848356
+ 106857490 384485313 909609131 495181596 102593572
+ 904975345 930782370 873097945 204690343 438357784
+ 273766669 961077042 459054082 35770365 756741021
+ 12192575 260936561 419490675 579106979 1031536535
+ 385239376 962691302 422224519 227324732 702684332
+ 449840748 725898452 796740095 225373181 878210059
+ 230206329 826618624 136042344 770070821 869773006
+ 614246019 129745703 347161517 875704024 191316168
+ 22767510 435932051 705196918 329836198 886331357
+ 1062930908 294608484 664584307 916959768 835689293
+ 98152703 859472534 1038105238 524920996 1049581464
+ 95707649 66279494 294063192 1051227778 201427905
+ 183090358 391138722 844074830 562816717 713088635
+ 706700435 626181952 381724870 296077687 617061176
+ 304224495 835818840 708619158 765735078 779514438
+ 734774823 668493217 787037007 969808915 259223203
+ 458934064 161801483 113652848 908211647 509348131
+ 628872557 888122575 607443542 959139811 473093206
+ 982623543 48271609 717670903 24960561 410662501
+ 165317207 664893006 627385859 452270420 684012315
+ 972507289 147024872 221868196 934843804 1049125314
+ 364586434 569161428 206703847 696740553 50639661
+ 176142456 654998800 225548049 930177702 743502067
+ 195958508 349290666 495481499 341680015 890580552
+ 544062158 297575095 362134692 254619238 259026483
+ 38275277 802572058 1043299060 233594690 183195525
+ 470520010 1072240657 800589057 98373243 513005037
+ 563380679 956665063 200194083 604216537 299918901
+ 349022688 199816425 162311926 220733230 819786706
+ 475520350 96821198 943062991 779004267 818890210
+ 138490440 1053940323 743136451 948863056 902162646
+ 884588603 371561322 265174506 950814478 336663982
+ 679748072 628076719 767998468 999841873 946753921
+ 644000945 653723894 825344835 279970870 111120260
+ 227900549 203925762 566041946 308056596 825159897
+ 745289515 1060897754 792634623 516596543 260556241
+ 247198052 1012852572 1003742273 637188376 131990134
+ 309017331 72223237 218821399 890004285 734862851
+ 957948328 841340935 18184806 1001698940 289856529
+ 431122012 436057750 915036855 1051221848 483124764
+ 345458668 780685836 296167875 926451431 185509360
+ 513013539 793408842 213516771 884073681 527518069
+ 291612934 203380115 325312921 713854166 297910902
+ 31438370 692451345 128410767 607933363 554340481
+ 791189236 180786627 134519167 547160630 419918953
+ 210125148 993469773 15704498 656605441 70498809
+ 980617649 799461895 1063908725 36924921 614025305
+ 77636487 19687441 186460255 153736208 729999838
+ 873850524 1059872258 599707904 4437397 568914753
+ 229029524 845922096 839944403 120116963 465309127
+ 528964659 116915461 85803649 870909545 223574265
+ 191216858 395413371 894414056 219452546 920990178
+ 682108783 892991971 321638621 778720083 113398251
+ 993155142 774757366 426281406 688790283 738341431
+ 300323659 782439120 637385110 1007954474 734907714
+ 1069894186 552918200 117937847 73433090 324617312
+ 798094521 443622858 29562514 708047059 619065729
+ 443419234 397390533 522060786 82415771 1016634721
+ 352255992 115744640 594938813 242636667 818530328
+ 88895909 767073865 555770904 979070416 830162594
+ 546000439 225646567 104890524 141902477 344852033
+ 918029976 384083593 1064347351 840717628 529098881
+ 109693671 527503764 760900875 383006097 771709371
+ 675186267 859763068 950899919 50316009 301231701
+ 586749239 890641944 251654998 462751431 358378550
+ 800941929 477039579 390959537 497929935 757970979
+ 1023164462 1033299983 486875983 150800412 934787555
+ 1048152288 993144081 810326573 910153180 678677599
+ 340010972 1053084002 391953854 971794166 1047353331
+ 310557774 676536427 990517538 924447410 718678453
+ 512378693 773519994 976835266 986859281 253821648
+ 763459316 428358356 407915481 81106782 156780838
+ 108236037 428871789 858849000 544082228 1045122898
+ 101268477 712952086 843630627 671728292 559328609
+ 849070344 323194896 742146732 1059192145 777168937
+ 1025779434 649397366 186175208 343638049 259657063
+ 756988338 850625811 49452294 885379096 65246434
+ 544071136 134982319 832135055 46735726 708830626
+ 222422366 370756905 674277230 649874106 43158899
+ 1029296409 512116953 601763585 17307843 477006187
+ 853810994 295130061 691341330 455521986 779646516
+ 570595029 140949043 423633081 986367565 244576037
+ 298089869 870867222 590775949 713329718 388252192
+ 625428656 743942416 518587643 878088225 89064079
+ 544266145 806235221 801439055 1068070215 310332638
+ 120007541 224843440 709151555 738192879 905737498
+ 56216906 438533630 711270668 154349485 246269621
+ 696313260 705996232 527176039 200011259 732831122
+ 486591001 191040623 1035411263 498932567 601647609
+ 1058212006 844390568 361564392 773762316 443436606
+ 1055892535 503358302 368445969 432903165 519726200
+ 191444340 450634348 88731808 456167223 205199827
+ 795346665 607649671 706827827 286436331 1062207281
+ 689401706 20147382 846344837 420157434 829672497
+ 449626082 619317997 206216719 537195248 544368833
+ 363078530 149528016 582822598 827574039 591640749
+ 993206183 390577675 750757281 359999800 859315064
+ 113737335 1046637496 165885564 221965371 510565988
+ 316002170 644317834 637480278 360030218 604849087
+ 944391039 817453193 263391297 331219409 882175521
+ 575950480 873520043 739576603 877820523 869886831
+ 770010030 415449296 316018327 174387438 1053873633
+ 708985268 23459152 100691764 331704755 1071206509
+ 182135358 866415096 1049547362 489901388 1069765715
+ 825731027 649917848 818291608 799749992 906376979
+ 755483964 283664197 410128774 1016158588 674972462
+ 974168649 295784175 221514332 107231977 170490813
+ 503653028 115561212 335027102 673500992 254626131
+ 328285222 679187989 307773176 919889450 558030354
+ 932861475 425249971 652188164 341582421 881678413
+ 233965470 588370373 797999219 190211481 839319828
+ 164590685 372264725 896818792 905112049 199101611
+ 926374720 758641841 176409136 416615973 952667138
+ 465242501 498419746 868890936 661149847 204529997
+ 230554713 982389795 791879502 906734661 236668601
+ 972279233 796707078 562408581 771768122 998714124
+ 18514448 893769410 1007724637 610055631 861838563
+ 602944292 468629810 432942370 113434703 665026271
+ 975731726 279680752 811322505 186022738 614430168
+ 247798166 915092564 212695794 599718041 653208104
+ 235279208 433751453 26004884 37286336 591151333
+ 526164672 290547213 194792078 197323738 273149404
+ 1052878993 792428632 1046592271 536691632 214890976
+ 724656484 189671774 376268081 655172513 473174210
+ 817760222 983585935 538464450 479609734 365769455
+ 817978580 833390698 623673319 24833480 761346967
+ 528972191 865087794 1063252693 187873787 981009314
+ 367021357 688416494 521657890 884015618 824713596
+ 812296699 725212262 20082271 215046813 566342940
+ 651773998 894171210 745988307 873758991 994497653
+ 240742544 376380665 728358930 201645082 742299872
+ 123024762 574183238 853575107 636335918 862387940
+ 280438691 659352432 644052570 42052790 740006637
+ 705234501 616202277 127043122 1015977671 507666969
+ 582453700 625105778 400092953 875775738 473878754
+ 461267103 755715102 504582178 864276533 537169458
+ 89020296 235460072 913352228 551202483 154074459
+ 13886537 115140344 5802543 427830747 850887718
+ 215857217 523356681 630823439 628427707 478658605
+ 131482477 891316716 182156093 324255919 571012008
+ 607800789 170164610 845900598 464032675 502342538
+ 791535830 292385988 607324204 556351514 27652516
+ 976072613 447419777 1007599228 1043850323 173570699
+ 77557187 997453262 531067372 13218981 65925275
+ 364419983 963221077 808263585 733813403 105845245
+ 293954693 34741825 607618170 835891674 289545577
+ 303411869 986112196 268635239 557658890 547284852
+ 944112766 298859063 448410682 602205963 95124167
+ 966037195 848419736 983645750 240864464 82420631
+ 728038225 263559914 394645890 51136699 81240999
+ 956765977 564711070 252794746 578770520 1059958490
+ 908961920 1053709599 586212683 667094535 957909739
+ 488212514 747058556 508388015 399334141 306142172
+ 24608062 7970378 576088849 954758074 584645553
+ 732712127 215975734 587942398 409333468 1043075996
+ 336193502 646583067 919733178 793556342 597511069
+ 794381300 857518662 600022804 55650088 895162832
+ 250870127 538576261 879658008 62771719 142722469
+ 25366092 830703245 998428065 352106179 34601187
+ 833522120 632854780 490534525 708203573 162224407
+ 294437359 12679420 520810162 744937355 78205904
+ 13785199 691712884 976226262 297710458 570850089
+ 314808792 656745075 9063273 373478489 536309129
+ 372183014 710426179 422779020 1036005512 767837189
+ 292218158 830863749 195150720 759615287 68079955
+ 574912247 476322284 744020946 577220458 1070895643
+ 631305738 430791810 649304125 889700295 540043811
+ 659786818 121020416 496821279 505344668 45414849
+ 1023164462 1033299983 486875983 150800412 934787555
+ 1048152288 993144081 810326573 910153180 678677599
+ 340010972 1053084002 391953854 971794166 1047353331
+ 310557774 676536427 990517538 924447410 718678453
+ 512378693 773519994 976835266 986859281 253821648
+ 763459316 428358356 407915481 81106782 156780838
+ 108236037 428871789 858849000 544082228 1045122898
+ 101268477 712952086 843630627 671728292 559328609
+ 849070344 323194896 742146732 1059192145 777168937
+ 1025779434 649397366 186175208 343638049 259657063
+ 756988338 850625811 49452294 885379096 65246434
+ 544071136 134982319 832135055 46735726 708830626
+ 222422366 370756905 674277230 649874106 43158899
+ 1029296409 512116953 601763585 17307843 477006187
+ 853810994 295130061 691341330 455521986 779646516
+ 570595029 140949043 423633081 986367565 244576037
+ 298089869 870867222 590775949 713329718 388252192
+ 625428656 743942416 518587643 878088225 89064079
+ 544266145 806235221 801439055 1068070215 310332638
+ 120007541 224843440 709151555 738192879 905737498
+ 56216906 438533630 711270668 154349485 246269621
+ 696313260 705996232 527176039 200011259 732831122
+ 486591001 191040623 1035411263 498932567 601647609
+ 1058212006 844390568 361564392 773762316 443436606
+ 1055892535 503358302 368445969 432903165 519726200
+ 191444340 450634348 88731808 456167223 205199827
+ 795346665 607649671 706827827 286436331 1062207281
+ 689401706 20147382 846344837 420157434 829672497
+ 449626082 619317997 206216719 537195248 544368833
+ 363078530 149528016 582822598 827574039 591640749
+ 993206183 390577675 750757281 359999800 859315064
+ 113737335 1046637496 165885564 221965371 510565988
+ 316002170 644317834 637480278 360030218 604849087
+ 944391039 817453193 263391297 331219409 882175521
+ 575950480 873520043 739576603 877820523 869886831
+ 770010030 415449296 316018327 174387438 1053873633
+ 708985268 23459152 100691764 331704755 1071206509
+ 182135358 866415096 1049547362 489901388 1069765715
+ 825731027 649917848 818291608 799749992 906376979
+ 755483964 283664197 410128774 1016158588 674972462
+ 974168649 295784175 221514332 107231977 170490813
+ 503653028 115561212 335027102 673500992 254626131
+ 328285222 679187989 307773176 919889450 558030354
+ 932861475 425249971 652188164 341582421 881678413
+ 233965470 588370373 797999219 190211481 839319828
+ 164590685 372264725 896818792 905112049 199101611
+ 926374720 758641841 176409136 416615973 952667138
+ 465242501 498419746 868890936 661149847 204529997
+ 230554713 982389795 791879502 906734661 236668601
+ 972279233 796707078 562408581 771768122 998714124
+ 18514448 893769410 1007724637 610055631 861838563
+ 602944292 468629810 432942370 113434703 665026271
+ 975731726 279680752 811322505 186022738 614430168
+ 247798166 915092564 212695794 599718041 653208104
+ 235279208 433751453 26004884 37286336 591151333
+ 526164672 290547213 194792078 197323738 273149404
+ 1052878993 792428632 1046592271 536691632 214890976
+ 724656484 189671774 376268081 655172513 473174210
+ 817760222 983585935 538464450 479609734 365769455
+ 817978580 833390698 623673319 24833480 761346967
+ 528972191 865087794 1063252693 187873787 981009314
+ 367021357 688416494 521657890 884015618 824713596
+ 812296699 725212262 20082271 215046813 566342940
+ 651773998 894171210 745988307 873758991 994497653
+ 240742544 376380665 728358930 201645082 742299872
+ 123024762 574183238 853575107 636335918 862387940
+ 280438691 659352432 644052570 42052790 740006637
+ 705234501 616202277 127043122 1015977671 507666969
+ 582453700 625105778 400092953 875775738 473878754
+ 461267103 755715102 504582178 864276533 537169458
+ 89020296 235460072 913352228 551202483 154074459
+ 13886537 115140344 5802543 427830747 850887718
+ 215857217 523356681 630823439 628427707 478658605
+ 131482477 891316716 182156093 324255919 571012008
+ 607800789 170164610 845900598 464032675 502342538
+ 791535830 292385988 607324204 556351514 27652516
+ 976072613 447419777 1007599228 1043850323 173570699
+ 77557187 997453262 531067372 13218981 65925275
+ 364419983 963221077 808263585 733813403 105845245
+ 293954693 34741825 607618170 835891674 289545577
+ 303411869 986112196 268635239 557658890 547284852
+ 944112766 298859063 448410682 602205963 95124167
+ 966037195 848419736 983645750 240864464 82420631
+ 728038225 263559914 394645890 51136699 81240999
+ 956765977 564711070 252794746 578770520 1059958490
+ 908961920 1053709599 586212683 667094535 957909739
+ 488212514 747058556 508388015 399334141 306142172
+ 24608062 7970378 576088849 954758074 584645553
+ 732712127 215975734 587942398 409333468 1043075996
+ 336193502 646583067 919733178 793556342 597511069
+ 794381300 857518662 600022804 55650088 895162832
+ 250870127 538576261 879658008 62771719 142722469
+ 25366092 830703245 998428065 352106179 34601187
+ 833522120 632854780 490534525 708203573 162224407
+ 294437359 12679420 520810162 744937355 78205904
+ 13785199 691712884 976226262 297710458 570850089
+ 314808792 656745075 9063273 373478489 536309129
+ 372183014 710426179 422779020 1036005512 767837189
+ 292218158 830863749 195150720 759615287 68079955
+ 574912247 476322284 744020946 577220458 1070895643
+ 631305738 430791810 649304125 889700295 540043811
+ 659786818 121020416 496821279 505344668 45414849
+ 960984074 345969298 169752508 278386551 689532507
+ 229747797 365513932 1013503743 334637077 335167587
+ 621159885 703677806 1017388798 724904457 104270474
+ 846984205 414127560 534283536 914508366 859468449
+ 1011384628 251537694 801890521 737881388 831823562
+ 611605378 34720707 43252578 790885584 136773286
+ 1023164462 1033299983 486875983 150800412 934787555
+ 1048152288 993144081 810326573 910153180 678677599
+ 340010972 1053084002 391953854 971794166 1047353331
+ 310557774 676536427 990517538 924447410 718678453
+ 512378693 773519994 976835266 986859281 253821648
+ 763459316 428358356 407915481 81106782 156780838
+ 108236037 428871789 858849000 544082228 1045122898
+ 101268477 712952086 843630627 671728292 559328609
+ 849070344 323194896 742146732 1059192145 777168937
+ 1025779434 649397366 186175208 343638049 259657063
+ 756988338 850625811 49452294 885379096 65246434
+ 544071136 134982319 832135055 46735726 708830626
+ 222422366 370756905 674277230 649874106 43158899
+ 1029296409 512116953 601763585 17307843 477006187
+ 853810994 295130061 691341330 455521986 779646516
+ 570595029 140949043 423633081 986367565 244576037
+ 298089869 870867222 590775949 713329718 388252192
+ 625428656 743942416 518587643 878088225 89064079
+ 544266145 806235221 801439055 1068070215 310332638
+ 120007541 224843440 709151555 738192879 905737498
+ 56216906 438533630 711270668 154349485 246269621
+ 696313260 705996232 527176039 200011259 732831122
+ 486591001 191040623 1035411263 498932567 601647609
+ 1058212006 844390568 361564392 773762316 443436606
+ 1055892535 503358302 368445969 432903165 519726200
+ 191444340 450634348 88731808 456167223 205199827
+ 795346665 607649671 706827827 286436331 1062207281
+ 689401706 20147382 846344837 420157434 829672497
+ 449626082 619317997 206216719 537195248 544368833
+ 363078530 149528016 582822598 827574039 591640749
+ 993206183 390577675 750757281 359999800 859315064
+ 113737335 1046637496 165885564 221965371 510565988
+ 316002170 644317834 637480278 360030218 604849087
+ 944391039 817453193 263391297 331219409 882175521
+ 575950480 873520043 739576603 877820523 869886831
+ 770010030 415449296 316018327 174387438 1053873633
+ 708985268 23459152 100691764 331704755 1071206509
+ 182135358 866415096 1049547362 489901388 1069765715
+ 825731027 649917848 818291608 799749992 906376979
+ 755483964 283664197 410128774 1016158588 674972462
+ 974168649 295784175 221514332 107231977 170490813
+ 503653028 115561212 335027102 673500992 254626131
+ 328285222 679187989 307773176 919889450 558030354
+ 932861475 425249971 652188164 341582421 881678413
+ 233965470 588370373 797999219 190211481 839319828
+ 164590685 372264725 896818792 905112049 199101611
+ 926374720 758641841 176409136 416615973 952667138
+ 465242501 498419746 868890936 661149847 204529997
+ 230554713 982389795 791879502 906734661 236668601
+ 972279233 796707078 562408581 771768122 998714124
+ 18514448 893769410 1007724637 610055631 861838563
+ 602944292 468629810 432942370 113434703 665026271
+ 975731726 279680752 811322505 186022738 614430168
+ 247798166 915092564 212695794 599718041 653208104
+ 235279208 433751453 26004884 37286336 591151333
+ 526164672 290547213 194792078 197323738 273149404
+ 1052878993 792428632 1046592271 536691632 214890976
+ 724656484 189671774 376268081 655172513 473174210
+ 817760222 983585935 538464450 479609734 365769455
+ 817978580 833390698 623673319 24833480 761346967
+ 528972191 865087794 1063252693 187873787 981009314
+ 367021357 688416494 521657890 884015618 824713596
+ 812296699 725212262 20082271 215046813 566342940
+ 651773998 894171210 745988307 873758991 994497653
+ 240742544 376380665 728358930 201645082 742299872
+ 123024762 574183238 853575107 636335918 862387940
+ 280438691 659352432 644052570 42052790 740006637
+ 705234501 616202277 127043122 1015977671 507666969
+ 582453700 625105778 400092953 875775738 473878754
+ 461267103 755715102 504582178 864276533 537169458
+ 89020296 235460072 913352228 551202483 154074459
+ 13886537 115140344 5802543 427830747 850887718
+ 215857217 523356681 630823439 628427707 478658605
+ 131482477 891316716 182156093 324255919 571012008
+ 607800789 170164610 845900598 464032675 502342538
+ 791535830 292385988 607324204 556351514 27652516
+ 976072613 447419777 1007599228 1043850323 173570699
+ 77557187 997453262 531067372 13218981 65925275
+ 364419983 963221077 808263585 733813403 105845245
+ 293954693 34741825 607618170 835891674 289545577
+ 303411869 986112196 268635239 557658890 547284852
+ 944112766 298859063 448410682 602205963 95124167
+ 966037195 848419736 983645750 240864464 82420631
+ 728038225 263559914 394645890 51136699 81240999
+ 956765977 564711070 252794746 578770520 1059958490
+ 908961920 1053709599 586212683 667094535 957909739
+ 488212514 747058556 508388015 399334141 306142172
+ 24608062 7970378 576088849 954758074 584645553
+ 732712127 215975734 587942398 409333468 1043075996
+ 336193502 646583067 919733178 793556342 597511069
+ 794381300 857518662 600022804 55650088 895162832
+ 250870127 538576261 879658008 62771719 142722469
+ 25366092 830703245 998428065 352106179 34601187
+ 833522120 632854780 490534525 708203573 162224407
+ 294437359 12679420 520810162 744937355 78205904
+ 13785199 691712884 976226262 297710458 570850089
+ 314808792 656745075 9063273 373478489 536309129
+ 372183014 710426179 422779020 1036005512 767837189
+ 292218158 830863749 195150720 759615287 68079955
+ 574912247 476322284 744020946 577220458 1070895643
+ 631305738 430791810 649304125 889700295 540043811
+ 659786818 121020416 496821279 505344668 45414849
+ 960984074 345969298 169752508 278386551 689532507
+ 229747797 365513932 1013503743 334637077 335167587
+ 621159885 703677806 1017388798 724904457 104270474
+ 846984205 414127560 534283536 914508366 859468449
+ 1011384628 251537694 801890521 737881388 831823562
+ 611605378 34720707 43252578 790885584 136773286
+ 443784219 875118028 1049590878 351093578 476675955
+ 1037300153 2447020 109051529 553615200 754530393
+ 600805392 438213642 95692349 600823188 607051368
+ 824897153 328245131 892285056 64250346 517288093
+ 240522222 94746661 245831792 844507162 1054539505
+ 459358445 363292422 208174471 671212159 577210693
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 773420472 88471564 32253968 231626161 828582435
+ 720948396 409268908 758219076 444844559 649824062
+ 335078840 411951414 969305882 437301205 78500334
+ 415111085 680564967 1009180981 43296592 396100662
+ 1018514949 1062340950 964885037 262912542 1028534579
+ 123024523 175843588 523818192 1019410193 968577935
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 773420472 88471564 32253968 231626161 828582435
+ 720948396 409268908 758219076 444844559 649824062
+ 335078840 411951414 969305882 437301205 78500334
+ 415111085 680564967 1009180981 43296592 396100662
+ 1018514949 1062340950 964885037 262912542 1028534579
+ 123024523 175843588 523818192 1019410193 968577935
+ 698989051 1026617520 592212652 16917417 872939692
+ 1003101786 458388209 743941615 414393651 977200316
+ 608357223 753803878 254313413 1061832416 39507558
+ 209981140 125123042 878021041 902724290 211017464
+ 275177840 347694113 1035558373 994002094 983787403
+ 513219469 516637845 688604219 148345807 697437815
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 773420472 88471564 32253968 231626161 828582435
+ 720948396 409268908 758219076 444844559 649824062
+ 335078840 411951414 969305882 437301205 78500334
+ 415111085 680564967 1009180981 43296592 396100662
+ 1018514949 1062340950 964885037 262912542 1028534579
+ 123024523 175843588 523818192 1019410193 968577935
+ 698989051 1026617520 592212652 16917417 872939692
+ 1003101786 458388209 743941615 414393651 977200316
+ 608357223 753803878 254313413 1061832416 39507558
+ 209981140 125123042 878021041 902724290 211017464
+ 275177840 347694113 1035558373 994002094 983787403
+ 513219469 516637845 688604219 148345807 697437815
+ 85373353 41026694 435515502 558243527 475112758
+ 117730693 521117005 621771441 1033598398 943971803
+ 473698141 391612188 265809041 523808633 364628514
+ 370703421 72257096 98723669 487716389 453498626
+ 825453613 6216615 191433655 1067652761 698390387
+ 11635372 865981776 749239115 942684432 224700402
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 773420472 88471564 32253968 231626161 828582435
+ 720948396 409268908 758219076 444844559 649824062
+ 335078840 411951414 969305882 437301205 78500334
+ 415111085 680564967 1009180981 43296592 396100662
+ 1018514949 1062340950 964885037 262912542 1028534579
+ 123024523 175843588 523818192 1019410193 968577935
+ 698989051 1026617520 592212652 16917417 872939692
+ 1003101786 458388209 743941615 414393651 977200316
+ 608357223 753803878 254313413 1061832416 39507558
+ 209981140 125123042 878021041 902724290 211017464
+ 275177840 347694113 1035558373 994002094 983787403
+ 513219469 516637845 688604219 148345807 697437815
+ 85373353 41026694 435515502 558243527 475112758
+ 117730693 521117005 621771441 1033598398 943971803
+ 473698141 391612188 265809041 523808633 364628514
+ 370703421 72257096 98723669 487716389 453498626
+ 825453613 6216615 191433655 1067652761 698390387
+ 11635372 865981776 749239115 942684432 224700402
+ 677921487 949388344 419667971 105499185 1034177416
+ 1028537797 152376642 1063916002 906073469 571644777
+ 124875761 459706764 400501930 11175164 980086883
+ 502829959 424033451 277820629 331320135 104557881
+ 777730384 449720263 883069224 310189885 193731024
+ 40521387 238359170 698272195 960872462 159715187
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 773420472 88471564 32253968 231626161 828582435
+ 720948396 409268908 758219076 444844559 649824062
+ 335078840 411951414 969305882 437301205 78500334
+ 415111085 680564967 1009180981 43296592 396100662
+ 1018514949 1062340950 964885037 262912542 1028534579
+ 123024523 175843588 523818192 1019410193 968577935
+ 698989051 1026617520 592212652 16917417 872939692
+ 1003101786 458388209 743941615 414393651 977200316
+ 608357223 753803878 254313413 1061832416 39507558
+ 209981140 125123042 878021041 902724290 211017464
+ 275177840 347694113 1035558373 994002094 983787403
+ 513219469 516637845 688604219 148345807 697437815
+ 85373353 41026694 435515502 558243527 475112758
+ 117730693 521117005 621771441 1033598398 943971803
+ 473698141 391612188 265809041 523808633 364628514
+ 370703421 72257096 98723669 487716389 453498626
+ 825453613 6216615 191433655 1067652761 698390387
+ 11635372 865981776 749239115 942684432 224700402
+ 677921487 949388344 419667971 105499185 1034177416
+ 1028537797 152376642 1063916002 906073469 571644777
+ 124875761 459706764 400501930 11175164 980086883
+ 502829959 424033451 277820629 331320135 104557881
+ 777730384 449720263 883069224 310189885 193731024
+ 40521387 238359170 698272195 960872462 159715187
+ 323942931 225328017 279854044 240613586 1026647498
+ 60382888 233430154 116894154 130687415 745490404
+ 111667180 157724660 809739611 128118006 415514450
+ 327856395 331228388 51375416 913441421 855453900
+ 327476627 142261899 21186784 655055032 668577162
+ 889574337 246583921 426462184 151893861 787556056
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 773420472 88471564 32253968 231626161 828582435
+ 720948396 409268908 758219076 444844559 649824062
+ 335078840 411951414 969305882 437301205 78500334
+ 415111085 680564967 1009180981 43296592 396100662
+ 1018514949 1062340950 964885037 262912542 1028534579
+ 123024523 175843588 523818192 1019410193 968577935
+ 698989051 1026617520 592212652 16917417 872939692
+ 1003101786 458388209 743941615 414393651 977200316
+ 608357223 753803878 254313413 1061832416 39507558
+ 209981140 125123042 878021041 902724290 211017464
+ 275177840 347694113 1035558373 994002094 983787403
+ 513219469 516637845 688604219 148345807 697437815
+ 85373353 41026694 435515502 558243527 475112758
+ 117730693 521117005 621771441 1033598398 943971803
+ 473698141 391612188 265809041 523808633 364628514
+ 370703421 72257096 98723669 487716389 453498626
+ 825453613 6216615 191433655 1067652761 698390387
+ 11635372 865981776 749239115 942684432 224700402
+ 677921487 949388344 419667971 105499185 1034177416
+ 1028537797 152376642 1063916002 906073469 571644777
+ 124875761 459706764 400501930 11175164 980086883
+ 502829959 424033451 277820629 331320135 104557881
+ 777730384 449720263 883069224 310189885 193731024
+ 40521387 238359170 698272195 960872462 159715187
+ 323942931 225328017 279854044 240613586 1026647498
+ 60382888 233430154 116894154 130687415 745490404
+ 111667180 157724660 809739611 128118006 415514450
+ 327856395 331228388 51375416 913441421 855453900
+ 327476627 142261899 21186784 655055032 668577162
+ 889574337 246583921 426462184 151893861 787556056
+ 594232860 76141509 13161629 344356300 84658645
+ 605323069 747911003 677875844 386407692 89586567
+ 466985848 148544330 926759705 549743899 323974102
+ 884364250 486443679 919068178 1064561889 895051959
+ 923519812 756672573 864630634 28264255 361824183
+ 91083458 216227426 443372934 661184181 373530306
+ 1061926244 114768875 112263286 964112760 967795333
+ 53669973 47923996 629812413 246560404 881332647
+ 989593056 180575870 941743361 138726401 943903094
+ 55641790 292956876 313289088 768816877 1051217920
+ 152461088 568041823 286243133 811692771 151472163
+ 239518628 125379523 423650255 1037948906 864750896
+ 541959626 269311027 1007780475 1033048146 160819966
+ 741816300 374181701 811218977 393364081 430876854
+ 697336760 870053660 925580846 841110228 728085872
+ 725353117 109481084 275634691 309413362 290789600
+ 157170256 618255182 894908371 289391734 124849191
+ 751109577 821711282 662846021 605071702 775283645
+ 684660838 1021212828 563810334 49372624 537870513
+ 434708771 818258302 227829242 541852717 196643317
+ 125541599 893689221 33836733 1017116437 193032380
+ 385739964 272544150 1054881918 372953433 905571231
+ 256245999 10951957 592105768 877997207 450177063
+ 524932314 912678680 15154264 29842897 324997408
+ 179578727 297691397 163058938 1033653476 877893960
+ 787926620 27318865 715028892 809624356 260242114
+ 92530846 423551777 87792058 658379515 834109486
+ 468779994 448278685 551518662 614179401 558998244
+ 38608758 351679821 276698365 722453628 156499910
+ 264577479 1029127711 656852198 465743868 401355194
+ 186792738 168688434 903017008 672668610 38611945
+ 554952928 1011414566 537322546 17795198 401342184
+ 974210653 181107340 855423336 923466612 369357414
+ 19738532 317373907 998396244 726197972 768252026
+ 33900047 491643022 793577595 411845244 1055479106
+ 575213593 1018413687 287643934 122383051 857749942
+ 529129088 834812684 914977311 457297587 318057965
+ 663339572 88523373 191610550 232755023 124737422
+ 737378167 25049444 752622286 245016897 513288033
+ 849425556 77539375 933541855 598404931 876119920
+ 942670675 369646714 185305788 931981907 143052276
+ 41475520 751276762 151539766 1008517043 260950500
+ 339131971 971481754 336580377 209683851 551827982
+ 219400211 530437608 809207368 1052715777 335019524
+ 979009612 822752296 973872069 835976974 852018812
+ 46521781 974490855 971952568 761112951 1043686968
+ 215156538 801293244 455563983 29255516 194112731
+ 348495503 583189707 631031926 331617906 609655583
+ 263199614 690994071 1069809201 964701 715719625
+ 34789651 707893043 399399182 578411764 324584703
+ 311948281 17351654 318247902 629895677 612221902
+ 715308675 1072608458 796855567 764577765 212403890
+ 75314462 567224034 804051952 183024630 822256064
+ 274148261 1002745384 493441243 898891057 41433645
+ 985575434 1064453678 929152926 673637004 251807003
+ 375679550 902849694 334766136 641405027 648736476
+ 948251146 591942029 372293980 799494685 641617945
+ 826046462 519025937 533370699 681022926 526689432
+ 414076803 446307129 477228004 582877245 970317831
+ 1036480477 920006464 931688504 298090568 607838640
+ 839563929 920166344 778327059 490436456 986876915
+ 1001252430 755188170 785196879 410456949 804893687
+ 718866918 938071963 694566081 493800494 356894158
+ 186340668 597825450 577164725 25854467 443894607
+ 587984173 220140628 689222707 788184381 881752873
+ 536608109 987449657 793724305 110674192 1037608149
+ 958711766 504870452 672065158 1018897152 727521946
+ 428748043 320835872 945253423 902208395 723964471
+ 197180163 250738177 93838081 886310753 879838119
+ 251375703 68924212 485669426 950483650 1018890938
+ 64906849 446831788 632934156 238748215 688942463
+ 986694928 659553049 209136704 194284022 855361818
+ 287767750 588674734 317719248 1033238778 737751131
+ 590932464 423007920 426551986 331008820 812776940
+ 665981212 946301610 504492373 145613827 1022122613
+ 301335503 601026471 715430073 282540884 68187916
+ 981833659 96850577 109081214 691596140 381654435
+ 118603680 201105024 439369433 270012079 945160813
+ 946452732 352446853 426239049 604224889 112152138
+ 823239982 16603750 1026324904 288745069 652711484
+ 674483747 263833042 307338224 15556436 1049391642
+ 479791756 138935667 963500105 168876735 677504253
+ 123974107 360700403 693378702 991593153 502595841
+ 662746545 591569685 859882015 49031596 125477356
+ 915458654 339835983 900750386 376402160 583653663
+ 531451141 478822361 517414313 380945489 34450741
+ 983018615 26371671 945467892 246566946 540759931
+ 191557947 673554278 721875235 446156654 530467716
+ 780073175 3226743 902166142 570732127 1026171789
+ 514575308 391488457 343433124 334509905 887288954
+ 234901811 373777199 1006954072 358037386 161921596
+ 1038965414 31121501 503324754 72942966 448211409
+ 711816308 307061692 728471426 301222337 313229852
+ 935958259 161596818 877821345 505415971 375651631
+ 120347253 1027925730 119124697 161854532 297521092
+ 751188835 43315515 572302268 376404199 16258210
+ 706890494 562353078 403599913 136783731 550445832
+ 947738766 878489709 996736790 334790643 5112150
+ 962656271 296443207 499540547 817186147 941111473
+ 699482122 102527686 339872865 653968613 512239442
+ 316569010 817767438 758160806 1044235449 1054819676
+ 274817640 576498796 511430093 616349780 537560088
+ 270479951 368469822 164812177 669720006 193266553
+ 634910233 541804546 314836285 27978935 147186792
+ 565133252 526277944 290674141 343100701 913308523
+ 241635600 1063279002 1045792401 530802890 47160289
+ 757737513 780468404 38840190 913229406 607863944
+ 743787839 69446230 1032202265 253007332 41492926
+ 176138575 403735136 208831433 837958709 517582937
+ 764443399 173368661 1039202012 798842343 587455078
+ 766425518 449030266 895980786 971832087 151602062
+ 160804744 394728490 879873976 1056198090 315775822
+ 82537038 918939533 286018681 162403065 115047984
+ 1066785033 445823493 174407022 641419144 633933237
+ 1058304122 362414991 886661670 509602669 702382088
+ 547811216 675758726 895658542 158286731 996719339
+ 470437725 365880609 360843953 735277921 782708909
+ 865470470 80021454 401565143 379958939 638027164
+ 240627505 320428300 981291343 616546251 171290373
+ 796561279 227281110 644387511 412301420 191294166
+ 212123592 960507266 23396885 368763917 155828391
+ 71902023 381257264 732137583 960240460 62265300
+ 441772338 364346383 596406959 15666416 440586514
+ 720295169 623319110 601151114 1028666425 493806999
+ 228981604 73987751 608044448 633766478 653174164
+ 605745260 408254241 247342753 611177951 637069095
+ 446005731 371269289 679170042 392537680 802609414
+ 230146126 91981952 541551140 401183549 242344529
+ 451597742 51211779 291920308 23989394 849209718
+ 979379244 7892973 518622510 3893483 500299789
+ 636266443 522814356 658058086 118992811 140535015
+ 330079975 190317676 546967030 911916839 858676262
+ 428496883 959125322 375854033 147784748 534598292
+ 916317401 669004502 672751570 820567573 49511834
+ 1052724743 635110198 97964823 397136090 157939854
+ 415452546 782109232 582170073 628029603 286308950
+ 542587005 856706611 6926544 475744053 74361571
+ 530546504 127053121 818294059 652579002 819909568
+ 188879384 162709886 85305677 850905913 302692673
+ 1032927320 212106763 816547908 646099838 775641625
+ 816077653 488105295 731163485 1036813990 757387044
+ 548135053 522392350 190674956 978162582 959323820
+ 751141456 378203114 392530262 836054806 444845383
+ 92402249 845123475 739246470 10587699 956458128
+ 452782487 31794885 925112889 185118092 115538365
+ 308637062 779932433 695213811 438387941 829874861
+ 140576315 246517746 337566393 964172723 677416529
+ 308294218 68117458 1005366723 380378899 173778384
+ 906298027 669173187 772159794 645553293 87695374
+ 816578081 171198682 536342424 645678017 1058158512
+ 299566577 16184320 392738783 859088026 326010056
+ 1054426251 16035875 230515754 611203747 29409505
+ 2794619 231164431 537401434 180958772 667291400
+ 687439938 988579963 960516054 251486187 824121988
+ 439808557 395893026 957576034 1058299943 776971534
+ 783532514 801797418 931347945 781622902 512159991
+ 773420472 88471564 32253968 231626161 828582435
+ 720948396 409268908 758219076 444844559 649824062
+ 335078840 411951414 969305882 437301205 78500334
+ 415111085 680564967 1009180981 43296592 396100662
+ 1018514949 1062340950 964885037 262912542 1028534579
+ 123024523 175843588 523818192 1019410193 968577935
+ 698989051 1026617520 592212652 16917417 872939692
+ 1003101786 458388209 743941615 414393651 977200316
+ 608357223 753803878 254313413 1061832416 39507558
+ 209981140 125123042 878021041 902724290 211017464
+ 275177840 347694113 1035558373 994002094 983787403
+ 513219469 516637845 688604219 148345807 697437815
+ 85373353 41026694 435515502 558243527 475112758
+ 117730693 521117005 621771441 1033598398 943971803
+ 473698141 391612188 265809041 523808633 364628514
+ 370703421 72257096 98723669 487716389 453498626
+ 825453613 6216615 191433655 1067652761 698390387
+ 11635372 865981776 749239115 942684432 224700402
+ 677921487 949388344 419667971 105499185 1034177416
+ 1028537797 152376642 1063916002 906073469 571644777
+ 124875761 459706764 400501930 11175164 980086883
+ 502829959 424033451 277820629 331320135 104557881
+ 777730384 449720263 883069224 310189885 193731024
+ 40521387 238359170 698272195 960872462 159715187
+ 323942931 225328017 279854044 240613586 1026647498
+ 60382888 233430154 116894154 130687415 745490404
+ 111667180 157724660 809739611 128118006 415514450
+ 327856395 331228388 51375416 913441421 855453900
+ 327476627 142261899 21186784 655055032 668577162
+ 889574337 246583921 426462184 151893861 787556056
+ 594232860 76141509 13161629 344356300 84658645
+ 605323069 747911003 677875844 386407692 89586567
+ 466985848 148544330 926759705 549743899 323974102
+ 884364250 486443679 919068178 1064561889 895051959
+ 923519812 756672573 864630634 28264255 361824183
+ 91083458 216227426 443372934 661184181 373530306
+ 851006353 332788202 78980082 520170945 407846012
+ 451700726 953901768 255254266 1031115409 562917916
+ 30378553 466253377 900262041 164614587 547304459
+ 818157544 474399166 630824260 756815279 1006730636
+ 862989379 448173989 831876762 332605109 398585595
+ 637412945 773323708 753294615 92369848 779332381
diff --git a/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser
new file mode 100644
index 0000000..9f760ac
Binary files /dev/null and b/source/umontreal/iro/lecuyer/hups/dataSer/Nieder/NiedXingSequenceBase2Trans.ser differ
diff --git a/source/umontreal/iro/lecuyer/hups/guidehups.bbl b/source/umontreal/iro/lecuyer/hups/guidehups.bbl
new file mode 100644
index 0000000..ef93ef2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/guidehups.bbl
@@ -0,0 +1,259 @@
+\begin{thebibliography}{10}
+
+\bibitem{rANT79a}
+I.~A. Antonov and V.~M. Saleev.
+\newblock An economic method of computing {LP}$_\tau$-sequences.
+\newblock {\em Zh. Vychisl. Mat. i. Mat. Fiz.}, 19:243--245, 1979.
+\newblock In {R}ussian.
+
+\bibitem{rBRA88c}
+P.~Bratley and B.~L. Fox.
+\newblock Algorithm 659: Implementing {S}obol's quasirandom sequence generator.
+\newblock {\em ACM Transactions on Mathematical Software}, 14(1):88--100, 1988.
+
+\bibitem{rBRA92a}
+P.~Bratley, B.~L. Fox, and H.~Niederreiter.
+\newblock Implementation and tests of low-discrepancy sequences.
+\newblock {\em ACM Transactions on Modeling and Computer Simulation},
+ 2:195--213, 1992.
+
+\bibitem{vCRA76a}
+R.~Cranley and T.~N.~L. Patterson.
+\newblock Randomization of number theoretic methods for multiple integration.
+\newblock {\em {SIAM} Journal on Numerical Analysis}, 13(6):904--914, 1976.
+
+\bibitem{rFAU92a}
+H.~Faure.
+\newblock Good permutations for extreme discrepancy.
+\newblock {\em Journal of Number Theory}, 42:47--56, 1992.
+
+\bibitem{vFAU09a}
+H.~Faure and C.~Lemieux.
+\newblock Generalized {H}alton sequences in 2008: A comparative study.
+\newblock {\em {ACM} Transactions on Modeling and Computer Simulation},
+ 19(4):Article 15, 2009.
+
+\bibitem{rFAU02a}
+H.~Faure and S.~Tezuka.
+\newblock Another random scrambling of digital $(t,s)$-sequences.
+\newblock In K.-T. Fang, F.~J. Hickernell, and H.~Niederreiter, editors, {\em
+ {M}onte {C}arlo and Quasi-{M}onte {C}arlo Methods 2000}, pages 242--256,
+ Berlin, 2002. Springer-Verlag.
+
+\bibitem{rFOX86a}
+B.~L. Fox.
+\newblock Implementation and relative efficiency of quasirandom sequence
+ generators.
+\newblock {\em ACM Transactions on Mathematical Software}, 12:362--376, 1986.
+
+\bibitem{vFOX99a}
+B.~L. Fox.
+\newblock {\em Strategies for Quasi-{M}onte {C}arlo}.
+\newblock Kluwer Academic, Boston, MA, 1999.
+
+\bibitem{fGLA04a}
+P.~Glasserman.
+\newblock {\em Monte {C}arlo Methods in Financial Engineering}.
+\newblock Springer-Verlag, New York, 2004.
+
+\bibitem{rHAL60a}
+J.~H. Halton.
+\newblock On the efficiency of certain quasi-random sequences of points in
+ evaluating multi-dimensional integrals.
+\newblock {\em Numerische Mathematik}, 2:84--90, 1960.
+
+\bibitem{rHAM60a}
+J.~M. Hammersley.
+\newblock {M}onte {C}arlo methods for solving multivariate problems.
+\newblock {\em Annals of the {N}ew {Y}ork Academy of Science}, 86:844--874,
+ 1960.
+
+\bibitem{rHEL98a}
+P.~Hellekalek and H.~Niederreiter.
+\newblock The weighted spectral test: Diaphony.
+\newblock {\em ACM Transactions on Modeling and Computer Simulation},
+ 8(1):43--60, 1998.
+
+\bibitem{vHIC02a}
+F.~J. Hickernell.
+\newblock Obtaining ${O(N^{-2+\epsilon})}$ convergence for lattice quadrature
+ rules.
+\newblock In K.-T. Fang, F.~J. Hickernell, and H.~Niederreiter, editors, {\em
+ {M}onte {C}arlo and Quasi-{M}onte {C}arlo Methods 2000}, pages 274--289,
+ Berlin, 2002. Springer-Verlag.
+
+\bibitem{vHIC01a}
+F.~J. Hickernell, H.~S. Hong, P.~L'Ecuyer, and C.~Lemieux.
+\newblock Extensible lattice sequences for quasi-{M}onte {C}arlo quadrature.
+\newblock {\em {SIAM} Journal on Scientific Computing}, 22(3):1117--1138, 2001.
+
+\bibitem{vHON03a}
+H.~S. Hong and F.~H. Hickernell.
+\newblock Algorithm 823: Implementing scrambled digital sequences.
+\newblock {\em {ACM} Transactions on Mathematical Software}, 29:95--109, 2003.
+
+\bibitem{mKOR59a}
+N.~M. Korobov.
+\newblock The approximate computation of multiple integrals.
+\newblock {\em Dokl. Akad. Nauk {SSSR}}, 124:1207--1210, 1959.
+\newblock in Russian.
+
+\bibitem{rLEC99a}
+P.~L'Ecuyer.
+\newblock Tables of maximally equidistributed combined {LFSR} generators.
+\newblock {\em Mathematics of Computation}, 68(225):261--269, 1999.
+
+\bibitem{vLEC03b}
+P.~L'Ecuyer.
+\newblock Quasi-{M}onte {C}arlo methods for simulation.
+\newblock In {\em Proceedings of the 2003 Winter Simulation Conference}, pages
+ 81--90, Piscataway, NJ, 2003. {IEEE} Press.
+
+\bibitem{vLEC99a}
+P.~L'Ecuyer and C.~Lemieux.
+\newblock Quasi-{M}onte {C}arlo via linear shift-register sequences.
+\newblock In {\em Proceedings of the 1999 Winter Simulation Conference}, pages
+ 632--639. {IEEE} Press, 1999.
+
+\bibitem{vLEC00b}
+P.~L'Ecuyer and C.~Lemieux.
+\newblock Variance reduction via lattice rules.
+\newblock {\em Management Science}, 46(9):1214--1235, 2000.
+
+\bibitem{vLEC02a}
+P.~L'Ecuyer and C.~Lemieux.
+\newblock Recent advances in randomized quasi-{M}onte {C}arlo methods.
+\newblock In M.~Dror, P.~L'Ecuyer, and F.~Szidarovszky, editors, {\em Modeling
+ Uncertainty: An Examination of Stochastic Theory, Methods, and Applications},
+ pages 419--474. Kluwer Academic, Boston, 2002.
+
+\bibitem{rLEC02b}
+P.~L'Ecuyer and F.~Panneton.
+\newblock Construction of equidistributed generators based on linear
+ recurrences modulo 2.
+\newblock In K.-T. Fang, F.~J. Hickernell, and H.~Niederreiter, editors, {\em
+ {M}onte {C}arlo and Quasi-{M}onte {C}arlo Methods 2000}, pages 318--330.
+ Springer-Verlag, Berlin, 2002.
+
+\bibitem{iLEM04a}
+C.~Lemieux, M.~Cieslak, and K.~Luttmer.
+\newblock {\em {RandQMC} User's Guide: A Package for Randomized Quasi-{M}onte
+ {C}arlo Methods in {C}}, 2004.
+\newblock Software {user's} guide, available at
+ \url{http://www.math.uwaterloo.ca/~clemieux/randqmc.html}.
+
+\bibitem{vLEM03a}
+C.~Lemieux and P.~L'Ecuyer.
+\newblock Randomized polynomial lattice rules for multivariate integration and
+ simulation.
+\newblock {\em {SIAM} Journal on Scientific Computing}, 24(5):1768--1789, 2003.
+
+\bibitem{mMAT99a}
+J.~Matou\v{s}ek.
+\newblock {\em Geometric Discrepancy: An Illustrated Guide}.
+\newblock Springer-Verlag, Berlin, 1999.
+
+\bibitem{rNIE92b}
+H.~Niederreiter.
+\newblock {\em Random Number Generation and Quasi-{M}onte {C}arlo Methods},
+ volume~63 of {\em SIAM CBMS-NSF Regional Conference Series in Applied
+ Mathematics}.
+\newblock SIAM, Philadelphia, PA, 1992.
+
+\bibitem{rNIE98a}
+H.~Niederreiter and C.~Xing.
+\newblock Nets, $(t,s)$-sequences, and algebraic geometry.
+\newblock In P.~Hellekalek and G.~Larcher, editors, {\em Random and
+ Quasi-Random Point Sets}, volume 138 of {\em Lecture Notes in Statistics},
+ pages 267--302. Springer, New York, NY, 1998.
+
+\bibitem{vOWE97a}
+A.~B. Owen.
+\newblock {M}onte {C}arlo variance of scrambled equidistribution quadrature.
+\newblock {\em {SIAM} Journal on Numerical Analysis}, 34(5):1884--1910, 1997.
+
+\bibitem{vOWE97b}
+A.~B. Owen.
+\newblock Scrambled net variance for integrals of smooth functions.
+\newblock {\em Annals of Statistics}, 25(4):1541--1562, 1997.
+
+\bibitem{vOWE98a}
+A.~B. Owen.
+\newblock {L}atin supercube sampling for very high-dimensional simulations.
+\newblock {\em {ACM} Transactions on Modeling and Computer Simulation},
+ 8(1):71--102, 1998.
+
+\bibitem{vOWE03a}
+A.~B. Owen.
+\newblock Variance with alternative scramblings of digital nets.
+\newblock {\em {ACM} Transactions on Modeling and Computer Simulation},
+ 13(4):363--378, 2003.
+
+\bibitem{rPAN04t}
+F.~Panneton.
+\newblock {\em Construction d'ensembles de points bas\'ee sur des r\'ecurrences
+ lin\'eaires dans un corps fini de caract\'eristique 2 pour la simulation
+ {M}onte {C}arlo et l'int\'egration quasi-{M}onte {C}arlo}.
+\newblock PhD thesis, D\'epartement d'informatique et de recherche
+ op\'erationnelle, Universit\'e de Montr\'eal, Canada, August 2004.
+
+\bibitem{rPAN04a}
+F.~Panneton and P.~L'Ecuyer.
+\newblock Random number generators based on linear recurrences in {$F_{2^w}$}.
+\newblock In H.~Niederreiter, editor, {\em {M}onte {C}arlo and Quasi-{M}onte
+ {C}arlo Methods 2002}, pages 367--378, Berlin, 2004. Springer-Verlag.
+
+\bibitem{rPAN04d}
+F.~Panneton and P.~L'Ecuyer.
+\newblock Infinite-dimensional point sets based on linear recurrences over
+ {GF($2^w$)}.
+\newblock In H.~Niederreiter and D.~Talay, editors, {\em {M}onte {C}arlo and
+ Quasi-{M}onte {C}arlo Methods 2004}, Berlin, 2005. Springer-Verlag.
+\newblock to appear.
+
+\bibitem{rPIR01c}
+G.~Pirsic.
+\newblock A software implementation of {N}iederreiter-{X}ing sequences.
+\newblock In K.-T. Fang, F.~J. Hickernell, and H.~Niederreiter, editors, {\em
+ {M}onte {C}arlo and Quasi-{M}onte {C}arlo Methods 2000}. Springer, 2001.
+\newblock To appear.
+
+\bibitem{vSLO94a}
+I.~H. Sloan and S.~Joe.
+\newblock {\em Lattice Methods for Multiple Integration}.
+\newblock Clarendon Press, Oxford, 1994.
+
+\bibitem{rSOB67a}
+I.~M. Sobol'.
+\newblock The distribution of points in a cube and the approximate evaluation
+ of integrals.
+\newblock {\em U.S.S.R. Comput. Math. and Math. Phys.}, 7(4):86--112, 1967.
+
+\bibitem{rSOB76b}
+I.~M. Sobol'.
+\newblock Uniformly distributed sequences with an additional uniform property.
+\newblock {\em USSR Comput.\ Math.\ Math.\ Phys.\ Academy of Sciences},
+ 16:236--242, 1976.
+
+\bibitem{rSTR95a}
+J.~Struckmeier.
+\newblock Fast generation of low-discrepancy sequences.
+\newblock {\em Journal of Computational and Applied Mathematics}, 61(4):29--41,
+ 1995.
+
+\bibitem{rTEZ95a}
+S.~Tezuka.
+\newblock {\em Uniform Random Numbers: Theory and Practice}.
+\newblock Kluwer Academic Publishers, Norwell, MA, 1995.
+
+\bibitem{rTEZ02a}
+S.~Tezuka and H.~Faure.
+\newblock $i$-binomial scrambling of digital nets and sequences.
+\newblock Technical report, {IBM} Research, Tokyo Research Laboratory, 2002.
+
+\bibitem{vWAN99a}
+X.~Wang and F.~J. Hickernell.
+\newblock Randomized {H}alton sequences.
+\newblock {\em Mathematical and Computer Modelling}, 32:887--899, 2000.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/hups/guidehups.tex b/source/umontreal/iro/lecuyer/hups/guidehups.tex
new file mode 100644
index 0000000..a24007b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/guidehups.tex
@@ -0,0 +1,104 @@
+\documentclass[12pt]{article}
+
+\usepackage{amssymb}
+\usepackage{alltt}
+\usepackage{html}
+\usepackage{url}
+\usepackage{ssj}
+
+% \def\bq{\mbox{\boldmath $q$}}
+% \def\be{\mbox{\boldmath $e$}}
+\def\bu{\mbox{\boldmath $u$}}
+% \def\bv{\mbox{\boldmath $v$}}
+% \def\bC{\mbox{\boldmath $C$}}
+% \def\bV{\mbox{\boldmath $V$}}
+% \def\cS{\mathcal{S}}
+
+
+\mytwoheads\dateheadtrue
+\detailedfalse
+
+% \includeonly{F2wStructure}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+
+% \title{hups}{Highly Uniform Point Sets}
+\title{hups}{Tools for Quasi-Monte Carlo}
+
+This package provides classes implementing highly uniform point sets
+(HUPS) and tools for their randomization.
+These point sets can be used for quasi-Monte Carlo integration.
+Randomized quasi-Monte Carlo point sets can in fact replace streams
+of uniform random numbers in a simulation, for the purpose of
+reducing the variance of the estimator.
+
+\vfill
+\end{titlepage}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+
+\include{overview}
+
+\include{PointSet} % top class
+\include{PointSetIterator}
+
+\include{PointSetRandomization}
+\include{EmptyRandomization}
+\include{RandomShift}
+\include{LMScrambleShift}
+\include{SMScrambleShift}
+\include{RandomStart}
+
+\include{ContainerPointSet}
+\include{CachedPointSet}
+% \include{SortedPointSet}
+\include{SubsetOfPointSet}
+\include{PaddedPointSet}
+\include{AntitheticPointSet}
+\include{RandShiftedPointSet}
+%% \include{RandXoredPointSet} % remove ?
+\include{BakerTransformedPointSet}
+
+\include{CycleBasedPointSet}
+\include{LCGPointSet}
+\include{CycleBasedPointSetBase2}
+\include{F2wStructure}
+\include{F2wCycleBasedLFSR}
+\include{F2wCycleBasedPolyLCG}
+
+\include{DigitalNet}
+\include{DigitalSequence}
+\include{DigitalNetFromFile}
+\include{FaureSequence}
+
+\include{DigitalNetBase2}
+\include{DigitalSequenceBase2}
+\include{DigitalNetBase2FromFile}
+\include{SobolSequence}
+\include{NiedSequenceBase2}
+\include{NiedXingSequenceBase2}
+\include{F2wNetLFSR}
+\include{F2wNetPolyLCG}
+
+\include{RadicalInverse}
+\include{HammersleyPointSet}
+\include{HaltonSequence}
+
+\include{Rank1Lattice}
+\include{KorobovLattice}
+\include{KorobovLatticeSequence}
+
+\include{RQMCPointSet}
+
+\bibliographystyle{plain}
+\bibliography{stat,random,vrt,simul,math,ift,fin}
+
+\end{document}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/source/umontreal/iro/lecuyer/hups/overview.tex b/source/umontreal/iro/lecuyer/hups/overview.tex
new file mode 100644
index 0000000..a1ed5e6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/hups/overview.tex
@@ -0,0 +1,638 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+
+%%%%%%%%%%%%
+\subsection*{Monte Carlo and quasi-Monte Carlo}
+
+This package provides classes implementing \emph{highly uniform point sets}
+(HUPS) over the $s$-dimensional unit hypercube $[0,1)^s$,
+and tools for their randomization.
+The terminology \emph{low-discrepancy sequence} (LDS) is often used
+for infinite sequences of points such that the \emph{discrepancy}
+between the distribution of the first $n$ points of the sequence and
+the uniform distribution converges to zero at a certain rate
+when $n\to\infty$ \cite{rNIE92b}.
+HUPS and LDS are used for quasi-Monte Carlo integration,
+as we now briefly explain.
+See, e.g., \cite{vFOX99a,fGLA04a,rHEL98a,vLEC02a,vLEC03b,%
+vOWE98a,rNIE92b,vSLO94a,rTEZ95a}
+for further details.
+
+Suppose we want to estimate the integral of a function $f$ defined over
+the $s$-dimensional unit hypercube,
+\begin{equation}
+ \mu = \int_{[0,1)^s}\ f(\bu) d\bu. \label{eq:mu}
+\end{equation}
+Practically any mathematical expectation that can be estimated by
+simulation can be written in this way, usually for a very complicated
+$f$ and sometimes for $s=\infty$.
+Indeed, the source of randomness of stochastic simulations
+is usually a \emph{stream} of real numbers $\bu = (u_0,u_1,u_2,\dots)$
+whose purpose is to imitate i.i.d.\ $U(0,1)$ random variables.
+These real numbers are transformed in complicated ways to produce
+the estimator. Thus, the dimension $s$ of the integral (\ref{eq:mu})
+represents the number of calls to the uniform random number generator
+if that number is deterministic.
+If it is random and unbounded, we take $s = \infty$.
+In the latter case, however, we can assume that the \emph{actual}
+number of calls is finite with probability one (otherwise the simulation
+may never end).
+
+We consider an estimator of $\mu$ of the form
+\begin{equation}
+ Q_n = \frac{1}{n} \sum_{i=0}^{n-1} f(\bu_i), \label{eq:Qn}
+\end{equation}
+which is the average of $f$ over the \emph{point set}
+$P_n = \{\bu_0,\dots,\bu_{n-1}\} \subset [0,1)^s$.
+
+With the \emph{Monte Carlo} (MC) method, the $\bu_i$'s are
+i.i.d.{} random vectors uniformly distributed over $[0,1)^s$.
+Then, $Q_n$ is an unbiased estimator of $\mu$ with variance
+$\sigma^2/n$, where
+\begin{equation}
+ \sigma^2 = \int_{[0,1)^s} f^2(\bu) d\bu - \mu^2,
+\end{equation}
+and it obeys a central-limit theorem if $\sigma^2 < \infty$.
+
+\emph{Quasi-Monte Carlo} (QMC) methods use point sets $P_n$
+that are \emph{more evenly distributed} over the unit hypercube
+than typical random points.
+We call them \emph{highly uniform point sets} (HUPS).
+The aim is to reduce the size of the integration error $Q_n - \mu$.
+Two important classes of methods for constructing such point sets are
+\emph{digital nets} and \emph{integration lattices}
+\cite{rNIE92b,vSLO94a,vLEC02a,fGLA04a}.
+Both are implemented in this package, in various flavors.
+
+
+%%%%%%%%%%%%%%%%%%%%
+\subsection*{Elementary constructions}
+
+To give an idea of how HUPS and LDS can be constructed, we start with
+a simple one-dimensional example.
+If $s=1$ and $n$ is fixed, very simple highly uniform constructions are
+the point sets $P_n = \{0,\, 1/n,\, \dots, (n-1)/n\}$ and
+the shifted version $P'_n = \{1/(2n),\, 3/(2n),\, \dots, (2n-1)/(2n)\}$.
+
+In $s > 1$ dimensions, the simplest extensions would be as follows.
+Let $n = d^s$ for some integer $d$ and define $P_n$ as the
+Cartesian product of $s$ copies of the one-dimensional sets $P_d$;
+that is, $P_n = \{(u_0,\dots,u_{s-1}) :
+u_j \in \{0,\, 1/d,\, \dots, (d-1)/d\}$ for each $j\}$,
+and similarly for $P'_n$.
+The point sets thus obtained are regular rectangular grids.
+Unfortunately, this approach breaks down rapidly when $s$ gets large,
+because $n$ must increase exponentially fast with $s$ for fixed $d$.
+Another important drawback is that when $P_n$ is projected over
+lower-dimensional subspaces, several points are projected onto each other
+and become redundant \cite{vLEC02a}.
+
+A better idea is to construct a point set $P_n$ in $s$ dimensions
+such that each one-dimensional projection of $P_n$ is the set
+of values $\{0,\, 1/n,\, \dots, (n-1)/n\}$.
+Of course, these values should not be visited in the same order for
+all coordinates, because otherwise all the points would lie on the
+diagonal line going from $(0,\dots,0)$ to $(1,\dots,1)$.
+In other words, for each coordinate $j$, $0\le j < s$, we must define
+a different \emph{permutation} of the integers $\{0,\dots,n-1\}$ and
+visit the values $\{0,\, 1/n,\, \dots, (n-1)/n\}$ in the order determined
+by that permutation. The trick is to select those permutations in a
+way that $P_n$ itself is highly uniform over $[0,1)^s$ in a well-defined
+sense (to be determined).
+This is what most construction methods attempt to achieve.
+Before looking at concrete ways of defining such permutations,
+we introduce a related issue: what to do if $n$ is not fixed.
+
+For $s=1$, a simple way of filling up the unit interval $[0,1)$
+uniformly is via the low-discrepancy sequence
+0, 1/2, 1/4, 3/4, 1/8, 5/8, 3/8, 7/8, 1/16, 9/16, \ldots,
+called the \emph{van der Corput sequence} in base 2.
+More generally, select an integer $b \ge 2$, called the \emph{base}.
+The \emph{radical inverse} function in base $b$, $\psi_b : \NN\to[0,1)$,
+is defined as follows. If $i$ is a $k$-digit integer in base $b$
+with digital $b$-ary expansion
+\[
+ i = a_0 + a_1 b + \dots + a_{k-1} b^{k-1},
+\]
+then
+\[
+ \psi_b(i) = a_0 b^{-1} + a_1 b^{-2} + \cdots + a_{k-1} b^{-k}.
+\]
+For a given $b$, $\psi_b(0), \psi_b(1), \psi_b(2), \dots$
+is called the \emph{van der Corput sequence in base $b$}.
+This sequence fills up the unit interval $[0,1)$ quite uniformly.
+For example, for $b=2$ we obtain the sequence mentioned above
+and for $b=3$ we obtain
+0, 1/3, 2/3, 1/9, 4/9, 7/9, 2/9, 5/9, 8/9, 1/27, 10/27, 19/27, \ldots.
+Moreover, for two relatively prime bases $b_1$ and $b_2$, the two
+sequences have no value in common except 0.
+
+For $s > 1$, one could either take different (relatively prime)
+bases for the different coordinates, or take the same basis $b$
+but permute the successive values using a different permutation for
+each coordinate. These permutations are usually selected in a way
+that for every integer $k$, the first $b^k$ values that are
+enumerated remain the same (they are the values
+of $\psi_b(i)$ for $i=0,\dots,b^k-1$), but they are enumerated in a
+different order. Several digital net constructions (to be defined later)
+fit this framework.
+
+If we decide to take different bases, the most natural choice is to
+take the $j$th smallest prime, $b_j$, as a base for coordinate $j-1$;
+that is, base 2 for coordinate 0, base 3 for coordinate 1,
+base 5 for coordinate 2, and so on.
+The infinite sequence thus defined, where point $i$ is
+\eq
+ \bu_i = (\psi_{b_1}(i),\psi_{b_2}(i),\dots, \psi_{b_{s}}(i))
+ \eqlabel{eq:Halton-point}
+\endeq
+for $i \ge 0$, was proposed in \cite{rHAL60a} and is called
+the \emph{Halton sequence}.
+One drawback of this sequence is that for large $s$, the base $b_s$
+becomes quite large.
+
+In the case where $n$ is fixed,
+% for any point set for which each coordinate goes
+% through the values $0, 1/n, \ldots, (n-1)/n$ (in a different order),
+we can always take $i/n$ as the first coordinate of point $i$.
+In particular, the \emph{Hammersley point set} with $n$ points
+in $s$ dimensions contains the points
+\eq
+ \bu_i = (i/n,\psi_{b_1}(i),\psi_{b_2}(i),\dots, \psi_{b_{s-1}}(i)),
+ \eqlabel{eq:Hammersley-point}
+\endeq
+for $i=0,\dots,n-1$ \cite{rHAM60a}.
+Historically, Halton sequences were defined as extensions of
+Hammersley point sets.
+
+
+
+%%%%%%%%%%%%%%%%
+\subsection*{Digital nets}
+
+\emph{Digital nets and sequences} are an important class of HUPS
+and LDS constructions.
+Most concrete implementations, e.g., those proposed by
+Sobol', Faure, Niederreiter, and Niederreiter and Xing,
+are \emph{linear} digital nets and sequences, defined as follows
+(see also \cite{rNIE92b,rTEZ95a,vLEC02a}).
+
+Let $b\ge 2$ be an arbitrary integer (usually a prime number), called
+the \emph{base}.
+A net that contains $n = b^k$ points in $s$ dimensions is defined via
+$s$ \emph{generator matrices} $\bC_0,\dots,\bC_{s-1}$, which are
+(in theory) $\infty\times k$ matrices whose elements are in
+$\ZZ_b = \{0,\dots,b-1\}$.
+The matrix $\bC_j$ is used for coordinate $j$ of all the points, for $j\ge 0$.
+To define the $i$th point $\bu_i$, for $i=0,\dots,b^k-1$, write
+the digital expansion of $i$ in base $b$ and multiply the vector of its
+digits by $\bC_j$ to obtain the digits of the expansion of $u_{i,j}$,
+the $j$th coordinate of $\bu_i$. That is,
+\hrichard {Dans cette introduction, les indices dimensionnels varient
+ parfois de 1 \`a $s$, et parfois de 0 \`a $s-1$.
+ Ailleurs et dans les m\'ethodes, ils varient de 0 \`a $s-1$.}
+\begin{eqnarray}
+ i &=& \sum_{\ell=0}^{k-1} a_{i,\ell} b^\ell, \eqlabel{eq:digital-i} \\
+ \pmatrix{u_{i,j,1}\cr u_{i,j,2}\cr \vdots \cr }
+ &=& \bC_j \pmatrix{a_{i,0}\cr a_{i,1}\cr \vdots \cr a_{i,k-1}\cr},
+ \eqlabel{eq:digital-Cj} \\
+ u_{i,j} &=& \sum_{\ell=1}^\infty u_{i,j,\ell} b^{-\ell},
+ \eqlabel{eq:digital-uij} \\
+ \bu_i &=& (u_{i,0},\dots,u_{i,s-1}). \eqlabel{eq:digital-ui}
+\end{eqnarray}
+In practice, the expansion in (\ref{eq:digital-uij}) is truncated to the
+first $r$ digits for some positive integer $r$, so each matrix $\bC_j$ is
+actually truncated to a $r\times k$ matrix.
+Typically $r$ is equal to $k$, or is slightly larger,
+or is selected so that $b^r$ is near $2^{31}$.
+
+Usually, the first $k$ lines of each $\bC_j$ form a nonsingular
+$k\times k$ matrix. Then, the $n$ output values for coordinate $j$,
+$u_{0,j},\dots, u_{n-1,j}$, when truncated to their first $k$ fractional
+digits in base $b$, are a permutation of the numbers
+$0, 1/n, \dots, (n-1)/n$.
+Different coordinates simply use different permutations,
+implemented via the matrices $\bC_j$.
+
+When the first $k$ lines of $\bC_j$ form the identity and the other
+lines are zero, the first $n$ output values are the first $n$ elements
+of the van der Corput sequence in base $b$.
+If we reverse the order of the columns of that matrix $\bC_j$
+(i.e., column $c$ will contain a one in line $k-c+1$ and zeros elsewhere,
+for $0\le c < k$), we obtain the output values
+$0, 1/n, \dots, (n-1)/n$ in that order.
+With a slight abuse of language, we shall call this first matrix
+(with the identity followed by lines of zeros) the \emph{identity}
+and the second one (with the columns in reverse order) the
+\emph{reflected identity}.
+It is customary to take $\bC_0$ as the identity for digital sequences,
+and often for digital nets as well.
+But for digital nets (where $n$ is fixed in advance),
+one can take $\bC_0$ as the reflected identity instead,
+then $\bC_1$ as the identity, and so on.
+That is, the matrix $\bC_j$ for the digital net is taken as the matrix
+$\bC_{j-1}$ of the digital sequence.
+Our package often gives the choice.
+
+For digital sequences, the matrices $\bC_j$ actually have an infinite
+number of columns, although only the first $k$ columns are needed to
+generate the first $b^k$ points. So in practice, we never need to store
+more than a finite number of columns at a time.
+Whenever we find that we need more than $b^k$ points for the current
+value of $k$, we can simply increase $k$ and add the corresponding
+columns to the matrices $\bC_j$.
+
+The classes \externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}
+ and \externalclass{umontreal.iro.lecuyer.hups}{DigitalSequence}
+implement generic digital nets and sequences.
+Specific instances are constructed in subclasses of these two classes.
+
+
+%%%%%%%%%%%%%%%%
+\subsection*{Lattice Rules}
+
+An \emph{integration lattice} is a discrete (but infinite)
+subset of $\RR^s$ of the form
+\[
+ L_s = \left\{\bv = \sum_{j=1}^s h_j {\bv_j}
+ \mbox{ such that each } h_j\in\ZZ\right\},
+\]
+where $\bv_1,\dots,\bv_s \in \RR^s$ are linearly independent over $\RR$
+and $\ZZ^s \subseteq L_s$. This last condition means that
+$L_s$ must contain all integer vectors, and this implies that
+$L_s$ is periodic with period 1 along each of the $s$ coordinates.
+The approximation of $\mu$ by $Q_n$ with the point set
+$P_n = L_s \cap [0,1)^s$ is called a \emph{lattice rule}
+\cite{mKOR59a,vSLO94a}. The value of $n$ is the number of points
+of the lattice that are in the unit hypercube $[0,1)^s$.
+
+Let $\bV$ be the matrix whose rows are the basis vectors
+$\bv_1,\cdots,\bv_s$ and $\bV^{-1}$ its inverse.
+One has $\ZZ^s\subseteq L_s$ if and only if
+all entries of $\bV^{-1}$ are integer.
+When this holds, $n = \det(\bV^{-1})$
+and all entries of $\bV$ are multiples of $1/n$.
+
+The \emph{rank} of the lattice is the smallest $r$ such that one can
+find a basis of the form $\bv_1,\dots, \bv_r,\be_{r+1},\cdots,\be_s$,
+where $\be_j$ is the $j$th unit vector in $s$ dimensions.
+In particular, a lattice rule of \emph{rank 1} has a basis of the form
+$\bv_1 = (a_1, \dots, a_{s})/n$ and $\bv_j = \be_j$ for $j>1$,
+where $a_j \in\ZZ_n$ for each $j$.
+It is a \emph{Korobov} rule if $\bv_1$ has the special
+form $\bv_1 = (1,\; a,\; a^2 \mod n,\; \dots,\; a^{s-1} \mod n)/n$
+for some $a\in\ZZ_n$.
+The point set $P_n$ of a Korobov lattice rule can also be written as
+$P_n = \{(x_1,\dots,x_s)/n$ such that $x_1\in\ZZ_n$ and
+$x_j = a x_{j-1} \mod n$ for all $j > 1\}$. This is the set of all
+vectors of successive values produced by a linear congruential generator
+(LCG) with modulus $n$ and multiplier $a$, from all possible initial
+states, including 0. In this case, the points are easy to enumerate
+by using the recurrence.
+
+
+%%%%%%%%%%%%%%%%
+\subsection*{Cycle-based point sets}
+
+Certain types of point sets are defined pretty much like random number
+generators: choose a finite state space $\cS$, a transition function
+$f : \cS\to\cS$, an output function $g : \cS\to [0,1)$, and define
+\eq
+ P_n = \{\bu = (u_0,u_1,\dots) : s_0\in\cS,\ s_j = f(s_{j-1}),
+ \mbox{ and } u_j = g(s_j) \mbox{ for all } j\}.
+\endeq
+This is the set of all vectors of successive output values produced
+by the recurrence defined by $f$ and the output function $g$, from all
+possible initial states.
+The value of $n$ is the cardinality of $\cS$ and the dimension $s$
+is infinite. We could also have $n = \infty$ (an infinite sequence)
+if $\cS$ is infinite but denumerable and ordered (so we know in which
+order to enumerate the points).
+
+Let us assume that $n$ is finite and that for each $s_0\in\cS$,
+the recurrence $s_j = f(s_{j-1})$ is \emph{purely periodic}, i.e.,
+there is always an integer $j$ such that $s_j = s_0$.
+The smallest such $j$, called the \emph{period length}, depends in
+general on $s_0$. Thus, the state space $\cS$ is partitioned into a
+finite number of \emph{cycles}. The successive coordinates of any point
+$\bu\in P_n$ are periodic with period length equal to the length of the
+cycle that contains $s_0$ (and the following $s_j$'s).
+
+One way of implementing such a point set while avoiding to recompute
+$f$ and $g$ each time a coordinate is needed is to store explicitly
+all the cycles of the recurrence, in the form of a \emph{list of cycles}.
+We can store either the successive $u_j$'s directly, or the successive
+$s_j$'s, over each cycle.
+The class \externalclass{umontreal.iro.lecuyer.hups}{CycleBasedPointSet}
+ provides the framework for doing that.
+
+For example, a Korobov lattice point set is defined via the recurrence
+$x_j = a x_{j-1} \mod n$ and output function $u_j = x_j/n$.
+If $n$ is prime and $a$ is a primitive element modulo $n$, then there
+are two cycles: one of period 1 that contains only 0, and
+the other of period $n-1$.
+For more general $n$ and $a$, there will be more cycles.
+The class \externalclass{umontreal.iro.lecuyer.hups}{LCGPointSet}
+ constructs this type of point set and
+stores explicitly the successive values of $u_j$ over the different cycles.
+
+There are cases where $n$ is a power of two, say $n = 2^k$,
+and where the state $s_j$ is represented as a $k$-bit string.
+In that context, it is often more convenient to store the successive
+$s_j$'s instead of the successive $u_j$'s, over the set of cycles
+(e.g., if a random digital shift in base 2 is to be applied to
+randomize the points, it can be performed by applying a bitwise xor
+directly to $s_j$).
+When generating the coordinates, the $s_j$'s can be interpreted as
+$2^k$-bit integers and multiplied by $2^{-k}$ to produce the output.
+This is supported by the class
+ \externalclass{umontreal.iro.lecuyer.hups}{CycleBasedPointSetBase2}.
+Special instances of this class are usually based on
+linear recurrences modulo 2 and they include the Korobov-type
+\emph{polynomial lattice rules}
+\cite{rLEC99a,vLEC99a,rLEC02b,vLEM03a,rPAN04a}.
+
+
+%%%%%%%%%%%%%%%%%%%%
+\subsection*{Randomized quasi-Monte Carlo}
+
+In their original versions, these HUPS are deterministic, and the
+corresponding QMC methods give a \emph{deterministic} integration error
+that is difficult to estimate.
+In \emph{randomized} QMC methods, $P_n$ is randomized, preferably in a
+way that it retains its high uniformity over $[0,1)^s$ when taken as a set,
+while each of its points has the uniform distribution over $[0,1)^s$
+when taken individually. Then, $Q_n$ becomes an unbiased estimator
+of $\mu$, hopefully with smaller variance than the standard MC estimator.
+To estimate the variance and compute a confidence interval on
+$\mu$, one can apply $m$ independent randomizations to the same $P_n$,
+and compute ${\bar X_m}$ and ${S_{m,x}^2}$, the sample mean and sample
+variance of the $m$ corresponding (independent) copies of $Q_n$.
+Then, $E[\bar X_m] = \mu$ and $E[S_{m,x}^2] = \Var[Q_n] = m\Var[\bar X_m]$
+\cite{vLEC00b}.
+
+Two examples of such randomizations are the \emph{random shift modulo 1},
+proposed in \cite{vCRA76a} and implemented in class
+\externalclass{umontreal.iro.lecuyer.hups}{RandShiftedPointSet},
+and the \emph{random digital shift in base $b$}, described and
+implemented in class \externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}.
+These randomizations are also incorporated directly in certain types
+of point sets such as
+\externalclass{umontreal.iro.lecuyer.hups}{CycleBasedPointSet},
+\externalclass{umontreal.iro.lecuyer.hups}{CycleBasedPointSetBase2}, etc.
+
+In the random shift modulo 1, we generate a \emph{single} point $\bu$
+uniformly over $[0,1)^s$ and add it to each point of $P_n$,
+coordinate-wise, modulo 1.
+Since all points of $P_n$ are shifted by the same amount,
+the set retains most of its structure and uniformity.
+
+For the random digital shift in base $b$, we generate again a single
+$\bu = (u_1,\dots,u_s)$ uniformly over $[0,1)^s$,
+write the digital expansion in base $b$ of each of its coordinates,
+say $u_j = \sum_{\ell=1}^\infty d_{j,\ell} b^{-\ell}$,
+then add $d_{j,\ell}$ modulo $b$ to the $\ell$th digit of the
+digital expansion in base $b$ of the $j$th coordinate of each point
+$\bu_i\in P_n$. For $b=2$, the digit-wise addition modulo $b$ becomes
+a bitwise exclusive-or, which is fast to perform on a computer.
+
+An interesting property of the digital shift in base $b$ is that
+if the hypercube $[0,1)^s$ is partitioned into $b^{q_1 + \cdots + q_s}$
+rectangular boxes
+of the same size by partitioning the $j$th axis into $b^{q_j}$ equal
+parts for each $j$, for some integers $q_j \ge 0$
+(such a partition is called a \emph{$\bq$-equidissection in base $b$}
+of the unit hypercube, where $\bq = (q_1,\dots,q_s)$), then the number of
+boxes that contain $m$ points, for each integer $m$, is unchanged by
+the randomization. In particular, if each box contains the same number
+of points of $P_n$ before the randomization, then it also does after the
+randomization.
+In this case, we say that $P_n$ is \emph{$\bq$-equidistributed in base $b$}.
+Several other randomization methods exist and most are adapted to
+special types of point sets; see, e.g.,
+ \externalclass{umontreal.iro.lecuyer.hups}{DigitalNet} and \cite{vOWE03a}.
+
+
+%%%%%%%%%%%%%%%
+\subsection*{Point set implementations and enumeration tools}
+
+Let $\bu_i = (u_{i,0}, u_{i,1}, \dots, u_{i,s-1})$
+be the elements of the point set $P_n$, for $i=0,\dots,n-1$.
+Both the number of points $n$ and the dimension $s$ can be finite
+or infinite. The point set can be viewed as a two-dimensional array
+whose element $(i,j)$ contains $u_{i,j}$, the coordinate $j$ of point $i$.
+In the implementations of typical point sets, the values $u_{i,j}$ are
+not stored explicitly in a two-dimensional array, but pertinent
+information is organized so that the points and their coordinates can be
+generated efficiently. The base class for point sets is the abstract
+class \externalclass{umontreal.iro.lecuyer.hups}{PointSet}.
+
+To enumerate the successive points or the successive coordinates of a
+given point, we use \emph{point set iterators},
+which resemble the iterators defined
+in Java \emph{collections}, except that they loop over bi-dimensional sets.
+Their general behavior is defined in the interface
+ \externalclass{umontreal.iro.lecuyer.hups}{PointSetIterator}.
+Several independent iterators can coexist at any given time for the same
+point set. Each one maintains a current point index and a current
+coordinate index, which are incremented by one when the iterator advances
+to the next point or the next coordinate. Both are initialized to 0.
+Each subclass of \externalclass{umontreal.iro.lecuyer.hups}{PointSet}
+ has its own implementation of
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetIterator} and has
+ a method \texttt{iterator} that
+creates and returns a new point set iterator of the correct type.
+
+An important feature of the
+\externalclass{umontreal.iro.lecuyer.hups}{PointSetIterator} interface is that
+it extends the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+ interface. This means that any
+point set iterator can be used in place of a random stream that is
+supposed to generate i.i.d.{} $U(0,1)$ random variables, anywhere in a
+simulation program. It then becomes very easy to replace the
+(pseudo)random numbers by the coordinates $u_{i,j}$ of a randomized HUPS
+without changing the internal code of the simulation program.
+
+
+%%%%%%%%%%%%%%%
+\subsection*{Transformed point sets and containers}
+
+HUPS are often transformed either deterministically or randomly.
+Deterministic transformations can be applied to improve the uniformity,
+or to eliminate some points or coordinates (i.e., selecting subsets),
+or to concatenate point sets (padding), or to take an antithetic version
+of a point set, etc.
+Random transformations are used for randomized QMC.
+They are also useful when the \emph{average} of a uniformity measure
+of interest over the outcomes of a certain type of randomization is
+much better than the worst case and may be better than the uniformity
+measure of the original point set.
+When a point set is transformed, we often want to keep the original
+as well, and we may want to apply different types of transformations
+or different independent randomizations to the same point set.
+
+This can be achieved via \emph{container} point sets, which are defined
+in terms of another point set to which they keep a reference
+and apply certain transformations.
+\externalclass{umontreal.iro.lecuyer.hups}{ContainerPointSet} is the base
+ class for such containers.
+One example is \externalclass{umontreal.iro.lecuyer.hups}{RandShiftedPointSet},
+ which applies a random shift
+modulo 1 to the point set that it contains.
+Of course, the contained point set can be a container itself and this
+can be done recursively, but too many levels of recursiveness may impair
+the performance (speed).
+
+% CachedPointSet...
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection*{Examples}
+
+ To be done...
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{comment}
+\subsection*{Steps for performing Quasi-Monte Carlo simulation}
+
+\begin{enumerate}
+\item Decide which type of point set will be used and create it
+ by constructing a subclass of
+ \externalclass{umontreal.iro.lecuyer.hups}{PointSet}.
+\item Get an iterator for the point set and use it as an ordinary
+ random number stream. It can be used to create non-uniform
+ random number generators as well, although only inversion
+ should be used.
+\item For each of the $R$ replications of the simulation, do
+ the following to compute the statistic $x_r$, for $r=0,\dots,R-1$.
+\begin{enumerate}
+\item Randomize the point set using a uniform x1random number stream.
+\item Perform $n$ replications of the simulation, each with a
+ different point in the point set. For each replication, one should
+obtain the result $x_{r,i}$, where $i=0,\dots,n-1$.
+
+Note: All the points from a finite point set should be used to
+fully take advantage of its low discrepancy.
+\item Compute the statistic
+\[x_r=\latex{\frac1n}\html{(1/n)}\sum_{i=0}^{n-1} x_{r,i}.\]
+\item Reset the point set iterator for the next replication.
+\end{enumerate}
+\item The values $x_0,\dots,x_{r-1}$ can be used as statistical
+ observations to compute the $\bar x$ estimator, confidence interval, ...
+\end{enumerate}
+
+\subsection*{Representation of a point set}
+
+The (abstract) base class
+\externalclass{umontreal.iro.lecuyer.hups}{PointSet} views
+a \emph{point set} as
+a set of $n$ points in the $t$-dimensional unit hypercube $[0,1)^t$.
+A point set can also be viewed (and stored) as an $n\times t$ matrix
+whose element $(i,j)$ is the $j$th coordinate of the $i$th point,
+for $0\le i < n$ and $0\le j < t$ (both the point and coordinate
+indexes start from zero).
+One can directly use methods of this class, but this is sometimes not
+the most efficient technique for accessing coordinates of a point set
+and it results in two versions of the same simulation application,
+one for Monte Carlo and another one for Quasi-Monte Carlo.
+The class contains a method allowing one to construct a
+\emph{point set iterator} which can traverse the point set more
+efficiently.
+
+The \externalclass{umontreal.iro.lecuyer.hups}{PointSetIterator} contains
+all needed methods to traverse a point set. One can return only one
+coordinate, $t$ coordinates, change the current coordinate and
+current point index, reset the iterator, ... Since the class
+implements \externalclass{umontreal.iro.lecuyer.rng}{RandomStream},
+it can be used exactly as a uniform random number generator.
+This allows one to use the same application logic for MC and QMC
+simulation; only the initialization part will depend on the type of simulation.
+Every point set implements its own specialized iterator, allowing
+an efficient access to the coordinates.
+
+\subsection*{Supported types of point sets}
+
+The \texttt{hups} package supports several types of point sets implemented
+in different classes extending
+\externalclass{umontreal.iro.lecuyer.hups}{PointSet}.
+The \externalclass{umontreal.iro.lecuyer.hups}{CycleBasedPointSet}
+class provides the needed basis for implementing cycle-based point sets.
+Each point is obtained by an initial value which lies in a given cycle.
+The coordinates are obtained by visiting all the points of the given cycle
+periodically, so the point set has an infinite dimension.
+The \externalclass{umontreal.iro.lecuyer.hups}{LCGPointSet} is an
+implementation of a cycle-based point set whose cycles
+are generated using a small LCG. Such a point set
+corresponds to a Korobov lattice rule.
+
+Generic rank 1 lattices rules are implemented in the class
+\externalclass{umontreal.iro.lecuyer.hups}{Rank1Lattice}.
+Korobov lattice sequences are implemented in the class
+\externalclass{umontreal.iro.lecuyer.hups}{KorobovLatticeSequence}.
+
+Two types of digital nets are supported, namely
+base-2 and base-$b$. They are implemented in separate
+base class because some optimizations can be done
+for the special case of base~2.
+The \externalclass{umontreal.iro.lecuyer.hups}{DigitalNetBase2} class,
+which implements digital nets in base 2, provides all required
+facilities for computing points from such a digital net.
+It also supports scrambling as
+randomization. A digital net implementation needs
+to provide the adequate generating matrices to the base class.
+Sobol, Niederreiter and Niederreiter-Xing sequences are
+supported in base~2.
+
+The class \externalclass{umontreal.iro.lecuyer.hups}{DigitalNet}
+provides the required facilities for implementing digital
+nets in an arbitrary base $b$. For now, the only supported digital net
+in base $b > 2$ is the Faure sequence.
+
+Some supported point sets are nor lattice rules, nor digital nets.
+The Halton sequence and Hammersley net are ancestors of
+linear digital nets and sequences and they are implemented in
+ordinary and scrambled forms.
+
+\subsection*{Randomization and container point sets}
+
+Randomization can be performed in two ways. The interface
+\externalclass{umontreal.iro.lecuyer.hups}{Randomization} is used
+to specify randomizations transforming the structure of the point set,
+for example scrambling in the case of digital nets. The class
+\externalclass{umontreal.iro.lecuyer.hups}{PointSet} contains
+an internal list of randomizations that will be applied sequentially
+when calling the method
+\externalmethod{umontreal.iro.lecuyer.hups}{PointSet}{randomize}{}.
+The method \externalmethod{umontreal.iro.lecuyer.hups}{PointSet}{unrandomize}{}
+restores the point set to its original, deterministic, structure.
+Other randomizations act somewhat like a filter and are implemented as
+container point sets. For example, the class
+\externalclass{umontreal.iro.lecuyer.hups}{RandShiftedPointSet}
+represents a point set whose coordinates are shifted modulo 1 with a random
+factor. This shift is applied after the contained point set generates
+its coordinates and it can be applied to any point set.
+% The class \externalclass{umontreal.iro.lecuyer.hups}{RandXoredPointSet}
+% is also a container class that performs a XOR randomization
+% on a child point set.
+
+The package uses container point set for other purposes than
+randomization.
+The class \externalclass{umontreal.iro.lecuyer.hups}{CachedPointSet}
+can be used to store the coordinates of a point set. It is then stored
+internally as a matrix and can be accessed very efficiently.
+The \externalclass{umontreal.iro.lecuyer.hups}{SubsetOfPointSet}
+allows one to constrain a point set's size or dimension, for example
+to get the dimension of a cycle-based point set finite.
+The class \externalclass{umontreal.iro.lecuyer.hups}{PaddedPointSet}
+gathers some coordinates of several point sets to construct
+a padded point set.
+The class \externalclass{umontreal.iro.lecuyer.hups}{AntitheticPointSet}
+allows one to switch on or off the antithetic coordinates generation.
+
+Such container point sets implement their own iterators that use
+the iterators of the contained point sets to access the points almost
+as efficiently as if the contained point set iterators were used directly.
+
+\end{comment}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDist.java b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDist.java
new file mode 100644
index 0000000..412d125
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDist.java
@@ -0,0 +1,359 @@
+
+
+/*
+ * Class: AndersonDarlingDist
+ * Description: Anderson-Darling distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * <EM>Anderson-Darling</EM> distribution (see).
+ * Given a sample of <SPAN CLASS="MATH"><I>n</I></SPAN> independent uniforms <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> over <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ * the <EM>Anderson-Darling</EM> statistic <SPAN CLASS="MATH"><I>A</I><SUB>n</SUB><SUP>2</SUP></SPAN> is defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>A</I><SUB>n</SUB><SUP>2</SUP> = - <I>n</I> - 1/<I>n</I>∑<SUB>j=1</SUB><SUP>n</SUP>{(2<I>j</I> - 1)ln(<I>U</I><SUB>(j)</SUB>) + (2<I>n</I> + 1 - 2<I>j</I>)ln(1 - <I>U</I><SUB>(j)</SUB>)},
+ * </DIV><P></P>
+ * where the <SPAN CLASS="MATH"><I>U</I><SUB>(j)</SUB></SPAN> are the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> sorted in increasing order. The
+ * distribution function (the cumulative probabilities)
+ * is defined as
+ * <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>) = <I>P</I>[<I>A</I><SUB>n</SUB><SUP>2</SUP> <= <I>x</I>]</SPAN>.
+ *
+ */
+public class AndersonDarlingDist extends ContinuousDistribution {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+
+
+ /**
+ * Constructs an <EM>Anderson-Darling</EM> distribution for a sample of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public AndersonDarlingDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ private static double dclem (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+ protected static double density_N_1 (double x)
+ {
+ final double AD_X0 = 0.38629436111989062;
+ final double AD_X1 = 37.816242111357;
+ if (x <= AD_X0 || x >= AD_X1)
+ return 0.0;
+
+ final double t = Math.exp (-x - 1.0);
+ return 2.0 * t / Math.sqrt (1.0 - 4.0*t);
+ }
+
+ /**
+ * Computes the density of the <EM>Anderson-Darling</EM> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return density_N_1(x);
+
+ if (x >= XBIG || x <= 0.0)
+ return 0.0;
+ final double EPS = 1.0 / 64.0;
+ final double D1 = dclem(n, x, EPS);
+ final double D2 = dclem(n, x, 2.0 * EPS);
+ double res = D1 + (D1 - D2) / 3.0;
+ return res >= 0. ? res : 0.;
+ }
+
+ protected static double cdf_N_1 (double x)
+ {
+ // The Anderson-Darling distribution for N = 1
+ final double AD_X0 = 0.38629436111989062;
+ final double AD_X1 = 37.816242111357;
+
+ if (x <= AD_X0)
+ return 0.0;
+ if (x >= AD_X1)
+ return 1.0;
+ return Math.sqrt (1.0 - 4.0 * Math.exp (-x - 1.0));
+ }
+
+ private static double ADf (double z, int j)
+ { // called by ADinf(); see article.
+ final double T = (4.0 * j + 1.0) * (4.0 * j + 1.0) * 1.23370055013617 / z;
+ if (T > 150.)
+ return 0.;
+
+ double f, fnew, a, b, c, r;
+ int i;
+ a = 2.22144146907918 * Math.exp (-T) / Math.sqrt (T);
+ // initialization requires cPhi
+ // if you have erfc(), replace 2*cPhi(sqrt(2*t)) with erfc(sqrt(t))
+ b = 3.93740248643060 * 2. * NormalDistQuick.barF01 (Math.sqrt (2 * T));
+ r = z * .125;
+ f = a + b * r;
+ for (i = 1; i < 200; i++) {
+ c = ((i - .5 - T) * b + T * a) / i;
+ a = b;
+ b = c;
+ r *= z / (8 * i + 8);
+ if (Math.abs (r) < 1e-40 || Math.abs (c) < 1.e-40)
+ return f;
+ fnew = f + c * r;
+ if (f == fnew)
+ return f;
+ f = fnew;
+ }
+ return f;
+ }
+
+ private static double ADinf (double z)
+ {
+ if (z < .01)
+ return 0.; // avoids exponent limits; ADinf(.01)=.528e-52
+ int j;
+ double ad, adnew, r;
+ r = 1. / z;
+ ad = r * ADf (z, 0);
+ for (j = 1; j < 100; j++) {
+ r *= (.5 - j) / j;
+ adnew = ad + (4 * j + 1) * r * ADf (z, j);
+ if (ad == adnew) {
+ return ad;
+ }
+ ad = adnew;
+ }
+ return ad;
+ }
+
+ private static double adinf (double z)
+ {
+ if (z < 2.)
+ return Math.exp (-1.2337141 / z) / Math.sqrt (z) * (2.00012 + (.247105 -
+ (.0649821 - (.0347962 - (.011672 -
+ .00168691 * z) * z) * z) * z) * z);
+ // max |error| < .000002 for z<2, (p=.90816...)
+ return
+ Math.exp (-Math.exp (1.0776 - (2.30695 - (.43424 - (.082433 -
+ (.008056 - .0003146 * z) * z) * z) * z) * z));
+ // max |error|<.0000008 for 4<z<infinity
+ }
+
+ private static double AD (int n, double z, boolean isFastADinf)
+ {
+ double v, x;
+ /* If isFastADinf is true, use the fast approximation adinf (z),
+ if it is false, use the more exact ADinf (z) */
+ if (isFastADinf)
+ x = adinf (z);
+ else
+ x = ADinf (z);
+
+ // now x=adinf(z). Next, get v=errfix(n,x) and return x+v;
+ if (x > .8) {
+ v = (-130.2137 + (745.2337 - (1705.091 - (1950.646 - (1116.360 -
+ 255.7844 * x) * x) * x) * x) * x) / n;
+ return x + v;
+ }
+ final double C = .01265 + .1757 / n;
+ if (x < C) {
+ v = x / C;
+ v = Math.sqrt (v) * (1. - v) * (49 * v - 102);
+ return x + v * (.0037 / (n * n) + .00078 / n + .00006) / n;
+ }
+ v = (x - C) / (.8 - C);
+ v = -.00022633 + (6.54034 - (14.6538 - (14.458 - (8.259 -
+ 1.91864 * v) * v) * v) * v) * v;
+ return x + v * (.04213 + .01365 / n) / n;
+ }
+
+
+ /**
+ * Computes the <EM>Anderson-Darling</EM> distribution function <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>)</SPAN>, with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>,
+ * using Marsaglia's and al. algorithm. First the asymptotic
+ * distribution for
+ * <SPAN CLASS="MATH"><I>n</I> -> ∞</SPAN> is computed.
+ * Then an empirical correction obtained by simulation is added for finite <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double cdf (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (x <= 0)
+ return 0.0;
+ if (x >= XBIG)
+ return 1.0;
+ if (1 == n)
+ return cdf_N_1 (x);
+ final double RES = AD (n, x, true);
+ if (RES <= 0.0)
+ return 0.0;
+ return RES;
+ }
+
+
+ protected static double barF_N_1 (double x)
+ {
+ if (x <= 3.8629436111989E-1)
+ return 1.0;
+ if (x >= XBIGM)
+ return 0.0;
+
+ double q;
+ if (x < 6.0) {
+ q = 1.0 - 4.0 * Math.exp(-x - 1.0);
+ return 1.0 - Math.sqrt (q);
+ }
+ q = 4.0 * Math.exp(-x - 1.0);
+ return 0.5*q*(1.0 + 0.25*q*(1.0 + 0.5*q*(1.0 + 0.125*q*(5.0 + 3.5*q))));
+ }
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)<SUB>n</SUB>(<I>x</I>)</SPAN>
+ * with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return barF_N_1 (x);
+ return 1.0 - cdf (n, x);
+ }
+
+ protected static double inverse_N_1 (double u)
+ {
+ final double AD_X0 = 0.38629436111989062;
+ if (u <= 0.0)
+ return AD_X0;
+ final double AD_X1 = 37.816242111357;
+ if (u >= 1.0)
+ return AD_X1;
+ return AD_X0 - Math.log1p (-u*u);
+ }
+
+ /**
+ * Computes the inverse
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUB>n</SUB><SUP>-1</SUP>(<I>u</I>)</SPAN> of the
+ * <EM>Anderson-Darling</EM> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (n == 1)
+ return inverse_N_1 (u);
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 50.0, f, 1e-10);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public void setN (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n < 1");
+ this.n = n;
+ if (1 == n) {
+ supportA = 0.38629436111989062;
+ supportB = 37.816242111357;
+ } else {
+ supportA = 0.0;
+ supportB = 1000.0;
+ }
+ }
+
+
+ /**
+ * Return an array containing the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDist.tex b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDist.tex
new file mode 100644
index 0000000..c40c132
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDist.tex
@@ -0,0 +1,380 @@
+\defmodule {AndersonDarlingDist}
+\newcommand{\ad}{{\em Anderson-Darling\/}}
+
+
+Extends the class \class{ContinuousDistribution} for the
+\ad{} distribution (see \cite{tAND52a,tLEW61a,tMAR04a,tSTE86b}).
+Given a sample of $n$ independent uniforms $U_i$ over $(0,1)$,
+the \ad{} statistic $A_n^2$ is defined by
+\begin{latexonly}%
+\begin {eqnarray*}
+ A_n^2 &=& -n -\frac{1}{n} \sum_{j=1}^n \left\{ (2j-1)\ln(U_{(j)})
+ + (2n+1-2j) \ln(1-U_{(j)}) \right\}, \eqlabel {eq:Andar}
+\end {eqnarray*}
+\end{latexonly}%
+\begin{htmlonly}
+\eq
+ A_n^2 = -n -{1 / n} \sum_{j=1}^n \left\{ (2j-1)\ln(U_{(j)})
+ + (2n+1-2j) \ln(1-U_{(j)}) \right\},
+\endeq
+\end{htmlonly}%
+where the $U_{(j)}$ are the $U_i$ sorted in increasing order. The
+distribution function (the cumulative probabilities)
+ is defined as $F_n(x) = P[A_n^2 \le x]$.
+
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: AndersonDarlingDist
+ * Description: Anderson-Darling distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class AndersonDarlingDist extends ContinuousDistribution\begin{hide} {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public AndersonDarlingDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs an \ad{} distribution for a sample of size $n$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ private static double dclem (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+ protected static double density_N_1 (double x)
+ {
+ final double AD_X0 = 0.38629436111989062;
+ final double AD_X1 = 37.816242111357;
+ if (x <= AD_X0 || x >= AD_X1)
+ return 0.0;
+
+ final double t = Math.exp (-x - 1.0);
+ return 2.0 * t / Math.sqrt (1.0 - 4.0*t);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return density_N_1(x);
+
+ if (x >= XBIG || x <= 0.0)
+ return 0.0;
+ final double EPS = 1.0 / 64.0;
+ final double D1 = dclem(n, x, EPS);
+ final double D2 = dclem(n, x, 2.0 * EPS);
+ double res = D1 + (D1 - D2) / 3.0;
+ return res >= 0. ? res : 0.;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density of the \ad{} distribution with parameter $n$.
+\end{tabb}
+\begin{code}\begin{hide}
+ protected static double cdf_N_1 (double x)
+ {
+ // The Anderson-Darling distribution for N = 1
+ final double AD_X0 = 0.38629436111989062;
+ final double AD_X1 = 37.816242111357;
+
+ if (x <= AD_X0)
+ return 0.0;
+ if (x >= AD_X1)
+ return 1.0;
+ return Math.sqrt (1.0 - 4.0 * Math.exp (-x - 1.0));
+ }
+
+ private static double ADf (double z, int j)
+ { // called by ADinf(); see article.
+ final double T = (4.0 * j + 1.0) * (4.0 * j + 1.0) * 1.23370055013617 / z;
+ if (T > 150.)
+ return 0.;
+
+ double f, fnew, a, b, c, r;
+ int i;
+ a = 2.22144146907918 * Math.exp (-T) / Math.sqrt (T);
+ // initialization requires cPhi
+ // if you have erfc(), replace 2*cPhi(sqrt(2*t)) with erfc(sqrt(t))
+ b = 3.93740248643060 * 2. * NormalDistQuick.barF01 (Math.sqrt (2 * T));
+ r = z * .125;
+ f = a + b * r;
+ for (i = 1; i < 200; i++) {
+ c = ((i - .5 - T) * b + T * a) / i;
+ a = b;
+ b = c;
+ r *= z / (8 * i + 8);
+ if (Math.abs (r) < 1e-40 || Math.abs (c) < 1.e-40)
+ return f;
+ fnew = f + c * r;
+ if (f == fnew)
+ return f;
+ f = fnew;
+ }
+ return f;
+ }
+
+ private static double ADinf (double z)
+ {
+ if (z < .01)
+ return 0.; // avoids exponent limits; ADinf(.01)=.528e-52
+ int j;
+ double ad, adnew, r;
+ r = 1. / z;
+ ad = r * ADf (z, 0);
+ for (j = 1; j < 100; j++) {
+ r *= (.5 - j) / j;
+ adnew = ad + (4 * j + 1) * r * ADf (z, j);
+ if (ad == adnew) {
+ return ad;
+ }
+ ad = adnew;
+ }
+ return ad;
+ }
+
+ private static double adinf (double z)
+ {
+ if (z < 2.)
+ return Math.exp (-1.2337141 / z) / Math.sqrt (z) * (2.00012 + (.247105 -
+ (.0649821 - (.0347962 - (.011672 -
+ .00168691 * z) * z) * z) * z) * z);
+ // max |error| < .000002 for z<2, (p=.90816...)
+ return
+ Math.exp (-Math.exp (1.0776 - (2.30695 - (.43424 - (.082433 -
+ (.008056 - .0003146 * z) * z) * z) * z) * z));
+ // max |error|<.0000008 for 4<z<infinity
+ }
+
+ private static double AD (int n, double z, boolean isFastADinf)
+ {
+ double v, x;
+ /* If isFastADinf is true, use the fast approximation adinf (z),
+ if it is false, use the more exact ADinf (z) */
+ if (isFastADinf)
+ x = adinf (z);
+ else
+ x = ADinf (z);
+
+ // now x=adinf(z). Next, get v=errfix(n,x) and return x+v;
+ if (x > .8) {
+ v = (-130.2137 + (745.2337 - (1705.091 - (1950.646 - (1116.360 -
+ 255.7844 * x) * x) * x) * x) * x) / n;
+ return x + v;
+ }
+ final double C = .01265 + .1757 / n;
+ if (x < C) {
+ v = x / C;
+ v = Math.sqrt (v) * (1. - v) * (49 * v - 102);
+ return x + v * (.0037 / (n * n) + .00078 / n + .00006) / n;
+ }
+ v = (x - C) / (.8 - C);
+ v = -.00022633 + (6.54034 - (14.6538 - (14.458 - (8.259 -
+ 1.91864 * v) * v) * v) * v) * v;
+ return x + v * (.04213 + .01365 / n) / n;
+ }
+\end{hide}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (x <= 0)
+ return 0.0;
+ if (x >= XBIG)
+ return 1.0;
+ if (1 == n)
+ return cdf_N_1 (x);
+ final double RES = AD (n, x, true);
+ if (RES <= 0.0)
+ return 0.0;
+ return RES;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the \ad{} distribution function $F_n(x)$, with parameter $n$,
+ using Marsaglia's and al. algorithm \cite{tMAR04a}. First the asymptotic
+ distribution for $n\to\infty$ is computed.
+ % to within 6-digit accuracy according to the authors.
+ Then an empirical correction obtained by simulation is added for finite $n$.
+% For $n=1$, the method returns the exact value.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ protected static double barF_N_1 (double x)
+ {
+ if (x <= 3.8629436111989E-1)
+ return 1.0;
+ if (x >= XBIGM)
+ return 0.0;
+
+ double q;
+ if (x < 6.0) {
+ q = 1.0 - 4.0 * Math.exp(-x - 1.0);
+ return 1.0 - Math.sqrt (q);
+ }
+ q = 4.0 * Math.exp(-x - 1.0);
+ return 0.5*q*(1.0 + 0.25*q*(1.0 + 0.5*q*(1.0 + 0.125*q*(5.0 + 3.5*q))));
+ }\end{hide}
+
+ public static double barF (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return barF_N_1 (x);
+ return 1.0 - cdf (n, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function $\bar F_n(x)$
+ with parameter $n$.
+\end{tabb}
+\begin{code}\begin{hide}
+ protected static double inverse_N_1 (double u)
+ {
+ final double AD_X0 = 0.38629436111989062;
+ if (u <= 0.0)
+ return AD_X0;
+ final double AD_X1 = 37.816242111357;
+ if (u >= 1.0)
+ return AD_X1;
+ return AD_X0 - Math.log1p (-u*u);
+ }\end{hide}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (n == 1)
+ return inverse_N_1 (u);
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 50.0, f, 1e-10);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse $x = F_n^{-1}(u)$ of the
+ \ad{} distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n < 1");
+ this.n = n;
+ if (1 == n) {
+ supportA = 0.38629436111989062;
+ supportB = 37.816242111357;
+ } else {
+ supportA = 0.0;
+ supportB = 1000.0;
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameter $n$ of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDistQuick.java b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDistQuick.java
new file mode 100644
index 0000000..5bd0c8c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDistQuick.java
@@ -0,0 +1,447 @@
+
+
+/*
+ * Class: AndersonDarlingDistQuick
+ * Description: Anderson-Darling distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link AndersonDarlingDist} for the
+ * distribution (see).
+ * This class implements a version faster and more precise in the tails
+ * than class {@link AndersonDarlingDist}.
+ *
+ */
+public class AndersonDarlingDistQuick extends AndersonDarlingDist {
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+
+ private static double lower_Marsaglia (int n, double x) {
+ // queue inférieure de l'algorithme de Marsaglia pour n = inf
+
+ double p0 = (2.00012 + (.247105 - (.0649821 - (.0347962 -
+ (.011672 - .00168691 * x) * x) * x) * x) * x);
+ p0 *= Math.exp (-1.2337141 / x) / Math.sqrt (x);
+ return p0 >= 0 ? p0 : 0;
+ }
+
+
+ private static double diffcdf (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+
+
+
+ /**
+ * Constructs an distribution for a sample of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public AndersonDarlingDistQuick (int n) {
+ super (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ /**
+ * Computes the density of the distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return density_N_1(x);
+
+ if (x >= XBIG || x <= 0.0)
+ return 0.0;
+ final double EPS = 1.0 / 64.0;
+ final double D1 = diffcdf(n, x, EPS);
+ final double D2 = diffcdf(n, x, 2.0 * EPS);
+ double res = D1 + (D1 - D2) / 3.0;
+ return res >= 0. ? res : 0.;
+ }
+
+ // Tables for the approximation of the Anderson-Darling distribution
+ private static double[] F2AD = new double[103];
+ private static double[] CoAD = new double[103];
+
+ static {
+ F2AD[0] = 0.0;
+ F2AD[1] = 1.7315E-10;
+ F2AD[2] = 2.80781E-5;
+ F2AD[3] = 1.40856E-3;
+ F2AD[4] = 9.58772E-3;
+ F2AD[5] = 2.960552E-2;
+ F2AD[6] = 6.185146E-2;
+ F2AD[7] = 1.0357152E-1;
+ F2AD[8] = 1.5127241E-1;
+ F2AD[9] = 2.0190317E-1;
+ F2AD[10] = 2.5318023E-1;
+ F2AD[11] = 3.0354278E-1;
+ F2AD[12] = 3.5200015E-1;
+ F2AD[13] = 3.9797537E-1;
+ F2AD[14] = 4.4117692E-1;
+ F2AD[15] = 4.8150305E-1;
+ F2AD[16] = 5.1897375E-1;
+ F2AD[17] = 5.5368396E-1;
+ F2AD[18] = 5.8577199E-1;
+ F2AD[19] = 6.1539864E-1;
+ F2AD[20] = 6.4273362E-1;
+ F2AD[21] = 6.6794694E-1;
+ F2AD[22] = 6.9120359E-1;
+ F2AD[23] = 7.126605E-1;
+ F2AD[24] = 7.3246483E-1;
+ F2AD[25] = 7.507533E-1;
+ F2AD[26] = 7.6765207E-1;
+ F2AD[27] = 7.8327703E-1;
+ F2AD[28] = 7.9773426E-1;
+ F2AD[29] = 8.1112067E-1;
+ F2AD[30] = 8.2352466E-1;
+ F2AD[31] = 8.3502676E-1;
+ F2AD[32] = 8.4570037E-1;
+ F2AD[33] = 8.5561231E-1;
+ F2AD[34] = 8.6482346E-1;
+ F2AD[35] = 8.7338931E-1;
+ F2AD[36] = 8.8136046E-1;
+ F2AD[37] = 8.8878306E-1;
+ F2AD[38] = 8.9569925E-1;
+ F2AD[39] = 9.0214757E-1;
+ F2AD[40] = 9.081653E-1;
+ F2AD[41] = 9.1378043E-1;
+ F2AD[42] = 9.1902284E-1;
+ F2AD[43] = 9.2392345E-1;
+ F2AD[44] = 9.2850516E-1;
+ F2AD[45] = 9.3279084E-1;
+ F2AD[46] = 9.3680149E-1;
+ F2AD[47] = 9.4055647E-1;
+ F2AD[48] = 9.440736E-1;
+ F2AD[49] = 9.4736933E-1;
+ F2AD[50] = 9.5045883E-1;
+ F2AD[51] = 9.5335611E-1;
+ F2AD[52] = 9.5607414E-1;
+ F2AD[53] = 9.586249E-1;
+ F2AD[54] = 9.6101951E-1;
+ F2AD[55] = 9.6326825E-1;
+ F2AD[56] = 9.6538067E-1;
+ F2AD[57] = 9.6736563E-1;
+ F2AD[58] = 9.6923135E-1;
+ F2AD[59] = 9.7098548E-1;
+ F2AD[60] = 9.7263514E-1;
+ F2AD[61] = 9.7418694E-1;
+ F2AD[62] = 9.7564704E-1;
+ F2AD[63] = 9.7702119E-1;
+ F2AD[64] = 9.7831473E-1;
+ F2AD[65] = 9.7953267E-1;
+ F2AD[66] = 9.8067966E-1;
+ F2AD[67] = 9.8176005E-1;
+ F2AD[68] = 9.827779E-1;
+ F2AD[69] = 9.8373702E-1;
+ F2AD[70] = 9.8464096E-1;
+ F2AD[71] = 9.8549304E-1;
+ F2AD[72] = 9.8629637E-1;
+ F2AD[73] = 9.8705386E-1;
+ F2AD[74] = 9.8776824E-1;
+ F2AD[75] = 9.8844206E-1;
+ F2AD[76] = 9.8907773E-1;
+ F2AD[77] = 9.8967747E-1;
+ F2AD[78] = 9.9024341E-1;
+ F2AD[79] = 9.9077752E-1;
+ F2AD[80] = 9.9128164E-1;
+ F2AD[81] = 9.9175753E-1;
+ F2AD[82] = 9.9220682E-1;
+ F2AD[83] = 9.9263105E-1;
+ F2AD[84] = 9.9303165E-1;
+ F2AD[85] = 9.9340998E-1;
+ F2AD[86] = 9.9376733E-1;
+ F2AD[87] = 9.9410488E-1;
+ F2AD[88] = 9.9442377E-1;
+ F2AD[89] = 9.9472506E-1;
+ F2AD[90] = 9.9500974E-1;
+ F2AD[91] = 9.9527876E-1;
+ F2AD[92] = 9.95533E-1;
+ F2AD[93] = 9.9577329E-1;
+ F2AD[94] = 9.9600042E-1;
+ F2AD[95] = 9.9621513E-1;
+ F2AD[96] = 9.964181E-1;
+ F2AD[97] = 0.99661;
+ F2AD[98] = 9.9679145E-1;
+ F2AD[99] = 9.9696303E-1;
+ F2AD[100] = 9.9712528E-1;
+ F2AD[101] = 9.9727872E-1;
+ F2AD[102] = 9.9742384E-1;
+
+ CoAD[0] = 0.0;
+ CoAD[1] = 0.0;
+ CoAD[2] = 0.0;
+ CoAD[3] = 0.0;
+ CoAD[4] = 0.0;
+ CoAD[5] = -1.87E-3;
+ CoAD[6] = 0.00898;
+ CoAD[7] = 0.0209;
+ CoAD[8] = 0.03087;
+ CoAD[9] = 0.0377;
+ CoAD[10] = 0.0414;
+ CoAD[11] = 0.04386;
+ CoAD[12] = 0.043;
+ CoAD[13] = 0.0419;
+ CoAD[14] = 0.0403;
+ CoAD[15] = 0.038;
+ CoAD[16] = 3.54804E-2;
+ CoAD[17] = 0.032;
+ CoAD[18] = 0.0293;
+ CoAD[19] = 2.61949E-2;
+ CoAD[20] = 0.0228;
+ CoAD[21] = 0.0192;
+ CoAD[22] = 1.59865E-2;
+ CoAD[23] = 0.0129;
+ CoAD[24] = 0.0107;
+ CoAD[25] = 8.2464E-3;
+ CoAD[26] = 0.00611;
+ CoAD[27] = 0.00363;
+ CoAD[28] = 1.32272E-3;
+ CoAD[29] = -5.87E-4;
+ CoAD[30] = -2.75E-3;
+ CoAD[31] = -3.95248E-3;
+ CoAD[32] = -5.34E-3;
+ CoAD[33] = -6.892E-3;
+ CoAD[34] = -8.10208E-3;
+ CoAD[35] = -8.93E-3;
+ CoAD[36] = -9.552E-3;
+ CoAD[37] = -1.04605E-2;
+ CoAD[38] = -0.0112;
+ CoAD[39] = -1.175E-2;
+ CoAD[40] = -1.20216E-2;
+ CoAD[41] = -0.0124;
+ CoAD[42] = -1.253E-2;
+ CoAD[43] = -1.27076E-2;
+ CoAD[44] = -0.0129;
+ CoAD[45] = -1.267E-2;
+ CoAD[46] = -1.22015E-2;
+ CoAD[47] = -0.0122;
+ CoAD[48] = -1.186E-2;
+ CoAD[49] = -1.17218E-2;
+ CoAD[50] = -0.0114;
+ CoAD[51] = -1.113E-2;
+ CoAD[52] = -1.08459E-2;
+ CoAD[53] = -0.0104;
+ CoAD[54] = -9.93E-3;
+ CoAD[55] = -9.5252E-3;
+ CoAD[56] = -9.24E-3;
+ CoAD[57] = -9.16E-3;
+ CoAD[58] = -8.8004E-3;
+ CoAD[59] = -8.63E-3;
+ CoAD[60] = -8.336E-3;
+ CoAD[61] = -8.10512E-3;
+ CoAD[62] = -7.94E-3;
+ CoAD[63] = -7.71E-3;
+ CoAD[64] = -7.55064E-3;
+ CoAD[65] = -7.25E-3;
+ CoAD[66] = -7.11E-3;
+ CoAD[67] = -6.834E-3;
+ CoAD[68] = -0.0065;
+ CoAD[69] = -6.28E-3;
+ CoAD[70] = -6.11008E-3;
+ CoAD[71] = -5.86E-3;
+ CoAD[72] = -5.673E-3;
+ CoAD[73] = -5.35008E-3;
+ CoAD[74] = -5.11E-3;
+ CoAD[75] = -4.786E-3;
+ CoAD[76] = -4.59144E-3;
+ CoAD[77] = -4.38E-3;
+ CoAD[78] = -4.15E-3;
+ CoAD[79] = -4.07696E-3;
+ CoAD[80] = -3.93E-3;
+ CoAD[81] = -3.83E-3;
+ CoAD[82] = -3.74656E-3;
+ CoAD[83] = -3.49E-3;
+ CoAD[84] = -3.33E-3;
+ CoAD[85] = -3.20064E-3;
+ CoAD[86] = -3.09E-3;
+ CoAD[87] = -2.93E-3;
+ CoAD[88] = -2.78136E-3;
+ CoAD[89] = -2.72E-3;
+ CoAD[90] = -2.66E-3;
+ CoAD[91] = -2.56208E-3;
+ CoAD[92] = -2.43E-3;
+ CoAD[93] = -2.28E-3;
+ CoAD[94] = -2.13536E-3;
+ CoAD[95] = -2.083E-3;
+ CoAD[96] = -1.94E-3;
+ CoAD[97] = -1.82E-3;
+ CoAD[98] = -1.77E-3;
+ CoAD[99] = -1.72E-3;
+ CoAD[100] = -1.71104E-3;
+ CoAD[101] = -1.741E-3;
+ CoAD[102] = -0.0016;
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>)</SPAN> at <SPAN CLASS="MATH"><I>x</I></SPAN> for sample size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * For
+ * <SPAN CLASS="MATH">0.2 < <I>x</I> < 5</SPAN>,
+ * the asymptotic distribution
+ * <SPAN CLASS="MATH"><I>F</I><SUB>∞</SUB>(<I>x</I>) = lim<SUB>n -> ∞</SUB><I>F</I><SUB>n</SUB>(<I>x</I>)</SPAN>
+ * was first computed by numerical integration;
+ * then a linear correction <SPAN CLASS="MATH"><I>O</I>(1/<I>n</I>)</SPAN> obtained by simulation was added.
+ * For <SPAN CLASS="MATH">5 < <I>x</I></SPAN>, the Grace-Wood empirical approximation is used. For <SPAN CLASS="MATH"><I>x</I> < 0.2</SPAN>, the Marsaglias' approximation for <SPAN CLASS="MATH"><I>n</I> = ∞</SPAN> is used.
+ *
+ * <P>
+ * For <SPAN CLASS="MATH"><I>n</I> > 6</SPAN>, the method gives at least 3 decimal digits of precision except
+ * for small <SPAN CLASS="MATH"><I>x</I></SPAN>;
+ * for <SPAN CLASS="MATH"><I>n</I> <= 6</SPAN>, it gives at least 2 decimal digits of precision except for small <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ * For <SPAN CLASS="MATH"><I>n</I> = 1</SPAN>, the exact formula
+ *
+ * <SPAN CLASS="MATH"><I>F</I><SUB>1</SUB>(<I>x</I>) = (1 - 4e^-x-1)<SUP>1/2</SUP></SPAN>, for
+ * <SPAN CLASS="MATH"><I>x</I> >= ln(4) - 1</SPAN>, is used.
+ *
+ */
+ public static double cdf (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException (" n <= 0");
+ if (1 == n)
+ return cdf_N_1 (x);
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= XBIG)
+ return 1.0;
+ if (x <= 0.2)
+ return lower_Marsaglia (n, x);
+ return 1.0 - barF (n, x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)<SUB>n</SUB>(<I>x</I>)</SPAN>
+ * with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return barF_N_1 (x);
+
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= XBIGM)
+ return 0.0;
+
+ double q;
+ if (x > 5.) {
+ // Grace-Wood approx. in upper tail
+ double nd = n;
+ q = (0.23945*Math.pow(nd, -0.9379) - 0.1201*Math.pow(nd, -0.96) -
+ 1.0002816)*x - 1.437*Math.pow(nd, -0.9379) +
+ 1.441*Math.pow(nd, -0.96) - 0.0633101;
+ return Math.pow(x, -0.48897) * Math.exp(q);
+ }
+
+ if (x <= 0.2)
+ return 1.0 - cdf (n, x);
+
+ final double H = 0.05; // the step of the interpolation table
+ final int i = (int) (1 + x / H);
+ double res;
+ q = x/H - i;
+
+ // Newton backwards quadratic interpolation
+ res = (F2AD[i - 2] - 2.0*F2AD[i - 1] + F2AD[i])*q*(q + 1.0)/2.0
+ + (F2AD[i] - F2AD[i - 1])*q + F2AD[i];
+
+ // Empirical correction in 1/n
+ res += (CoAD[i]*(q + 1.0) - CoAD[i - 1]*q)/n;
+
+ res = 1.0 - res;
+ if (res >= 1.0)
+ return 1.0;
+ if (res <= 0.0)
+ return 0.0;
+ return res;
+ }
+
+
+ /**
+ * Computes the inverse
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUB>n</SUB><SUP>-1</SUP>(<I>u</I>)</SPAN> of the
+ * distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (n == 1)
+ return inverse_N_1 (u);
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 50.0, f, 1.0e-5);
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDistQuick.tex b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDistQuick.tex
new file mode 100644
index 0000000..d0b5eb4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/AndersonDarlingDistQuick.tex
@@ -0,0 +1,464 @@
+\defmodule {AndersonDarlingDistQuick}
+
+% \newcommand{\ad}{{\em Anderson-Darling\/}}
+
+
+Extends the class \class{AndersonDarlingDist} for the
+\ad{} distribution (see \cite{tAND52a,tLEW61a,tSTE86b}).
+This class implements a version faster and more precise in the tails
+than class \class{AndersonDarlingDist}.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: AndersonDarlingDistQuick
+ * Description: Anderson-Darling distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class AndersonDarlingDistQuick extends AndersonDarlingDist\begin{hide} {
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+
+ private static double lower_Marsaglia (int n, double x) {
+ // queue inférieure de l'algorithme de Marsaglia pour n = inf
+
+ double p0 = (2.00012 + (.247105 - (.0649821 - (.0347962 -
+ (.011672 - .00168691 * x) * x) * x) * x) * x);
+ p0 *= Math.exp (-1.2337141 / x) / Math.sqrt (x);
+ return p0 >= 0 ? p0 : 0;
+ }
+
+
+ private static double diffcdf (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public AndersonDarlingDistQuick (int n)\begin{hide} {
+ super (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs an \ad{} distribution for a sample of size $n$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return density_N_1(x);
+
+ if (x >= XBIG || x <= 0.0)
+ return 0.0;
+ final double EPS = 1.0 / 64.0;
+ final double D1 = diffcdf(n, x, EPS);
+ final double D2 = diffcdf(n, x, 2.0 * EPS);
+ double res = D1 + (D1 - D2) / 3.0;
+ return res >= 0. ? res : 0.;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density of the \ad{} distribution with parameter $n$.
+\end{tabb}
+\begin{code}\begin{hide}
+ // Tables for the approximation of the Anderson-Darling distribution
+ private static double[] F2AD = new double[103];
+ private static double[] CoAD = new double[103];
+
+ static {
+ F2AD[0] = 0.0;
+ F2AD[1] = 1.7315E-10;
+ F2AD[2] = 2.80781E-5;
+ F2AD[3] = 1.40856E-3;
+ F2AD[4] = 9.58772E-3;
+ F2AD[5] = 2.960552E-2;
+ F2AD[6] = 6.185146E-2;
+ F2AD[7] = 1.0357152E-1;
+ F2AD[8] = 1.5127241E-1;
+ F2AD[9] = 2.0190317E-1;
+ F2AD[10] = 2.5318023E-1;
+ F2AD[11] = 3.0354278E-1;
+ F2AD[12] = 3.5200015E-1;
+ F2AD[13] = 3.9797537E-1;
+ F2AD[14] = 4.4117692E-1;
+ F2AD[15] = 4.8150305E-1;
+ F2AD[16] = 5.1897375E-1;
+ F2AD[17] = 5.5368396E-1;
+ F2AD[18] = 5.8577199E-1;
+ F2AD[19] = 6.1539864E-1;
+ F2AD[20] = 6.4273362E-1;
+ F2AD[21] = 6.6794694E-1;
+ F2AD[22] = 6.9120359E-1;
+ F2AD[23] = 7.126605E-1;
+ F2AD[24] = 7.3246483E-1;
+ F2AD[25] = 7.507533E-1;
+ F2AD[26] = 7.6765207E-1;
+ F2AD[27] = 7.8327703E-1;
+ F2AD[28] = 7.9773426E-1;
+ F2AD[29] = 8.1112067E-1;
+ F2AD[30] = 8.2352466E-1;
+ F2AD[31] = 8.3502676E-1;
+ F2AD[32] = 8.4570037E-1;
+ F2AD[33] = 8.5561231E-1;
+ F2AD[34] = 8.6482346E-1;
+ F2AD[35] = 8.7338931E-1;
+ F2AD[36] = 8.8136046E-1;
+ F2AD[37] = 8.8878306E-1;
+ F2AD[38] = 8.9569925E-1;
+ F2AD[39] = 9.0214757E-1;
+ F2AD[40] = 9.081653E-1;
+ F2AD[41] = 9.1378043E-1;
+ F2AD[42] = 9.1902284E-1;
+ F2AD[43] = 9.2392345E-1;
+ F2AD[44] = 9.2850516E-1;
+ F2AD[45] = 9.3279084E-1;
+ F2AD[46] = 9.3680149E-1;
+ F2AD[47] = 9.4055647E-1;
+ F2AD[48] = 9.440736E-1;
+ F2AD[49] = 9.4736933E-1;
+ F2AD[50] = 9.5045883E-1;
+ F2AD[51] = 9.5335611E-1;
+ F2AD[52] = 9.5607414E-1;
+ F2AD[53] = 9.586249E-1;
+ F2AD[54] = 9.6101951E-1;
+ F2AD[55] = 9.6326825E-1;
+ F2AD[56] = 9.6538067E-1;
+ F2AD[57] = 9.6736563E-1;
+ F2AD[58] = 9.6923135E-1;
+ F2AD[59] = 9.7098548E-1;
+ F2AD[60] = 9.7263514E-1;
+ F2AD[61] = 9.7418694E-1;
+ F2AD[62] = 9.7564704E-1;
+ F2AD[63] = 9.7702119E-1;
+ F2AD[64] = 9.7831473E-1;
+ F2AD[65] = 9.7953267E-1;
+ F2AD[66] = 9.8067966E-1;
+ F2AD[67] = 9.8176005E-1;
+ F2AD[68] = 9.827779E-1;
+ F2AD[69] = 9.8373702E-1;
+ F2AD[70] = 9.8464096E-1;
+ F2AD[71] = 9.8549304E-1;
+ F2AD[72] = 9.8629637E-1;
+ F2AD[73] = 9.8705386E-1;
+ F2AD[74] = 9.8776824E-1;
+ F2AD[75] = 9.8844206E-1;
+ F2AD[76] = 9.8907773E-1;
+ F2AD[77] = 9.8967747E-1;
+ F2AD[78] = 9.9024341E-1;
+ F2AD[79] = 9.9077752E-1;
+ F2AD[80] = 9.9128164E-1;
+ F2AD[81] = 9.9175753E-1;
+ F2AD[82] = 9.9220682E-1;
+ F2AD[83] = 9.9263105E-1;
+ F2AD[84] = 9.9303165E-1;
+ F2AD[85] = 9.9340998E-1;
+ F2AD[86] = 9.9376733E-1;
+ F2AD[87] = 9.9410488E-1;
+ F2AD[88] = 9.9442377E-1;
+ F2AD[89] = 9.9472506E-1;
+ F2AD[90] = 9.9500974E-1;
+ F2AD[91] = 9.9527876E-1;
+ F2AD[92] = 9.95533E-1;
+ F2AD[93] = 9.9577329E-1;
+ F2AD[94] = 9.9600042E-1;
+ F2AD[95] = 9.9621513E-1;
+ F2AD[96] = 9.964181E-1;
+ F2AD[97] = 0.99661;
+ F2AD[98] = 9.9679145E-1;
+ F2AD[99] = 9.9696303E-1;
+ F2AD[100] = 9.9712528E-1;
+ F2AD[101] = 9.9727872E-1;
+ F2AD[102] = 9.9742384E-1;
+
+ CoAD[0] = 0.0;
+ CoAD[1] = 0.0;
+ CoAD[2] = 0.0;
+ CoAD[3] = 0.0;
+ CoAD[4] = 0.0;
+ CoAD[5] = -1.87E-3;
+ CoAD[6] = 0.00898;
+ CoAD[7] = 0.0209;
+ CoAD[8] = 0.03087;
+ CoAD[9] = 0.0377;
+ CoAD[10] = 0.0414;
+ CoAD[11] = 0.04386;
+ CoAD[12] = 0.043;
+ CoAD[13] = 0.0419;
+ CoAD[14] = 0.0403;
+ CoAD[15] = 0.038;
+ CoAD[16] = 3.54804E-2;
+ CoAD[17] = 0.032;
+ CoAD[18] = 0.0293;
+ CoAD[19] = 2.61949E-2;
+ CoAD[20] = 0.0228;
+ CoAD[21] = 0.0192;
+ CoAD[22] = 1.59865E-2;
+ CoAD[23] = 0.0129;
+ CoAD[24] = 0.0107;
+ CoAD[25] = 8.2464E-3;
+ CoAD[26] = 0.00611;
+ CoAD[27] = 0.00363;
+ CoAD[28] = 1.32272E-3;
+ CoAD[29] = -5.87E-4;
+ CoAD[30] = -2.75E-3;
+ CoAD[31] = -3.95248E-3;
+ CoAD[32] = -5.34E-3;
+ CoAD[33] = -6.892E-3;
+ CoAD[34] = -8.10208E-3;
+ CoAD[35] = -8.93E-3;
+ CoAD[36] = -9.552E-3;
+ CoAD[37] = -1.04605E-2;
+ CoAD[38] = -0.0112;
+ CoAD[39] = -1.175E-2;
+ CoAD[40] = -1.20216E-2;
+ CoAD[41] = -0.0124;
+ CoAD[42] = -1.253E-2;
+ CoAD[43] = -1.27076E-2;
+ CoAD[44] = -0.0129;
+ CoAD[45] = -1.267E-2;
+ CoAD[46] = -1.22015E-2;
+ CoAD[47] = -0.0122;
+ CoAD[48] = -1.186E-2;
+ CoAD[49] = -1.17218E-2;
+ CoAD[50] = -0.0114;
+ CoAD[51] = -1.113E-2;
+ CoAD[52] = -1.08459E-2;
+ CoAD[53] = -0.0104;
+ CoAD[54] = -9.93E-3;
+ CoAD[55] = -9.5252E-3;
+ CoAD[56] = -9.24E-3;
+ CoAD[57] = -9.16E-3;
+ CoAD[58] = -8.8004E-3;
+ CoAD[59] = -8.63E-3;
+ CoAD[60] = -8.336E-3;
+ CoAD[61] = -8.10512E-3;
+ CoAD[62] = -7.94E-3;
+ CoAD[63] = -7.71E-3;
+ CoAD[64] = -7.55064E-3;
+ CoAD[65] = -7.25E-3;
+ CoAD[66] = -7.11E-3;
+ CoAD[67] = -6.834E-3;
+ CoAD[68] = -0.0065;
+ CoAD[69] = -6.28E-3;
+ CoAD[70] = -6.11008E-3;
+ CoAD[71] = -5.86E-3;
+ CoAD[72] = -5.673E-3;
+ CoAD[73] = -5.35008E-3;
+ CoAD[74] = -5.11E-3;
+ CoAD[75] = -4.786E-3;
+ CoAD[76] = -4.59144E-3;
+ CoAD[77] = -4.38E-3;
+ CoAD[78] = -4.15E-3;
+ CoAD[79] = -4.07696E-3;
+ CoAD[80] = -3.93E-3;
+ CoAD[81] = -3.83E-3;
+ CoAD[82] = -3.74656E-3;
+ CoAD[83] = -3.49E-3;
+ CoAD[84] = -3.33E-3;
+ CoAD[85] = -3.20064E-3;
+ CoAD[86] = -3.09E-3;
+ CoAD[87] = -2.93E-3;
+ CoAD[88] = -2.78136E-3;
+ CoAD[89] = -2.72E-3;
+ CoAD[90] = -2.66E-3;
+ CoAD[91] = -2.56208E-3;
+ CoAD[92] = -2.43E-3;
+ CoAD[93] = -2.28E-3;
+ CoAD[94] = -2.13536E-3;
+ CoAD[95] = -2.083E-3;
+ CoAD[96] = -1.94E-3;
+ CoAD[97] = -1.82E-3;
+ CoAD[98] = -1.77E-3;
+ CoAD[99] = -1.72E-3;
+ CoAD[100] = -1.71104E-3;
+ CoAD[101] = -1.741E-3;
+ CoAD[102] = -0.0016;
+ }
+\end{hide}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException (" n <= 0");
+ if (1 == n)
+ return cdf_N_1 (x);
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= XBIG)
+ return 1.0;
+ if (x <= 0.2)
+ return lower_Marsaglia (n, x);
+ return 1.0 - barF (n, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the \ad{} distribution function $F_n(x)$ at $x$ for sample size $n$.
+ For $0.2 < x < 5$,
+ the asymptotic distribution $F_\infty(x) = \lim_{n\to\infty} F_n(x)$
+ was first computed by numerical integration;
+ then a linear correction $O(1/n)$ obtained by simulation was added.
+ % The absolute error on $F_n(x)$ is estimated to be
+% less than $0.001$ for $n > 6$.
+% For $n = 2$, 3, 4, 6, it is estimated to be
+% less than 0.04, 0.01, 0.005, 0.002, respectively.
+ For $5 < x$, the Grace-Wood empirical approximation
+ \cite{tGRA12a} is used. %, which gives at least 3 decimal digits of precision.
+ For $x < 0.2$, the Marsaglias' approximation \cite{tMAR04a} for $n=\infty$ is used.
+
+ For $n>6$, the method gives at least 3 decimal digits of precision except
+ for small $x$;
+ for $n \le 6$, it gives at least 2 decimal digits of precision except for small $x$.
+ For $n=1$, the exact formula
+ $F_1(x) = \sqrt{1 - 4e^{-x-1}}$, for $x\ge \ln(4) - 1$, is used.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (n == 1)
+ return barF_N_1 (x);
+
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= XBIGM)
+ return 0.0;
+
+ double q;
+ if (x > 5.) {
+ // Grace-Wood approx. in upper tail
+ double nd = n;
+ q = (0.23945*Math.pow(nd, -0.9379) - 0.1201*Math.pow(nd, -0.96) -
+ 1.0002816)*x - 1.437*Math.pow(nd, -0.9379) +
+ 1.441*Math.pow(nd, -0.96) - 0.0633101;
+ return Math.pow(x, -0.48897) * Math.exp(q);
+ }
+
+ if (x <= 0.2)
+ return 1.0 - cdf (n, x);
+
+ final double H = 0.05; // the step of the interpolation table
+ final int i = (int) (1 + x / H);
+ double res;
+ q = x/H - i;
+
+ // Newton backwards quadratic interpolation
+ res = (F2AD[i - 2] - 2.0*F2AD[i - 1] + F2AD[i])*q*(q + 1.0)/2.0
+ + (F2AD[i] - F2AD[i - 1])*q + F2AD[i];
+
+ // Empirical correction in 1/n
+ res += (CoAD[i]*(q + 1.0) - CoAD[i - 1]*q)/n;
+
+ res = 1.0 - res;
+ if (res >= 1.0)
+ return 1.0;
+ if (res <= 0.0)
+ return 0.0;
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function $\bar F_n(x)$
+ with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (n == 1)
+ return inverse_N_1 (u);
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 50.0, f, 1.0e-5);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse $x = F_n^{-1}(u)$ of the
+ \ad{} distribution with parameter $n$.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/BernoulliDist.java b/source/umontreal/iro/lecuyer/probdist/BernoulliDist.java
new file mode 100644
index 0000000..d5c814b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BernoulliDist.java
@@ -0,0 +1,316 @@
+
+
+/*
+ * Class: BernoulliDist
+ * Description: Bernoulli distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since August 2010
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for the <SPAN CLASS="textit">Bernoulli</SPAN>
+ * distribution with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>, where
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN>.
+ * Its mass function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">1 - <I>p</I>,</TD>
+ * <TD ALIGN="LEFT"> if <I>x</I> = 0;</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT"><I>p</I>,</TD>
+ * <TD ALIGN="LEFT"> if <I>x</I> = 1;</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">0,</TD>
+ * <TD ALIGN="LEFT"> otherwise. </TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ * Its distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">0,</TD>
+ * <TD ALIGN="LEFT"> if <I>x</I> < 0;</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">1 - <I>p</I>,</TD>
+ * <TD ALIGN="LEFT"> if 0 <= <I>x</I> < 1;</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">1,</TD>
+ * <TD ALIGN="LEFT"> if <I>x</I> >= 1.</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ *
+ */
+public class BernoulliDist extends DiscreteDistributionInt {
+ private double p;
+ private double q;
+
+
+
+ /**
+ * Creates a Bernoulli distribution object.
+ *
+ */
+ public BernoulliDist (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ this.p = p;
+ q = 1 - p;
+ supportA = 0;
+ supportB = 1;
+ }
+
+ public double prob (int x) {
+ if (1 == x) return p;
+ if (0 == x) return q;
+ return 0.0;
+ }
+
+ public double cdf (int x) {
+ if (x < 0) return 0.0;
+ if (x < 1) return q;
+ return 1.0;
+ }
+
+ public double barF (int x) {
+ if (x > 1) return 0.0;
+ if (x > 0) return p;
+ return 1.0;
+ }
+
+ public int inverseFInt (double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u > q) return 1;
+ return 0;
+ }
+
+ public double getMean() {
+ return BernoulliDist.getMean (p);
+ }
+
+ public double getVariance() {
+ return BernoulliDist.getVariance (p);
+ }
+
+ public double getStandardDeviation() {
+ return BernoulliDist.getStandardDeviation (p);
+ }
+
+
+
+ /**
+ * Returns the Bernoulli probability <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN> with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>
+ * (see eq.).
+ *
+ */
+ public static double prob (double p, int x) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (1 == x) return p;
+ if (0 == x) return 1.0 - p;
+ return 0.0;
+ }
+
+
+ /**
+ * Returns the Bernoulli distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>
+ * with parameter <SPAN CLASS="MATH"><I>p</I></SPAN> (see eq.).
+ *
+ */
+ public static double cdf (double p, int x) {
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x < 0) return 0.0;
+ if (x < 1) return 1.0 - p;
+ return 1.0;
+ }
+
+
+ /**
+ * Returns the complementary Bernoulli distribution
+ * function
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN> with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ */
+ public static double barF (double p, int x) {
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x > 1) return 0.0;
+ if (x > 0) return p;
+ return 1.0;
+ }
+
+
+ /**
+ * Returns the inverse of the Bernoulli distribution function
+ * with parameter <SPAN CLASS="MATH"><I>p</I></SPAN> at <SPAN CLASS="MATH"><I>u</I></SPAN>.
+ *
+ */
+ public static int inverseF (double p, double u) {
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u > 1.0 - p) return 1;
+ return 0;
+ }
+
+
+ /**
+ * Estimates the parameters <SPAN CLASS="MATH"><I>p</I></SPAN> of the Bernoulli distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The estimate is returned in a one-element
+ * array: [<SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH">hat(p)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int m) {
+ if (m < 2)
+ throw new UnsupportedOperationException(" m < 2");
+
+ // compute the empirical mean
+ double sum = 0.0;
+ for (int i = 0; i < m; i++)
+ sum += x[i];
+ sum /= (double) m;
+
+ double param[] = new double[1];
+ param[0] = sum;
+ return param;
+ }
+
+
+ /**
+ * Creates a new instance of a Bernoulli distribution with parameter
+ * <SPAN CLASS="MATH"><I>p</I></SPAN> estimated using the maximum likelihood method, from
+ * the <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to estimate the parameters
+ *
+ * @param m the number of observations to use to estimate the parameters
+ *
+ *
+ */
+ public static BernoulliDist getInstanceFromMLE (int[] x, int m) {
+ double param[] = new double[1];
+ param = getMLE (x, m);
+ return new BernoulliDist (param[0]);
+ }
+
+
+ /**
+ * Returns the mean <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>p</I></SPAN> of the Bernoulli distribution with
+ * parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the mean of the Bernoulli distribution <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>np</I></SPAN>
+ *
+ */
+ public static double getMean (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+
+ return (p);
+ }
+
+
+ /**
+ * Computes the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>p</I>(1 - <I>p</I>)</SPAN> of the Bernoulli
+ * distribution with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the variance of the Bernoulli distribution
+ *
+ */
+ public static double getVariance (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ return (p * (1.0 - p));
+ }
+
+
+ /**
+ * Computes the standard deviation of the Bernoulli distribution
+ * with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the standard deviation of the Bernoulli distribution
+ *
+ */
+ public static double getStandardDeviation (double p) {
+ return Math.sqrt (BernoulliDist.getVariance (p));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+ /**
+ * Returns an array that contains the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of the current
+ * distribution: [<SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {p};
+ return retour;
+ }
+
+
+ /**
+ * Resets the parameter to this new value.
+ *
+ *
+ */
+ public void setParams (double p) {
+ this.p = p;
+ q = 1 - p;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : p = " + p;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/BernoulliDist.tex b/source/umontreal/iro/lecuyer/probdist/BernoulliDist.tex
new file mode 100644
index 0000000..b5875e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BernoulliDist.tex
@@ -0,0 +1,323 @@
+\defmodule {BernoulliDist}
+
+Extends the class \class{DiscreteDistributionInt} for the \emph{Bernoulli}
+distribution \cite{sLAW00a} with parameter $p$, where $0\le p\le 1$.
+Its mass function is given by
+\begin{htmlonly}
+\[
+\begin{array}{rll}
+f(x) = & 1 - p, & \qquad\mbox { if } x = 0; \\[5pt]
+f(x) = & p, & \qquad\mbox { if } x = 1; \\[5pt]
+f(x) = & 0, & \qquad\mbox { otherwise. }
+\end{array}
+\]
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \begin{cases}
+ 1 - p, \qquad & \text{if $x = 0$;} \\
+ p, & \text{if $x = 1$;} \label{eq:fmass-bernoulli} \\
+ 0, & \text{otherwise.}
+ \end{cases}
+\endeq
+\end{latexonly}
+
+Its distribution function is
+\begin{htmlonly}
+\[
+\begin{array}{rll}
+F(x) = & 0, & \qquad\mbox { if } x < 0; \\[5pt]
+F(x) = & 1- p, & \qquad\mbox { if } 0 \le x < 1; \\[5pt]
+F(x) = & 1, & \qquad\mbox { if } x \ge 1.
+\end{array}
+\]
+\end{htmlonly}
+\begin{latexonly}
+ \eq
+ F(x) = \begin{cases}
+ 0, & \text{if $x < 0$;} \\
+ 1 - p, \qquad & \text{if $0 \le x < 1$;} \label{eq:cdf-bernoulli} \\
+ 1, & \text{if $x \ge 1$.}
+ \end{cases}
+ \endeq
+\end{latexonly}
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BernoulliDist
+ * Description: Bernoulli distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since August 2010
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class BernoulliDist extends DiscreteDistributionInt\begin{hide} {
+ private double p;
+ private double q;
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public BernoulliDist (double p)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ this.p = p;
+ q = 1 - p;
+ supportA = 0;
+ supportB = 1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Bernoulli distribution object.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ public double prob (int x) {
+ if (1 == x) return p;
+ if (0 == x) return q;
+ return 0.0;
+ }
+
+ public double cdf (int x) {
+ if (x < 0) return 0.0;
+ if (x < 1) return q;
+ return 1.0;
+ }
+
+ public double barF (int x) {
+ if (x > 1) return 0.0;
+ if (x > 0) return p;
+ return 1.0;
+ }
+
+ public int inverseFInt (double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u > q) return 1;
+ return 0;
+ }
+
+ public double getMean() {
+ return BernoulliDist.getMean (p);
+ }
+
+ public double getVariance() {
+ return BernoulliDist.getVariance (p);
+ }
+
+ public double getStandardDeviation() {
+ return BernoulliDist.getStandardDeviation (p);
+ }
+
+\end{hide}
+
+ public static double prob (double p, int x)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (1 == x) return p;
+ if (0 == x) return 1.0 - p;
+ return 0.0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the Bernoulli probability $f(x)$ with parameter $p$
+(see eq. (\ref{eq:fmass-bernoulli})).
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double p, int x)\begin{hide} {
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x < 0) return 0.0;
+ if (x < 1) return 1.0 - p;
+ return 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the Bernoulli distribution function $F(x)$
+ with parameter $p$ (see eq. (\ref{eq:cdf-bernoulli})).
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double p, int x)\begin{hide} {
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x > 1) return 0.0;
+ if (x > 0) return p;
+ return 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the complementary Bernoulli distribution
+ function $\bar F(x) = P[X \ge x]$ with parameter $p$.
+ \end{tabb}
+\begin{code}
+
+ public static int inverseF (double p, double u) \begin{hide} {
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u > 1.0 - p) return 1;
+ return 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inverse of the Bernoulli distribution function
+ with parameter $p$ at $u$.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int m)\begin{hide} {
+ if (m < 2)
+ throw new UnsupportedOperationException(" m < 2");
+
+ // compute the empirical mean
+ double sum = 0.0;
+ for (int i = 0; i < m; i++)
+ sum += x[i];
+ sum /= (double) m;
+
+ double param[] = new double[1];
+ param[0] = sum;
+ return param;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $p$ of the Bernoulli distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1,\ldots, m-1$. The estimate is returned in a one-element
+ array: [$p$].
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \return{returns the parameter [$\hat{p}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static BernoulliDist getInstanceFromMLE (int[] x, int m)\begin{hide} {
+ double param[] = new double[1];
+ param = getMLE (x, m);
+ return new BernoulliDist (param[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Bernoulli distribution with parameter
+ ${p}$ estimated using the maximum likelihood method, from
+ the $m$ observations $x[i]$, $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to estimate the parameters}
+ \param{m}{the number of observations to use to estimate the parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double p)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+
+ return (p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean $E[X] = p$ of the Bernoulli distribution with
+ parameter $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Bernoulli distribution $E[X] = np$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double p)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ return (p * (1.0 - p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the variance $\mbox{Var}[X] = p(1 - p)$ of the Bernoulli
+ distribution with parameter $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Bernoulli distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double p)\begin{hide} {
+ return Math.sqrt (BernoulliDist.getVariance (p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard deviation of the Bernoulli distribution
+ with parameter $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Bernoulli distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {p};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an array that contains the parameter $p$ of the current
+ distribution: [$p$].
+\end{tabb}
+\begin{code}
+
+ public void setParams (double p)\begin{hide} {
+ this.p = p;
+ q = 1 - p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Resets the parameter to this new value.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : p = " + p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/BetaDist.java b/source/umontreal/iro/lecuyer/probdist/BetaDist.java
new file mode 100644
index 0000000..b9b327c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BetaDist.java
@@ -0,0 +1,1013 @@
+
+
+/*
+ * Class: BetaDist
+ * Description: beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>beta</EM> distribution with shape parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>, over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>, where <SPAN CLASS="MATH"><I>a</I> < <I>b</I></SPAN>.
+ * It has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>x</I> - <I>a</I>)<SUP><I>α</I>-1</SUP>(<I>b</I> - <I>x</I>)<SUP><I>β</I>-1</SUP>/[<I>B</I>(<I>α</I>, <I>β</I>)(<I>b</I> - <I>a</I>)<SUP><I>α</I>+<I>β</I>-1</SUP>]
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>a</I> <= <I>x</I> <= <I>b</I></SPAN>, and 0 elsewhere. It has distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>I</I><SUB><I>α</I>, <I>β</I></SUB>(<I>x</I>) = ∫<SUB>a</SUB><SUP>x</SUP>(<I>ξ</I> - <I>a</I>)<SUP><I>α</I>-1</SUP>(<I>b</I> - <I>ξ</I>)<SUP><I>β</I>-1</SUP>/[<I>B</I>(<I>α</I>, <I>β</I>)(<I>b</I> - <I>a</I>)<SUP><I>α</I>+<I>β</I>-1</SUP>]<I>dξ</I>, for <I>a</I> <= <I>x</I> <= <I>b</I>,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>B</I>(<I>α</I>, <I>β</I>)</SPAN> is the <EM>beta</EM> function defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:betadef"></A>
+ * <I>B</I>(<I>α</I>, <I>β</I>) = <I>Γ</I>(<I>α</I>)<I>Γ</I>(<I>β</I>)/<I>Γ</I>(<I>α</I> + <I>β</I>),
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined in
+ * {@link GammaDist}.
+ *
+ */
+public class BetaDist extends ContinuousDistribution {
+ protected double alpha; // First parameter
+ protected double beta; // Second parameter
+ protected double a, b; // Interval x in [a, b]
+ protected double bminusa;
+ protected double logFactor;
+ protected double Beta; // Function Beta(alpha, beta)
+ protected double logBeta; // Ln(Beta(alpha, beta))
+
+ private static class Optim implements Lmder_fcn
+ {
+ private double a;
+ private double b;
+
+ public Optim (double a, double b)
+ {
+ this.a = a;
+ this.b = b;
+ }
+
+ public void fcn (int m, int n, double[] x, double[] fvec, double[][] fjac, int iflag[])
+ {
+ if (x[1] <= 0.0 || x[2] <= 0.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fjac[1][1] = BIG;
+ fjac[1][2] = 0.0;
+ fjac[2][1] = 0.0;
+ fjac[2][2] = BIG;
+ return;
+ }
+
+ double trig;
+ if (iflag[1] == 1)
+ {
+ trig = Num.digamma (x[1] + x[2]);
+ fvec[1] = Num.digamma(x[1]) - trig - a;
+ fvec[2] = Num.digamma(x[2]) - trig - b;
+ }
+ else if (iflag[1] == 2)
+ {
+ trig = Num.trigamma (x[1] + x[2]);
+
+ fjac[1][1] = Num.trigamma (x[1]) - trig;
+ fjac[1][2] = - trig;
+ fjac[2][1] = - trig;
+ fjac[2][2] = Num.trigamma (x[2]) - trig;
+ }
+ }
+ }
+
+
+ /**
+ * Constructs a <TT>BetaDist</TT> object with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT> and default domain <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ */
+ public BetaDist (double alpha, double beta) {
+ setParams (alpha, beta, 0.0, 1.0);
+ }
+
+
+ /**
+ * Constructs a <TT>BetaDist</TT> object with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT> and domain <SPAN CLASS="MATH">[</SPAN><TT>a</TT><SPAN CLASS="MATH">,</SPAN> <TT>b</TT><SPAN CLASS="MATH">]</SPAN>.
+ *
+ *
+ */
+ public BetaDist (double alpha, double beta, double a, double b) {
+ setParams (alpha, beta, a, b);
+ }
+
+
+ @Deprecated
+ public BetaDist (double alpha, double beta, int d) {
+ setParams (alpha, beta, 0.0, 1.0, d);
+ }
+
+ @Deprecated
+ public BetaDist (double alpha, double beta, double a, double b, int d) {
+ setParams (alpha, beta, a, b, d);
+ }
+
+
+ @Override
+ public double density (double x) {
+ if (x <= a || x >= b)
+ return 0;
+ double temp = (alpha - 1) * Math.log(x - a) + (beta - 1) * Math.log(b - x);
+// return factor*Math.pow (x - a, alpha - 1)*Math.pow (b - x, beta - 1);
+ return Math.exp(logFactor + temp);
+ }
+
+ @Override
+ public double cdf (double x) {
+ return cdf (alpha, beta, (x - a)/bminusa);
+ }
+
+ @Override
+ public double barF (double x) {
+ return barF (alpha, beta, (x - a)/bminusa);
+ }
+
+ @Override
+ public double inverseF (double u) {
+ return a + (b - a)*inverseF (alpha, beta, u);
+ }
+
+ @Override
+ public double getMean() {
+ return BetaDist.getMean (alpha, beta, a, b);
+ }
+
+ @Override
+ public double getVariance() {
+ return BetaDist.getVariance (alpha, beta, a, b);
+ }
+
+ @Override
+ public double getStandardDeviation() {
+ return BetaDist.getStandardDeviation (alpha, beta, a, b);
+ }
+
+ /**
+ * Same as
+ * {@link #density(double,double,double,double,double) density} <TT>(alpha, beta, 0, 1, x)</TT>.
+ *
+ */
+ public static double density (double alpha, double beta, double x) {
+ return density (alpha, beta, 0.0, 1.0, x);
+ }
+
+
+ /**
+ * Computes the density function of the <EM>beta</EM> distribution.
+ *
+ */
+ public static double density (double alpha, double beta,
+ double a, double b, double x) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ if (x <= a || x >= b)
+ return 0;
+
+ double z = -Num.lnBeta (alpha, beta) - (alpha + beta - 1)* Math.log(b-a) +
+ (alpha-1)*Math.log(x-a) + (beta-1)*Math.log(b-x);
+ return Math.exp(z);
+ }
+
+
+ static double beta_g (double x)
+ /*
+ * Used in the normal approximation of beta. This is the function
+ * (1 - x^2 + 2x*ln(x)) / (1 - x)^2.
+ */
+ {
+ if (x > 1.0)
+ return -beta_g(1.0/x);
+ if (x < 1.0e-200)
+ return 1.0;
+
+ final double y = 1.0 - x;
+ if (x < 0.9)
+ return (1.0 - x*x + 2.0*x*Math.log(x)) / (y*y);
+ if (x == 1.0)
+ return 0.0;
+
+ // For x near 1, use a series expansion to avoid loss of precision
+ final double EPS = 1.0e-12;
+ double term;
+ double ypow = 1.0;
+ double sum = 0.0;
+ int j = 2;
+ do {
+ ypow *= y;
+ term = ypow / (j * (j + 1));
+ sum += term;
+ j++;
+ } while (Math.abs (term / sum) > EPS);
+
+ return 2.0 * sum;
+ }
+
+
+ private static double bolshev (double alpha, double beta, int d, double x) {
+ // Bol'shev approximation for large max (alpha, beta)
+ // and small min (alpha, beta)
+ /* if (x > 0.5)
+ return barF (beta, alpha, 1.0 - x); */
+ boolean flag = false;
+ double u, temp, yd, gam;
+
+ if (alpha < beta) {
+ u = alpha;
+ alpha = beta;
+ beta = u;
+ flag = false;
+ } else
+ flag = true;
+
+ u = alpha + 0.5 * beta - 0.5;
+ if (!flag)
+ temp = x / (2.0 - x);
+ else
+ temp = (1.0 - x) / (1.0 + x);
+ yd = 2.0 * u * temp;
+ gam = (Math.exp (beta * Math.log (yd) - yd -
+ Num.lnGamma (beta)) * (2.0 * yd * yd - (beta - 1.0) * yd -
+ (beta * beta - 1.0))) / (24.0 * u * u);
+ if (flag) {
+ yd = GammaDist.barF (beta, d, yd);
+ return Math.max(0, yd - gam);
+ } else {
+ yd = GammaDist.cdf (beta, d, yd);
+ return yd + gam;
+ }
+ }
+
+
+ private static double peizer (double alpha, double beta, double x) {
+ // Normal approximation of Peizer and Pratt. Reference: \cite{tPEI68a}
+ double temp, h1, h3, y;
+ h1 = alpha + beta - 1.0;
+ y = 1.0 - x;
+ if (x > 1.0e-15)
+ temp = 1.0 + y*beta_g ((alpha - 0.5)/(h1*x));
+ else
+ temp = GammaDist.mybelog ((alpha - 0.5) / (h1 * x));
+
+ h3 = Math.sqrt ((temp + x*beta_g ((beta - 0.5)/(h1*y)))
+ /((h1 + 1.0/6.0)*x*y))
+ *((h1 + 1.0/3.0 + 0.02*(1.0/alpha + 1.0/beta + 1.0/(alpha + beta)))
+ *x - alpha + 1.0/3.0 - 0.02/alpha - 0.01/(alpha + beta));
+
+ return NormalDist.cdf01 (h3);
+ }
+
+
+ private static double donato (double alpha, double beta, double x) {
+ // Cuyt p. 387, 18.5.20b
+ // distribution Beta avec fractions continues
+ // Il faut choisir MMAX >= sqrt(max(alpha, beta))
+
+ double mid = (alpha + 1.0) / (alpha + beta + 2.0);
+ if (x > mid)
+ return 1.0 - donato (beta, alpha, 1.0 - x);
+
+ final int MMAX = 100; // pour ALPHABETAMAX = 10000
+ double[] Ta = new double[1 + MMAX];
+ double[] Tb = new double[1 + MMAX];
+ int M1 = MMAX;
+ double tem, tem1;
+ int m;
+
+ if ((beta <= MMAX) && (beta % 1.0 < 1.0e-100)) {
+ // if beta is integer, Ta[i0] = 0; it is useless to evaluate
+ // the other Ta[i] for i > i0
+ M1 = (int) beta;
+ }
+
+ Ta[1] = 1;
+ for (m = 1; m < M1; m++) {
+ tem = alpha + 2 * m - 1;
+ Ta[m + 1] = (alpha + m - 1) * (alpha + beta + m - 1) *
+ (beta - m) * m * x * x / (tem * tem);
+ }
+
+ // term m = 0 in the next loop; avoid tem1 = 0/0 for alpha = 1
+ tem = alpha * (alpha + beta) / (alpha + 1);
+ Tb[1] = alpha - tem * x;
+
+ for (m = 1; m < M1; m++) {
+ tem = (alpha + m) * (alpha + beta + m) / (alpha + 2 * m + 1);
+ tem1 = m * (beta - m) / (alpha + 2 * m - 1);
+ Tb[m + 1] = alpha + 2 * m + (tem1 - tem) * x;
+ }
+
+ while (0. == Tb[M1] && M1 > 1) {
+ --M1;
+ }
+
+ // evaluate continuous fraction
+ double con = 0;
+ for (m = M1; m > 0; m--) {
+ con = Ta[m] / (Tb[m] + con);
+ }
+
+ tem = Num.lnBeta (alpha, beta) - alpha * Math.log (x) - beta * Math.log1p (-x);
+ return con * Math.exp (-tem);
+ }
+
+ @Deprecated
+ public static double cdf (double alpha, double beta, int d, double x) {
+ return cdf (alpha, beta, x);
+ }
+
+
+ @Deprecated
+ public static double cdf (double alpha, double beta,
+ double a, double b, int d, double x) {
+ return cdf (alpha, beta, d, (x - a)/(b - a));
+ }
+
+
+ @Deprecated
+ public static double barF (double alpha, double beta, int d, double x) {
+ return 1.0 - cdf (alpha, beta, d, x);
+ }
+
+
+ @Deprecated
+ public static double barF (double alpha, double beta,
+ double a, double b, int d, double x) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return 1.0 - cdf (alpha, beta, d, (x - a)/(b - a));
+ }
+
+
+
+ /**
+ * Same as
+ * {@link #cdf(double,double,double,double,double) cdf} <TT>(alpha, beta, 0, 1, x)</TT>.
+ *
+ */
+ public static double cdf (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= 1.0)
+ return 1.0;
+ if (1.0 == beta)
+ return Math.pow(x, alpha);
+
+ final double ALPHABETAMAX = 10000.0;
+ final double ALPHABETALIM = 30.0;
+
+ if (Math.max (alpha, beta) <= ALPHABETAMAX) {
+ return donato (alpha, beta, x);
+ }
+
+ if ((alpha > ALPHABETAMAX && beta < ALPHABETALIM) ||
+ (beta > ALPHABETAMAX && alpha < ALPHABETALIM)) {
+ return bolshev (alpha, beta, 12, x);
+ }
+
+ return peizer (alpha, beta, x);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double alpha, double beta,
+ double a, double b, double x) {
+ return cdf (alpha, beta, (x - a)/(b - a));
+ }
+
+
+ /**
+ * Same as
+ * {@link #barF(double,double,double,double,double) barF} <TT>(alpha, beta, 0, 1, x)</TT>.
+ *
+ */
+ public static double barF (double alpha, double beta, double x) {
+ return cdf (beta, alpha, 1.0 - x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (double alpha, double beta,
+ double a, double b, double x) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return cdf (beta, alpha, (b - x)/(b - a));
+ }
+
+
+ @Deprecated
+ public static double inverseF (double alpha, double beta, int d, double u) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0)
+ return 0;
+ if (u >= 1)
+ return 1;
+
+ /*
+ * Code taken from
+ * Cephes Math Library Release 2.8: June, 2000
+ * Copyright 1984, 1996, 2000 by Stephen L. Moshier
+ */
+ final double MACHEP = 1.11022302462515654042E-16;
+ final double MAXLOG = 7.09782712893383996843E2;
+ final double MINLOG = -7.08396418532264106224E2;
+ // final double MAXNUM = 1.7976931348623158E308;
+
+ boolean ihalve = false;
+ boolean newt = false;
+
+ double p = 0, q = 0, y0 = 0, z = 0, y = 0, x = 0, x0, x1, lgm = 0,
+ yp = 0, di = 0, dithresh = 0, yl, yh, xt = 0;
+ int i, dir;
+ boolean rflg, nflg;
+ x0 = 0.0;
+ yl = 0.0;
+ x1 = 1.0;
+ yh = 1.0;
+ nflg = false;
+ rflg = false;
+ if (alpha <= 1.0 || beta <= 1.0) {
+ dithresh = 1.0e-6;
+ rflg = false;
+ p = alpha;
+ q = beta;
+ y0 = u;
+ x = p/(p+q);
+ y = cdf (p, q, x);
+ ihalve = true;
+ }
+ else
+ dithresh = 1.0e-4;
+
+mainloop:
+ while (true) {
+ if (ihalve) {
+ ihalve = false;
+ dir = 0;
+ di = 0.5;
+ for (i = 0; i<100; i++) {
+ if (i != 0) {
+ x = x0 + di*(x1 - x0);
+ if (x == 1.0)
+ x = 1.0 - MACHEP;
+ if (x == 0.0) {
+ di = 0.5;
+ x = x0 + di*(x1 - x0);
+ if (x == 0.0) {
+ // System.err.println ("BetaDist.inverseF: underflow");
+ return 0;
+ }
+ }
+ y = cdf (p, q, x);
+ yp = (x1 - x0)/(x1 + x0);
+ if (Math.abs (yp) < dithresh) {
+ newt = true;
+ continue mainloop;
+ }
+ yp = (y-y0)/y0;
+ if (Math.abs (yp) < dithresh) {
+ newt = true;
+ continue mainloop;
+ }
+ }
+ if (y < y0) {
+ x0 = x;
+ yl = y;
+ if (dir < 0) {
+ dir = 0;
+ di = 0.5;
+ }
+ else if (dir > 3)
+ di = 1.0 - (1.0 - di)*(1.0 - di);
+ else if (dir > 1)
+ di = 0.5*di + 0.5;
+ else
+ di = (y0 - y)/(yh - yl);
+ dir += 1;
+ if (x0 > 0.75) {
+ // if (0 == y)
+ // y = EPS;
+ if (rflg) {
+ rflg = false;
+ p = alpha;
+ q = beta;
+ y0 = u;
+ }
+ else {
+ rflg = true;
+ p = beta;
+ q = alpha;
+ y0 = 1.0 - u;
+ }
+ x = 1.0 - x;
+ y = cdf (p, q, x);
+ x0 = 0.0;
+ yl = 0.0;
+ x1 = 1.0;
+ yh = 1.0;
+ ihalve = true;
+ continue mainloop;
+ }
+ }
+ else {
+ x1 = x;
+ if (rflg && x1 < MACHEP) {
+ x = 0.0;
+ break mainloop;
+ }
+ yh = y;
+ if (dir > 0) {
+ dir = 0;
+ di = 0.5;
+ }
+ else if (dir < -3)
+ di = di*di;
+ else if (dir < -1)
+ di = 0.5*di;
+ else
+ di = (y - y0)/(yh - yl);
+ dir -= 1;
+ }
+ }
+ // PLOSS error
+ if (x0 >= 1.0) {
+ x = 1.0 - MACHEP;
+ break mainloop;
+ }
+ if (x <= 0.0) {
+ // System.err.println ("BetaDist.inverseF: underflow");
+ return 0 ;
+ }
+ newt = true;
+ }
+ if (newt) {
+ newt = false;
+ if (nflg)
+ break mainloop;
+ nflg = true;
+ lgm = Num.lnGamma (p+q) - Num.lnGamma (p) - Num.lnGamma (q);
+
+ for (i=0; i<8; i++) {
+ /* Compute the function at this point. */
+ if (i != 0)
+ y = cdf (p, q, x);
+ if (y < yl) {
+ x = x0;
+ y = yl;
+ }
+ else if (y > yh) {
+ x = x1;
+ y = yh;
+ }
+ else if (y < y0) {
+ x0 = x;
+ yl = y;
+ }
+ else {
+ x1 = x;
+ yh = y;
+ }
+ if (x >= 1.0 || x <= 0.0)
+ break;
+ /* Compute the derivative of the function at this point. */
+ z = (p - 1.0)*Math.log (x) + (q - 1.0)*Math.log1p (-x) + lgm;
+ if (z < MINLOG)
+ break mainloop;
+ if (z > MAXLOG)
+ break;
+ z = Math.exp (z);
+ /* Compute the step to the next approximation of x. */
+ z = (y - y0)/z;
+ xt = x - z;
+ if (xt <= x0) {
+ y = (x - x0) / (x1 - x0);
+ xt = x0 + 0.5*y*(x - x0);
+ if (xt <= 0.0)
+ break;
+ }
+ if (xt >= x1) {
+ y = (x1 - x) / (x1 - x0);
+ xt = x1 - 0.5*y*(x1 - x);
+ if (xt >= 1.0)
+ break;
+ }
+ x = xt;
+ if (Math.abs (z/x) < 128.0*MACHEP)
+ break mainloop;
+ }
+ /* Did not converge. */
+ dithresh = 256.0*MACHEP;
+ ihalve = true;
+ continue mainloop;
+ }
+
+ yp = -NormalDist.inverseF01 (u);
+
+ if (u > 0.5) {
+ rflg = true;
+ p = beta;
+ q = alpha;
+ y0 = 1.0 - u;
+ yp = -yp;
+ }
+ else {
+ rflg = false;
+ p = alpha;
+ q = beta;
+ y0 = u;
+ }
+
+ lgm = (yp*yp - 3.0)/6.0;
+ x = 2.0/(1.0/(2.0*p-1.0) + 1.0/(2.0*q-1.0));
+ z = yp*Math.sqrt (x + lgm)/x
+ - (1.0/(2.0*q-1.0) - 1.0/(2.0*p-1.0) )
+ * (lgm + 5.0/6.0 - 2.0/(3.0*x));
+ z = 2.0*z;
+ if (z < MINLOG) {
+ x = 1.0;
+ // System.err.println ("BetaDist.inverseF: underflow");
+ return 0;
+ }
+ x = p/( p + q*Math.exp (z));
+ y = cdf (p, q, x);
+ yp = (y - y0)/y0;
+ if (Math.abs (yp) < 0.2) {
+ newt = true;
+ continue mainloop;
+ }
+ ihalve = true;
+ }
+
+ // Done
+ if (rflg) {
+ if (x <= MACHEP)
+ x = 1.0 - MACHEP;
+ else
+ x = 1.0 - x;
+ }
+ return x;
+ }
+
+ /**
+ * Same as
+ * {@link #inverseF(double,double,double,double,double) inverseF} <TT>(alpha, beta, 0, 1, u)</TT>.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double u) {
+ return inverseF (alpha, beta, 12, u);
+ }
+
+
+ @Deprecated
+ public static double inverseF (double alpha, double beta,
+ double a, double b, int d, double u) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return a + (b - a)*inverseF (alpha, beta, d, u);
+ }
+
+ /**
+ * Returns the inverse beta distribution function
+ * using the algorithm implemented in
+ * the <A NAME="tex2html1"
+ * HREF="http://www.moshier.net/">Cephes math library</A>.
+ * The method performs interval halving or Newton iterations to
+ * compute the inverse.
+ *
+ */
+ public static double inverseF (double alpha, double beta,
+ double a, double b, double u) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return a + (b - a)*inverseF (alpha, beta, u);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>β</I>)</SPAN> of the beta distribution over the
+ * interval <SPAN CLASS="MATH">[0, 1]</SPAN> using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sum = 0.0;
+ double a = 0.0;
+ double b = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ sum += x[i];
+ if (x[i] > 0.0)
+ a += Math.log (x[i]);
+ else
+ a -= 709.0;
+ if (x[i] < 1.0)
+ b += Math.log1p (-x[i]);
+ else
+ b -= 709.0;
+ }
+ double mean = sum / n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double var = sum / (n - 1);
+
+ Optim system = new Optim (a, b);
+
+ double[] param = new double[3];
+ param[1] = mean * ((mean * (1.0 - mean) / var) - 1.0);
+ param[2] = (1.0 - mean) * ((mean * (1.0 - mean) / var) - 1.0);
+ double[] fvec = new double [3];
+ double[][] fjac = new double[3][3];
+ int[] info = new int[2];
+ int[] ipvt = new int[3];
+
+ Minpack_f77.lmder1_f77 (system, 2, 2, param, fvec, fjac, 1e-5, info, ipvt);
+
+ double parameters[] = new double[2];
+ parameters[0] = param[1];
+ parameters[1] = param[2];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a beta distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>β</I></SPAN> over the interval <SPAN CLASS="MATH">[0, 1]</SPAN> estimated using the maximum likelihood
+ * method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static BetaDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new BetaDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I>/(<I>α</I> + <I>β</I>)</SPAN>
+ * of the beta distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>, over the
+ * interval <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ * @return the mean of the Beta distribution
+ *
+ */
+ public static double getMean (double alpha, double beta) {
+ return getMean (alpha, beta, 0.0, 1.0);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (<I>bα</I> + <I>aβ</I>)/(<I>α</I> + <I>β</I>)</SPAN>
+ * of the beta distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> over the
+ * interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ *
+ * @return the mean of the Beta distribution
+ *
+ */
+ public static double getMean (double alpha, double beta, double a,
+ double b) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return (alpha*b + beta*a) / (alpha + beta);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1#1</SPAN>
+ * of the beta distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>, over the
+ * interval <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ * @return the variance of the beta distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>αβ</I>/[(<I>α</I> + <I>β</I>)<SUP>2</SUP>(<I>α</I> + <I>β</I> + 1)]</SPAN>.
+ *
+ */
+ public static double getVariance (double alpha, double beta) {
+ return getVariance (alpha, beta, 0.0, 1.0);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 2#2</SPAN>
+ * of the beta distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>, over the
+ * interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ *
+ * @return the variance of the beta distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>αβ</I>/[(<I>α</I> + <I>β</I>)<SUP>2</SUP>(<I>α</I> + <I>β</I> + 1)]</SPAN>.
+ *
+ */
+ public static double getVariance (double alpha, double beta, double a,
+ double b) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return ((alpha * beta)*(b-a)*(b-a)) /
+ ((alpha + beta) * (alpha + beta) * (alpha + beta + 1));
+ }
+
+
+ /**
+ * Computes the standard deviation of the beta distribution with
+ * parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>, over the interval <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ * @return the standard deviation of the Beta distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta) {
+ return Math.sqrt (BetaDist.getVariance (alpha, beta));
+ }
+
+
+ /**
+ * Computes the standard deviation of the beta distribution with
+ * parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>, over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ *
+ * @return the standard deviation of the Beta distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta,
+ double a, double b) {
+ return Math.sqrt (BetaDist.getVariance (alpha, beta, a, b));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>a</I></SPAN> of this object.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>b</I></SPAN> of this object.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+ @Deprecated
+ public void setParams (double alpha, double beta,
+ double a, double b, int d) {
+ setParams (alpha, beta, a, b);
+ // this.decPrec = d;
+ }
+
+ /**
+ * Sets the parameters of the current distribution. See the constructor.
+ *
+ */
+ public void setParams (double alpha, double beta, double a, double b) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ this.alpha = alpha;
+ this.beta = beta;
+ supportA = this.a = a;
+ supportB = this.b = b;
+ bminusa = b - a;
+ double temp = Num.lnGamma (alpha);
+ if (alpha == beta)
+ temp *= 2.0;
+ else
+ temp += Num.lnGamma (beta);
+ logBeta = temp - Num.lnGamma (alpha + beta);
+ Beta = Math.exp(logBeta);
+// this.factor = 1.0 / (Beta * Math.pow (bminusa, alpha + beta - 1));
+ this.logFactor = - logBeta - Math.log (bminusa) * (alpha + beta - 1);
+ }
+
+
+ /**
+ * Return an array containing the parameters of the current distribution as [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>].
+ *
+ *
+ */
+ @Override
+ public double[] getParams () {
+ double[] retour = {alpha, beta, a, b};
+ return retour;
+ }
+
+
+ @Override
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/BetaDist.tex b/source/umontreal/iro/lecuyer/probdist/BetaDist.tex
new file mode 100644
index 0000000..e933588
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BetaDist.tex
@@ -0,0 +1,1047 @@
+\defmodule {BetaDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em beta\/} distribution
+\cite[page 210]{tJOH95b} with shape parameters
+$\alpha > 0$ and $\beta > 0$, over the interval $[a,b]$, where $a < b$.
+\begin{htmlonly}
+It has density
+\eq
+ f(x) = (x - a)^{\alpha - 1}(b - x)^{\beta - 1}
+ / [B(\alpha,\beta)(b - a)^{\alpha + \beta - 1}]
+\endeq
+for $a\le x \le b$, and 0 elsewhere. It has distribution function
+\eq
+ F(x) = I_{\alpha,\beta}(x)
+ = \int_a^x (\xi - a)^{\alpha-1} (b - \xi)^{\beta-1}/
+ [B(\alpha, \beta)(b - a)^{\alpha + \beta - 1}] d\xi,
+ \qquad \mbox{for } a \le x \le b,
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+This distribution has density
+\eq
+ f(x) = \frac{ (x-a)^{\alpha - 1}(b - x)^{\beta - 1}}
+ {\mathcal{B} (\alpha, \beta)(b - a)^{\alpha + \beta - 1}},
+ \qquad \mbox{for } a\le x\le b,
+ \mbox{ and }0\mbox{ elsewhere},
+\endeq
+and distribution function
+\eq
+ F(x) = I_{\alpha,\beta}(x)
+ = \int_a^x \frac {(\xi - a)^{\alpha-1} (b - \xi)^{\beta-1}}
+ {\mathcal{B} (\alpha, \beta)(b - a)^{\alpha + \beta - 1}} d\xi,
+ \qquad \mbox{for } a\le x\le b, \eqlabel{eq:Fbeta}
+\endeq
+\end{latexonly}
+where $\mathcal{B}(\alpha,\beta)$ is the {\em beta\/} function defined by
+\begin{htmlonly}
+\eq
+ B(\alpha, \beta) = \Gamma(\alpha)\Gamma(\beta)/\Gamma(\alpha+\beta),
+\label{eq:betadef}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ \mathcal{B} (\alpha,\beta) = \frac{\Gamma (\alpha) \Gamma (\beta)}
+ { \Gamma (\alpha+\beta)},\label{eq:betadef}
+\endeq
+\end{latexonly}
+and $\Gamma(x)$ is the gamma function defined in
+\latex{(\ref{eq:Gamma})}\html{\class{GammaDist}}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BetaDist
+ * Description: beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import optimization.*;
+\end{hide}
+
+public class BetaDist extends ContinuousDistribution\begin{hide} {
+ protected double alpha; // First parameter
+ protected double beta; // Second parameter
+ protected double a, b; // Interval x in [a, b]
+ protected double bminusa;
+ protected double logFactor;
+ protected double Beta; // Function Beta(alpha, beta)
+ protected double logBeta; // Ln(Beta(alpha, beta))
+
+ private static class Optim implements Lmder_fcn
+ {
+ private double a;
+ private double b;
+
+ public Optim (double a, double b)
+ {
+ this.a = a;
+ this.b = b;
+ }
+
+ public void fcn (int m, int n, double[] x, double[] fvec, double[][] fjac, int iflag[])
+ {
+ if (x[1] <= 0.0 || x[2] <= 0.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fjac[1][1] = BIG;
+ fjac[1][2] = 0.0;
+ fjac[2][1] = 0.0;
+ fjac[2][2] = BIG;
+ return;
+ }
+
+ double trig;
+ if (iflag[1] == 1)
+ {
+ trig = Num.digamma (x[1] + x[2]);
+ fvec[1] = Num.digamma(x[1]) - trig - a;
+ fvec[2] = Num.digamma(x[2]) - trig - b;
+ }
+ else if (iflag[1] == 2)
+ {
+ trig = Num.trigamma (x[1] + x[2]);
+
+ fjac[1][1] = Num.trigamma (x[1]) - trig;
+ fjac[1][2] = - trig;
+ fjac[2][1] = - trig;
+ fjac[2][2] = Num.trigamma (x[2]) - trig;
+ }
+ }
+ }
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BetaDist (double alpha, double beta)\begin{hide} {
+ setParams (alpha, beta, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{BetaDist} object with parameters $\alpha =$
+ \texttt{alpha}, $\beta =$ \texttt{beta} and default domain $[0,1]$.
+ \end{tabb}
+\begin{code}
+
+ public BetaDist (double alpha, double beta, double a, double b)\begin{hide} {
+ setParams (alpha, beta, a, b);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{BetaDist} object with parameters $\alpha =$
+ \texttt{alpha}, $\beta =$ \texttt{beta} and domain $[$\texttt{a}$,$~\texttt{b}$]$.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public BetaDist (double alpha, double beta, int d) {
+ setParams (alpha, beta, 0.0, 1.0, d);
+ }
+
+ @Deprecated
+ public BetaDist (double alpha, double beta, double a, double b, int d) {
+ setParams (alpha, beta, a, b, d);
+ }
+\end{code}\end{hide}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ @Override
+ public double density (double x) {
+ if (x <= a || x >= b)
+ return 0;
+ double temp = (alpha - 1) * Math.log(x - a) + (beta - 1) * Math.log(b - x);
+// return factor*Math.pow (x - a, alpha - 1)*Math.pow (b - x, beta - 1);
+ return Math.exp(logFactor + temp);
+ }
+
+ @Override
+ public double cdf (double x) {
+ return cdf (alpha, beta, (x - a)/bminusa);
+ }
+
+ @Override
+ public double barF (double x) {
+ return barF (alpha, beta, (x - a)/bminusa);
+ }
+
+ @Override
+ public double inverseF (double u) {
+ return a + (b - a)*inverseF (alpha, beta, u);
+ }
+
+ @Override
+ public double getMean() {
+ return BetaDist.getMean (alpha, beta, a, b);
+ }
+
+ @Override
+ public double getVariance() {
+ return BetaDist.getVariance (alpha, beta, a, b);
+ }
+
+ @Override
+ public double getStandardDeviation() {
+ return BetaDist.getStandardDeviation (alpha, beta, a, b);
+ }\end{hide}
+
+ public static double density (double alpha, double beta, double x)\begin{hide} {
+ return density (alpha, beta, 0.0, 1.0, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as
+ \method{density}{double,double,double,double,double}~\texttt{(alpha, beta, 0, 1, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double density (double alpha, double beta,
+ double a, double b, double x)\begin{hide} {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ if (x <= a || x >= b)
+ return 0;
+
+ double z = -Num.lnBeta (alpha, beta) - (alpha + beta - 1)* Math.log(b-a) +
+ (alpha-1)*Math.log(x-a) + (beta-1)*Math.log(b-x);
+ return Math.exp(z);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function of the {\em beta\/} distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ static double beta_g (double x)
+ /*
+ * Used in the normal approximation of beta. This is the function
+ * (1 - x^2 + 2x*ln(x)) / (1 - x)^2.
+ */
+ {
+ if (x > 1.0)
+ return -beta_g(1.0/x);
+ if (x < 1.0e-200)
+ return 1.0;
+
+ final double y = 1.0 - x;
+ if (x < 0.9)
+ return (1.0 - x*x + 2.0*x*Math.log(x)) / (y*y);
+ if (x == 1.0)
+ return 0.0;
+
+ // For x near 1, use a series expansion to avoid loss of precision
+ final double EPS = 1.0e-12;
+ double term;
+ double ypow = 1.0;
+ double sum = 0.0;
+ int j = 2;
+ do {
+ ypow *= y;
+ term = ypow / (j * (j + 1));
+ sum += term;
+ j++;
+ } while (Math.abs (term / sum) > EPS);
+
+ return 2.0 * sum;
+ }
+
+
+ private static double bolshev (double alpha, double beta, int d, double x) {
+ // Bol'shev approximation for large max (alpha, beta)
+ // and small min (alpha, beta)
+ /* if (x > 0.5)
+ return barF (beta, alpha, 1.0 - x); */
+ boolean flag = false;
+ double u, temp, yd, gam;
+
+ if (alpha < beta) {
+ u = alpha;
+ alpha = beta;
+ beta = u;
+ flag = false;
+ } else
+ flag = true;
+
+ u = alpha + 0.5 * beta - 0.5;
+ if (!flag)
+ temp = x / (2.0 - x);
+ else
+ temp = (1.0 - x) / (1.0 + x);
+ yd = 2.0 * u * temp;
+ gam = (Math.exp (beta * Math.log (yd) - yd -
+ Num.lnGamma (beta)) * (2.0 * yd * yd - (beta - 1.0) * yd -
+ (beta * beta - 1.0))) / (24.0 * u * u);
+ if (flag) {
+ yd = GammaDist.barF (beta, d, yd);
+ return Math.max(0, yd - gam);
+ } else {
+ yd = GammaDist.cdf (beta, d, yd);
+ return yd + gam;
+ }
+ }
+
+
+ private static double peizer (double alpha, double beta, double x) {
+ // Normal approximation of Peizer and Pratt. Reference: \cite{tPEI68a}
+ double temp, h1, h3, y;
+ h1 = alpha + beta - 1.0;
+ y = 1.0 - x;
+ if (x > 1.0e-15)
+ temp = 1.0 + y*beta_g ((alpha - 0.5)/(h1*x));
+ else
+ temp = GammaDist.mybelog ((alpha - 0.5) / (h1 * x));
+
+ h3 = Math.sqrt ((temp + x*beta_g ((beta - 0.5)/(h1*y)))
+ /((h1 + 1.0/6.0)*x*y))
+ *((h1 + 1.0/3.0 + 0.02*(1.0/alpha + 1.0/beta + 1.0/(alpha + beta)))
+ *x - alpha + 1.0/3.0 - 0.02/alpha - 0.01/(alpha + beta));
+
+ return NormalDist.cdf01 (h3);
+ }
+
+
+ private static double donato (double alpha, double beta, double x) {
+ // Cuyt p. 387, 18.5.20b
+ // distribution Beta avec fractions continues
+ // Il faut choisir MMAX >= sqrt(max(alpha, beta))
+
+ double mid = (alpha + 1.0) / (alpha + beta + 2.0);
+ if (x > mid)
+ return 1.0 - donato (beta, alpha, 1.0 - x);
+
+ final int MMAX = 100; // pour ALPHABETAMAX = 10000
+ double[] Ta = new double[1 + MMAX];
+ double[] Tb = new double[1 + MMAX];
+ int M1 = MMAX;
+ double tem, tem1;
+ int m;
+
+ if ((beta <= MMAX) && (beta % 1.0 < 1.0e-100)) {
+ // if beta is integer, Ta[i0] = 0; it is useless to evaluate
+ // the other Ta[i] for i > i0
+ M1 = (int) beta;
+ }
+
+ Ta[1] = 1;
+ for (m = 1; m < M1; m++) {
+ tem = alpha + 2 * m - 1;
+ Ta[m + 1] = (alpha + m - 1) * (alpha + beta + m - 1) *
+ (beta - m) * m * x * x / (tem * tem);
+ }
+
+ // term m = 0 in the next loop; avoid tem1 = 0/0 for alpha = 1
+ tem = alpha * (alpha + beta) / (alpha + 1);
+ Tb[1] = alpha - tem * x;
+
+ for (m = 1; m < M1; m++) {
+ tem = (alpha + m) * (alpha + beta + m) / (alpha + 2 * m + 1);
+ tem1 = m * (beta - m) / (alpha + 2 * m - 1);
+ Tb[m + 1] = alpha + 2 * m + (tem1 - tem) * x;
+ }
+
+ while (0. == Tb[M1] && M1 > 1) {
+ --M1;
+ }
+
+ // evaluate continuous fraction
+ double con = 0;
+ for (m = M1; m > 0; m--) {
+ con = Ta[m] / (Tb[m] + con);
+ }
+
+ tem = Num.lnBeta (alpha, beta) - alpha * Math.log (x) - beta * Math.log1p (-x);
+ return con * Math.exp (-tem);
+ }
+
+ @Deprecated
+ public static double cdf (double alpha, double beta, int d, double x) {
+ return cdf (alpha, beta, x);
+ }
+
+
+ @Deprecated
+ public static double cdf (double alpha, double beta,
+ double a, double b, int d, double x) {
+ return cdf (alpha, beta, d, (x - a)/(b - a));
+ }
+
+
+ @Deprecated
+ public static double barF (double alpha, double beta, int d, double x) {
+ return 1.0 - cdf (alpha, beta, d, x);
+ }
+
+
+ @Deprecated
+ public static double barF (double alpha, double beta,
+ double a, double b, int d, double x) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return 1.0 - cdf (alpha, beta, d, (x - a)/(b - a));
+ }
+
+ \end{hide}
+
+ public static double cdf (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= 1.0)
+ return 1.0;
+ if (1.0 == beta)
+ return Math.pow(x, alpha);
+
+ final double ALPHABETAMAX = 10000.0;
+ final double ALPHABETALIM = 30.0;
+
+ if (Math.max (alpha, beta) <= ALPHABETAMAX) {
+ return donato (alpha, beta, x);
+ }
+
+ if ((alpha > ALPHABETAMAX && beta < ALPHABETALIM) ||
+ (beta > ALPHABETAMAX && alpha < ALPHABETALIM)) {
+ return bolshev (alpha, beta, 12, x);
+ }
+
+ return peizer (alpha, beta, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as
+\method{cdf}{double,double,double,double,double}~\texttt{(alpha, beta, 0, 1, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta,
+ double a, double b, double x)\begin{hide} {
+ return cdf (alpha, beta, (x - a)/(b - a));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function.
+\begin{detailed} If
+ $\max (\alpha, \beta) \le 10^4$, uses a continuous fraction
+ in $\alpha$ and $\beta$ given in \cite{tDID92a,tCUY08a}.
+ Otherwise, if $\min (\alpha, \beta) \le 30$,
+ uses an approximation due to Bol'shev \cite{tMAR78a},
+ else uses a normal approximation \cite{tPEI68a}.
+\end{detailed}
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double x)\begin{hide} {
+ return cdf (beta, alpha, 1.0 - x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as
+ \method{barF}{double,double,double,double,double}~\texttt{(alpha, beta, 0, 1, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta,
+ double a, double b, double x)\begin{hide} {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return cdf (beta, alpha, (b - x)/(b - a));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ @Deprecated
+ public static double inverseF (double alpha, double beta, int d, double u) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0)
+ return 0;
+ if (u >= 1)
+ return 1;
+
+ /*
+ * Code taken from
+ * Cephes Math Library Release 2.8: June, 2000
+ * Copyright 1984, 1996, 2000 by Stephen L. Moshier
+ */
+ final double MACHEP = 1.11022302462515654042E-16;
+ final double MAXLOG = 7.09782712893383996843E2;
+ final double MINLOG = -7.08396418532264106224E2;
+ // final double MAXNUM = 1.7976931348623158E308;
+
+ boolean ihalve = false;
+ boolean newt = false;
+
+ double p = 0, q = 0, y0 = 0, z = 0, y = 0, x = 0, x0, x1, lgm = 0,
+ yp = 0, di = 0, dithresh = 0, yl, yh, xt = 0;
+ int i, dir;
+ boolean rflg, nflg;
+ x0 = 0.0;
+ yl = 0.0;
+ x1 = 1.0;
+ yh = 1.0;
+ nflg = false;
+ rflg = false;
+ if (alpha <= 1.0 || beta <= 1.0) {
+ dithresh = 1.0e-6;
+ rflg = false;
+ p = alpha;
+ q = beta;
+ y0 = u;
+ x = p/(p+q);
+ y = cdf (p, q, x);
+ ihalve = true;
+ }
+ else
+ dithresh = 1.0e-4;
+
+mainloop:
+ while (true) {
+ if (ihalve) {
+ ihalve = false;
+ dir = 0;
+ di = 0.5;
+ for (i = 0; i<100; i++) {
+ if (i != 0) {
+ x = x0 + di*(x1 - x0);
+ if (x == 1.0)
+ x = 1.0 - MACHEP;
+ if (x == 0.0) {
+ di = 0.5;
+ x = x0 + di*(x1 - x0);
+ if (x == 0.0) {
+ // System.err.println ("BetaDist.inverseF: underflow");
+ return 0;
+ }
+ }
+ y = cdf (p, q, x);
+ yp = (x1 - x0)/(x1 + x0);
+ if (Math.abs (yp) < dithresh) {
+ newt = true;
+ continue mainloop;
+ }
+ yp = (y-y0)/y0;
+ if (Math.abs (yp) < dithresh) {
+ newt = true;
+ continue mainloop;
+ }
+ }
+ if (y < y0) {
+ x0 = x;
+ yl = y;
+ if (dir < 0) {
+ dir = 0;
+ di = 0.5;
+ }
+ else if (dir > 3)
+ di = 1.0 - (1.0 - di)*(1.0 - di);
+ else if (dir > 1)
+ di = 0.5*di + 0.5;
+ else
+ di = (y0 - y)/(yh - yl);
+ dir += 1;
+ if (x0 > 0.75) {
+ // if (0 == y)
+ // y = EPS;
+ if (rflg) {
+ rflg = false;
+ p = alpha;
+ q = beta;
+ y0 = u;
+ }
+ else {
+ rflg = true;
+ p = beta;
+ q = alpha;
+ y0 = 1.0 - u;
+ }
+ x = 1.0 - x;
+ y = cdf (p, q, x);
+ x0 = 0.0;
+ yl = 0.0;
+ x1 = 1.0;
+ yh = 1.0;
+ ihalve = true;
+ continue mainloop;
+ }
+ }
+ else {
+ x1 = x;
+ if (rflg && x1 < MACHEP) {
+ x = 0.0;
+ break mainloop;
+ }
+ yh = y;
+ if (dir > 0) {
+ dir = 0;
+ di = 0.5;
+ }
+ else if (dir < -3)
+ di = di*di;
+ else if (dir < -1)
+ di = 0.5*di;
+ else
+ di = (y - y0)/(yh - yl);
+ dir -= 1;
+ }
+ }
+ // PLOSS error
+ if (x0 >= 1.0) {
+ x = 1.0 - MACHEP;
+ break mainloop;
+ }
+ if (x <= 0.0) {
+ // System.err.println ("BetaDist.inverseF: underflow");
+ return 0 ;
+ }
+ newt = true;
+ }
+ if (newt) {
+ newt = false;
+ if (nflg)
+ break mainloop;
+ nflg = true;
+ lgm = Num.lnGamma (p+q) - Num.lnGamma (p) - Num.lnGamma (q);
+
+ for (i=0; i<8; i++) {
+ /* Compute the function at this point. */
+ if (i != 0)
+ y = cdf (p, q, x);
+ if (y < yl) {
+ x = x0;
+ y = yl;
+ }
+ else if (y > yh) {
+ x = x1;
+ y = yh;
+ }
+ else if (y < y0) {
+ x0 = x;
+ yl = y;
+ }
+ else {
+ x1 = x;
+ yh = y;
+ }
+ if (x >= 1.0 || x <= 0.0)
+ break;
+ /* Compute the derivative of the function at this point. */
+ z = (p - 1.0)*Math.log (x) + (q - 1.0)*Math.log1p (-x) + lgm;
+ if (z < MINLOG)
+ break mainloop;
+ if (z > MAXLOG)
+ break;
+ z = Math.exp (z);
+ /* Compute the step to the next approximation of x. */
+ z = (y - y0)/z;
+ xt = x - z;
+ if (xt <= x0) {
+ y = (x - x0) / (x1 - x0);
+ xt = x0 + 0.5*y*(x - x0);
+ if (xt <= 0.0)
+ break;
+ }
+ if (xt >= x1) {
+ y = (x1 - x) / (x1 - x0);
+ xt = x1 - 0.5*y*(x1 - x);
+ if (xt >= 1.0)
+ break;
+ }
+ x = xt;
+ if (Math.abs (z/x) < 128.0*MACHEP)
+ break mainloop;
+ }
+ /* Did not converge. */
+ dithresh = 256.0*MACHEP;
+ ihalve = true;
+ continue mainloop;
+ }
+
+ yp = -NormalDist.inverseF01 (u);
+
+ if (u > 0.5) {
+ rflg = true;
+ p = beta;
+ q = alpha;
+ y0 = 1.0 - u;
+ yp = -yp;
+ }
+ else {
+ rflg = false;
+ p = alpha;
+ q = beta;
+ y0 = u;
+ }
+
+ lgm = (yp*yp - 3.0)/6.0;
+ x = 2.0/(1.0/(2.0*p-1.0) + 1.0/(2.0*q-1.0));
+ z = yp*Math.sqrt (x + lgm)/x
+ - (1.0/(2.0*q-1.0) - 1.0/(2.0*p-1.0) )
+ * (lgm + 5.0/6.0 - 2.0/(3.0*x));
+ z = 2.0*z;
+ if (z < MINLOG) {
+ x = 1.0;
+ // System.err.println ("BetaDist.inverseF: underflow");
+ return 0;
+ }
+ x = p/( p + q*Math.exp (z));
+ y = cdf (p, q, x);
+ yp = (y - y0)/y0;
+ if (Math.abs (yp) < 0.2) {
+ newt = true;
+ continue mainloop;
+ }
+ ihalve = true;
+ }
+
+ // Done
+ if (rflg) {
+ if (x <= MACHEP)
+ x = 1.0 - MACHEP;
+ else
+ x = 1.0 - x;
+ }
+ return x;
+ }\end{hide}
+
+ public static double inverseF (double alpha, double beta, double u)\begin{hide} {
+ return inverseF (alpha, beta, 12, u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as
+ \method{inverseF}{double,double,double,double,double}~\texttt{(alpha, beta, 0, 1, u)}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ @Deprecated
+ public static double inverseF (double alpha, double beta,
+ double a, double b, int d, double u) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return a + (b - a)*inverseF (alpha, beta, d, u);
+ }\end{hide}
+
+ public static double inverseF (double alpha, double beta,
+ double a, double b, double u)\begin{hide} {
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ return a + (b - a)*inverseF (alpha, beta, u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inverse beta distribution function
+ using the algorithm implemented in
+ \latex{\cite{iMOS00a}}%
+ \html{ the \htmladdnormallink{Cephes math library}{http://www.moshier.net/}}.
+ The method performs interval halving or Newton iterations to
+ compute the inverse.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sum = 0.0;
+ double a = 0.0;
+ double b = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ sum += x[i];
+ if (x[i] > 0.0)
+ a += Math.log (x[i]);
+ else
+ a -= 709.0;
+ if (x[i] < 1.0)
+ b += Math.log1p (-x[i]);
+ else
+ b -= 709.0;
+ }
+ double mean = sum / n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double var = sum / (n - 1);
+
+ Optim system = new Optim (a, b);
+
+ double[] param = new double[3];
+ param[1] = mean * ((mean * (1.0 - mean) / var) - 1.0);
+ param[2] = (1.0 - mean) * ((mean * (1.0 - mean) / var) - 1.0);
+ double[] fvec = new double [3];
+ double[][] fjac = new double[3][3];
+ int[] info = new int[2];
+ int[] ipvt = new int[3];
+
+ Minpack_f77.lmder1_f77 (system, 2, 2, param, fvec, fjac, 1e-5, info, ipvt);
+
+ double parameters[] = new double[2];
+ parameters[0] = param[1];
+ parameters[1] = param[2];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\beta)$ of the beta distribution over the
+ interval $[0,1]$ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\beta$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\alpha, \hat\beta)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \psi(\alpha) - \psi(\alpha + \beta) & = & \frac1n \sum_{i=1}^{n} \ln(x_i)\\
+ \psi(\beta) - \psi(\alpha + \beta) & = & \frac1n \sum_{i=1}^{n} \ln(1 - x_i)
+ \end{eqnarray*}
+ where $\bar x_n$ is the average of $x[0],\dots,x[n-1]$, and
+ $\psi$ is the logarithmic derivative of the Gamma function
+ $\psi(x) = \Gamma'(x) / \Gamma(x)$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static BetaDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new BetaDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a beta distribution with parameters $\alpha$ and
+ $\beta$ over the interval $[0,1]$ estimated using the maximum likelihood
+ method based on the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta)\begin{hide} {
+ return getMean (alpha, beta, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \alpha / (\alpha + \beta)$
+ of the beta distribution with parameters $\alpha$ and $\beta$, over the
+ interval $[0, 1]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Beta distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta, double a,
+ double b) \begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return (alpha*b + beta*a) / (alpha + beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+ $E[X] = (b\alpha + a\beta)/ (\alpha + \beta)$
+ of the beta distribution with parameters $\alpha$ and $\beta$ over the
+ interval $[a, b]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Beta distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta)\begin{hide} {
+ return getVariance (alpha, beta, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+ $\mbox{Var}[X] = \frac{\alpha\beta}{(\alpha + \beta)^2 (\alpha + \beta + 1)}$
+ of the beta distribution with parameters $\alpha$ and $\beta$, over the
+ interval $[0, 1]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the beta distribution
+ $\mbox{Var}[X] = \alpha\beta / [(\alpha + \beta)^2 (\alpha + \beta + 1)]$.}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta, double a,
+ double b) \begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return ((alpha * beta)*(b-a)*(b-a)) /
+ ((alpha + beta) * (alpha + beta) * (alpha + beta + 1));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+ $\mbox{Var}[X] = \frac{\alpha\beta(b-a)^2}
+ {(\alpha + \beta)^2 (\alpha + \beta + 1)}$
+ of the beta distribution with parameters $\alpha$ and $\beta$, over the
+ interval $[a, b]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the beta distribution
+ $\mbox{Var}[X] = \alpha\beta / [(\alpha + \beta)^2 (\alpha + \beta + 1)]$.}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta) \begin{hide} {
+ return Math.sqrt (BetaDist.getVariance (alpha, beta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard deviation of the beta distribution with
+ parameters $\alpha$ and $\beta$, over the interval $[0, 1]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Beta distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta,
+ double a, double b) \begin{hide} {
+ return Math.sqrt (BetaDist.getVariance (alpha, beta, a, b));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard deviation of the beta distribution with
+ parameters $\alpha$ and $\beta$, over the interval $[a, b]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Beta distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $a$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $b$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+ @Deprecated
+ public void setParams (double alpha, double beta,
+ double a, double b, int d) {
+ setParams (alpha, beta, a, b);
+ // this.decPrec = d;
+ }\end{hide}
+
+ public void setParams (double alpha, double beta, double a, double b)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ this.alpha = alpha;
+ this.beta = beta;
+ supportA = this.a = a;
+ supportB = this.b = b;
+ bminusa = b - a;
+ double temp = Num.lnGamma (alpha);
+ if (alpha == beta)
+ temp *= 2.0;
+ else
+ temp += Num.lnGamma (beta);
+ logBeta = temp - Num.lnGamma (alpha + beta);
+ Beta = Math.exp(logBeta);
+// this.factor = 1.0 / (Beta * Math.pow (bminusa, alpha + beta - 1));
+ this.logFactor = - logBeta - Math.log (bminusa) * (alpha + beta - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters of the current distribution. See the constructor.
+\end{tabb}
+\begin{code}
+
+ @Override
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, beta, a, b};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameters of the current distribution as [$\alpha$, $\beta$, $a$, $b$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ @Override
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/BetaSymmetricalDist.java b/source/umontreal/iro/lecuyer/probdist/BetaSymmetricalDist.java
new file mode 100644
index 0000000..8299705
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BetaSymmetricalDist.java
@@ -0,0 +1,971 @@
+
+/*
+ * Class: BetaSymmetricalDist
+ * Description: Symmetrical beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since April 2005
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Specializes the class {@link BetaDist} to the case of a <SPAN CLASS="textit">symmetrical</SPAN>
+ * <EM>beta</EM> distribution over the interval <SPAN CLASS="MATH">[0, 1]</SPAN>,
+ * with shape parameters
+ * <SPAN CLASS="MATH"><I>α</I> = <I>β</I></SPAN>.
+ * Faster methods are implemented here for this special case.
+ * Because of the symmetry around 1/2, four series are used to compute the
+ * <TT>cdf</TT>, two around <SPAN CLASS="MATH"><I>x</I> = 0</SPAN> and two around <SPAN CLASS="MATH"><I>x</I> = 1/2</SPAN>.
+ *
+ */
+public class BetaSymmetricalDist extends BetaDist {
+ private static final double PI_2 = 1.5707963267948966; // Pi/2
+ private static final int MAXI = 11; // Max number of Newton iterations
+ private static final int MAXIB = 50; // Max number of bisection iterations
+ private static final int MAXJ = 2000; // Max number of terms in series
+ private static final double EPSSINGLE = 1.0e-5;
+ private static final double EPSBETA = 1.0e-10; // < 0.75 sqrt(DBL_EPSILON)
+ private static final double SQPI_2 = 0.88622692545275801; // Sqrt(Pi)/2
+ private static final double LOG_SQPI_2 = -0.1207822376352453; // Ln(Sqrt(Pi)/2)
+ private static final double ALIM1 = 100000.0; // limit for normal approximation
+ private static final double LOG2 = 0.6931471805599453; // Ln(2)
+ private static final double LOG4 = 1.3862943611198906; // Ln(4)
+ private static final double INV2PI = 0.6366197723675813; // 2 / PI
+ private double Ceta;
+ private double logCeta;
+
+ private static class Function implements MathFunction
+ {
+ protected int n;
+ protected double sum;
+
+ public Function (double sum, int n) {
+ this.n = n;
+ this.sum = sum;
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0) return 1.0e100;
+ return (- 2.0 * n * Num.digamma (x) + n * 2.0 * Num.digamma (2.0 * x) + sum);
+ }
+ }
+
+
+
+ /**
+ * Constructs a <TT>BetaSymmetricalDist</TT> object with parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I> = <I>β</I> =</SPAN> <TT>alpha</TT>, over the unit interval <SPAN CLASS="MATH">(0, 1)</SPAN>.
+ *
+ */
+ public BetaSymmetricalDist (double alpha) {
+ super(alpha, alpha);
+ setParams (alpha, 14);
+ }
+
+
+ /**
+ * Same as <TT>BetaSymmetricalDist (alpha)</TT>, but using approximations of
+ * roughly <TT>d</TT> decimal digits of precision when computing the
+ * distribution, complementary distribution, and inverse functions.
+ *
+ */
+ public BetaSymmetricalDist (double alpha, int d) {
+ super(alpha, alpha, d);
+ setParams (alpha, d);
+ }
+
+
+ public double cdf (double x) {
+ return calcCdf (alpha, x, decPrec, logFactor, logBeta, logCeta, Ceta);
+ }
+
+ public double barF (double x) {
+ return calcCdf (alpha, 1.0 - x, decPrec, logFactor, logBeta,
+ logCeta, Ceta);
+ }
+
+ public double inverseF (double u) {
+ return calcInverseF (alpha, u, decPrec, logFactor, logBeta,
+ logCeta, Ceta);
+ }
+
+ /**
+ * Returns the density evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double density (double alpha, double x) {
+ return density (alpha, alpha, 0.0, 1.0, x);
+ }
+
+
+ /**
+ * Same as
+ * {@link #cdf(double,double,int,double) cdf} <TT>(alpha, alpha, d, x)</TT>.
+ *
+ */
+ public static double cdf (double alpha, int d, double x) {
+ return calcCdf (alpha, x, d, Num.DBL_MIN, 0.0, 0.0, 0.0);
+ }
+
+
+ /**
+ * Returns the complementary distribution function.
+ *
+ */
+ public static double barF (double alpha, int d, double x) {
+ return cdf (alpha, d, 1.0 - x);
+ }
+
+
+ /**
+ * Returns the inverse distribution function evaluated at <SPAN CLASS="MATH"><I>u</I></SPAN>, for the
+ * symmetrical beta distribution over the interval <SPAN CLASS="MATH">[0, 1]</SPAN>, with shape
+ * parameters
+ * <SPAN CLASS="MATH">0 < <I>α</I> = <I>β</I></SPAN> = <TT>alpha</TT>.
+ * Uses four different hypergeometric series
+ * to compute the distribution <SPAN CLASS="MATH"><I>u</I> = <I>F</I>(<I>x</I>)</SPAN>
+ * (for the four cases <SPAN CLASS="MATH"><I>x</I></SPAN> close to 0 and
+ * <SPAN CLASS="MATH"><I>α</I> < 1</SPAN>,
+ * <SPAN CLASS="MATH"><I>x</I></SPAN> close to 0 and
+ * <SPAN CLASS="MATH"><I>α</I> > 1</SPAN>, <SPAN CLASS="MATH"><I>x</I></SPAN> close to 1/2 and
+ * <SPAN CLASS="MATH"><I>α</I> < 1</SPAN>,
+ * and <SPAN CLASS="MATH"><I>x</I></SPAN> close to 1/2 and
+ * <SPAN CLASS="MATH"><I>α</I> > 1</SPAN>),
+ * which are then solved by Newton's method for the solution of equations.
+ * For
+ * <SPAN CLASS="MATH"><I>α</I> > 100000</SPAN>, uses a normal approximation given in.
+ *
+ */
+ public static double inverseF (double alpha, double u) {
+ return calcInverseF (alpha, u, 14, Num.DBL_MIN, 0.0, 0.0, 0.0);
+ }
+
+ /*----------------------------------------------------------------------*/
+
+ private static double series1 (double alpha, double x, double epsilon)
+ /*
+ * Compute the series for F(x).
+ * This series is used for alpha < 1 and x close to 0.
+ */
+ {
+ int j;
+ double sum, term;
+ double poc = 1.0;
+ sum = 1.0 / alpha;
+ j = 1;
+ do {
+ poc *= x * (j - alpha) / j;
+ term = poc / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((term > sum * epsilon) && (j < MAXJ));
+
+ return sum * Math.pow (x, alpha);
+ }
+
+
+ /*------------------------------------------------------------------------*/
+
+ private static double series2 (double alpha, double y, double epsilon)
+ /*
+ * Compute the series for G(y). y = 0.5 - x.
+ * This series is used for alpha < 1 and x close to 1/2.
+ */
+ {
+ int j;
+ double term, sum;
+ double poc;
+ final double z = 4.0 * y * y;
+
+ /* Compute the series for G(y) */
+ poc = sum = 1.0;
+ j = 1;
+ do {
+ poc *= z * (j - alpha) / j;
+ term = poc / (2 * j + 1);
+ sum += term;
+ ++j;
+ } while ((term > sum * epsilon) && (j < MAXJ));
+
+ return sum * y;
+ }
+
+
+ /*------------------------------------------------------------------------*/
+
+ private static double series3 (double alpha, double x, double epsilon)
+ /*
+ * Compute the series for F(x).
+ * This series is used for alpha > 1 and x close to 0.
+ */
+ {
+ int j;
+ double sum, term;
+ final double z = -x / (1.0 - x);
+
+ sum = term = 1.0;
+ j = 1;
+ do {
+ term *= z * (j - alpha) / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((Math.abs (term) > sum * epsilon) && (j < MAXJ));
+
+ return sum * x;
+ }
+
+
+ /*------------------------------------------------------------------------*/
+
+ private static double series4 (double alpha, double y, double epsilon)
+ /*
+ * Compute the series for G(y). y = 0.5 - x.
+ * This series is used for alpha > 1 and x close to 1/2.
+ */
+ {
+ int j;
+ double term, sum;
+ final double z = 4.0 * y * y;
+
+ term = sum = 1.0;
+ j = 1;
+ do {
+ term *= z * (j + alpha - 0.5) / (0.5 + j);
+ sum += term;
+ ++j;
+ } while ((term > sum * epsilon) && (j < MAXJ));
+
+ return sum * y;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ private static double Peizer (double alpha, double x)
+ /*
+ * Normal approximation of Peizer and Pratt
+ */
+ {
+ final double y = 1.0 - x;
+ double z;
+ z = Math.sqrt ((1.0 - y * BetaDist.beta_g (2.0 * x) -
+ x * BetaDist.beta_g (2.0 * y)) / ((2.0*alpha - 5.0 / 6.0) * x * y)) *
+ (2.0*x - 1.0) * (alpha - 1.0 / 3.0 + 0.025 / alpha);
+
+ return NormalDist.cdf01 (z);
+ }
+
+ /*-------------------------------------------------------------------------*/
+
+ private static double inverse1 (
+ double alpha, // Shape parameter
+ double bu, // u * Beta(alpha, alpha)
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha < 1 and x close to 0.
+ */
+ {
+ int i, j;
+ double x, xnew, poc, sum, term;
+ final double EPSILON = EPSARRAY[d];
+
+ // First term of series
+ x = Math.pow (bu * alpha, 1.0 / alpha);
+
+ // If T1/T0 is very small, neglect all terms of series except T0
+ term = alpha * (1.0 - alpha) * x / (1.0 + alpha);
+ if (term < EPSILON)
+ return x;
+
+ x = bu * alpha / (1.0 + term);
+ xnew = Math.pow (x, 1.0/alpha); // Starting point of Newton's iterates
+
+ i = 0;
+ do {
+ ++i;
+ x = xnew;
+
+ /* Compute the series for F(x) */
+ poc = 1.0;
+ sum = 1.0 / alpha;
+ j = 1;
+ do {
+ poc *= x * (j - alpha) / j;
+ term = poc / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((term > sum * EPSILON) && (j < MAXJ));
+ sum *= Math.pow (x, alpha);
+
+ /* Newton's method */
+ term = (sum - bu) * Math.pow (x*(1.0 - x), 1.0 - alpha);
+ xnew = x - term;
+
+ } while ((Math.abs (term) > EPSILON) && (i <= MAXI));
+
+ return xnew;
+ }
+
+ /*----------------------------------------------------------------------*/
+
+ private static double inverse2 (
+ double alpha, // Shape parameter
+ double w, // (0.5 - u)B/pow(4, 1 - alpha)
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha < 1 and x close to 1/2.
+ */
+ {
+ int i, j;
+ double term, y, ynew, z, sum;
+ double poc;
+ final double EPSILON = EPSARRAY[d];
+
+ term = (1.0 - alpha) * w * w * 4.0 / 3.0;
+ /* If T1/T0 is very small, neglect all terms of series except T0 */
+ if (term < EPSILON)
+ return 0.5 - w;
+
+ ynew = w / (1 + term); /* Starting point of Newton's iterates */
+ i = 0;
+ do {
+ ++i;
+ y = ynew;
+ z = 4.0 * y * y;
+
+ // Compute the series for G(y)
+ poc = sum = 1.0;
+ j = 1;
+ do {
+ poc *= z * (j - alpha) / j;
+ term = poc / (2 * j + 1);
+ sum += term;
+ ++j;
+ } while ((term > sum * EPSILON) && (j < MAXJ));
+
+ sum *= y;
+
+ // Newton's method
+ ynew = y - (sum - w) * Math.pow (1.0 - z, 1.0 - alpha);
+
+ } while ((Math.abs (ynew - y) > EPSILON) && (i <= MAXI));
+
+ return 0.5 - ynew;
+ }
+
+
+ /*---------------------------------------------------------------------*/
+
+ private static double bisect (
+ double alpha, // Shape parameter
+ double logBua, // Ln(alpha * u * Beta(alpha, alpha))
+ double a, // x is presumed in [a, b]
+ double b,
+ int d // Digits of precision
+)
+ /*
+ * This method is used for alpha > 1 and u very close to 0. It will almost
+ * never be called, if at all.
+ */
+ {
+ int i, j;
+ double z, sum, term;
+ double x, xprev;
+ final double EPSILON = EPSARRAY[d];
+
+ if (a >= 0.5 || a > b) {
+ a = 0.0;
+ b = 0.5;
+ }
+
+ x = 0.5 * (a + b);
+ i = 0;
+ do {
+ ++i;
+ z = -x / (1 - x);
+
+ /* Compute the series for F(x) */
+ sum = term = 1.0;
+ j = 1;
+ do {
+ term *= z * (j - alpha) / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((Math.abs (term/sum) > EPSILON) && (j < MAXJ));
+ sum *= x;
+
+ /* Bisection method */
+ term = Math.log (x * (1.0 - x));
+ z = logBua - (alpha - 1.0) * term;
+ if (z > Math.log(sum))
+ a = x;
+ else
+ b = x;
+ xprev = x;
+ x = 0.5 * (a + b);
+
+ } while ((Math.abs(xprev - x) > EPSILON) && (i < MAXIB));
+
+ return x;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static double inverse3 (
+ double alpha, // Shape parameter
+ double logBua, // Ln(alpha * u * Beta(alpha, alpha))
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha > 1 and x close to 0.
+ */
+ {
+ int i, j;
+ double z, x, w, xnew, sum = 0., term;
+ double eps = EPSSINGLE;
+ final double EPSILON = EPSARRAY[d];
+ // For alpha <= 100000 and u < 1.0/(2.5 + 2.25*sqrt(alpha)), X0 is always
+ // to the right of the solution, so Newton is certain to converge.
+ final double X0 = 0.497;
+
+ /* Compute starting point of Newton's iterates */
+ w = logBua / alpha;
+ x = Math.exp (w);
+ term = (Math.log1p(-x) + logBua) / alpha;
+ z = Math.exp (term);
+ if (z >= 0.25)
+ xnew = X0;
+ else if (z > 1.0e-6)
+ xnew = (1.0 - Math.sqrt(1.0 - 4.0*z)) / 2.0;
+ else
+ xnew = z;
+
+ i = 0;
+ do {
+ ++i;
+ if (xnew >= 0.5)
+ xnew = X0;
+ x = xnew;
+
+ sum = Math.log (x * (1.0 - x));
+ w = logBua - (alpha - 1.0) * sum;
+ if (Math.abs (w) >= Num.DBL_MAX_EXP * Num.LN2) {
+ xnew = X0;
+ continue;
+ }
+ w = Math.exp (w);
+ z = -x / (1 - x);
+
+ /* Compute the series for F(x) */
+ sum = term = 1.0;
+ j = 1;
+ do {
+ term *= z * (j - alpha) / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((Math.abs (term/sum) > eps) && (j < MAXJ));
+ sum *= x;
+
+ /* Newton's method */
+ term = (sum - w) / alpha;
+ xnew = x - term;
+ if (Math.abs(term) < 32.0*EPSSINGLE)
+ eps = EPSILON;
+
+ } while ( (Math.abs (xnew - x) > sum * EPSILON) &&
+ (Math.abs (xnew - x) > EPSILON) && (i <= MAXI));
+
+ /* If Newton has not converged with enough precision, call bisection
+ method. It is very slow, but will be called very rarely. */
+ if (i >= MAXI && Math.abs (xnew - x) > 10.0 * EPSILON)
+ return bisect (alpha, logBua, 0.0, 0.5, d);
+ return xnew;
+ }
+
+
+ /*---------------------------------------------------------------------*/
+
+ private static double inverse4 (
+ double alpha, // Shape parameter
+ double logBva, // Ln(B) + Ln(1/2 - u) + (alpha - 1)*Ln(4)
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha > 1 and x close to 1/2.
+ */
+ {
+ int i, j;
+ double term, sum, y, ynew, z;
+ double eps = EPSSINGLE;
+ final double EPSILON = EPSARRAY[d];
+
+ ynew = Math.exp (logBva); // Starting point of Newton's iterates
+ i = 0;
+ do {
+ ++i;
+ y = ynew;
+
+ /* Compute the series for G(y) */
+ z = 4.0 * y * y;
+ term = sum = 1.0;
+ j = 1;
+ do {
+ term *= z * (j + alpha - 0.5) / (0.5 + j);
+ sum += term;
+ ++j;
+ } while ((term > sum * eps) && (j < MAXJ));
+ sum *= y * (1.0 - z);
+
+ /* Newton's method */
+ term = Math.log1p (-z);
+ term = sum - Math.exp (logBva - (alpha - 1.0) * term);
+ ynew = y - term;
+ if (Math.abs(term) < 32.0*EPSSINGLE)
+ eps = EPSILON;
+
+ } while ((Math.abs (ynew - y) > EPSILON) &&
+ (Math.abs (ynew - y) > sum*EPSILON) && (i <= MAXI));
+
+ return 0.5 - ynew;
+ }
+
+
+ /*---------------------------------------------------------------------*/
+
+ private static double PeizerInverse (double alpha, double u)
+ {
+ /* Inverse of the normal approximation of Peizer and Pratt */
+ double t1, t3, xprev;
+ final double C2 = alpha - 1.0 / 3.0 + 0.025 / alpha;
+ final double z = NormalDist.inverseF01 (u);
+ double x = 0.5;
+ double y = 1.0 - x;
+ int i = 0;
+
+ do {
+ i++;
+ t1 = (2.0 * alpha - 5.0 / 6.0) * x * y;
+ t3 = 1.0 - y * BetaDist.beta_g (2.0 * x) - x * BetaDist.beta_g (2.0 * y);
+ xprev = x;
+ x = 0.5 + 0.5 * z * Math.sqrt(t1 / t3) / C2;
+ y = 1.0 - x;
+ } while (i <= MAXI && Math.abs (x - xprev) > EPSBETA);
+
+ return x;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static void CalcB4 (double alpha, double [] bc, double epsilon)
+ {
+ double temp;
+ double pB = 0.0;
+ double plogB = 0.0;
+ double plogC = 0.0;
+
+ /* Compute Beta(alpha, alpha) or Beta(alpha, alpha)*4^(alpha-1). */
+
+ if (alpha <= EPSBETA) {
+ /* For a -> 0, B(a,a) = (2/a)*(1 - 1.645*a^2 + O(a^3)) */
+ pB = 2.0 / alpha;
+ plogB = Math.log(pB);
+ plogC = plogB + (alpha - 1.0)*LOG4;
+
+ } else if (alpha <= 1.0) {
+ temp = Num.lnGamma(alpha);
+ plogB = 2.0 * temp - Num.lnGamma(2.0*alpha);
+ plogC = plogB + (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+
+ } else if (alpha <= 10.0) {
+ plogC = Num.lnGamma(alpha) - Num.lnGamma(0.5 + alpha) + LOG_SQPI_2;
+ plogB = plogC - (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+
+ } else if (alpha <= 200.0) {
+ /* Convergent series for Gamma(x + 0.5) / Gamma(x) */
+ double term = 1.0;
+ double sum = 1.0;
+ int i = 1;
+ while (term > epsilon*sum) {
+ term *= (i - 1.5)*(i - 1.5) /(i*(alpha + i - 1.5));
+ sum += term;
+ i++;
+ }
+ temp = SQPI_2 / Math.sqrt ((alpha - 0.5)*sum);
+ plogC = Math.log(temp);
+ plogB = plogC - (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+
+ } else {
+ /* Asymptotic series for Gamma(a + 0.5) / (Gamma(a) * Sqrt(a)) */
+ double z = 1.0 / (8.0*alpha);
+ temp = 1.0 + z*(-1.0 + z*(0.5 + z*(2.5 - z*(2.625 + 49.875*z))));
+ /* This is 4^(alpha - 1)*B(alpha, alpha) */
+ temp = SQPI_2 / (Math.sqrt(alpha) * temp);
+ plogC = Math.log(temp);
+ plogB = plogC - (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+ }
+ bc[0] = pB;
+ bc[1] = plogB;
+ bc[2] = plogC;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static double calcInverseF (double alpha, double u, int d,
+ double logFact, double logBeta, double logCeta, double Ceta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u == 0.0) return 0.0;
+ if (u == 1.0) return 1.0;
+ if (u == 0.5) return 0.5;
+
+ // Case alpha = 1 is the uniform law
+ if (alpha == 1.0) return u;
+
+ // Case alpha = 1/2 is the arcsin law
+ double temp;
+ if (alpha == 0.5) {
+ temp = Math.sin (u * PI_2);
+ return temp * temp;
+ }
+
+ if (alpha > ALIM1)
+ return PeizerInverse (alpha, u);
+
+ boolean isUpper; // True if u > 0.5
+ if (u > 0.5) {
+ isUpper = true;
+ u = 1.0 - u;
+ } else
+ isUpper = false;
+
+ double x;
+ double C = 0.0, B = 0.0, logB = 0.0, logC = 0.0;
+
+ if (logFact == Num.DBL_MIN) {
+ double [] bc = new double[] {0.0, 0.0, 0.0};
+ CalcB4 (alpha, bc, EPSARRAY[d]);
+ B = bc[0]; logB = bc[1]; logC = bc[2];
+ C = Math.exp(logC);
+ } else {
+ B = 1.0/ Math.exp(logFact);
+ logB = logBeta;
+ logC = logCeta;
+ C = Ceta;
+ }
+
+ if (alpha <= 1.0) {
+ // First term of integrated series around 1/2
+ double y0 = C * (0.5 - u);
+ if (y0 > 0.25)
+ x = inverse1 (alpha, B * u, d);
+ else
+ x = inverse2 (alpha, y0, d);
+
+ } else {
+ if (u < 1.0 / (2.5 + 2.25*Math.sqrt(alpha))) {
+ double logBua = logB + Math.log (u * alpha);
+ x = inverse3 (alpha, logBua, d);
+ } else {
+ // logBva = Ln(Beta(a,a) * (0.5 - u)*pow(4, a - 1)
+ double logBva = logC - LOG2 + Math.log1p (-2.0*u);
+
+ x = inverse4 (alpha, logBva, d);
+ }
+ }
+
+ if (isUpper)
+ return 1.0 - x - Num.DBL_EPSILON;
+ else
+ return x;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static double calcCdf (double alpha, double x, int d,
+ double logFact, double logBeta, double logCeta, double Ceta) {
+ double temp, u, logB = 0.0, logC = 0.0, C = 0.0;
+ boolean isUpper; /* True if x > 0.5 */
+ double B = 0.0; /* Beta(alpha, alpha) */
+ double x0;
+ final double EPSILON = EPSARRAY[d]; // Absolute precision
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ if (x <= 0.0) return 0.0;
+ if (x >= 1.0) return 1.0;
+ if (x == 0.5) return 0.5;
+ if (alpha == 1.0) return x; /* alpha = 1 is the uniform law */
+ if (alpha == 0.5) /* alpha = 1/2 is the arcsin law */
+ return INV2PI * Math.asin(Math.sqrt(x));
+
+ if (alpha > ALIM1)
+ return Peizer (alpha, x);
+
+ if (x > 0.5) {
+ x = 1.0 - x;
+ isUpper = true;
+ } else
+ isUpper = false;
+
+ if (logFact == Num.DBL_MIN) {
+ double [] bc = new double[3];
+ bc[0] = B; bc[1] = logB; bc[2] = logC;
+ CalcB4 (alpha, bc, EPSILON);
+ B = bc[0]; logB = bc[1]; logC = bc[2];
+ C = Math.exp(logC);
+ } else {
+ B = 1.0/ Math.exp(logFact);
+ logB = logBeta;
+ logC = logCeta;
+ C = Ceta;
+ }
+
+ if (alpha <= 1.0) {
+ /* For x = x0, both series use the same number of terms to get the
+ required precision */
+ if (x > 0.25) {
+ temp = -Math.log (alpha);
+ if (alpha >= 1.0e-6)
+ x0 = 0.25 + 0.005 * temp;
+ else
+ x0 = 0.13863 + .01235 * temp;
+ } else
+ x0 = 0.25;
+
+ if (x <= x0)
+ u = (series1 (alpha, x, EPSILON)) / B;
+ else
+ u = 0.5 - (series2 (alpha, 0.5 - x, EPSILON)) / C;
+
+ } else { /* 1 < alpha < ALIM1 */
+ if (alpha < 400.0)
+ x0 = 0.5 - 0.9 / Math.sqrt(4.0*alpha);
+ else
+ x0 = 0.5 - 1.0 / Math.sqrt(alpha);
+ if (x0 < 0.25)
+ x0 = 0.25;
+
+ if (x <= x0) {
+ temp = (alpha - 1.0) * Math.log (x * (1.0 - x)) - logB;
+ u = series3 (alpha, x, EPSILON) * Math.exp(temp) / alpha;
+
+ } else {
+ final double y = 0.5 - x;
+ if (y > 0.05) {
+ temp = Math.log(1.0 - 4.0*y*y);
+ } else {
+ u = 4.0*y*y;
+ temp = -u * (1.0 + u * (0.5 + u *(1.0/3.0 + u*(0.25 +
+ u*(0.2 + u*(1.0/6.0 + u*1.0/7.0))))));
+ }
+ temp = alpha * temp - logC;
+ u = 0.5 - (series4 (alpha, y, EPSILON)) * Math.exp(temp);
+ }
+ }
+
+ if (isUpper)
+ return 1.0 - u;
+ else
+ return u;
+ }
+
+ public double getMean() {
+ return 0.5;
+ }
+
+ public double getVariance() {
+ return BetaSymmetricalDist.getVariance (alpha);
+ }
+
+ public double getStandardDeviation() {
+ return BetaSymmetricalDist.getStandardDeviation (alpha);
+ }
+
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of the symmetrical beta distribution
+ * over the interval [0, 1] using the maximum likelihood method, from the
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimate is returned
+ * in element 0 of the returned array.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double var = 0.0;
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ var += ((x[i] - 0.5) * (x[i] - 0.5));
+ if (x[i] > 0.0 && x[i] < 1.0)
+ sum += Math.log (x[i] * (1.0 - x[i]));
+ else
+ sum -= 709.0;
+ }
+ var /= n;
+
+ Function f = new Function (sum, n);
+
+ double[] parameters = new double[1];
+ double alpha0 = (1.0 - 4.0 * var) / (8.0 * var);
+
+ double a = alpha0 - 5.0;
+ if (a <= 0.0)
+ a = 1e-15;
+
+ parameters[0] = RootFinder.brentDekker (a, alpha0 + 5.0, f, 1e-5);
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a symmetrical beta distribution with parameter <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static BetaSymmetricalDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new BetaSymmetricalDist (parameters[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = 1/2</SPAN> of the symmetrical beta
+ * distribution with parameter <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ * @return the mean of the symmetrical beta distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = 1/2</SPAN>
+ *
+ */
+ public static double getMean (double alpha) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ return 0.5;
+ }
+
+
+ /**
+ * Computes and returns the variance,
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1/(8<I>α</I> + 4)</SPAN>,
+ * of the symmetrical beta distribution with parameter <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ * @return the variance of the symmetrical beta distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1/[4(2<I>α</I> + 1)]</SPAN>
+ *
+ */
+ public static double getVariance (double alpha) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ return (1 / (8 * alpha + 4));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the
+ * symmetrical beta distribution with parameter <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ * @return the standard deviation of the symmetrical beta distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ return (1 / Math.sqrt(8 * alpha + 4));
+ }
+
+
+ public void setParams (double alpha, double beta, double a, double b, int d) {
+ // We don't want to calculate Beta, logBeta and logFactor twice.
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ this.decPrec = d;
+ supportA = this.a = a;
+ supportB = this.b = b;
+ bminusa = b - a;
+ }
+
+ private void setParams (double alpha, int d) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.alpha = alpha;
+ beta = alpha;
+
+ double [] bc = new double[] {0.0, 0.0, 0.0};
+ CalcB4 (alpha, bc, EPSARRAY[d]);
+ Beta = bc[0]; logBeta = bc[1]; logCeta = bc[2];
+ Ceta = Math.exp (logCeta);
+ if (Beta > 0.0)
+ logFactor = -logBeta - (2.0*alpha - 1) * Math.log(bminusa);
+ else
+ logFactor = 0.0;
+ }
+
+ /**
+ * Return a table containing the parameter of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/BetaSymmetricalDist.tex b/source/umontreal/iro/lecuyer/probdist/BetaSymmetricalDist.tex
new file mode 100644
index 0000000..6d7e001
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BetaSymmetricalDist.tex
@@ -0,0 +1,984 @@
+\defmodule {BetaSymmetricalDist}
+
+Specializes the class \class{BetaDist} to the case of a \emph{symmetrical}
+{\em beta\/} distribution over the interval $[0,1]$,
+with shape parameters $\alpha = \beta$.
+Faster methods are implemented here for this special case \cite{rLEC06a}.
+Because of the symmetry around 1/2, four series are used to compute the
+\texttt{cdf}, two around $x = 0$ and two around $x = 1/2$.
+% Given $u$, one then solves each series for $x$ by using the
+% Newton-Raphson method which shows quadratic convergence when the
+% starting iterate is close enough to the solution $x$.
+% one uses a series expansion of the distribution around 0 and the Newton
+% method of iteration for the solution of equations.
+% ^^ Unclear. Expansion of the distribution? Solution of which equations?
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: BetaSymmetricalDist
+ * Description: Symmetrical beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since April 2005
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class BetaSymmetricalDist extends BetaDist\begin{hide} {
+ private static final double PI_2 = 1.5707963267948966; // Pi/2
+ private static final int MAXI = 11; // Max number of Newton iterations
+ private static final int MAXIB = 50; // Max number of bisection iterations
+ private static final int MAXJ = 2000; // Max number of terms in series
+ private static final double EPSSINGLE = 1.0e-5;
+ private static final double EPSBETA = 1.0e-10; // < 0.75 sqrt(DBL_EPSILON)
+ private static final double SQPI_2 = 0.88622692545275801; // Sqrt(Pi)/2
+ private static final double LOG_SQPI_2 = -0.1207822376352453; // Ln(Sqrt(Pi)/2)
+ private static final double ALIM1 = 100000.0; // limit for normal approximation
+ private static final double LOG2 = 0.6931471805599453; // Ln(2)
+ private static final double LOG4 = 1.3862943611198906; // Ln(4)
+ private static final double INV2PI = 0.6366197723675813; // 2 / PI
+ private double Ceta;
+ private double logCeta;
+
+ private static class Function implements MathFunction
+ {
+ protected int n;
+ protected double sum;
+
+ public Function (double sum, int n) {
+ this.n = n;
+ this.sum = sum;
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0) return 1.0e100;
+ return (- 2.0 * n * Num.digamma (x) + n * 2.0 * Num.digamma (2.0 * x) + sum);
+ }
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BetaSymmetricalDist (double alpha)\begin{hide} {
+ super(alpha, alpha);
+ setParams (alpha, 14);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{BetaSymmetricalDist} object with parameters
+ $\alpha = \beta =$ \texttt{alpha}, over the unit interval $(0,1)$.
+ \end{tabb}
+\begin{code}
+
+ public BetaSymmetricalDist (double alpha, int d)\begin{hide} {
+ super(alpha, alpha, d);
+ setParams (alpha, d);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \texttt{BetaSymmetricalDist (alpha)}, but using approximations of
+ roughly \texttt{d} decimal digits of precision when computing the
+ distribution, complementary distribution, and inverse functions.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double cdf (double x) {
+ return calcCdf (alpha, x, decPrec, logFactor, logBeta, logCeta, Ceta);
+ }
+
+ public double barF (double x) {
+ return calcCdf (alpha, 1.0 - x, decPrec, logFactor, logBeta,
+ logCeta, Ceta);
+ }
+
+ public double inverseF (double u) {
+ return calcInverseF (alpha, u, decPrec, logFactor, logBeta,
+ logCeta, Ceta);
+ }\end{hide}
+
+ public static double density (double alpha, double x)\begin{hide} {
+ return density (alpha, alpha, 0.0, 1.0, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the density evaluated at $x$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, int d, double x)\begin{hide} {
+ return calcCdf (alpha, x, d, Num.DBL_MIN, 0.0, 0.0, 0.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as
+ \method{cdf}{double,double,int,double}~\texttt{(alpha, alpha, d, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, int d, double x)\begin{hide} {
+ return cdf (alpha, d, 1.0 - x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the complementary distribution function.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double u)\begin{hide} {
+ return calcInverseF (alpha, u, 14, Num.DBL_MIN, 0.0, 0.0, 0.0);
+ }
+
+ /*----------------------------------------------------------------------*/
+
+ private static double series1 (double alpha, double x, double epsilon)
+ /*
+ * Compute the series for F(x).
+ * This series is used for alpha < 1 and x close to 0.
+ */
+ {
+ int j;
+ double sum, term;
+ double poc = 1.0;
+ sum = 1.0 / alpha;
+ j = 1;
+ do {
+ poc *= x * (j - alpha) / j;
+ term = poc / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((term > sum * epsilon) && (j < MAXJ));
+
+ return sum * Math.pow (x, alpha);
+ }
+
+
+ /*------------------------------------------------------------------------*/
+
+ private static double series2 (double alpha, double y, double epsilon)
+ /*
+ * Compute the series for G(y). y = 0.5 - x.
+ * This series is used for alpha < 1 and x close to 1/2.
+ */
+ {
+ int j;
+ double term, sum;
+ double poc;
+ final double z = 4.0 * y * y;
+
+ /* Compute the series for G(y) */
+ poc = sum = 1.0;
+ j = 1;
+ do {
+ poc *= z * (j - alpha) / j;
+ term = poc / (2 * j + 1);
+ sum += term;
+ ++j;
+ } while ((term > sum * epsilon) && (j < MAXJ));
+
+ return sum * y;
+ }
+
+
+ /*------------------------------------------------------------------------*/
+
+ private static double series3 (double alpha, double x, double epsilon)
+ /*
+ * Compute the series for F(x).
+ * This series is used for alpha > 1 and x close to 0.
+ */
+ {
+ int j;
+ double sum, term;
+ final double z = -x / (1.0 - x);
+
+ sum = term = 1.0;
+ j = 1;
+ do {
+ term *= z * (j - alpha) / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((Math.abs (term) > sum * epsilon) && (j < MAXJ));
+
+ return sum * x;
+ }
+
+
+ /*------------------------------------------------------------------------*/
+
+ private static double series4 (double alpha, double y, double epsilon)
+ /*
+ * Compute the series for G(y). y = 0.5 - x.
+ * This series is used for alpha > 1 and x close to 1/2.
+ */
+ {
+ int j;
+ double term, sum;
+ final double z = 4.0 * y * y;
+
+ term = sum = 1.0;
+ j = 1;
+ do {
+ term *= z * (j + alpha - 0.5) / (0.5 + j);
+ sum += term;
+ ++j;
+ } while ((term > sum * epsilon) && (j < MAXJ));
+
+ return sum * y;
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ private static double Peizer (double alpha, double x)
+ /*
+ * Normal approximation of Peizer and Pratt
+ */
+ {
+ final double y = 1.0 - x;
+ double z;
+ z = Math.sqrt ((1.0 - y * BetaDist.beta_g (2.0 * x) -
+ x * BetaDist.beta_g (2.0 * y)) / ((2.0*alpha - 5.0 / 6.0) * x * y)) *
+ (2.0*x - 1.0) * (alpha - 1.0 / 3.0 + 0.025 / alpha);
+
+ return NormalDist.cdf01 (z);
+ }
+
+ /*-------------------------------------------------------------------------*/
+
+ private static double inverse1 (
+ double alpha, // Shape parameter
+ double bu, // u * Beta(alpha, alpha)
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha < 1 and x close to 0.
+ */
+ {
+ int i, j;
+ double x, xnew, poc, sum, term;
+ final double EPSILON = EPSARRAY[d];
+
+ // First term of series
+ x = Math.pow (bu * alpha, 1.0 / alpha);
+
+ // If T1/T0 is very small, neglect all terms of series except T0
+ term = alpha * (1.0 - alpha) * x / (1.0 + alpha);
+ if (term < EPSILON)
+ return x;
+
+ x = bu * alpha / (1.0 + term);
+ xnew = Math.pow (x, 1.0/alpha); // Starting point of Newton's iterates
+
+ i = 0;
+ do {
+ ++i;
+ x = xnew;
+
+ /* Compute the series for F(x) */
+ poc = 1.0;
+ sum = 1.0 / alpha;
+ j = 1;
+ do {
+ poc *= x * (j - alpha) / j;
+ term = poc / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((term > sum * EPSILON) && (j < MAXJ));
+ sum *= Math.pow (x, alpha);
+
+ /* Newton's method */
+ term = (sum - bu) * Math.pow (x*(1.0 - x), 1.0 - alpha);
+ xnew = x - term;
+
+ } while ((Math.abs (term) > EPSILON) && (i <= MAXI));
+
+ return xnew;
+ }
+
+ /*----------------------------------------------------------------------*/
+
+ private static double inverse2 (
+ double alpha, // Shape parameter
+ double w, // (0.5 - u)B/pow(4, 1 - alpha)
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha < 1 and x close to 1/2.
+ */
+ {
+ int i, j;
+ double term, y, ynew, z, sum;
+ double poc;
+ final double EPSILON = EPSARRAY[d];
+
+ term = (1.0 - alpha) * w * w * 4.0 / 3.0;
+ /* If T1/T0 is very small, neglect all terms of series except T0 */
+ if (term < EPSILON)
+ return 0.5 - w;
+
+ ynew = w / (1 + term); /* Starting point of Newton's iterates */
+ i = 0;
+ do {
+ ++i;
+ y = ynew;
+ z = 4.0 * y * y;
+
+ // Compute the series for G(y)
+ poc = sum = 1.0;
+ j = 1;
+ do {
+ poc *= z * (j - alpha) / j;
+ term = poc / (2 * j + 1);
+ sum += term;
+ ++j;
+ } while ((term > sum * EPSILON) && (j < MAXJ));
+
+ sum *= y;
+
+ // Newton's method
+ ynew = y - (sum - w) * Math.pow (1.0 - z, 1.0 - alpha);
+
+ } while ((Math.abs (ynew - y) > EPSILON) && (i <= MAXI));
+
+ return 0.5 - ynew;
+ }
+
+
+ /*---------------------------------------------------------------------*/
+
+ private static double bisect (
+ double alpha, // Shape parameter
+ double logBua, // Ln(alpha * u * Beta(alpha, alpha))
+ double a, // x is presumed in [a, b]
+ double b,
+ int d // Digits of precision
+)
+ /*
+ * This method is used for alpha > 1 and u very close to 0. It will almost
+ * never be called, if at all.
+ */
+ {
+ int i, j;
+ double z, sum, term;
+ double x, xprev;
+ final double EPSILON = EPSARRAY[d];
+
+ if (a >= 0.5 || a > b) {
+ a = 0.0;
+ b = 0.5;
+ }
+
+ x = 0.5 * (a + b);
+ i = 0;
+ do {
+ ++i;
+ z = -x / (1 - x);
+
+ /* Compute the series for F(x) */
+ sum = term = 1.0;
+ j = 1;
+ do {
+ term *= z * (j - alpha) / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((Math.abs (term/sum) > EPSILON) && (j < MAXJ));
+ sum *= x;
+
+ /* Bisection method */
+ term = Math.log (x * (1.0 - x));
+ z = logBua - (alpha - 1.0) * term;
+ if (z > Math.log(sum))
+ a = x;
+ else
+ b = x;
+ xprev = x;
+ x = 0.5 * (a + b);
+
+ } while ((Math.abs(xprev - x) > EPSILON) && (i < MAXIB));
+
+ return x;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static double inverse3 (
+ double alpha, // Shape parameter
+ double logBua, // Ln(alpha * u * Beta(alpha, alpha))
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha > 1 and x close to 0.
+ */
+ {
+ int i, j;
+ double z, x, w, xnew, sum = 0., term;
+ double eps = EPSSINGLE;
+ final double EPSILON = EPSARRAY[d];
+ // For alpha <= 100000 and u < 1.0/(2.5 + 2.25*sqrt(alpha)), X0 is always
+ // to the right of the solution, so Newton is certain to converge.
+ final double X0 = 0.497;
+
+ /* Compute starting point of Newton's iterates */
+ w = logBua / alpha;
+ x = Math.exp (w);
+ term = (Math.log1p(-x) + logBua) / alpha;
+ z = Math.exp (term);
+ if (z >= 0.25)
+ xnew = X0;
+ else if (z > 1.0e-6)
+ xnew = (1.0 - Math.sqrt(1.0 - 4.0*z)) / 2.0;
+ else
+ xnew = z;
+
+ i = 0;
+ do {
+ ++i;
+ if (xnew >= 0.5)
+ xnew = X0;
+ x = xnew;
+
+ sum = Math.log (x * (1.0 - x));
+ w = logBua - (alpha - 1.0) * sum;
+ if (Math.abs (w) >= Num.DBL_MAX_EXP * Num.LN2) {
+ xnew = X0;
+ continue;
+ }
+ w = Math.exp (w);
+ z = -x / (1 - x);
+
+ /* Compute the series for F(x) */
+ sum = term = 1.0;
+ j = 1;
+ do {
+ term *= z * (j - alpha) / (j + alpha);
+ sum += term;
+ ++j;
+ } while ((Math.abs (term/sum) > eps) && (j < MAXJ));
+ sum *= x;
+
+ /* Newton's method */
+ term = (sum - w) / alpha;
+ xnew = x - term;
+ if (Math.abs(term) < 32.0*EPSSINGLE)
+ eps = EPSILON;
+
+ } while ( (Math.abs (xnew - x) > sum * EPSILON) &&
+ (Math.abs (xnew - x) > EPSILON) && (i <= MAXI));
+
+ /* If Newton has not converged with enough precision, call bisection
+ method. It is very slow, but will be called very rarely. */
+ if (i >= MAXI && Math.abs (xnew - x) > 10.0 * EPSILON)
+ return bisect (alpha, logBua, 0.0, 0.5, d);
+ return xnew;
+ }
+
+
+ /*---------------------------------------------------------------------*/
+
+ private static double inverse4 (
+ double alpha, // Shape parameter
+ double logBva, // Ln(B) + Ln(1/2 - u) + (alpha - 1)*Ln(4)
+ int d // Digits of precision
+ )
+ /*
+ * This method is used for alpha > 1 and x close to 1/2.
+ */
+ {
+ int i, j;
+ double term, sum, y, ynew, z;
+ double eps = EPSSINGLE;
+ final double EPSILON = EPSARRAY[d];
+
+ ynew = Math.exp (logBva); // Starting point of Newton's iterates
+ i = 0;
+ do {
+ ++i;
+ y = ynew;
+
+ /* Compute the series for G(y) */
+ z = 4.0 * y * y;
+ term = sum = 1.0;
+ j = 1;
+ do {
+ term *= z * (j + alpha - 0.5) / (0.5 + j);
+ sum += term;
+ ++j;
+ } while ((term > sum * eps) && (j < MAXJ));
+ sum *= y * (1.0 - z);
+
+ /* Newton's method */
+ term = Math.log1p (-z);
+ term = sum - Math.exp (logBva - (alpha - 1.0) * term);
+ ynew = y - term;
+ if (Math.abs(term) < 32.0*EPSSINGLE)
+ eps = EPSILON;
+
+ } while ((Math.abs (ynew - y) > EPSILON) &&
+ (Math.abs (ynew - y) > sum*EPSILON) && (i <= MAXI));
+
+ return 0.5 - ynew;
+ }
+
+
+ /*---------------------------------------------------------------------*/
+
+ private static double PeizerInverse (double alpha, double u)
+ {
+ /* Inverse of the normal approximation of Peizer and Pratt */
+ double t1, t3, xprev;
+ final double C2 = alpha - 1.0 / 3.0 + 0.025 / alpha;
+ final double z = NormalDist.inverseF01 (u);
+ double x = 0.5;
+ double y = 1.0 - x;
+ int i = 0;
+
+ do {
+ i++;
+ t1 = (2.0 * alpha - 5.0 / 6.0) * x * y;
+ t3 = 1.0 - y * BetaDist.beta_g (2.0 * x) - x * BetaDist.beta_g (2.0 * y);
+ xprev = x;
+ x = 0.5 + 0.5 * z * Math.sqrt(t1 / t3) / C2;
+ y = 1.0 - x;
+ } while (i <= MAXI && Math.abs (x - xprev) > EPSBETA);
+
+ return x;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static void CalcB4 (double alpha, double [] bc, double epsilon)
+ {
+ double temp;
+ double pB = 0.0;
+ double plogB = 0.0;
+ double plogC = 0.0;
+
+ /* Compute Beta(alpha, alpha) or Beta(alpha, alpha)*4^(alpha-1). */
+
+ if (alpha <= EPSBETA) {
+ /* For a -> 0, B(a,a) = (2/a)*(1 - 1.645*a^2 + O(a^3)) */
+ pB = 2.0 / alpha;
+ plogB = Math.log(pB);
+ plogC = plogB + (alpha - 1.0)*LOG4;
+
+ } else if (alpha <= 1.0) {
+ temp = Num.lnGamma(alpha);
+ plogB = 2.0 * temp - Num.lnGamma(2.0*alpha);
+ plogC = plogB + (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+
+ } else if (alpha <= 10.0) {
+ plogC = Num.lnGamma(alpha) - Num.lnGamma(0.5 + alpha) + LOG_SQPI_2;
+ plogB = plogC - (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+
+ } else if (alpha <= 200.0) {
+ /* Convergent series for Gamma(x + 0.5) / Gamma(x) */
+ double term = 1.0;
+ double sum = 1.0;
+ int i = 1;
+ while (term > epsilon*sum) {
+ term *= (i - 1.5)*(i - 1.5) /(i*(alpha + i - 1.5));
+ sum += term;
+ i++;
+ }
+ temp = SQPI_2 / Math.sqrt ((alpha - 0.5)*sum);
+ plogC = Math.log(temp);
+ plogB = plogC - (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+
+ } else {
+ /* Asymptotic series for Gamma(a + 0.5) / (Gamma(a) * Sqrt(a)) */
+ double z = 1.0 / (8.0*alpha);
+ temp = 1.0 + z*(-1.0 + z*(0.5 + z*(2.5 - z*(2.625 + 49.875*z))));
+ /* This is 4^(alpha - 1)*B(alpha, alpha) */
+ temp = SQPI_2 / (Math.sqrt(alpha) * temp);
+ plogC = Math.log(temp);
+ plogB = plogC - (alpha - 1.0)*LOG4;
+ pB = Math.exp(plogB);
+ }
+ bc[0] = pB;
+ bc[1] = plogB;
+ bc[2] = plogC;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static double calcInverseF (double alpha, double u, int d,
+ double logFact, double logBeta, double logCeta, double Ceta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u == 0.0) return 0.0;
+ if (u == 1.0) return 1.0;
+ if (u == 0.5) return 0.5;
+
+ // Case alpha = 1 is the uniform law
+ if (alpha == 1.0) return u;
+
+ // Case alpha = 1/2 is the arcsin law
+ double temp;
+ if (alpha == 0.5) {
+ temp = Math.sin (u * PI_2);
+ return temp * temp;
+ }
+
+ if (alpha > ALIM1)
+ return PeizerInverse (alpha, u);
+
+ boolean isUpper; // True if u > 0.5
+ if (u > 0.5) {
+ isUpper = true;
+ u = 1.0 - u;
+ } else
+ isUpper = false;
+
+ double x;
+ double C = 0.0, B = 0.0, logB = 0.0, logC = 0.0;
+
+ if (logFact == Num.DBL_MIN) {
+ double [] bc = new double[] {0.0, 0.0, 0.0};
+ CalcB4 (alpha, bc, EPSARRAY[d]);
+ B = bc[0]; logB = bc[1]; logC = bc[2];
+ C = Math.exp(logC);
+ } else {
+ B = 1.0/ Math.exp(logFact);
+ logB = logBeta;
+ logC = logCeta;
+ C = Ceta;
+ }
+
+ if (alpha <= 1.0) {
+ // First term of integrated series around 1/2
+ double y0 = C * (0.5 - u);
+ if (y0 > 0.25)
+ x = inverse1 (alpha, B * u, d);
+ else
+ x = inverse2 (alpha, y0, d);
+
+ } else {
+ if (u < 1.0 / (2.5 + 2.25*Math.sqrt(alpha))) {
+ double logBua = logB + Math.log (u * alpha);
+ x = inverse3 (alpha, logBua, d);
+ } else {
+ // logBva = Ln(Beta(a,a) * (0.5 - u)*pow(4, a - 1)
+ double logBva = logC - LOG2 + Math.log1p (-2.0*u);
+
+ x = inverse4 (alpha, logBva, d);
+ }
+ }
+
+ if (isUpper)
+ return 1.0 - x - Num.DBL_EPSILON;
+ else
+ return x;
+ }
+
+ /*---------------------------------------------------------------------*/
+
+ private static double calcCdf (double alpha, double x, int d,
+ double logFact, double logBeta, double logCeta, double Ceta) {
+ double temp, u, logB = 0.0, logC = 0.0, C = 0.0;
+ boolean isUpper; /* True if x > 0.5 */
+ double B = 0.0; /* Beta(alpha, alpha) */
+ double x0;
+ final double EPSILON = EPSARRAY[d]; // Absolute precision
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ if (x <= 0.0) return 0.0;
+ if (x >= 1.0) return 1.0;
+ if (x == 0.5) return 0.5;
+ if (alpha == 1.0) return x; /* alpha = 1 is the uniform law */
+ if (alpha == 0.5) /* alpha = 1/2 is the arcsin law */
+ return INV2PI * Math.asin(Math.sqrt(x));
+
+ if (alpha > ALIM1)
+ return Peizer (alpha, x);
+
+ if (x > 0.5) {
+ x = 1.0 - x;
+ isUpper = true;
+ } else
+ isUpper = false;
+
+ if (logFact == Num.DBL_MIN) {
+ double [] bc = new double[3];
+ bc[0] = B; bc[1] = logB; bc[2] = logC;
+ CalcB4 (alpha, bc, EPSILON);
+ B = bc[0]; logB = bc[1]; logC = bc[2];
+ C = Math.exp(logC);
+ } else {
+ B = 1.0/ Math.exp(logFact);
+ logB = logBeta;
+ logC = logCeta;
+ C = Ceta;
+ }
+
+ if (alpha <= 1.0) {
+ /* For x = x0, both series use the same number of terms to get the
+ required precision */
+ if (x > 0.25) {
+ temp = -Math.log (alpha);
+ if (alpha >= 1.0e-6)
+ x0 = 0.25 + 0.005 * temp;
+ else
+ x0 = 0.13863 + .01235 * temp;
+ } else
+ x0 = 0.25;
+
+ if (x <= x0)
+ u = (series1 (alpha, x, EPSILON)) / B;
+ else
+ u = 0.5 - (series2 (alpha, 0.5 - x, EPSILON)) / C;
+
+ } else { /* 1 < alpha < ALIM1 */
+ if (alpha < 400.0)
+ x0 = 0.5 - 0.9 / Math.sqrt(4.0*alpha);
+ else
+ x0 = 0.5 - 1.0 / Math.sqrt(alpha);
+ if (x0 < 0.25)
+ x0 = 0.25;
+
+ if (x <= x0) {
+ temp = (alpha - 1.0) * Math.log (x * (1.0 - x)) - logB;
+ u = series3 (alpha, x, EPSILON) * Math.exp(temp) / alpha;
+
+ } else {
+ final double y = 0.5 - x;
+ if (y > 0.05) {
+ temp = Math.log(1.0 - 4.0*y*y);
+ } else {
+ u = 4.0*y*y;
+ temp = -u * (1.0 + u * (0.5 + u *(1.0/3.0 + u*(0.25 +
+ u*(0.2 + u*(1.0/6.0 + u*1.0/7.0))))));
+ }
+ temp = alpha * temp - logC;
+ u = 0.5 - (series4 (alpha, y, EPSILON)) * Math.exp(temp);
+ }
+ }
+
+ if (isUpper)
+ return 1.0 - u;
+ else
+ return u;
+ }
+
+ public double getMean() {
+ return 0.5;
+ }
+
+ public double getVariance() {
+ return BetaSymmetricalDist.getVariance (alpha);
+ }
+
+ public double getStandardDeviation() {
+ return BetaSymmetricalDist.getStandardDeviation (alpha);
+ }
+
+\end{hide}\end{code}
+\begin{tabb}
+ Returns the inverse distribution function evaluated at $u$, for the
+ symmetrical beta distribution over the interval $[0,1]$, with shape
+ parameters $0 < \alpha = \beta$ = \texttt{alpha}.
+ Uses four different hypergeometric series
+ to compute the distribution $u = F(x)$
+ (for the four cases $x$ close to 0 and $\alpha < 1$,
+ $x$ close to 0 and $\alpha > 1$, $x$ close to 1/2 and $\alpha < 1$,
+ and $x$ close to 1/2 and $\alpha > 1$),
+ which are then solved by Newton's method for the solution of equations.
+ For $\alpha > 100000$, uses a normal approximation given in \cite{tPEI68a}.
+\hpierre{To be done: It should be possible to make the non-static
+ method more efficient by precomputing certain quantities.}
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double var = 0.0;
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ var += ((x[i] - 0.5) * (x[i] - 0.5));
+ if (x[i] > 0.0 && x[i] < 1.0)
+ sum += Math.log (x[i] * (1.0 - x[i]));
+ else
+ sum -= 709.0;
+ }
+ var /= n;
+
+ Function f = new Function (sum, n);
+
+ double[] parameters = new double[1];
+ double alpha0 = (1.0 - 4.0 * var) / (8.0 * var);
+
+ double a = alpha0 - 5.0;
+ if (a <= 0.0)
+ a = 1e-15;
+
+ parameters[0] = RootFinder.brentDekker (a, alpha0 + 5.0, f, 1e-5);
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $\alpha$ of the symmetrical beta distribution
+ over the interval [0, 1] using the maximum likelihood method, from the
+ $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$. The estimate is returned
+ in element 0 of the returned array.
+ \begin{detailed}
+ The maximum likelihood estimator $\hat{\alpha}$ satisfies the equation
+ \begin{eqnarray*}
+ \psi(\hat\alpha) - \psi(2\hat\alpha) = \frac1{2n} \sum_{i=1}^{n} \ln(x_i(1 - x_i))
+ \end{eqnarray*}
+ where $\bar{x}_n$ is the average of $x[0], \ldots, x[n-1]$, and
+ $\psi$ is the logarithmic derivative of the Gamma function
+ $\psi(x) = \Gamma'(x) / \Gamma(x)$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameter [$\hat{\alpha}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static BetaSymmetricalDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new BetaSymmetricalDist (parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a symmetrical beta distribution with parameter $\alpha$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ return 0.5;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = 1/2$ of the symmetrical beta
+ distribution with parameter $\alpha$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the symmetrical beta distribution $E[X] = 1/2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ return (1 / (8 * alpha + 4));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance, $\mbox{Var}[X] = 1/(8\alpha + 4)$,
+ of the symmetrical beta distribution with parameter $\alpha$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the symmetrical beta distribution
+ $\mbox{Var}[X] = 1 / [4 (2\alpha + 1)]$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+
+ return (1 / Math.sqrt(8 * alpha + 4));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the
+ symmetrical beta distribution with parameter $\alpha$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the symmetrical beta distribution}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void setParams (double alpha, double beta, double a, double b, int d) {
+ // We don't want to calculate Beta, logBeta and logFactor twice.
+ if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ this.decPrec = d;
+ supportA = this.a = a;
+ supportB = this.b = b;
+ bminusa = b - a;
+ }
+
+ private void setParams (double alpha, int d) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.alpha = alpha;
+ beta = alpha;
+
+ double [] bc = new double[] {0.0, 0.0, 0.0};
+ CalcB4 (alpha, bc, EPSARRAY[d]);
+ Beta = bc[0]; logBeta = bc[1]; logCeta = bc[2];
+ Ceta = Math.exp (logCeta);
+ if (Beta > 0.0)
+ logFactor = -logBeta - (2.0*alpha - 1) * Math.log(bminusa);
+ else
+ logFactor = 0.0;
+ }\end{hide}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameter of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/BinomialDist.java b/source/umontreal/iro/lecuyer/probdist/BinomialDist.java
new file mode 100644
index 0000000..0f07e35
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BinomialDist.java
@@ -0,0 +1,847 @@
+
+
+/*
+ * Class: BinomialDist
+ * Description: binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for the
+ * <SPAN CLASS="textit">binomial</SPAN> distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>, where
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> is a positive integer and
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN>.
+ * Its mass function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:fmass-binomial"></A>
+ * <I>p</I>(<I>x</I>) = nCr(<I>n</I>, <I>x</I>)<I>p</I><SUP>x</SUP>(1 - <I>p</I>)<SUP>n-x</SUP> = <I>n</I>!/[<I>x</I>!(<I>n</I> - <I>x</I>)!] <I>p</I><SUP>x</SUP>(1 - <I>p</I>)<SUP>n-x</SUP> for <I>x</I> = 0, 1, 2,…<I>n</I>,
+ * </DIV><P></P>
+ * and its distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = ∑<SUB>j=0</SUB><SUP>x</SUP>nCr(<I>n</I>, <I>j</I>) <I>p</I><SUP>j</SUP>(1 - <I>p</I>)<SUP>n-j</SUP> for <I>x</I> = 0, 1, 2,…<I>n</I>,
+ * </DIV><P></P>
+ * where nCr<SPAN CLASS="MATH">(<I>n</I>, <I>x</I>)</SPAN> is the number of possible combinations of <SPAN CLASS="MATH"><I>x</I></SPAN> elements
+ * chosen among a set of <SPAN CLASS="MATH"><I>n</I></SPAN> elements.
+ *
+ */
+public class BinomialDist extends DiscreteDistributionInt {
+ private int n;
+ private double p;
+ private double q;
+ private static final double EPS2 = 100.0*EPSILON;
+
+ private static class Function implements MathFunction {
+ protected int m;
+ protected int R;
+ protected double mean;
+ protected int f[];
+
+ public Function (int m, double mean, int R, int f[]) {
+ this.m = m;
+ this.mean = mean;
+ this.R = R;
+ this.f = new int[f.length];
+ System.arraycopy (f, 0, this.f, 0, f.length);
+ }
+
+
+ public double evaluate (double x) {
+ if (x < R)
+ return 1e100;
+
+ double sum = 0.0;
+ for (int j = 0; j < R; j++)
+ sum += f[j] / (x - (double) j);
+
+ return (sum + m * Math.log1p (-mean / x));
+ }
+ }
+
+
+ public static double MAXN = 100000;
+
+
+ /**
+ * Creates an object that contains the binomial terms, for
+ * <SPAN CLASS="MATH">0 <= <I>x</I> <= <I>n</I></SPAN>, and the corresponding
+ * cumulative function.
+ * These values are computed and stored in dynamic arrays, unless
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> exceeds <TT>MAXN</TT>.
+ *
+ */
+ public BinomialDist (int n, double p) {
+ setBinomial (n, p);
+ }
+
+ public double prob (int x) {
+ if (n == 0)
+ return 1.0;
+
+ if (x < 0 || x > n)
+ return 0.0;
+
+ if (p == 0.0) {
+ if (x > 0)
+ return 0.0;
+ else
+ return 1.0;
+ }
+
+ if (q == 0.0) {
+ if (x < n)
+ return 0.0;
+ else
+ return 1.0;
+ }
+
+ if (pdf == null)
+ return prob (n, p, q, x);
+
+ if (x > xmax || x < xmin)
+ return prob (n, p, q, x);
+
+ return pdf[x - xmin];
+ }
+
+
+ public double cdf (int x) {
+ if (n == 0)
+ return 1.0;
+
+ if (x < 0)
+ return 0.0;
+
+ if (x >= n)
+ return 1.0;
+
+ if (p == 0.0)
+ return 1.0;
+
+ if (p == 1.0)
+ return 0.0;
+
+ if (cdf != null) {
+ if (x >= xmax)
+ return 1.0;
+ if (x < xmin) {
+ final int RMAX = 20;
+ int i;
+ double term = prob(x);
+ double Sum = term;
+ final double z = (1.0 - p) / p;
+ i = x;
+ while (i > 0 && i >= x - RMAX) {
+ term *= z * i / (n - i + 1);
+ i--;
+ Sum += term;
+ }
+ return Sum;
+ }
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+ } else
+ return cdf (n, p, x);
+ }
+
+
+ public double barF (int x) {
+ if (n == 0)
+ return 1.0;
+
+ if (x < 1)
+ return 1.0;
+
+ if (x > n)
+ return 0.0;
+
+ if (p == 0.0)
+ return 0.0;
+
+ if (p == 1.0)
+ return 1.0;
+
+ if (cdf != null) {
+ if (x > xmax) {
+ // Add IMAX dominant terms to get a few decimals in the tail
+ final double q = 1.0 - p;
+ double z, sum, term;
+ int i;
+ sum = term = prob(x);
+ z = p / q;
+ i = x;
+ final int IMAX = 20;
+ while (i < n && i < x + IMAX) {
+ term = term * z * (n - i) / (i + 1);
+ sum += term;
+ i++;
+ }
+ return sum;
+ // return fdist_Beta (x, n - x + 1, 10, p);
+ }
+
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ } else
+ return 1.0 - cdf (n, p, x - 1);
+ }
+
+
+ public int inverseFInt (double u) {
+ if ((cdf == null) || (u <= EPS2))
+ return inverseF (n, p, u);
+ else
+ return super.inverseFInt (u);
+ }
+
+ public double getMean() {
+ return BinomialDist.getMean (n, p);
+ }
+
+ public double getVariance() {
+ return BinomialDist.getVariance (n, p);
+ }
+
+ public double getStandardDeviation() {
+ return BinomialDist.getStandardDeviation (n, p);
+ }
+
+
+
+ /**
+ * Computes and returns the binomial probability <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN> in eq..
+ *
+ */
+ public static double prob (int n, double p, int x) {
+ return prob (n, p, 1.0 - p, x);
+ }
+
+
+ /**
+ * A generalization of the previous method.
+ * Computes and returns the binomial term
+ *
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = (<I>n</I>!/<I>x</I>!(<I>n</I>-<I>x</I>)!)<I>p</I><SUP>x</SUP><I>q</I><SUP>n-x</SUP></SPAN>,
+ * where <SPAN CLASS="MATH"><I>p</I></SPAN> and <SPAN CLASS="MATH"><I>q</I></SPAN> are arbitrary real numbers
+ * (<SPAN CLASS="MATH"><I>q</I></SPAN> is not necessarily equal to <SPAN CLASS="MATH">1 - <I>p</I></SPAN>).
+ * In the case where
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN> and <SPAN CLASS="MATH"><I>q</I> = 1 - <I>p</I></SPAN>, the returned
+ * value is a probability term for the binomial distribution.
+ *
+ */
+ public static double prob (int n, double p, double q, int x) {
+ final int SLIM = 50; // To avoid overflow
+ final double MAXEXP = (Num.DBL_MAX_EXP - 1)*Num.LN2;// To avoid overflow
+ final double MINEXP = (Num.DBL_MIN_EXP - 1)*Num.LN2;// To avoid underflow
+ int signe = 1;
+ double Res;
+
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+ if (n == 0)
+ return 1.0;
+ if (x < 0 || x > n)
+ return 0.0;
+
+ // Combination (n, x) are symmetric between x and n-x
+ if (x > n/2) {
+ x = n - x;
+ Res = p;
+ p = q;
+ q = Res;
+ }
+
+ if (p < 0.0) {
+ p = -p;
+ if ((x & 1) != 0)
+ signe *= -1; // odd x
+ }
+ if (q < 0.0) {
+ q = -q;
+ if (((n - x) & 1) != 0)
+ signe *= -1; // odd n - x
+ }
+
+ if (n <= SLIM) {
+ Res = Math.pow (p, (double)x)*Num.combination (n, x)*Math.pow (q,
+ (double)(n - x));
+ return signe*Res;
+ }
+ else {
+ /* This could be calculated with more precision as there is some
+ cancellation because of subtraction of the large LnFactorial: the
+ last few digits can be lost. But we need the function lgammal in
+ long double precision. Another possibility would be to use an
+ asymptotic expansion for the binomial coefficient. */
+
+ Res = x*Math.log (p) + (n - x)*Math.log (q) + Num.lnFactorial (n)
+ - Num.lnFactorial (n - x) - Num.lnFactorial (x);
+ if (Res >= MAXEXP)
+ throw new IllegalArgumentException ("term overflow");
+
+ if (Res < MINEXP)
+ return 0.0;
+
+ return signe*Math.exp (Res);
+ }
+ }
+
+
+ /**
+ * Computes <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>, the distribution function of a
+ * binomial
+ * random variable with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double cdf (int n, double p, int x) {
+ final int NLIM1 = 10000;
+ final double VARLIM = 50.0;
+// final double EPS = DiscreteDistributionInt.EPSILON * EPS_EXTRA;
+ double y, z, q = 1.0 - p;
+ double sum, term, termmid;
+ int i, mid;
+ boolean flag = false;
+
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+
+ if (n == 0)
+ return 1.0;
+ if (x < 0)
+ return 0.0;
+ if (x >= n)
+ return 1.0;
+ if (p <= 0.0)
+ return 1.0;
+ if (p >= 1.0)
+ return 0.0; // For any x < n
+
+ if (n < NLIM1) { // Exact Binomial
+ /* Sum RMAX terms to get a few decimals in the lower tail */
+ final int RMAX = 20;
+ mid = (int)((n + 1)*p);
+ if (mid > x)
+ mid = x;
+ sum = term = termmid = prob (n, p, 1.0 - p, mid);
+
+ z = q/p;
+ i = mid;
+ while (term >= EPSILON || i >= mid - RMAX) {
+ term *= z * i / (n - i + 1);
+ sum += term;
+ i--;
+ if (i == 0) break;
+ }
+
+ z = p/q;
+ term = termmid;
+ for (i = mid; i < x; i++) {
+ term *= z*(n - i)/(i + 1);
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+ if (sum >= 1.0) return 1.0;
+ return sum;
+
+ } else {
+ if (p > 0.5 || ((p == 0.5) && (x > n/2))) {
+ // use F (p, n, x) = 1 - F (q, n, n-x-1)
+ p = q;
+ q = 1.0 - p;
+ flag = true;
+ x = n - x - 1;
+ }
+ if (n*p*q > VARLIM) { // Normal approximation
+ /* Uses the Camp-Paulson approximation based on the F-distribution.
+ Its maximum absolute error is smaller than 0.007 / sqrt (npq).
+ Ref: W. Molenaar; Approximations to the Poisson, Binomial,....
+ QA273.6 M64, p. 93 (1970) */
+ term = Math.pow ((x+1)*q/((n-x)*p), 1.0/3.0);
+ y = term*(9.0 - 1.0/(x + 1)) - 9.0 + 1.0/(n - x);
+ z = 3.0*Math.sqrt (term*term/(x + 1) + 1.0/(n - x));
+ y /= z;
+ if (flag)
+ return NormalDist.barF01 (y);
+ else
+ return NormalDist.cdf01 (y);
+ }
+ else { // Poisson approximation
+ /* Uses a Bol'shev approximation based on the Poisson distribution.
+ Error is O (1/n^4) as n -> infinity. Ref: W. Molenaar;
+ Approximations to the Poisson, Binomial,... QA273.6 M64, p. 107,
+ Table 6.2, Formule lambda_9 (1970). */
+ y = (2.0*n - x)*p/(2.0 - p);
+ double t = 2.0*n - x;
+ z = (2.0*y*y - x*y - x*(double)x - 2.0*x)/(6*t*t);
+ z = y/(1.0 - z);
+ if (flag)
+ return PoissonDist.barF(z, x - 1);
+ else
+ return PoissonDist.cdf (z, x);
+ }
+ }
+ }
+
+
+ /**
+ * Returns
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>, the complementary
+ * distribution function.
+ *
+ */
+ public static double barF (int n, double p, int x) {
+ return 1.0 - cdf (n, p, x - 1);
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, the inverse of the binomial distribution.
+ *
+ */
+ public static int inverseF (int n, double p, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= prob (n, p, 0))
+ return 0;
+ if ((u > 1.0 - prob (n, p, n)) || (u >= 1.0))
+ return n;
+ final int NLIM1 = 10000;
+ int i, mid;
+ final double q = 1.0 - p;
+ double z, sum, term, termmid;
+
+ if (n < NLIM1) { // Exact Binomial
+ i = (int)((n + 1)*p);
+ if (i > n)
+ i = n;
+ term = prob (n, p, i);
+ while ((term >= u) && (term > Double.MIN_NORMAL)) {
+ i /= 2;
+ term = prob (n, p, i);
+ }
+
+ z = q/p;
+ if (term <= Double.MIN_NORMAL) {
+ i *= 2;
+ term = prob (n, p, i);
+ while (term >= u && (term > Double.MIN_NORMAL)) {
+ term *= z*i/(n - i + 1);
+ i--;
+ }
+ }
+
+ mid = i;
+ term = prob (n, p, i);
+ sum = termmid = term;
+
+ z = q/p;
+ for (i = mid; i > 0; i--) {
+ term *= z*i/(n - i + 1);
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+
+ i = mid ;
+ term = termmid;
+ if (sum >= u) {
+ while (sum >= u){
+ z = q/p;
+ sum -= term;
+ term *= z*i/(n - i + 1);
+ i--;
+ }
+ return i+1;
+ }
+
+ double prev = -1;
+ while ((sum < u) && (sum > prev)) {
+ z = p/q;
+ term *= z*(n - i)/(i + 1);
+ prev = sum;
+ sum += term;
+ i++;
+ }
+ return i;
+
+ } else{ // Normal or Poisson approximation
+ for(i = 0 ; i <= n ; i++){
+ sum = cdf(n,p,i);
+ if (sum >= u)
+ break;
+ }
+ return i;
+ }
+ }
+
+
+ /**
+ * Estimates the parameters <SPAN CLASS="MATH">(<I>n</I>, <I>p</I>)</SPAN> of the binomial distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>n</I></SPAN>, <SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(n)</SPAN>, <SPAN CLASS="MATH">hat(p)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int m) {
+ if (m <= 1)
+ throw new UnsupportedOperationException(" m < 2");
+
+ int i;
+ int r = 0;
+ double mean = 0.0;
+ for (i = 0; i < m; i++) {
+ mean += x[i];
+ if (x[i] > r)
+ r = x[i];
+ }
+ mean /= (double) m;
+
+ double sum = 0.0;
+ for (i = 0; i < m; i++)
+ sum += (x[i] - mean)*(x[i] - mean);
+ double var = sum / m;
+ if (mean <= var)
+ throw new UnsupportedOperationException("mean <= variance");
+
+ int f[] = new int[r];
+ for (int j = 0; j < r; j++) {
+ f[j] = 0;
+ for (i = 0; i < m; i++)
+ if (x[i] > j) f[j]++;
+ }
+
+ double p = 1.0 - var/mean;
+ double rup = (int) (5*mean/p);
+ if (rup < 1) rup = 1;
+
+ Function fct = new Function (m, mean, r, f);
+ double parameters[] = new double[2];
+ parameters[0] = (int) RootFinder.brentDekker (r-1, rup, fct, 1e-5);
+ if (parameters[0] < r)
+ parameters[0] = r;
+ parameters[1] = mean / parameters[0];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a binomial distribution with both parameters
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> estimated using the maximum likelihood method, from
+ * the <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to estimate the parameters
+ *
+ * @param m the number of observations to use to estimate the parameters
+ *
+ *
+ */
+ public static BinomialDist getInstanceFromMLE (int[] x, int m) {
+ double parameters[] = new double[2];
+ parameters = getMLE (x, m);
+ return new BinomialDist ((int) parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of the binomial distribution with
+ * given (fixed) parameter <SPAN CLASS="MATH"><I>n</I></SPAN>, by the maximum likelihood method,
+ * from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ * Returns the estimator in an array with a single element.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @param n the number of success
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH">hat(p)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int m, int n) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[1];
+ double mean = 0.0;
+ for (int i = 0; i < m; i++)
+ mean += x[i];
+ mean /= (double) m;
+
+ parameters[0] = mean / (double) n;
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a binomial distribution with given (fixed) parameter <SPAN CLASS="MATH"><I>n</I></SPAN>, and
+ * with parameter <SPAN CLASS="MATH"><I>p</I></SPAN> estimated by the maximum likelihood method based on the
+ * <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ * @param n the parameter n of the binomial
+ *
+ *
+ */
+ public static BinomialDist getInstanceFromMLE (int[] x, int m, int n) {
+ double parameters[] = new double[1];
+ parameters = getMLE (x, m, n);
+ return new BinomialDist (n, parameters[0]);
+ }
+
+
+ /**
+ * Computes the mean <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>np</I></SPAN> of the binomial distribution with
+ * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the mean of the Binomial distribution <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>np</I></SPAN>
+ *
+ */
+ public static double getMean (int n, double p) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0, 1)");
+
+ return (n * p);
+ }
+
+
+ /**
+ * Computes the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>np</I>(1 - <I>p</I>)</SPAN> of the binomial
+ * distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the variance of the binomial distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>np</I>(1 - <I>p</I>)</SPAN>
+ *
+ */
+ public static double getVariance (int n, double p) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0, 1)");
+
+ return (n * p * (1 - p));
+ }
+
+
+ /**
+ * Computes the standard deviation of the Binomial distribution with
+ * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the standard deviation of the binomial distribution
+ *
+ */
+ public static double getStandardDeviation (int n, double p) {
+ return Math.sqrt (BinomialDist.getVariance (n, p));
+ }
+
+
+ private void setBinomial (int n, double p) {
+ /*
+ * Compute all probability terms of the binomial distribution; start near
+ * the mean, and calculate probabilities on each side until they become
+ * smaller than EPSILON, then stop there.
+ * However, this is more general than the binomial probability distribu-
+ * tion as this will compute the binomial terms when p + q != 1, and
+ * even when p or q are negative. However in this case, the cumulative
+ * terms will be meaningless.
+ */
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0, 1)");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ supportA = 0;
+ supportB = n;
+ double q = 1.0 - p;
+ final double EPS = DiscreteDistributionInt.EPSILON * EPS_EXTRA;
+
+ this.n = n;
+ this.p = p;
+ this.q = q;
+
+ int i, mid;
+ int imin, imax;
+ double z = 0.0;
+ double[] P; // Binomial "probability" terms
+ double[] F; // Binomial cumulative "probabilities"
+
+ // For n > MAXN, we shall not use pre-computed arrays
+ if (n > MAXN) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ P = new double[1 + n];
+ F = new double[1 + n];
+
+ // the maximum term in absolute value
+ mid = (int)((n + 1)*Math.abs (p)/(Math.abs (p) + Math.abs (q)));
+ if (mid > n)
+ mid = n;
+ P[mid] = prob (n, p, q, mid);
+
+ if (p != 0.0 || p != -0.0)
+ z = q/p;
+ i = mid;
+
+ while (i > 0 && Math.abs (P[i]) > EPS) {
+ P[i - 1] = P[i]*z*i/(n - i + 1);
+ i--;
+ }
+ imin = i;
+
+ if (q != 0.0 || q != -0.0)
+ z = p/q;
+ i = mid;
+
+ while (i < n && Math.abs (P[i]) > EPS) {
+ P[i + 1] = P[i]*z*(n - i)/(i + 1);
+ i++;
+ }
+ imax = i;
+
+ /* Here, we assume that we are dealing with a probability distribution.
+ Compute the cumulative probabilities for F and keep them in the
+ lower part of CDF.*/
+ F[imin] = P[imin];
+ i = imin;
+ while (i < n && F[i] < 0.5) {
+ i++;
+ F[i] = F[i - 1] + P[i];
+ }
+
+ // This is the boundary between F (i <= xmed) and 1 - F (i > xmed) in
+ // the array CDF
+ xmed = i;
+
+ // Compute the cumulative probabilities of the complementary
+ // distribution and keep them in the upper part of the array
+ F[imax] = P[imax];
+ i = imax - 1;
+ while (i > xmed) {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ }
+
+ // Reset imin because we lose too much precision for a few terms near
+ // imin when we stop adding terms < epsilon.
+ i = imin;
+ while (i < xmed && F[i] < DiscreteDistributionInt.EPSILON)
+ i++;
+ xmin = imin = i;
+
+ /* Same thing with imax */
+ i = imax;
+ while (i > xmed && F[i] < DiscreteDistributionInt.EPSILON)
+ i--;
+ xmax = imax = i;
+
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax+1-imin);
+ System.arraycopy (F, imin, cdf, 0, imax+1-imin);
+
+ }
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+ /**
+ * Returns a table that contains the parameters <SPAN CLASS="MATH">(<I>n</I>, <I>p</I>)</SPAN> of the current distribution,
+ * in regular order: [<SPAN CLASS="MATH"><I>n</I></SPAN>, <SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n, p};
+ return retour;
+ }
+
+
+ /**
+ * Resets the parameters to these new values and recomputes everything
+ * as in the constructor. From the performance viewpoint, it is
+ * essentially the same as constructing a new {@link BinomialDist} object.
+ *
+ *
+ */
+ public void setParams (int n, double p) {
+ setBinomial (n, p);
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n + ", p = " + p;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/BinomialDist.tex b/source/umontreal/iro/lecuyer/probdist/BinomialDist.tex
new file mode 100644
index 0000000..8251cf5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/BinomialDist.tex
@@ -0,0 +1,906 @@
+\defmodule {BinomialDist}
+
+Extends the class \class{DiscreteDistributionInt} for the
+\emph{binomial} distribution
+\cite[page 321]{sLAW00a} with parameters $n$ and $p$, where
+$n$ is a positive integer and $0\le p\le 1$.
+Its mass function is given by
+\eq
+ p(x) = \latex{\binom{n}{x}}\html{\mbox{nCr}(n, x)} p^x (1-p)^{n-x} =
+ \latex{\frac {n!}{x!(n-x)!}}\html{n!/[x!(n-x)!]}\; p^x (1-p)^{n-x}
+ \qquad \mbox{for } x = 0, 1, 2,\ldots n,
+ \label{eq:fmass-binomial}
+\endeq
+and its distribution function is
+ \eq
+ F (x) = \sum_{j=0}^x \latex{\binom{n}{j}}\html{\mbox{nCr}(n,j)}\; p^j (1-p)^{n-j}
+ \qquad \mbox{for } x = 0, 1, 2, \ldots n,
+ \eqlabel{eq:Fbinomial}
+ \endeq
+\begin{htmlonly}
+where nCr$(n,x)$ is the number of possible combinations of $x$ elements
+chosen among a set of $n$ elements.
+\end{htmlonly}
+\hpierre{Il faut aussi une version
+ avec seulement $n$ et $p$, pour le cas (fr\'equent) o\`u $p$ est
+ tr\'es proche de 0?}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BinomialDist
+ * Description: binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class BinomialDist extends DiscreteDistributionInt\begin{hide} {
+ private int n;
+ private double p;
+ private double q;
+ private static final double EPS2 = 100.0*EPSILON;
+
+ private static class Function implements MathFunction {
+ protected int m;
+ protected int R;
+ protected double mean;
+ protected int f[];
+
+ public Function (int m, double mean, int R, int f[]) {
+ this.m = m;
+ this.mean = mean;
+ this.R = R;
+ this.f = new int[f.length];
+ System.arraycopy (f, 0, this.f, 0, f.length);
+ }
+
+
+ public double evaluate (double x) {
+ if (x < R)
+ return 1e100;
+
+ double sum = 0.0;
+ for (int j = 0; j < R; j++)
+ sum += f[j] / (x - (double) j);
+
+ return (sum + m * Math.log1p (-mean / x));
+ }
+ }
+\end{hide}
+\end{code}
+\unmoved\begin{detailed}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constant}
+\begin{code}
+ public static double MAXN = 100000;
+\end{code}
+ \begin{tabb} The value of the parameter $n$ above which
+ the tables are {\em not\/} precomputed by
+ the constructor.
+ \end{tabb}
+
+\end{detailed}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public BinomialDist (int n, double p)\begin{hide} {
+ setBinomial (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an object that contains the binomial terms
+ (\ref{eq:fmass-binomial}), for $0\le x\le n$, and the corresponding
+ cumulative function.
+ These values are computed and stored in dynamic arrays, unless
+ $n$ exceeds \texttt{MAXN}.
+\end{tabb}
+%% \ifdetailed %%%
+%% Only the terms larger than \texttt{EPSILON} in absolute value are kept
+%% in the arrays. The other terms are recomputed directly each time
+%% they are needed.
+%% \fi %%% detailed
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ public double prob (int x) {
+ if (n == 0)
+ return 1.0;
+
+ if (x < 0 || x > n)
+ return 0.0;
+
+ if (p == 0.0) {
+ if (x > 0)
+ return 0.0;
+ else
+ return 1.0;
+ }
+
+ if (q == 0.0) {
+ if (x < n)
+ return 0.0;
+ else
+ return 1.0;
+ }
+
+ if (pdf == null)
+ return prob (n, p, q, x);
+
+ if (x > xmax || x < xmin)
+ return prob (n, p, q, x);
+
+ return pdf[x - xmin];
+ }
+
+
+ public double cdf (int x) {
+ if (n == 0)
+ return 1.0;
+
+ if (x < 0)
+ return 0.0;
+
+ if (x >= n)
+ return 1.0;
+
+ if (p == 0.0)
+ return 1.0;
+
+ if (p == 1.0)
+ return 0.0;
+
+ if (cdf != null) {
+ if (x >= xmax)
+ return 1.0;
+ if (x < xmin) {
+ final int RMAX = 20;
+ int i;
+ double term = prob(x);
+ double Sum = term;
+ final double z = (1.0 - p) / p;
+ i = x;
+ while (i > 0 && i >= x - RMAX) {
+ term *= z * i / (n - i + 1);
+ i--;
+ Sum += term;
+ }
+ return Sum;
+ }
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+ } else
+ return cdf (n, p, x);
+ }
+
+
+ public double barF (int x) {
+ if (n == 0)
+ return 1.0;
+
+ if (x < 1)
+ return 1.0;
+
+ if (x > n)
+ return 0.0;
+
+ if (p == 0.0)
+ return 0.0;
+
+ if (p == 1.0)
+ return 1.0;
+
+ if (cdf != null) {
+ if (x > xmax) {
+ // Add IMAX dominant terms to get a few decimals in the tail
+ final double q = 1.0 - p;
+ double z, sum, term;
+ int i;
+ sum = term = prob(x);
+ z = p / q;
+ i = x;
+ final int IMAX = 20;
+ while (i < n && i < x + IMAX) {
+ term = term * z * (n - i) / (i + 1);
+ sum += term;
+ i++;
+ }
+ return sum;
+ // return fdist_Beta (x, n - x + 1, 10, p);
+ }
+
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ } else
+ return 1.0 - cdf (n, p, x - 1);
+ }
+
+
+ public int inverseFInt (double u) {
+ if ((cdf == null) || (u <= EPS2))
+ return inverseF (n, p, u);
+ else
+ return super.inverseFInt (u);
+ }
+
+ public double getMean() {
+ return BinomialDist.getMean (n, p);
+ }
+
+ public double getVariance() {
+ return BinomialDist.getVariance (n, p);
+ }
+
+ public double getStandardDeviation() {
+ return BinomialDist.getStandardDeviation (n, p);
+ }
+
+\end{hide}
+
+ public static double prob (int n, double p, int x)\begin{hide} {
+ return prob (n, p, 1.0 - p, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the binomial probability $p(x)$ in eq.
+(\ref{eq:fmass-binomial}).
+\end{tabb}
+\begin{code}
+
+ public static double prob (int n, double p, double q, int x)\begin{hide} {
+ final int SLIM = 50; // To avoid overflow
+ final double MAXEXP = (Num.DBL_MAX_EXP - 1)*Num.LN2;// To avoid overflow
+ final double MINEXP = (Num.DBL_MIN_EXP - 1)*Num.LN2;// To avoid underflow
+ int signe = 1;
+ double Res;
+
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+ if (n == 0)
+ return 1.0;
+ if (x < 0 || x > n)
+ return 0.0;
+
+ // Combination (n, x) are symmetric between x and n-x
+ if (x > n/2) {
+ x = n - x;
+ Res = p;
+ p = q;
+ q = Res;
+ }
+
+ if (p < 0.0) {
+ p = -p;
+ if ((x & 1) != 0)
+ signe *= -1; // odd x
+ }
+ if (q < 0.0) {
+ q = -q;
+ if (((n - x) & 1) != 0)
+ signe *= -1; // odd n - x
+ }
+
+ if (n <= SLIM) {
+ Res = Math.pow (p, (double)x)*Num.combination (n, x)*Math.pow (q,
+ (double)(n - x));
+ return signe*Res;
+ }
+ else {
+ /* This could be calculated with more precision as there is some
+ cancellation because of subtraction of the large LnFactorial: the
+ last few digits can be lost. But we need the function lgammal in
+ long double precision. Another possibility would be to use an
+ asymptotic expansion for the binomial coefficient. */
+
+ Res = x*Math.log (p) + (n - x)*Math.log (q) + Num.lnFactorial (n)
+ - Num.lnFactorial (n - x) - Num.lnFactorial (x);
+ if (Res >= MAXEXP)
+ throw new IllegalArgumentException ("term overflow");
+
+ if (Res < MINEXP)
+ return 0.0;
+
+ return signe*Math.exp (Res);
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ A generalization of the previous method.
+ Computes and returns the binomial term
+\begin{htmlonly}
+ $f(x) = ({n!}/{x!(n-x)!}) p^x q^{n-x}$,
+% See the pdf documentataion for more information about f (x).
+\end{htmlonly}%
+\begin{latexonly}
+ \eq
+ f(x) = \binom{n}{x} p^x q^{n-x} =
+ \frac {n!}{x!(n-x)!}\; p^x q^{n-x}, \eqlabel{eq:fmass-binom}
+ \endeq
+\end{latexonly}
+ where $p$ and $q$ are arbitrary real numbers
+ ($q$ is not necessarily equal to $1-p$).
+ In the case where $0 \le p \le 1$ and $q = 1-p$, the returned
+ value is a probability term for the binomial distribution.
+ \end{tabb}
+\begin{code}
+
+ public static double cdf (int n, double p, int x)\begin{hide} {
+ final int NLIM1 = 10000;
+ final double VARLIM = 50.0;
+// final double EPS = DiscreteDistributionInt.EPSILON * EPS_EXTRA;
+ double y, z, q = 1.0 - p;
+ double sum, term, termmid;
+ int i, mid;
+ boolean flag = false;
+
+ if (p < 0.0 | p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (n < 0)
+ throw new IllegalArgumentException ("n < 0");
+
+ if (n == 0)
+ return 1.0;
+ if (x < 0)
+ return 0.0;
+ if (x >= n)
+ return 1.0;
+ if (p <= 0.0)
+ return 1.0;
+ if (p >= 1.0)
+ return 0.0; // For any x < n
+
+ if (n < NLIM1) { // Exact Binomial
+ /* Sum RMAX terms to get a few decimals in the lower tail */
+ final int RMAX = 20;
+ mid = (int)((n + 1)*p);
+ if (mid > x)
+ mid = x;
+ sum = term = termmid = prob (n, p, 1.0 - p, mid);
+
+ z = q/p;
+ i = mid;
+ while (term >= EPSILON || i >= mid - RMAX) {
+ term *= z * i / (n - i + 1);
+ sum += term;
+ i--;
+ if (i == 0) break;
+ }
+
+ z = p/q;
+ term = termmid;
+ for (i = mid; i < x; i++) {
+ term *= z*(n - i)/(i + 1);
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+ if (sum >= 1.0) return 1.0;
+ return sum;
+
+ } else {
+ if (p > 0.5 || ((p == 0.5) && (x > n/2))) {
+ // use F (p, n, x) = 1 - F (q, n, n-x-1)
+ p = q;
+ q = 1.0 - p;
+ flag = true;
+ x = n - x - 1;
+ }
+ if (n*p*q > VARLIM) { // Normal approximation
+ /* Uses the Camp-Paulson approximation based on the F-distribution.
+ Its maximum absolute error is smaller than 0.007 / sqrt (npq).
+ Ref: W. Molenaar; Approximations to the Poisson, Binomial,....
+ QA273.6 M64, p. 93 (1970) */
+ term = Math.pow ((x+1)*q/((n-x)*p), 1.0/3.0);
+ y = term*(9.0 - 1.0/(x + 1)) - 9.0 + 1.0/(n - x);
+ z = 3.0*Math.sqrt (term*term/(x + 1) + 1.0/(n - x));
+ y /= z;
+ if (flag)
+ return NormalDist.barF01 (y);
+ else
+ return NormalDist.cdf01 (y);
+ }
+ else { // Poisson approximation
+ /* Uses a Bol'shev approximation based on the Poisson distribution.
+ Error is O (1/n^4) as n -> infinity. Ref: W. Molenaar;
+ Approximations to the Poisson, Binomial,... QA273.6 M64, p. 107,
+ Table 6.2, Formule lambda_9 (1970). */
+ y = (2.0*n - x)*p/(2.0 - p);
+ double t = 2.0*n - x;
+ z = (2.0*y*y - x*y - x*(double)x - 2.0*x)/(6*t*t);
+ z = y/(1.0 - z);
+ if (flag)
+ return PoissonDist.barF(z, x - 1);
+ else
+ return PoissonDist.cdf (z, x);
+ }
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes $F(x)$, the distribution function of a
+ binomial
+ random variable with parameters $n$ and $p$, evaluated at $x$.
+\begin{detailed}
+ If $n \le 10000$, the non-negligible terms of the sum are
+ added explicitly.
+ If $n > 10000$ and $np (1-p) > 100$, the Camp-Paulson normal
+ approximation \cite{tCAM51a,tMOL70a} is used, otherwise, a Poisson
+ approximation due to Bol'shev \cite{tBOL64a,tMOL70a} is used.
+\end{detailed}
+%% When the {\em binomial\/} distribution has to be computed more than
+%% once with the same parameters $n$ and $p$, it is more efficient to
+%% construct and use a \texttt{BinomialDist} object instead.,
+%% unless $n$ is very large (e.g., $n > 10^5$).
+% Restrictions: $0 \le p \le 1$ and $n \ge 0$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double p, int x)\begin{hide} {
+ return 1.0 - cdf (n, p, x - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\bar F(x) = P[X \ge x]$, the complementary
+ distribution function.
+\begin{detailed}%
+ The default implementation returns \texttt{1 - cdf(x-1)}, which
+ is not accurate when $F(x)$ is close to 1.
+\end{detailed}%
+\end{tabb}
+\begin{code}
+
+ public static int inverseF (int n, double p, double u) \begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= prob (n, p, 0))
+ return 0;
+ if ((u > 1.0 - prob (n, p, n)) || (u >= 1.0))
+ return n;
+ final int NLIM1 = 10000;
+ int i, mid;
+ final double q = 1.0 - p;
+ double z, sum, term, termmid;
+
+ if (n < NLIM1) { // Exact Binomial
+ i = (int)((n + 1)*p);
+ if (i > n)
+ i = n;
+ term = prob (n, p, i);
+ while ((term >= u) && (term > Double.MIN_NORMAL)) {
+ i /= 2;
+ term = prob (n, p, i);
+ }
+
+ z = q/p;
+ if (term <= Double.MIN_NORMAL) {
+ i *= 2;
+ term = prob (n, p, i);
+ while (term >= u && (term > Double.MIN_NORMAL)) {
+ term *= z*i/(n - i + 1);
+ i--;
+ }
+ }
+
+ mid = i;
+ term = prob (n, p, i);
+ sum = termmid = term;
+
+ z = q/p;
+ for (i = mid; i > 0; i--) {
+ term *= z*i/(n - i + 1);
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+
+ i = mid ;
+ term = termmid;
+ if (sum >= u) {
+ while (sum >= u){
+ z = q/p;
+ sum -= term;
+ term *= z*i/(n - i + 1);
+ i--;
+ }
+ return i+1;
+ }
+
+ double prev = -1;
+ while ((sum < u) && (sum > prev)) {
+ z = p/q;
+ term *= z*(n - i)/(i + 1);
+ prev = sum;
+ sum += term;
+ i++;
+ }
+ return i;
+
+ } else{ // Normal or Poisson approximation
+ for(i = 0 ; i <= n ; i++){
+ sum = cdf(n,p,i);
+ if (sum >= u)
+ break;
+ }
+ return i;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes $x = F^{-1}(u)$, the inverse of the binomial distribution.
+\begin{detailed}%
+ If $n$ is larger than 10000, the linear search starts from 0 and the
+ \method{cdf}{int,double,int} static method is used to compute $F(x)$ at
+ different values of $x$, which is much less efficient.
+ \hpierre{Why not always use binary search starting from the mode?
+ Because we do not have $F$ precomputed either.}
+\end{detailed}%
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int m)\begin{hide} {
+ if (m <= 1)
+ throw new UnsupportedOperationException(" m < 2");
+
+ int i;
+ int r = 0;
+ double mean = 0.0;
+ for (i = 0; i < m; i++) {
+ mean += x[i];
+ if (x[i] > r)
+ r = x[i];
+ }
+ mean /= (double) m;
+
+ double sum = 0.0;
+ for (i = 0; i < m; i++)
+ sum += (x[i] - mean)*(x[i] - mean);
+ double var = sum / m;
+ if (mean <= var)
+ throw new UnsupportedOperationException("mean <= variance");
+
+ int f[] = new int[r];
+ for (int j = 0; j < r; j++) {
+ f[j] = 0;
+ for (i = 0; i < m; i++)
+ if (x[i] > j) f[j]++;
+ }
+
+ double p = 1.0 - var/mean;
+ double rup = (int) (5*mean/p);
+ if (rup < 1) rup = 1;
+
+ Function fct = new Function (m, mean, r, f);
+ double parameters[] = new double[2];
+ parameters[0] = (int) RootFinder.brentDekker (r-1, rup, fct, 1e-5);
+ if (parameters[0] < r)
+ parameters[0] = r;
+ parameters[1] = mean / parameters[0];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(n,p)$ of the binomial distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1,\ldots, m-1$. The estimates are returned in a two-element
+ array, in regular order: [$n$, $p$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat n, \hat p)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat{n}\hat{p} & = & \bar{x}_m\\
+ \sum_{i=0}^{R-1} \frac{f_i}{\hat{n} - i} & = & - m \ln \left(1 - \frac{\bar{x}_m}{\hat{n}}\right)
+ \end{eqnarray*}
+ where $\bar x_m$ is the average of $x[0],\dots,x[m-1]$,
+ $f_i$ is the number of $x[i]$'s that exceed $i$,
+ and $R =\max(x[0],\ldots,x[m-1])$ is the largest observation
+ \cite[page 57]{tJOH69a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{n}$, $\hat{p}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static BinomialDist getInstanceFromMLE (int[] x, int m)\begin{hide} {
+ double parameters[] = new double[2];
+ parameters = getMLE (x, m);
+ return new BinomialDist ((int) parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a binomial distribution with both parameters
+ ${n}$ and ${p}$ estimated using the maximum likelihood method, from
+ the $m$ observations $x[i]$, $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to estimate the parameters}
+ \param{m}{the number of observations to use to estimate the parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int m, int n)\begin{hide} {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[1];
+ double mean = 0.0;
+ for (int i = 0; i < m; i++)
+ mean += x[i];
+ mean /= (double) m;
+
+ parameters[0] = mean / (double) n;
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter ${p}$ of the binomial distribution with
+ given (fixed) parameter $n$, by the maximum likelihood method,
+ from the $m$ observations $x[i]$, $i = 0, 1,\ldots, m-1$.
+ Returns the estimator in an array with a single element.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \param{n}{the number of success}
+ \return{returns the parameter [$\hat{p}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static BinomialDist getInstanceFromMLE (int[] x, int m, int n)\begin{hide} {
+ double parameters[] = new double[1];
+ parameters = getMLE (x, m, n);
+ return new BinomialDist (n, parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a binomial distribution with given (fixed) parameter $n$, and
+ with parameter ${p}$ estimated by the maximum likelihood method based on the
+ $m$ observations $x[i]$, $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+ \param{n}{the parameter n of the binomial}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (int n, double p)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0, 1)");
+
+ return (n * p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the mean $E[X] = np$ of the binomial distribution with
+ parameters $n$ and $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Binomial distribution $E[X] = np$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int n, double p)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0, 1)");
+
+ return (n * p * (1 - p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the variance $\mbox{Var}[X] = np(1 - p)$ of the binomial
+ distribution with parameters $n$ and $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the binomial distribution $\mbox{Var}[X] = np(1 - p)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int n, double p)\begin{hide} {
+ return Math.sqrt (BinomialDist.getVariance (n, p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard deviation of the Binomial distribution with
+ parameters $n$ and $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the binomial distribution}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+ private void setBinomial (int n, double p) {
+ /*
+ * Compute all probability terms of the binomial distribution; start near
+ * the mean, and calculate probabilities on each side until they become
+ * smaller than EPSILON, then stop there.
+ * However, this is more general than the binomial probability distribu-
+ * tion as this will compute the binomial terms when p + q != 1, and
+ * even when p or q are negative. However in this case, the cumulative
+ * terms will be meaningless.
+ */
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0, 1)");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ supportA = 0;
+ supportB = n;
+ double q = 1.0 - p;
+ final double EPS = DiscreteDistributionInt.EPSILON * EPS_EXTRA;
+
+ this.n = n;
+ this.p = p;
+ this.q = q;
+
+ int i, mid;
+ int imin, imax;
+ double z = 0.0;
+ double[] P; // Binomial "probability" terms
+ double[] F; // Binomial cumulative "probabilities"
+
+ // For n > MAXN, we shall not use pre-computed arrays
+ if (n > MAXN) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ P = new double[1 + n];
+ F = new double[1 + n];
+
+ // the maximum term in absolute value
+ mid = (int)((n + 1)*Math.abs (p)/(Math.abs (p) + Math.abs (q)));
+ if (mid > n)
+ mid = n;
+ P[mid] = prob (n, p, q, mid);
+
+ if (p != 0.0 || p != -0.0)
+ z = q/p;
+ i = mid;
+
+ while (i > 0 && Math.abs (P[i]) > EPS) {
+ P[i - 1] = P[i]*z*i/(n - i + 1);
+ i--;
+ }
+ imin = i;
+
+ if (q != 0.0 || q != -0.0)
+ z = p/q;
+ i = mid;
+
+ while (i < n && Math.abs (P[i]) > EPS) {
+ P[i + 1] = P[i]*z*(n - i)/(i + 1);
+ i++;
+ }
+ imax = i;
+
+ /* Here, we assume that we are dealing with a probability distribution.
+ Compute the cumulative probabilities for F and keep them in the
+ lower part of CDF.*/
+ F[imin] = P[imin];
+ i = imin;
+ while (i < n && F[i] < 0.5) {
+ i++;
+ F[i] = F[i - 1] + P[i];
+ }
+
+ // This is the boundary between F (i <= xmed) and 1 - F (i > xmed) in
+ // the array CDF
+ xmed = i;
+
+ // Compute the cumulative probabilities of the complementary
+ // distribution and keep them in the upper part of the array
+ F[imax] = P[imax];
+ i = imax - 1;
+ while (i > xmed) {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ }
+
+ // Reset imin because we lose too much precision for a few terms near
+ // imin when we stop adding terms < epsilon.
+ i = imin;
+ while (i < xmed && F[i] < DiscreteDistributionInt.EPSILON)
+ i++;
+ xmin = imin = i;
+
+ /* Same thing with imax */
+ i = imax;
+ while (i > xmed && F[i] < DiscreteDistributionInt.EPSILON)
+ i--;
+ xmax = imax = i;
+
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax+1-imin);
+ System.arraycopy (F, imin, cdf, 0, imax+1-imin);
+
+ }\end{hide}
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n, p};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a table that contains the parameters $(n,p)$ of the current distribution,
+ in regular order: [$n$, $p$].
+\end{tabb}
+\begin{code}
+
+ public void setParams (int n, double p)\begin{hide} {
+ setBinomial (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Resets the parameters to these new values and recomputes everything
+ as in the constructor. From the performance viewpoint, it is
+ essentially the same as constructing a new \class{BinomialDist} object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n + ", p = " + p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/CauchyDist.java b/source/umontreal/iro/lecuyer/probdist/CauchyDist.java
new file mode 100644
index 0000000..4ceaa75
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/CauchyDist.java
@@ -0,0 +1,359 @@
+
+/*
+ * Class: CauchyDist
+ * Description: Cauchy distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since March 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Misc;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Cauchy</EM> distribution
+ * with location parameter <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>β</I>/(<I>π</I>[(<I>x</I> - <I>α</I>)<SUP>2</SUP> + <I>β</I><SUP>2</SUP>]) for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1/2 + arctan((<I>x</I> - <I>α</I>)/<I>β</I>)/<I>π</I>, for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * and its inverse is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>α</I> + <I>β</I>tan(<I>π</I>(<I>u</I> - 1/2)). for 0 < <I>u</I> < 1.
+ * </DIV><P></P>
+ *
+ */
+public class CauchyDist extends ContinuousDistribution {
+ private double alpha;
+ private double beta;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+
+ public Optim (double[] x, int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ double sum = 0.0;
+
+ if (p[2] <= 0.0) // barrier at 0
+ return 1.0e200;
+
+ for (int i = 0; i < n; i++)
+ sum -= Math.log (density (p[1], p[2], xi[i]));
+
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+
+
+
+ /**
+ * Constructs a <TT>CauchyDist</TT> object
+ * with parameters <SPAN CLASS="MATH"><I>α</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>.
+ *
+ */
+ public CauchyDist() {
+ setParams (0.0, 1.0);
+ }
+
+
+ /**
+ * Constructs a <TT>CauchyDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>.
+ *
+ */
+ public CauchyDist (double alpha, double beta) {
+ setParams (alpha, beta);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean() {
+ return CauchyDist.getMean (alpha, beta);
+ }
+
+ public double getVariance() {
+ return CauchyDist.getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation() {
+ return CauchyDist.getStandardDeviation (alpha, beta);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (double alpha, double beta, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ double t = (x - alpha)/beta;
+ return 1.0/(beta * Math.PI*(1 + t*t));
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double alpha, double beta, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ double z = (x - alpha)/beta;
+ if (z < -0.5)
+ return Math.atan(-1./z)/Math.PI;
+ return Math.atan(z)/Math.PI + 0.5;
+ }
+
+
+ /**
+ * Computes the complementary distribution.
+ *
+ */
+ public static double barF (double alpha, double beta, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ double z = (x - alpha)/beta;
+ if (z > 0.5)
+ return Math.atan(1./z)/Math.PI;
+ return 0.5 - Math.atan(z)/Math.PI;
+ }
+
+
+ /**
+ * Computes the inverse of the distribution.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double u) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u < 0.5)
+ return alpha - 1.0/Math.tan (Math.PI*u) * beta;
+ return alpha + Math.tan (Math.PI*(u - 0.5)) * beta;
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>β</I>)</SPAN> of the Cauchy distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ Optim system = new Optim (x, n);
+
+ double[] parameters = new double[2];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[3][3];
+ double[] udiag = new double[3];
+
+ param[1] = EmpiricalDist.getMedian (x, n);
+
+ int m = Math.round ((float) n / 4.0f);
+ double q3 = Misc.quickSelect (x, n, 3 * m);
+ double q1 = Misc.quickSelect (x, n, m);
+ param[2] = (q3 - q1) / 2.0;
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (int i = 0; i < 2; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a Cauchy distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static CauchyDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new CauchyDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Throws an exception since the mean does not exist.
+ *
+ * @exception UnsupportedOperationException the mean of the Cauchy distribution is undefined.
+ *
+ *
+ */
+ public static double getMean (double alpha, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ throw new UnsupportedOperationException("Undefined mean");
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH">∞</SPAN> since the variance does not exist.
+ *
+ * @return <SPAN CLASS="MATH">∞</SPAN>.
+ *
+ */
+ public static double getVariance (double alpha, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return Double.POSITIVE_INFINITY;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH">∞</SPAN> since the standard deviation does not exist.
+ *
+ * @return <SPAN CLASS="MATH">∞</SPAN>
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta) {
+ return Double.POSITIVE_INFINITY;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>α</I></SPAN> for this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>β</I></SPAN> for this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> for this object.
+ *
+ */
+ public void setParams (double alpha, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+
+
+ /**
+ * Return a table containing parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/CauchyDist.tex b/source/umontreal/iro/lecuyer/probdist/CauchyDist.tex
new file mode 100644
index 0000000..d83bced
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/CauchyDist.tex
@@ -0,0 +1,376 @@
+\defmodule {CauchyDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Cauchy\/} distribution \cite[page 299]{tJOH95a}
+with location parameter $\alpha$
+and scale parameter $\beta > 0$.
+The density function is given by
+\begin{htmlonly}
+\eq
+ f (x) = \beta/(\pi[(x - \alpha)^2 + \beta^2])\mbox{ for }-\infty < x < \infty.
+\endeq
+The distribution function is
+\eq
+ F (x) = 1/2 + \arctan ((x - \alpha)/\beta)/\pi,
+ \qquad \qquad \mbox{for } -\infty < x < \infty,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f (x) = \frac{\beta}{\pi[(x-\alpha)^2 + \beta^2]},
+ \qquad \qquad \mbox{for } -\infty < x < \infty . \eqlabel{eq:fcuachy}
+\endeq
+The distribution function is
+\eq
+ F (x) = \frac12 + \frac{\arctan ((x - \alpha)/\beta)}{\pi},
+ \qquad \qquad \mbox{for } -\infty < x < \infty,
+\endeq
+\end{latexonly}
+and its inverse is
+\eq
+F^{-1} (u) = \alpha + \beta\tan (\pi(u - 1/2)).
+\qquad \mbox{for } 0 < u < 1.
+\endeq
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: CauchyDist
+ * Description: Cauchy distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since March 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Misc;
+import optimization.*;
+\end{hide}
+
+public class CauchyDist extends ContinuousDistribution\begin{hide} {
+ private double alpha;
+ private double beta;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+
+ public Optim (double[] x, int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ double sum = 0.0;
+
+ if (p[2] <= 0.0) // barrier at 0
+ return 1.0e200;
+
+ for (int i = 0; i < n; i++)
+ sum -= Math.log (density (p[1], p[2], xi[i]));
+
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public CauchyDist()\begin{hide} {
+ setParams (0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{CauchyDist} object
+ with parameters $\alpha=0$ and $\beta=1$.
+ \end{tabb}
+\begin{code}
+
+ public CauchyDist (double alpha, double beta)\begin{hide} {
+ setParams (alpha, beta);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{CauchyDist} object with parameters
+ $\alpha=$ \texttt{alpha} and $\beta=$ \texttt{beta}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (alpha, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean() {
+ return CauchyDist.getMean (alpha, beta);
+ }
+
+ public double getVariance() {
+ return CauchyDist.getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation() {
+ return CauchyDist.getStandardDeviation (alpha, beta);
+ }\end{hide}
+
+ public static double density (double alpha, double beta, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ double t = (x - alpha)/beta;
+ return 1.0/(beta * Math.PI*(1 + t*t));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ double z = (x - alpha)/beta;
+ if (z < -0.5)
+ return Math.atan(-1./z)/Math.PI;
+ return Math.atan(z)/Math.PI + 0.5;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ double z = (x - alpha)/beta;
+ if (z > 0.5)
+ return Math.atan(1./z)/Math.PI;
+ return 0.5 - Math.atan(z)/Math.PI;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double beta, double u)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u < 0.5)
+ return alpha - 1.0/Math.tan (Math.PI*u) * beta;
+ return alpha + Math.tan (Math.PI*(u - 0.5)) * beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the distribution.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ Optim system = new Optim (x, n);
+
+ double[] parameters = new double[2];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[3][3];
+ double[] udiag = new double[3];
+
+ param[1] = EmpiricalDist.getMedian (x, n);
+
+ int m = Math.round ((float) n / 4.0f);
+ double q3 = Misc.quickSelect (x, n, 3 * m);
+ double q1 = Misc.quickSelect (x, n, m);
+ param[2] = (q3 - q1) / 2.0;
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (int i = 0; i < 2; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\beta)$ of the Cauchy distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\beta$].
+ \begin{detailed}
+ The estimates of the parameters are given by maximizing numerically the
+ log-likelihood function, using the Uncmin package \cite{iSCHa,iVERa}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static CauchyDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new CauchyDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Cauchy distribution with parameters $\alpha$ and $\beta$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ throw new UnsupportedOperationException("Undefined mean");
+ }\end{hide}
+\end{code}
+\begin{tabb} Throws an exception since the mean does not exist.
+\end{tabb}
+\begin{htmlonly}
+ %\return{the mean of the Cauchy distribution.}%
+ \exception{UnsupportedOperationException}{the mean of the Cauchy distribution is undefined.}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return Double.POSITIVE_INFINITY;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\infty$ since the variance does not exist.
+\end{tabb}
+\begin{htmlonly}
+ \return{$\infty$.}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta)\begin{hide} {
+ return Double.POSITIVE_INFINITY;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\infty$ since the standard deviation does not exist.
+\end{tabb}
+\begin{htmlonly}
+ \return{$\infty$}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\alpha$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\beta$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $\alpha$ and $\beta$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\beta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiDist.java b/source/umontreal/iro/lecuyer/probdist/ChiDist.java
new file mode 100644
index 0000000..de94925
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiDist.java
@@ -0,0 +1,316 @@
+
+
+/*
+ * Class: ChiDist
+ * Description: chi distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the <EM>chi</EM>
+ * distribution with shape parameter
+ * <SPAN CLASS="MATH"><I>v</I> > 0</SPAN>, where the number of degrees of freedom
+ * <SPAN CLASS="MATH"><I>v</I></SPAN> is a positive integer.
+ * The density function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>e</I><SUP>-x<SUP>2</SUP>/2</SUP><I>x</I><SUP>v-1</SUP>/(2<SUP>(v/2)-1</SUP><I>Γ</I>(<I>v</I>/2)) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined in
+ * {@link GammaDist}.
+ * The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1/<I>Γ</I>(<I>v</I>/2)∫<SUB>0</SUB><SUP>x<SUP>2</SUP>/2</SUP><I>t</I><SUP>v/2-1</SUP><I>e</I><SUP>-t</SUP> <I>dt</I>.
+ * </DIV><P></P>
+ * It is equivalent to the gamma distribution function with parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I> = <I>v</I>/2</SPAN> and <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>, evaluated at <SPAN CLASS="MATH"><I>x</I><SUP>2</SUP>/2</SPAN>.
+ *
+ */
+public class ChiDist extends ContinuousDistribution {
+ private int nu;
+ private double C1;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double sum;
+
+ public Function (double s, int n)
+ {
+ this.n = n;
+ this.sum = s;
+ }
+
+ public double evaluate (double k)
+ {
+ if (k < 1.0) return 1.0e200;
+ return (sum + n * (Num.lnGamma (k / 2.0) - 0.5*(Num.LN2) - Num.lnGamma ((k + 1.0) / 2.0)));
+ }
+ }
+
+
+
+ /**
+ * Constructs a <TT>ChiDist</TT> object.
+ *
+ */
+ public ChiDist (int nu) {
+ setNu (nu);
+ }
+
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp ((nu - 1)*Math.log (x) - x*x/2.0 - C1);
+ }
+
+ public double cdf (double x) {
+ return cdf (nu, x);
+ }
+
+ public double barF (double x) {
+ return barF (nu, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (nu, u);
+ }
+
+ public double getMean() {
+ return ChiDist.getMean (nu);
+ }
+
+ public double getVariance() {
+ return ChiDist.getVariance (nu);
+ }
+
+ public double getStandardDeviation() {
+ return ChiDist.getStandardDeviation (nu);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (int nu, double x) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp ((nu - 1)*Math.log (x) - x*x/2.0
+ - (nu/2.0 - 1.0)*Num.LN2 - Num.lnGamma (nu/2.0));
+ }
+
+
+ /**
+ * Computes the distribution function by using the
+ * gamma distribution function.
+ *
+ */
+ public static double cdf (int nu, double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return GammaDist.cdf (nu/2.0, 15, x*x/2.0);
+ }
+
+
+ /**
+ * Computes the complementary distribution.
+ *
+ */
+ public static double barF (int nu, double x) {
+ if (x <= 0.0)
+ return 1.0;
+ return GammaDist.barF (nu/2.0, 15, x*x/2.0);
+ }
+
+
+ /**
+ * Returns the inverse distribution function computed
+ * using the gamma inversion.
+ *
+ */
+ public static double inverseF (int nu, double u) {
+ double res = GammaDist.inverseF (nu/2.0, 15, u);
+ return Math.sqrt (2*res);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN> of the chi distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimate is returned in element 0
+ * of the returned array.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH">hat(ν)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double[] parameters = new double[1];
+
+ double mean = 0.0;
+ for (int i = 0; i < n; i++)
+ mean += x[i];
+ mean /= (double) n;
+
+ double var = 0.0;
+ for (int i = 0; i < n; i++)
+ var += ((x[i] - mean) * (x[i] - mean));
+ var /= (double) n;
+
+ double k = Math.round (var + mean * mean) - 5.0;
+ if (k < 1.0)
+ k = 1.0;
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ sum += Math.log (x[i]);
+ else
+ sum -= 709.0;
+ }
+
+ Function f = new Function (sum, n);
+ while (f.evaluate(k) > 0.0)
+ k++;
+ parameters[0] = k;
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a chi distribution with parameter <SPAN CLASS="MATH"><I>ν</I></SPAN> estimated using
+ * the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static ChiDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new ChiDist ((int) parameters[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * of the chi distribution with parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ *
+ * @return the mean of the chi distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (2)<SUP>1/2</SUP><I>Γ</I>((<I>ν</I> +1)/2)/<I>Γ</I>(<I>ν</I>/2)</SPAN>
+ *
+ */
+ public static double getMean (int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ return Num.RAC2 * Num.gammaRatioHalf(nu / 2.0);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * of the chi distribution with parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ *
+ * @return the variance of the chi distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 2[<I>Γ</I>(<I>ν</I>/2)<I>Γ</I>(1 + <I>ν</I>/2) - <I>Γ</I><SUP>2</SUP>(1/2(<I>ν</I> +1))]/<I>Γ</I>(<I>ν</I>/2)</SPAN>
+ *
+ */
+ public static double getVariance (int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ double mean = ChiDist.getMean(nu);
+ return (nu - (mean * mean));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the chi distribution
+ * with parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ *
+ * @return the standard deviation of the chi distribution
+ *
+ */
+ public static double getStandardDeviation (int nu) {
+ return Math.sqrt (ChiDist.getVariance (nu));
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>ν</I></SPAN> for this object.
+ *
+ */
+ public int getNu() {
+ return nu;
+ }
+
+
+
+ /**
+ * Sets the value of <SPAN CLASS="MATH"><I>ν</I></SPAN> for this object.
+ *
+ */
+ public void setNu (int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ this.nu = nu;
+ supportA = 0.0;
+ C1 = (nu/2.0 - 1.0)*Num.LN2 + Num.lnGamma (nu/2.0);
+ }
+
+
+ /**
+ * Return a table containing parameters of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {nu};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : nu = " + nu;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiDist.tex b/source/umontreal/iro/lecuyer/probdist/ChiDist.tex
new file mode 100644
index 0000000..b456a89
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiDist.tex
@@ -0,0 +1,337 @@
+\defmodule {ChiDist}
+
+Extends the class \class{ContinuousDistribution} for the {\em chi\/}
+ distribution \cite[page 417]{tJOH95a} with shape parameter
+ $\latex{\nu}\html{v} > 0$, where the number of degrees of freedom
+ $\latex{\nu}\html{v}$ is a positive integer.
+The density function is given by
+\begin{htmlonly}
+\eq
+f (x) = e^{-x^2/2} x^{v - 1} / (2^{(v/2) - 1}\Gamma (v/2))\mbox{ for }x > 0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq f (x) = \frac {e^{-x^2 /2} x^{\nu-1}}{2^{(\nu /2)-1}\Gamma (\nu /2)},
+ \qquad\mbox { for } x > 0,
+ \eqlabel{eq:Fchi}
+\endeq
+\end{latexonly}
+where $\Gamma (x)$ is the gamma function defined in
+\latex{(\ref{eq:Gamma})}\html{\class{GammaDist}}.
+The distribution function is
+\eq F (x) = \latex{\frac{1}{\Gamma(\nu/2)}}\html{1/\Gamma(v/2)}
+ \int_0^{x^2/2} t^{\latex{\nu}\html{v}/2-1}e^{-t}\ dt.
+\endeq
+% F(x) obtained by integration with Mathematica.
+% Formula for the incomplete gamma taken
+% from http://mathworld.wolfram.com/IncompleteGammaFunction.html
+% Found to be equivalent to GammaDist
+It is equivalent to the gamma distribution function with parameters
+$\alpha=\latex{\nu}\html{v}/2$ and $\lambda=1$, evaluated at $x^2/2$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiDist
+ * Description: chi distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class ChiDist extends ContinuousDistribution\begin{hide} {
+ private int nu;
+ private double C1;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double sum;
+
+ public Function (double s, int n)
+ {
+ this.n = n;
+ this.sum = s;
+ }
+
+ public double evaluate (double k)
+ {
+ if (k < 1.0) return 1.0e200;
+ return (sum + n * (Num.lnGamma (k / 2.0) - 0.5*(Num.LN2) - Num.lnGamma ((k + 1.0) / 2.0)));
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ChiDist (int nu)\begin{hide} {
+ setNu (nu);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ChiDist} object.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp ((nu - 1)*Math.log (x) - x*x/2.0 - C1);
+ }
+
+ public double cdf (double x) {
+ return cdf (nu, x);
+ }
+
+ public double barF (double x) {
+ return barF (nu, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (nu, u);
+ }
+
+ public double getMean() {
+ return ChiDist.getMean (nu);
+ }
+
+ public double getVariance() {
+ return ChiDist.getVariance (nu);
+ }
+
+ public double getStandardDeviation() {
+ return ChiDist.getStandardDeviation (nu);
+ }\end{hide}
+
+ public static double density (int nu, double x)\begin{hide} {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp ((nu - 1)*Math.log (x) - x*x/2.0
+ - (nu/2.0 - 1.0)*Num.LN2 - Num.lnGamma (nu/2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int nu, double x)\begin{hide} {
+ if (x <= 0.0)
+ return 0.0;
+ return GammaDist.cdf (nu/2.0, 15, x*x/2.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function by using the
+ gamma distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int nu, double x)\begin{hide} {
+ if (x <= 0.0)
+ return 1.0;
+ return GammaDist.barF (nu/2.0, 15, x*x/2.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (int nu, double u)\begin{hide} {
+ double res = GammaDist.inverseF (nu/2.0, 15, u);
+ return Math.sqrt (2*res);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inverse distribution function computed
+ using the gamma inversion.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double[] parameters = new double[1];
+
+ double mean = 0.0;
+ for (int i = 0; i < n; i++)
+ mean += x[i];
+ mean /= (double) n;
+
+ double var = 0.0;
+ for (int i = 0; i < n; i++)
+ var += ((x[i] - mean) * (x[i] - mean));
+ var /= (double) n;
+
+ double k = Math.round (var + mean * mean) - 5.0;
+ if (k < 1.0)
+ k = 1.0;
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ sum += Math.log (x[i]);
+ else
+ sum -= 709.0;
+ }
+
+ Function f = new Function (sum, n);
+ while (f.evaluate(k) > 0.0)
+ k++;
+ parameters[0] = k;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $\nu$ of the chi distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$. The estimate is returned in element 0
+ of the returned array.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameter [$\hat{\nu}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static ChiDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new ChiDist ((int) parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a chi distribution with parameter $\nu$ estimated using
+ the maximum likelihood method based on the $n$ observations $x[i]$,
+ $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (int nu)\begin{hide} {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ return Num.RAC2 * Num.gammaRatioHalf(nu / 2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+\begin{latexonly}
+ $$E[X] = \frac{\sqrt{2}\,\Gamma( \frac{\nu + 1}{2} )}{\Gamma(\frac{\nu}{2})}$$
+\end{latexonly}
+ of the chi distribution with parameter $\nu$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the chi distribution
+ $E[X] = \sqrt{2}\Gamma((\nu + 1) / 2) / \Gamma(\nu / 2)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int nu)\begin{hide} {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ double mean = ChiDist.getMean(nu);
+ return (nu - (mean * mean));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+\begin{latexonly}
+ $$\mbox{Var}[X] = \frac{2\,\Gamma(\frac{\nu}{2}) \Gamma(1 + \frac{\nu}{2}) -
+ \Gamma^2(\frac{\nu + 1}{2})}{\Gamma (\frac{\nu}{2})}$$
+\end{latexonly}
+ of the chi distribution with parameter $\nu$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the chi distribution
+ $\mbox{Var}[X] = 2 [ \Gamma(\nu / 2) \Gamma(1 + \nu / 2) - \Gamma^2(1/2 (\nu + 1)) ]
+ / \Gamma (\nu / 2)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int nu)\begin{hide} {
+ return Math.sqrt (ChiDist.getVariance (nu));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the chi distribution
+ with parameter $\nu$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the chi distribution}
+\end{htmlonly}
+\begin{code}
+
+ public int getNu()\begin{hide} {
+ return nu;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\nu$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public void setNu (int nu)\begin{hide} {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ this.nu = nu;
+ supportA = 0.0;
+ C1 = (nu/2.0 - 1.0)*Num.LN2 + Num.lnGamma (nu/2.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of $\nu$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {nu};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing parameters of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : nu = " + nu;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiSquareDist.java b/source/umontreal/iro/lecuyer/probdist/ChiSquareDist.java
new file mode 100644
index 0000000..67fb070
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiSquareDist.java
@@ -0,0 +1,448 @@
+
+
+/*
+ * Class: ChiSquareDist
+ * Description: chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>chi-square</EM> distribution with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom,
+ * where <SPAN CLASS="MATH"><I>n</I></SPAN> is a positive integer.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>x</I><SUP>(n/2)-1</SUP><I>e</I><SUP>-x/2</SUP>/(2<SUP>n/2</SUP><I>Γ</I>(<I>n</I>/2)), for <I>x</I> > 0.
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined in
+ * {@link GammaDist}.
+ * The <EM>chi-square</EM> distribution is a special case of the <EM>gamma</EM>
+ * distribution with shape parameter <SPAN CLASS="MATH"><I>n</I>/2</SPAN> and scale parameter <SPAN CLASS="MATH">1/2</SPAN>.
+ * Therefore, one can use the methods of {@link GammaDist} for this distribution.
+ *
+ * <P>
+ * The non-static versions of the methods <TT>cdf</TT>, <TT>barF</TT>,
+ * and <TT>inverseF</TT> call the static version of the same name.
+ *
+ */
+public class ChiSquareDist extends ContinuousDistribution {
+ protected int n;
+ protected double C1;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double sumLog;
+
+ public Function (double s, int n)
+ {
+ this.n = n;
+ this.sumLog = s;
+ }
+
+ public double evaluate (double k)
+ {
+ if (k < 1.0) return 1.0e200;
+ return (sumLog + n * (Num.lnGamma (k / 2.0) - 0.5*Num.LN2 - Num.lnGamma ((k + 1.0) / 2.0)));
+ }
+ }
+
+
+
+ /**
+ * Constructs a chi-square distribution with <TT>n</TT> degrees of freedom.
+ *
+ */
+ public ChiSquareDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ if (x <= 0)
+ return 0.0;
+ return Math.exp ((n/2.0 - 1)*Math.log (x) - x/2.0 - C1);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, decPrec, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, decPrec, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return ChiSquareDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return ChiSquareDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return ChiSquareDist.getStandardDeviation (n);
+ }
+
+ /**
+ * Computes the density function
+ * for a <EM>chi-square</EM> distribution with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ *
+ */
+ public static double density (int n, double x) {
+ if (x <= 0)
+ return 0.0;
+ return Math.exp ((n/2.0 - 1)*Math.log (x) - x/2.0 -
+ (n/2.0)*Num.LN2 - Num.lnGamma(n/2.0));
+ }
+
+
+ /**
+ * Computes the chi-square distribution function with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom,
+ * evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>. The method tries to return <SPAN CLASS="MATH"><I>d</I></SPAN> decimals digits of precision,
+ * but there is no guarantee.
+ *
+ */
+ public static double cdf (int n, int d, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= XBIG*n)
+ return 1.0;
+ return GammaDist.cdf (n/2.0, d, x/2.0);
+ }
+
+
+ /**
+ * Computes the complementary chi-square distribution function with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees
+ * of freedom, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>. The method tries to return <SPAN CLASS="MATH"><I>d</I></SPAN> decimals digits
+ * of precision, but there is no guarantee.
+ *
+ */
+ public static double barF (int n, int d, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ return GammaDist.barF (n/2.0, d, x/2.0);
+ }
+
+
+ /**
+ * Computes an approximation of <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, where <SPAN CLASS="MATH"><I>F</I></SPAN> is the
+ * chi-square distribution with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ *
+ * It gives at least 6 decimal digits of precision, except far in the tails
+ * (that is, for
+ * <SPAN CLASS="MATH"><I>u</I> < 10<SUP>-5</SUP></SPAN> or
+ * <SPAN CLASS="MATH"><I>u</I> > 1 - 10<SUP>-5</SUP></SPAN>) where the function
+ * calls the method <TT>GammaDist.inverseF (n/2, 7, u)</TT> and
+ * multiplies the result by 2.0.
+ * To get better precision, one may
+ * call <TT>GammaDist.inverseF</TT>, but this method is slower than the
+ * current method, especially for large <SPAN CLASS="MATH"><I>n</I></SPAN>. For instance, for <SPAN CLASS="MATH"><I>n</I> =</SPAN> 16, 1024,
+ * and 65536, the <TT>GammaDist.inverseF</TT> method is 2, 5, and 8 times slower,
+ * respectively, than the current method.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ /*
+ * Returns an approximation of the inverse of Chi square cdf
+ * with n degrees of freedom.
+ * As in Figure L.23 of P.Bratley, B.L.Fox, and L.E.Schrage.
+ * A Guide to Simulation Springer-Verlag,
+ * New York, second edition, 1987.
+ */
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+
+ final double E = 0.5e-5; // Precision of this approximation
+ final double AA = 0.6931471805;
+ double A, XX, X, C, G, CH, Q, P1, P2, T, B, S1, S2, S3, S4, S5, S6;
+
+ if (u < 0.00001 || u > 1.0 - 1.0e-5)
+ return 2.0 * GammaDist.inverseF (n / 2.0, 7, u);
+ if (u >= 1.0)
+ return n * XBIG;
+ if (u >= 0.999998)
+ return (n + 4.0 * Math.sqrt (2.0 * n));
+
+ G = Num.lnGamma (n / 2.0);
+ XX = 0.5 * n;
+ C = XX - 1.0;
+
+ if (n >= -1.24 * Math.log (u)) {
+ X = NormalDist.inverseF01 (u);
+ P1 = 0.222222 / n;
+ Q = X * Math.sqrt (P1) + 1.0 - P1;
+ CH = n * Q * Q * Q;
+ if (CH > 2.2 * n + 6.0)
+ CH = -2.0 * (Math.log1p (-u) - C * Math.log (0.5 * CH) + G);
+
+ } else {
+ CH = Math.pow (u * XX * Math.exp (G + XX * AA), 1.0 / XX);
+ if (CH - E < 0)
+ return CH;
+ }
+
+ Q = CH;
+ P1 = 0.5 * CH;
+ P2 = u - GammaDist.cdf (XX, 5, P1);
+ if (GammaDist.cdf (XX, 5, P1) == -1.0)
+ throw new IllegalArgumentException ("RESULT = -1");
+
+ T = P2 * Math.exp (XX * AA + G + P1 - C * Math.log (CH));
+ B = T / CH;
+ A = 0.5 * T - B * C;
+ S1 = (210.0 + A * (140.0 +
+ A * (105.0 + A * (84.0 + A * (70.0 + 60.0 * A))))) / 420.0;
+ S2 = (420.0 + A * (735.0 + A * (966.0 + A * (1141.0 + 1278.0 * A))))
+ / 2520.0;
+ S3 = (210.0 + A * (462.0 + A * (707.0 + 932.0 * A))) / 2520.0;
+ S4 = (252.0 + A * (672.0 + 1182.0 * A) +
+ C * (294.0 + A * (889.0 + 1740.0 * A))) / 5040.0;
+ S5 = (84.0 + 264.0 * A + C * (175.0 + 606.0 * A)) / 2520.0;
+ S6 = (120.0 + C * (346.0 + 127.0 * C)) / 5040.0;
+ CH = CH + T * (1.0 + 0.5 * T * S1 - B * C * (S1 - B * (S2 -
+ B * (S3 - B * (S4 - B * (S5 - B * S6))))));
+
+ double temp;
+ while (Math.abs (Q / CH - 1.0) > E) {
+ Q = CH;
+ P1 = 0.5 * CH;
+ temp = GammaDist.cdf (XX, 6, P1);
+ P2 = u - temp;
+
+ if (temp == -1.0)
+ return -1.0;
+
+ T = P2 * Math.exp (XX * AA + G + P1 - C * Math.log (CH));
+ B = T / CH;
+ A = 0.5 * T - B * C;
+ S1 = (210.0 + A * (140.0 + A * (105.0 + A * (84.0 +
+ A * (70.0 + 60.0 * A))))) / 420.0;
+ S2 = (420.0 + A * (735.0 + A * (966.0 + A * (1141.0 +
+ 1278.0 * A)))) / 2520.0;
+ S3 = (210.0 + A * (462.0 + A * (707.0 + 932.0 * A))) / 2520.0;
+ S4 = (252.0 + A * (672.0 + 1182.0 * A) +
+ C * (294.0 + A * (889.0 + 1740.0 * A))) / 5040.0;
+ S5 = (84.0 + 264.0 * A + C * (175.0 + 606.0 * A)) / 2520.0;
+ S6 = (120.0 + C * (346.0 + 127.0 * C)) / 5040.0;
+ CH = CH + T * (1.0 + 0.5 * T * S1 - B * C * (S1 - B * (S2 -
+ B * (S3 - B * (S4 - B * (S5 - B * S6))))));
+ }
+ return CH;
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of the chi-square distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The estimate is returned in element 0
+ * of the returned array.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH">hat(n)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int m) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ double[] parameters;
+
+ parameters = getMomentsEstimate (x, m);
+ double k = Math.round (parameters[0]) - 5.0;
+ if (k < 1.0)
+ k = 1.0;
+
+ double sum = 0.0;
+ for (int i = 0; i < m; i++) {
+ if (x[i] > 0.0)
+ sum += 0.5*Math.log (x[i]);
+ else
+ sum -= 709.0;
+ }
+
+ Function f = new Function (sum, m);
+ while (f.evaluate(k) > 0.0)
+ k++;
+ parameters[0] = k;
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a chi-square distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN> estimated using
+ * the maximum likelihood method based on the <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static ChiSquareDist getInstanceFromMLE (double[] x, int m) {
+ double parameters[] = getMLE (x, m);
+ return new ChiSquareDist ((int) parameters[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>n</I></SPAN> of the
+ * chi-square distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the mean of the Chi-square distribution <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>n</I></SPAN>
+ *
+ */
+ public static double getMean (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ return n;
+ }
+
+
+ /**
+ * Estimates and returns the parameter [<SPAN CLASS="MATH">hat(n)</SPAN>] of the chi-square
+ * distribution using the moments method based on the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH">hat(n)</SPAN>]
+ *
+ */
+ public static double[] getMomentsEstimate (double[] x, int m) {
+ double[] parameters = new double[1];
+
+ double sum = 0.0;
+ for (int i = 0; i < m; i++)
+ sum += x[i];
+ parameters[0] = sum / (double) m;
+
+ return parameters;
+ }
+
+
+ /**
+ * Returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 2<I>n</I></SPAN>
+ * of the chi-square distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the variance of the chi-square distribution
+ * <SPAN CLASS="MATH">Var<I>X</I>] = 2<I>n</I></SPAN>
+ *
+ */
+ public static double getVariance (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ return (2 * n);
+ }
+
+
+ /**
+ * Returns the standard deviation
+ * of the chi-square distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the standard deviation of the chi-square distribution
+ *
+ */
+ public static double getStandardDeviation (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ return Math.sqrt(2 * n);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public void setN (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ this.n = n;
+ supportA = 0.0;
+ C1 = 0.5 * n *Num.LN2 + Num.lnGamma(n/2.0);
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiSquareDist.tex b/source/umontreal/iro/lecuyer/probdist/ChiSquareDist.tex
new file mode 100644
index 0000000..cad94aa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiSquareDist.tex
@@ -0,0 +1,519 @@
+\defmodule {ChiSquareDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em chi-square\/} distribution with $n$ degrees of freedom,
+where $n$ is a positive integer \cite[page 416]{tJOH95a}.
+Its density is
+\begin{htmlonly}
+\eq f(x) = x^{(n/2) - 1}e^{-x/2}/(2^{n/2}\Gamma(n/2)),
+\qquad\mbox{ for } x > 0.
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \frac{x^{(n/2)-1}e^{-x/2}}{2^{n/2}\Gamma(n/2)},\qquad\mbox {for } x > 0
+ \eqlabel{eq:Fchi2}
+\endeq
+\end{latexonly}%
+where $\Gamma(x)$ is the gamma function defined in
+\latex{(\ref{eq:Gamma})}\html{\class{GammaDist}}.
+The {\em chi-square\/} distribution is a special case of the {\em gamma\/}
+distribution with shape parameter $n/2$ and scale parameter $1/2$.
+Therefore, one can use the methods of \class{GammaDist} for this distribution.
+
+The non-static versions of the methods \texttt{cdf}, \texttt{barF},
+and \texttt{inverseF} call the static version of the same name.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiSquareDist
+ * Description: chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class ChiSquareDist extends ContinuousDistribution\begin{hide} {
+ protected int n;
+ protected double C1;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double sumLog;
+
+ public Function (double s, int n)
+ {
+ this.n = n;
+ this.sumLog = s;
+ }
+
+ public double evaluate (double k)
+ {
+ if (k < 1.0) return 1.0e200;
+ return (sumLog + n * (Num.lnGamma (k / 2.0) - 0.5*Num.LN2 - Num.lnGamma ((k + 1.0) / 2.0)));
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ChiSquareDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a chi-square distribution with \texttt{n} degrees of freedom.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ if (x <= 0)
+ return 0.0;
+ return Math.exp ((n/2.0 - 1)*Math.log (x) - x/2.0 - C1);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, decPrec, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, decPrec, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return ChiSquareDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return ChiSquareDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return ChiSquareDist.getStandardDeviation (n);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ if (x <= 0)
+ return 0.0;
+ return Math.exp ((n/2.0 - 1)*Math.log (x) - x/2.0 -
+ (n/2.0)*Num.LN2 - Num.lnGamma(n/2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:Fchi2})
+ for a {\em chi-square\/} distribution with $n$ degrees of freedom.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int n, int d, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= XBIG*n)
+ return 1.0;
+ return GammaDist.cdf (n/2.0, d, x/2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the chi-square distribution function with $n$ degrees of freedom,
+ evaluated at $x$. The method tries to return $d$ decimals digits of precision,
+ but there is no guarantee.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, int d, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ return GammaDist.barF (n/2.0, d, x/2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary chi-square distribution function with $n$ degrees
+ of freedom, evaluated at $x$. The method tries to return $d$ decimals digits
+ of precision, but there is no guarantee.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ /*
+ * Returns an approximation of the inverse of Chi square cdf
+ * with n degrees of freedom.
+ * As in Figure L.23 of P.Bratley, B.L.Fox, and L.E.Schrage.
+ * A Guide to Simulation Springer-Verlag,
+ * New York, second edition, 1987.
+ */
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+
+ final double E = 0.5e-5; // Precision of this approximation
+ final double AA = 0.6931471805;
+ double A, XX, X, C, G, CH, Q, P1, P2, T, B, S1, S2, S3, S4, S5, S6;
+
+ if (u < 0.00001 || u > 1.0 - 1.0e-5)
+ return 2.0 * GammaDist.inverseF (n / 2.0, 7, u);
+ if (u >= 1.0)
+ return n * XBIG;
+ if (u >= 0.999998)
+ return (n + 4.0 * Math.sqrt (2.0 * n));
+
+ G = Num.lnGamma (n / 2.0);
+ XX = 0.5 * n;
+ C = XX - 1.0;
+
+ if (n >= -1.24 * Math.log (u)) {
+ X = NormalDist.inverseF01 (u);
+ P1 = 0.222222 / n;
+ Q = X * Math.sqrt (P1) + 1.0 - P1;
+ CH = n * Q * Q * Q;
+ if (CH > 2.2 * n + 6.0)
+ CH = -2.0 * (Math.log1p (-u) - C * Math.log (0.5 * CH) + G);
+
+ } else {
+ CH = Math.pow (u * XX * Math.exp (G + XX * AA), 1.0 / XX);
+ if (CH - E < 0)
+ return CH;
+ }
+
+ Q = CH;
+ P1 = 0.5 * CH;
+ P2 = u - GammaDist.cdf (XX, 5, P1);
+ if (GammaDist.cdf (XX, 5, P1) == -1.0)
+ throw new IllegalArgumentException ("RESULT = -1");
+
+ T = P2 * Math.exp (XX * AA + G + P1 - C * Math.log (CH));
+ B = T / CH;
+ A = 0.5 * T - B * C;
+ S1 = (210.0 + A * (140.0 +
+ A * (105.0 + A * (84.0 + A * (70.0 + 60.0 * A))))) / 420.0;
+ S2 = (420.0 + A * (735.0 + A * (966.0 + A * (1141.0 + 1278.0 * A))))
+ / 2520.0;
+ S3 = (210.0 + A * (462.0 + A * (707.0 + 932.0 * A))) / 2520.0;
+ S4 = (252.0 + A * (672.0 + 1182.0 * A) +
+ C * (294.0 + A * (889.0 + 1740.0 * A))) / 5040.0;
+ S5 = (84.0 + 264.0 * A + C * (175.0 + 606.0 * A)) / 2520.0;
+ S6 = (120.0 + C * (346.0 + 127.0 * C)) / 5040.0;
+ CH = CH + T * (1.0 + 0.5 * T * S1 - B * C * (S1 - B * (S2 -
+ B * (S3 - B * (S4 - B * (S5 - B * S6))))));
+
+ double temp;
+ while (Math.abs (Q / CH - 1.0) > E) {
+ Q = CH;
+ P1 = 0.5 * CH;
+ temp = GammaDist.cdf (XX, 6, P1);
+ P2 = u - temp;
+
+ if (temp == -1.0)
+ return -1.0;
+
+ T = P2 * Math.exp (XX * AA + G + P1 - C * Math.log (CH));
+ B = T / CH;
+ A = 0.5 * T - B * C;
+ S1 = (210.0 + A * (140.0 + A * (105.0 + A * (84.0 +
+ A * (70.0 + 60.0 * A))))) / 420.0;
+ S2 = (420.0 + A * (735.0 + A * (966.0 + A * (1141.0 +
+ 1278.0 * A)))) / 2520.0;
+ S3 = (210.0 + A * (462.0 + A * (707.0 + 932.0 * A))) / 2520.0;
+ S4 = (252.0 + A * (672.0 + 1182.0 * A) +
+ C * (294.0 + A * (889.0 + 1740.0 * A))) / 5040.0;
+ S5 = (84.0 + 264.0 * A + C * (175.0 + 606.0 * A)) / 2520.0;
+ S6 = (120.0 + C * (346.0 + 127.0 * C)) / 5040.0;
+ CH = CH + T * (1.0 + 0.5 * T * S1 - B * C * (S1 - B * (S2 -
+ B * (S3 - B * (S4 - B * (S5 - B * S6))))));
+ }
+ return CH;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes an approximation of $F^{-1}(u)$, where $F$ is the
+ chi-square distribution with $n$ degrees of freedom.
+ \latex{Uses the approximation given in \cite{tBES75a} and in
+ Figure L.23 of \cite{sBRA87a}.}
+ It gives at least 6 decimal digits of precision, except far in the tails
+ (that is, for $u< 10^{-5}$ or $u > 1 - 10^{-5}$) where the function
+ calls the method \texttt{GammaDist.inverseF (n/2, 7, u)} and
+ multiplies the result by 2.0.
+ To get better precision, one may
+ call \texttt{GammaDist.inverseF}, but this method is slower than the
+ current method, especially for large $n$. For instance, for $n = $ 16, 1024,
+ and 65536, the \texttt{GammaDist.inverseF} method is 2, 5, and 8 times slower,
+ respectively, than the current method.
+\end{tabb}
+%% \begin{code}
+
+%% public static double inverseF2 (int n, double u)\begin{hide} {
+%% /*
+%% * Returns an approximation of the inverse of Chi square cdf
+%% * with n degrees of freedom.
+%% * As in Figure L.24 of P.Bratley, B.L.Fox, and L.E.Schrage.
+%% * A Guide to Simulation Springer-Verlag,
+%% * New York, second edition, 1987.
+%% */
+
+%% final double SQP5 = 0.70710678118654752440;
+%% final double DWARF = 0.1e-15;
+%% double Z, arg, zsq, ch, sqdf;
+%% if (u < 0.0 || u > 1.0)
+%% throw new IllegalArgumentException ("u is not in [0,1]");
+
+%% if (u <= 0.0)
+%% return 0.0;
+%% if (u >= 1.0)
+%% return Double.POSITIVE_INFINITY;
+
+%% if (n == 1) {
+%% Z = NormalDist.inverseF1 ((1.0 + u)/2);
+%% return Z*Z;
+%% }
+%% else if (n == 2) {
+%% arg = 1.0 - u;
+%% if (arg < DWARF)
+%% arg = DWARF;
+
+%% return -Math.log (arg)*2.0;
+%% }
+%% else if (u > DWARF) {
+%% Z = NormalDist.inverseF1 (u);
+%% sqdf = Math.sqrt ((double)n);
+%% zsq = Z*Z;
+
+%% ch = -(((3753.0*zsq + 4353.0)*zsq - 289517.0)*zsq -
+%% 289717.0)*Z*SQP5/9185400;
+
+%% ch = ch/sqdf + (((12.0*zsq - 243.0)*zsq - 923.0)
+%% *zsq + 1472.0)/25515.0;
+
+%% ch = ch/sqdf + ((9.0*zsq + 256.0)*zsq - 433.0)
+%% *Z*SQP5/4860;
+
+%% ch = ch/sqdf - ((6.0*zsq + 14.0)*zsq - 32.0)/405.0;
+%% ch = ch/sqdf + (zsq - 7.0)*Z*SQP5/9;
+%% ch = ch/sqdf + 2.0*(zsq - 1.0)/3.0;
+%% ch = ch/sqdf + Z/SQP5;
+%% return n*(ch/sqdf + 1.0);
+%% }
+%% else
+%% return 0.0;
+%% }\end{hide}
+%% \end{code}
+%% \begin{tabb}
+%% Computes a quick-and-dirty approximation of $F^{-1}(u)$,
+%% where $F$ is the {\em chi-square\/} distribution with $n$ degrees of freedom.
+%% \latex{Uses the approximation given in Figure L.24 of \cite{sBRA87a}.}
+%% \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int m)\begin{hide} {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ double[] parameters;
+
+ parameters = getMomentsEstimate (x, m);
+ double k = Math.round (parameters[0]) - 5.0;
+ if (k < 1.0)
+ k = 1.0;
+
+ double sum = 0.0;
+ for (int i = 0; i < m; i++) {
+ if (x[i] > 0.0)
+ sum += 0.5*Math.log (x[i]);
+ else
+ sum -= 709.0;
+ }
+
+ Function f = new Function (sum, m);
+ while (f.evaluate(k) > 0.0)
+ k++;
+ parameters[0] = k;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $n$ of the chi-square distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1, \ldots, m-1$. The estimate is returned in element 0
+ of the returned array.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+ \return{returns the parameter [$\hat{n}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static ChiSquareDist getInstanceFromMLE (double[] x, int m)\begin{hide} {
+ double parameters[] = getMLE (x, m);
+ return new ChiSquareDist ((int) parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a chi-square distribution with parameter $n$ estimated using
+ the maximum likelihood method based on the $m$ observations $x[i]$,
+ $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ return n;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = n$ of the
+ chi-square distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Chi-square distribution $E[X] = n$}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMomentsEstimate (double[] x, int m)\begin{hide} {
+ double[] parameters = new double[1];
+
+ double sum = 0.0;
+ for (int i = 0; i < m; i++)
+ sum += x[i];
+ parameters[0] = sum / (double) m;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates and returns the parameter [$\hat{n}$] of the chi-square
+ distribution using the moments method based on the $m$ observations
+ in table $x[i]$, $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+ \return{returns the parameter [$\hat{n}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ return (2 * n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance $\mbox{Var}[X] = 2n$
+ of the chi-square distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the chi-square distribution $\mbox{Var}X] = 2n$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ return Math.sqrt(2 * n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation
+ of the chi-square distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the chi-square distribution}
+\end{htmlonly}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("degrees of freedom " +
+ "must be non-null and positive.");
+
+ this.n = n;
+ supportA = 0.0;
+ C1 = 0.5 * n *Num.LN2 + Num.lnGamma(n/2.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiSquareDistQuick.java b/source/umontreal/iro/lecuyer/probdist/ChiSquareDistQuick.java
new file mode 100644
index 0000000..6796a15
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiSquareDistQuick.java
@@ -0,0 +1,158 @@
+
+
+/*
+ * Class: ChiSquareDistQuick
+ * Description: chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Provides a variant of {@link ChiSquareDist} with
+ * faster but less accurate methods.
+ * The non-static version of
+ * <TT>inverseF</TT> calls the static version.
+ * This method is not very accurate for small <SPAN CLASS="MATH"><I>n</I></SPAN> but becomes
+ * better as <SPAN CLASS="MATH"><I>n</I></SPAN> increases.
+ * The other methods are the same as in {@link ChiSquareDist}.
+ *
+ */
+public class ChiSquareDistQuick extends ChiSquareDist {
+
+
+
+ /**
+ * Constructs a chi-square distribution with <TT>n</TT> degrees of freedom.
+ *
+ */
+ public ChiSquareDistQuick (int n) {
+ super (n);
+ }
+
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ /**
+ * Computes a quick-and-dirty approximation of <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>,
+ * where <SPAN CLASS="MATH"><I>F</I></SPAN> is the <EM>chi-square</EM> distribution with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ * Uses the approximation given in Figure L.24 of
+ * Bratley, Fox and Schrage (1987) over most of the range.
+ * For <SPAN CLASS="MATH"><I>u</I> < 0.02</SPAN> or <SPAN CLASS="MATH"><I>u</I> > 0.98</SPAN>, it uses the approximation given in
+ * Goldstein for <SPAN CLASS="MATH"><I>n</I> >= 10</SPAN>, and returns
+ * <TT>2.0 *</TT>
+ * {@link GammaDist#inverseF(double,int,double) GammaDist.inverseF} <TT>(n/2, 6, u)</TT>
+ * for <SPAN CLASS="MATH"><I>n</I> < 10</SPAN> in order to avoid
+ * the loss of precision of the above approximations.
+ * When <SPAN CLASS="MATH"><I>n</I> >= 10</SPAN> or
+ * <SPAN CLASS="MATH">0.02 < <I>u</I> < 0.98</SPAN>,
+ * it is between 20 to 30 times faster than the same method in
+ * {@link ChiSquareDist} for <SPAN CLASS="MATH"><I>n</I></SPAN> between <SPAN CLASS="MATH">10</SPAN> and <SPAN CLASS="MATH">1000</SPAN> and even faster
+ * for larger <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * <P>
+ * Note that the number <SPAN CLASS="MATH"><I>d</I></SPAN> of decimal digits of precision
+ * generally increases with <SPAN CLASS="MATH"><I>n</I></SPAN>. For <SPAN CLASS="MATH"><I>n</I> = 3</SPAN>, we only have <SPAN CLASS="MATH"><I>d</I> = 3</SPAN> over
+ * most of the range. For <SPAN CLASS="MATH"><I>n</I> = 10</SPAN>, <SPAN CLASS="MATH"><I>d</I> = 5</SPAN> except far in the tails where <SPAN CLASS="MATH"><I>d</I> = 3</SPAN>.
+ * For <SPAN CLASS="MATH"><I>n</I> = 100</SPAN>, one has more than <SPAN CLASS="MATH"><I>d</I> = 7</SPAN> over most of the range and for
+ * <SPAN CLASS="MATH"><I>n</I> = 1000</SPAN>, at least <SPAN CLASS="MATH"><I>d</I> = 8</SPAN>.
+ * The cases <SPAN CLASS="MATH"><I>n</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>n</I> = 2</SPAN> are exceptions, with precision of about <SPAN CLASS="MATH"><I>d</I> = 10</SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ /*
+ * Returns an approximation of the inverse of Chi square cdf
+ * with n degrees of freedom.
+ * As in Figure L.24 of P.Bratley, B.L.Fox, and L.E.Schrage.
+ * A Guide to Simulation Springer-Verlag,
+ * New York, second edition, 1987.
+ */
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ final double SQP5 = 0.70710678118654752440;
+ final double DWARF = 0.1e-15;
+ final double ULOW = 0.02;
+ double Z, arg, v, ch, sqdf;
+
+ if (n == 1) {
+ Z = NormalDist.inverseF01 ((1.0 + u)/2.0);
+ return Z*Z;
+
+ } else if (n == 2) {
+ arg = 1.0 - u;
+ if (arg < DWARF)
+ arg = DWARF;
+ return -Math.log (arg)*2.0;
+
+ } else if ((u > ULOW) && (u < 1.0 - ULOW)) {
+ Z = NormalDist.inverseF01 (u);
+ sqdf = Math.sqrt ((double)n);
+ v = Z * Z;
+
+ ch = -(((3753.0 * v + 4353.0) * v - 289517.0) * v -
+ 289717.0) * Z * SQP5 / 9185400;
+
+ ch = ch / sqdf + (((12.0 * v - 243.0) * v - 923.0)
+ * v + 1472.0) / 25515.0;
+
+ ch = ch / sqdf + ((9.0 * v + 256.0) * v - 433.0)
+ * Z * SQP5 / 4860;
+
+ ch = ch / sqdf - ((6.0 * v + 14.0) * v - 32.0) / 405.0;
+ ch = ch / sqdf + (v - 7.0) * Z * SQP5 / 9;
+ ch = ch / sqdf + 2.0 * (v - 1.0) / 3.0;
+ ch = ch / sqdf + Z / SQP5;
+ return n * (ch / sqdf + 1.0);
+
+ } else if (n >= 10) {
+ Z = NormalDist.inverseF01 (u);
+ v = Z * Z;
+ double temp;
+ temp = 1.0 / 3.0 + (-v + 3.0) / (162.0 * n) -
+ (3.0 * v * v + 40.0 * v + 45.0) / (5832.0 * n * n) +
+ (301.0 * v * v * v - 1519.0 * v * v - 32769.0 * v -
+ 79349.0) / (7873200.0 * n * n * n);
+ temp *= Z * Math.sqrt (2.0 / n);
+
+ ch = 1.0 - 2.0 / (9.0 * n) + (4.0 * v * v + 16.0 * v -
+ 28.0) / (1215.0 * n * n) + (8.0 * v * v * v + 720.0 * v * v +
+ 3216.0 * v + 2904.0) / (229635.0 * n * n * n) + temp;
+
+ return n * ch * ch * ch;
+
+ } else {
+ // Note: this implementation is quite slow.
+ // Since it is restricted to the tails, we could perhaps replace
+ // this with some other approximation (series expansion ?)
+ return 2.0*GammaDist.inverseF (n/2.0, 6, u);
+ }
+}
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiSquareDistQuick.tex b/source/umontreal/iro/lecuyer/probdist/ChiSquareDistQuick.tex
new file mode 100644
index 0000000..699a91a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiSquareDistQuick.tex
@@ -0,0 +1,167 @@
+\defmodule {ChiSquareDistQuick}
+
+Provides a variant of \class{ChiSquareDist} with
+faster but less accurate methods.
+The non-static version of
+% the methods \texttt{cdf}, \texttt{barF},
+\texttt{inverseF} calls the static version.
+This method is not very accurate for small $n$ but becomes
+better as $n$ increases.
+The other methods are the same as in \class{ChiSquareDist}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiSquareDistQuick
+ * Description: chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class ChiSquareDistQuick extends ChiSquareDist\begin{hide} {
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ChiSquareDistQuick (int n)\begin{hide} {
+ super (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a chi-square distribution with \texttt{n} degrees of freedom.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }\end{hide}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ /*
+ * Returns an approximation of the inverse of Chi square cdf
+ * with n degrees of freedom.
+ * As in Figure L.24 of P.Bratley, B.L.Fox, and L.E.Schrage.
+ * A Guide to Simulation Springer-Verlag,
+ * New York, second edition, 1987.
+ */
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ final double SQP5 = 0.70710678118654752440;
+ final double DWARF = 0.1e-15;
+ final double ULOW = 0.02;
+ double Z, arg, v, ch, sqdf;
+
+ if (n == 1) {
+ Z = NormalDist.inverseF01 ((1.0 + u)/2.0);
+ return Z*Z;
+
+ } else if (n == 2) {
+ arg = 1.0 - u;
+ if (arg < DWARF)
+ arg = DWARF;
+ return -Math.log (arg)*2.0;
+
+ } else if ((u > ULOW) && (u < 1.0 - ULOW)) {
+ Z = NormalDist.inverseF01 (u);
+ sqdf = Math.sqrt ((double)n);
+ v = Z * Z;
+
+ ch = -(((3753.0 * v + 4353.0) * v - 289517.0) * v -
+ 289717.0) * Z * SQP5 / 9185400;
+
+ ch = ch / sqdf + (((12.0 * v - 243.0) * v - 923.0)
+ * v + 1472.0) / 25515.0;
+
+ ch = ch / sqdf + ((9.0 * v + 256.0) * v - 433.0)
+ * Z * SQP5 / 4860;
+
+ ch = ch / sqdf - ((6.0 * v + 14.0) * v - 32.0) / 405.0;
+ ch = ch / sqdf + (v - 7.0) * Z * SQP5 / 9;
+ ch = ch / sqdf + 2.0 * (v - 1.0) / 3.0;
+ ch = ch / sqdf + Z / SQP5;
+ return n * (ch / sqdf + 1.0);
+
+ } else if (n >= 10) {
+ Z = NormalDist.inverseF01 (u);
+ v = Z * Z;
+ double temp;
+ temp = 1.0 / 3.0 + (-v + 3.0) / (162.0 * n) -
+ (3.0 * v * v + 40.0 * v + 45.0) / (5832.0 * n * n) +
+ (301.0 * v * v * v - 1519.0 * v * v - 32769.0 * v -
+ 79349.0) / (7873200.0 * n * n * n);
+ temp *= Z * Math.sqrt (2.0 / n);
+
+ ch = 1.0 - 2.0 / (9.0 * n) + (4.0 * v * v + 16.0 * v -
+ 28.0) / (1215.0 * n * n) + (8.0 * v * v * v + 720.0 * v * v +
+ 3216.0 * v + 2904.0) / (229635.0 * n * n * n) + temp;
+
+ return n * ch * ch * ch;
+
+ } else {
+ // Note: this implementation is quite slow.
+ // Since it is restricted to the tails, we could perhaps replace
+ // this with some other approximation (series expansion ?)
+ return 2.0*GammaDist.inverseF (n/2.0, 6, u);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Computes a quick-and-dirty approximation of $F^{-1}(u)$,
+ where $F$ is the {\em chi-square\/} distribution with $n$ degrees of freedom.
+ Uses the approximation given in Figure L.24 of \latex{\cite{sBRA87a}}
+ \html {Bratley, Fox and Schrage (1987)} over most of the range.
+ For $u < 0.02$ or $u > 0.98$, it uses the approximation given in
+ \latex{\cite{tGOL73a}} \html {Goldstein} for $n \ge 10$, and returns
+ \texttt{2.0 *}
+ \clsexternalmethod{}{GammaDist}{inverseF}{double,int,double}~\texttt{(n/2, 6, u)}
+ for $n < 10$ in order to avoid
+ the loss of precision of the above approximations.
+ When $n \ge 10$ or $0.02 < u < 0.98$,
+ it is between 20 to 30 times faster than the same method in
+ \class{ChiSquareDist} for $n$ between $10$ and $1000$ and even faster
+ for larger $n$.
+
+ Note that the number $d$ of decimal digits of precision
+ generally increases with $n$. For $n=3$, we only have $d = 3$ over
+ most of the range. For $n=10$, $d=5$ except far in the tails where $d = 3$.
+ For $n=100$, one has more than $d=7$ over most of the range and for
+ $n=1000$, at least $d=8$.
+ The cases $n = 1$ and $n = 2$ are exceptions, with precision of about $d=10$.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiSquareNoncentralDist.java b/source/umontreal/iro/lecuyer/probdist/ChiSquareNoncentralDist.java
new file mode 100644
index 0000000..902d250
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiSquareNoncentralDist.java
@@ -0,0 +1,713 @@
+
+/*
+ * Class: ChiSquareNoncentralDist
+ * Description: Non-central chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since March 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>noncentral chi-square</EM> distribution with <SPAN CLASS="MATH"><I>ν</I></SPAN> degrees of freedom
+ * and noncentrality parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>, where <SPAN CLASS="MATH"><I>ν</I> > 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>e</I><SUP>-(x+<I>λ</I>)/2</SUP>)/2(<I>x</I>/<I>λ</I>)<SUP>(<I>ν</I>-2)/4</SUP><I>I</I><SUB><I>ν</I>/2-1</SUB>((λx)<SUP>1/2</SUP>) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>I</I><SUB><I>ν</I></SUB>(<I>x</I>)</SPAN> is the modified Bessel function of the first kind of
+ * order <SPAN CLASS="MATH"><I>ν</I></SPAN> given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>I</I><SUB><I>ν</I></SUB>(<I>z</I>) = ∑<SUB>j=0</SUB><SUP>∞</SUP>[(<I>z</I>/2)<SUP><I>ν</I>+2j</SUP>]/[<I>j</I>! <I>Γ</I>(<I>ν</I> + <I>j</I> + 1)],
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function.
+ * Notice that this distribution is more general than the <EM>chi-square</EM>
+ * distribution since its number of degrees of freedom can be any positive real
+ * number. For
+ * <SPAN CLASS="MATH"><I>λ</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>ν</I></SPAN> a positive integer, we have the ordinary
+ * <EM>chi-square</EM> distribution.
+ *
+ * <P>
+ * The cumulative probability function can be written as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>[<I>X</I> <= <I>x</I>] = ∑<SUB>j=0</SUB><SUP>∞</SUP>(<I>e</I><SUP>-<I>λ</I>/2</SUP>(<I>λ</I>/2)<SUP>j</SUP>/<I>j</I>!)<I>P</I>[<I>χ</I><SUP>2</SUP><SUB><I>ν</I>+2j</SUB> <= <I>x</I>],
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>χ</I><SUP>2</SUP><SUB><I>ν</I>+2j</SUB></SPAN> is the <EM>central chi-square</EM> distribution
+ * with <SPAN CLASS="MATH"><I>ν</I> + 2<I>j</I></SPAN> degrees of freedom.
+ *
+ */
+public class ChiSquareNoncentralDist extends ContinuousDistribution {
+
+ protected static final int PREC = 15; // num. decimal digits of precision
+ protected static final double EPS = Num.TEN_NEG_POW[PREC];
+
+ // To get better precision for very small probabilities
+ private static final double PROB_MIN = Double.MIN_VALUE / EPS;
+ private static final boolean DETAIL = false; // For debugging
+ private static final double PARLIM = 1000.0; // Parameters limit
+
+ private double nu;
+ private double lambda;
+ private PoissonDist pois;
+ private int jmin = -1;
+ private int jmax = -1;
+
+ private static class Function implements MathFunction {
+ protected double nu;
+ protected double lambda;
+ protected double u;
+
+ public Function (double nu, double lambda, double u) {
+ this.nu = nu;
+ this.lambda = lambda;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(nu, lambda, x);
+ }
+ }
+
+ private static double getXLIM (double nu, double lambda) {
+ // for x >= XLIM, density(x) = 0, and cdf(x) = 1
+ return (1600.0 + 20.0*(nu + lambda));
+ }
+
+ private static int calcJmin (PoissonDist pois) {
+ /* Find first lower j such that pois.cdf(j) < EPS. If EPS is chosen
+ larger, must change the 7*sig below to find jmin. */
+ double lam = pois.getLambda();
+ double sig = Math.sqrt(lam);
+ int j = (int) (lam - 7.0*sig);
+ if (j < 0) return 0;
+ while (j > 0 && pois.cdf(j) > EPS)
+ j--;
+ return j + 1;
+ }
+
+
+ private static int calcJmax (PoissonDist pois) {
+ /* Find first upper j such that pois.barF(j) < EPS. If EPS is chosen
+ larger, must change the 7*sig below to find jmax. */
+ double lam = pois.getLambda();
+ double sig = Math.sqrt(lam);
+ int j = (int) (lam + 7.0*sig);
+ while (pois.barF(j) > EPS)
+ j++;
+ return j - 1;
+ }
+
+
+
+ /**
+ * Constructs a noncentral chi-square distribution with <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT>
+ * degrees of freedom and noncentrality parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>.
+ *
+ */
+ public ChiSquareNoncentralDist (double nu, double lambda) {
+ setParams (nu, lambda);
+ }
+
+
+ public double density (double x) {
+ return density (nu, lambda, x);
+ }
+
+ public double cdf (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= getXLIM (nu, lambda))
+ return 1.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (false, nu, lambda, x);
+
+ return cdfExact (pois, jmax, nu, lambda, x);
+ }
+
+ public double barF (double x) {
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= getXLIM (nu, lambda))
+ return 0.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (true, nu, lambda, x);
+
+ return barFExact (pois, jmin, nu, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (nu, lambda, u);
+ }
+
+ public double getMean() {
+ return ChiSquareNoncentralDist.getMean (nu, lambda);
+ }
+
+ public double getVariance() {
+ return ChiSquareNoncentralDist.getVariance (nu, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return ChiSquareNoncentralDist.getStandardDeviation (nu, lambda);
+ }
+
+ /**
+ * Computes the density function for a
+ * <EM>noncentral chi-square</EM> distribution with <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> degrees
+ * of freedom and parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>.
+ *
+ */
+ public static double density (double nu, double lambda, double x) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.density (nu/2.0, 0.5, x);
+ if (x >= getXLIM (nu, lambda))
+ return 0.0;
+
+ final double a = 0.5*nu - 1.0; // a = order of Bessel I_a(x)
+ double z = lambda*x;
+
+ // The series term is maximal for j = JMAX
+ final long JMAX = (long) (0.5*z/(a + Math.sqrt(a*a + z)));
+
+ // calculer le terme pour j = JMAX --> termMax
+ double termMax = -0.5*(x + lambda) + 0.25*(nu - 2)* Math.log (x/lambda) +
+ 0.5*(a + 2*JMAX)*Math.log(0.25*x * lambda) - Num.lnFactorial(JMAX) -
+ Num.lnGamma(a + JMAX + 1) - Num.LN2;
+
+ termMax = Math.exp(termMax);
+
+ // Sum non negligible terms on each side of the max term
+ double sum = termMax;
+ double term = termMax;
+ long j = JMAX;
+ double y = 4.0/z;
+
+ while (j > 0 && term > sum*EPS) {
+ term *= y*j*(j + a);
+ sum += term;
+ j--;
+ }
+
+ term = termMax;
+ j = JMAX + 1;
+ y = z/4.0;
+ while (term > sum*EPS) {
+ term *= y/(j*(j + a));
+ sum += term;
+ j++;
+ }
+ return sum;
+ }
+
+
+ /**
+ * Computes the noncentral chi-square distribution function
+ * with <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> degrees of freedom and parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN>
+ * <TT>lambda</TT>.
+ *
+ */
+ public static double cdf (double nu, double lambda, double x) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.cdf (nu/2.0, 0.5, PREC, x);
+ if (x >= getXLIM (nu, lambda))
+ return 1.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (false, nu, lambda, x);
+
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int j = calcJmax (pois);
+ return cdfExact (pois, j, nu, lambda, x);
+ }
+
+
+ private static double cdfExact (PoissonDist pois, int jmax, double nu,
+ double lambda, double x) {
+ final int JMED = (int) (lambda / 2.0);
+
+ int j = jmax;
+ double chicdf = GammaDist.cdf (j + 0.5*nu, 0.5, PREC, x);
+ if (chicdf >= 1.0) return 1.0;
+
+ double chiterm = x/(j + 0.5*nu) * GammaDist.density(j + 0.5*nu, 0.5, x);
+ double prob = pois.prob(j);
+ double sum = prob * chicdf;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "----------------------------------- cdf efficace");
+ System.out.print (" j chi term chi CDF");
+ System.out.println (" Poisson p somme" +
+ PrintfFormat.NEWLINE);
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 10, chiterm) + " " +
+ PrintfFormat.g (22, 15, chicdf) + " " +
+ PrintfFormat.g (20, 10, prob) + " " +
+ PrintfFormat.g (22, 15, sum));
+ }
+
+ --j;
+ while (j >= 0 && (prob > sum*EPS || j >= JMED)) {
+ if (chicdf <= PROB_MIN) {
+ chicdf = GammaDist.cdf (j + nu/2.0, 0.5, PREC, x);
+ chiterm = x / (j + 0.5*nu) *GammaDist.density(j + nu/2.0, 0.5, x);
+ } else {
+ chiterm *= (2 + 2*j + nu) / x;
+ chicdf += chiterm;
+ }
+ if (chicdf >= 1.0 - EPS)
+ return sum + pois.cdf(j);
+ prob = pois.prob(j);
+ sum += prob * chicdf;
+ if (DETAIL) {
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 10, chiterm) + " " +
+ PrintfFormat.g (22, 15, chicdf) + " " +
+ PrintfFormat.g (20, 10, prob) + " " +
+ PrintfFormat.g (22, 15, sum));
+ }
+ --j;
+ }
+ return sum;
+ }
+
+/* ******************
+ public static double cdf4 (double nu, double lambda, double x)
+ {
+ // Version naive et lente
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.cdf (nu/2.0, 0.5, PREC, x);
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int jmed = (int) (lambda / 2.0);
+ int j = 0;
+ double sum = 0.0;
+ double prob = 1.0e100;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "-------------------------------------- cdf naif");
+ System.out.print (" j Poisson p chi CDF");
+ System.out.println (" somme" + PrintfFormat.NEWLINE);
+ }
+
+ while (j <= jmed || prob > sum*EPS)
+ {
+ double chi2cdf = GammaDist.cdf (j + nu/2.0, 0.5, PREC, x);
+ prob = pois.prob(j);
+ sum += prob * chi2cdf;
+ if (DETAIL)
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 10, prob) + " " +
+ PrintfFormat.g (20, 10, chi2cdf) + " " +
+ PrintfFormat.g (20, 10, sum));
+ ++j;
+ }
+ if (DETAIL)
+ System.out.println ("");
+ return sum;
+ }
+********************/
+
+ private static double cdfPearson (boolean bar, double nu, double lambda,
+ double x) {
+ // Pearson's central chi square approximation from \cite{tPEA59a}
+ // If bar is true, then returns u = barF; else returns u = cdf
+ double t2 = (nu + 2.0*lambda);
+ double t3 = (nu + 3.0*lambda);
+ double lib = t2*t2*t2/(t3*t3);
+ double z = x + lambda*lambda/t3;
+ if (bar)
+ return GammaDist.barF(lib/2.0, 0.5, PREC, t2*z/t3);
+ else
+ return GammaDist.cdf(lib/2.0, 0.5, PREC, t2*z/t3);
+ }
+
+ private static double penevH (double y) {
+ // The h function in Penev-Raykov paper
+ if (0.0 == y)
+ return 0.0;
+ if (1.0 == y)
+ return 0.5;
+ return ((1.0 - y)*Math.log1p(-y) + y - 0.5*y*y) / (y*y);
+ }
+
+
+ private static double penevB (double mu, double s, double h1) {
+ // The B function in Penev-Raykov paper
+ final double f = 1.0 + 2.0 * mu * s;
+ final double g = 1.0 + 3.0 * mu * s;
+ final double c = (f - 2.0 * h1 - s *f) / (f - 2.0 * h1);
+ double B = -1.5 * (1.0 + 4.0 * mu * s) / (f * f);
+ B += 5.0 * g * g / (3.0 * f * f * f) + 2.0 * g / ((s - 1.0) * f * f);
+ B += 3.0 * c / ((s - 1.0) * (s - 1.0) * f);
+ B -= c * c * (1.0 + 2.0 * penevH (c)) / (2.0 * (s - 1.0) * (s - 1.0) * f);
+ return B;
+ }
+
+ private static double getPenevLim (double nu, double lam) {
+ if (lam >= 100000.0)
+ return 5.0;
+ if (nu >= 20000.0)
+ return 3.0;
+ return 2.0;
+ }
+
+ private static double cdfPenev (boolean bar, double nu, double lambda,
+ double x) {
+ /* Penev-Raykov normal approximation from \cite{tPEN00a}
+ If bar is true, then returns u = barF; else returns u = cdf.
+ This approximation is very good except very near the mean where it
+ is bad or give a NaN. Thus, near the mean, we use the Pearson approx.
+ */
+ double lim = getPenevLim(nu, lambda);
+ double mean = nu + lambda;
+ if (x >= mean - lim && x <= mean + lim)
+ return cdfPearson (bar, nu, lambda, x);
+
+ final double mu = lambda / nu;
+ final double s = (Math.sqrt(1.0 + 4.0 * x * mu / nu) - 1.0) / (2.0 * mu);
+ if (s == 1.0)
+ return 0.5;
+ final double h1 = penevH (1.0 - s);
+ final double B = penevB (mu, s, h1);
+ final double f = 1.0 + 2.0 * mu * s;
+ double z = nu * (s - 1.0) * (s - 1.0) * (0.5 / s + mu - h1 / s) -
+ Math.log(1.0 / s - 2.0 * h1 / (s * f)) + 2.0 * B / nu;
+ // If z is a NaN, then z != z
+ if (z < 0.0 || z != z)
+ return cdfPearson (bar, nu, lambda, x);
+
+ z = Math.sqrt(z);
+ if (s < 1)
+ z = -z;
+ if (bar)
+ return NormalDist.barF01(z);
+ else
+ return NormalDist.cdf01(z);
+ }
+
+
+
+ /**
+ * Computes the complementary noncentral chi-square distribution function with
+ * <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> degrees of freedom and parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN>
+ * <TT>lambda</TT>.
+ *
+ */
+ public static double barF (double nu, double lambda, double x) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (lambda == 0.0)
+ return GammaDist.barF (nu/2.0, 0.5, PREC, x);
+ if (x >= getXLIM (nu, lambda))
+ return 0.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (true, nu, lambda, x);
+
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int j = calcJmin (pois);
+ return barFExact (pois, j, nu, lambda, x);
+ }
+
+/* **********************
+ public static double bar4 (double nu, double lambda, double x)
+ {
+ // Version naive et lente
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (lambda == 0.0)
+ return GammaDist.barF (nu/2.0, 0.5, PREC, x);
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int jmed = (int) (lambda / 2.0);
+ int j = 0;
+ double sum = 0.0;
+ double pdfterm = 0.0;
+ double prob = 1.0e100;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "-------------------------------------- barF naif");
+ System.out.print (" j Poisson p chi term");
+ System.out.println (" chi barF somme" +
+ PrintfFormat.NEWLINE);
+ }
+
+ while (j <= jmed || prob > sum*EPS)
+ {
+ double chi2cdf = GammaDist.barF (j + nu/2.0, 0.5, PREC, x);
+ prob = pois.prob(j);
+ sum += prob * chi2cdf;
+ if (DETAIL)
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 12, prob) + " " +
+ PrintfFormat.g (20, 12, chi2cdf - pdfterm) + " " +
+ PrintfFormat.g (20, 12, chi2cdf) + " " +
+ PrintfFormat.g (20, 12, sum));
+ ++j;
+ pdfterm = chi2cdf;
+ }
+ if (DETAIL)
+ System.out.println ("");
+ return sum;
+ }
+********************/
+
+ private static double barFExact (PoissonDist pois, int jmin, double nu,
+ double lambda, double x) {
+ final int JMED = (int) (lambda / 2.0);
+
+ int j = jmin;
+ double chibar = GammaDist.barF (j + 0.5*nu, 0.5, PREC, x);
+ if (chibar >= 1.0) return 1.0;
+
+ double prob = pois.prob(j);
+ double sum = prob * chibar;
+ double chiterm = 2.0* GammaDist.density(j + 0.5*nu, 0.5, x);
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "--------------------------------- barF efficace");
+ System.out.print (" j Poisson p chi term");
+ System.out.println (" chi barF somme" +
+ PrintfFormat.NEWLINE);
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 12, prob) + " " +
+ PrintfFormat.g (20, 12, chiterm) + " " +
+ PrintfFormat.g (22, 12, chibar) + " " +
+ PrintfFormat.g (22, 12, sum));
+ }
+
+ ++j;
+ while (prob > sum*EPS || j <= JMED)
+ {
+ if (chibar <= PROB_MIN) {
+ chibar = GammaDist.barF (j + nu/2.0, 0.5, PREC, x);
+ chiterm = 2.0 *GammaDist.density(j + 0.5*nu, 0.5, x);
+ } else {
+ chiterm *= x/(2*j - 2 + nu);
+ chibar += chiterm;
+ }
+ if (chibar >= 1.0)
+ return sum + pois.barF(j);
+ prob = pois.prob(j);
+ sum += prob * chibar;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 12, prob) + " " +
+ PrintfFormat.g (20, 12, chiterm) + " " +
+ PrintfFormat.g (22, 12, chibar) + " " +
+ PrintfFormat.g (22, 12, sum));
+ }
+ ++j;
+ }
+ return sum;
+ }
+
+
+ /**
+ * Computes the inverse of the noncentral chi-square distribution with <SPAN CLASS="MATH"><I>ν</I> =</SPAN>
+ * <TT>nu</TT> degrees of freedom and parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>.
+ *
+ */
+ public static double inverseF (double nu, double lambda, double u) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.inverseF (nu/2.0, 0.5, PREC, u);
+
+ double x = inverse9 (nu, lambda, u);
+ double v = cdf (nu, lambda, x);
+
+ Function f = new Function (nu, lambda, u);
+ if (v >= u)
+ x = RootFinder.brentDekker (0.0, x, f, 1.0e-10);
+ else {
+ v = getXLIM (nu, lambda);
+ x = RootFinder.brentDekker (x, v, f, 1.0e-10);
+ }
+ return x;
+ }
+
+ private static double inverse9 (double nu, double lambda, double u) {
+ // Une approximation normale \cite{tKRI06a}
+ double z = NormalDist.inverseF01 (u);
+ double a = nu + lambda;
+ double b = (nu + 2.0*lambda)/(a*a);
+ double t = z*Math.sqrt(2.0*b/9.0) - 2.0*b/9.0 + 1.0;
+ return a*t*t*t;
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>ν</I> + <I>λ</I></SPAN> of the
+ * noncentral chi-square distribution with parameters <SPAN CLASS="MATH"><I>ν</I> =</SPAN>
+ * <TT>nu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>.
+ *
+ * @return the mean of the Noncentral noncentral chi-square distribution
+ *
+ */
+ public static double getMean (double nu, double lambda) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return nu + lambda;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 2(<I>ν</I> +2<I>λ</I>)</SPAN> of the noncentral chi-square distribution with parameters
+ * <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>.
+ *
+ * @return the variance of the noncentral chi-square distribution
+ *
+ */
+ public static double getVariance (double nu, double lambda) {
+ if (nu <= 0.)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return 2.0 * (nu + 2.0*lambda);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the noncentral
+ * chi-square distribution with parameters <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN>
+ * <TT>lambda</TT>.
+ *
+ * @return the standard deviation of the noncentral chi-square distribution
+ *
+ */
+ public static double getStandardDeviation (double nu, double lambda) {
+ return Math.sqrt(getVariance (nu, lambda));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN> of this object.
+ *
+ */
+ public double getNu() {
+ return nu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN>
+ * <TT>lambda</TT> of this object.
+ *
+ */
+ public void setParams (double nu, double lambda) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (lambda == 0.0)
+ throw new IllegalArgumentException ("lambda = 0");
+ this.nu = nu;
+ this.lambda = lambda;
+ supportA = 0.0;
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return;
+ pois = new PoissonDist (lambda / 2.0);
+ jmax = calcJmax (pois);
+ jmin = calcJmin (pois);
+ }
+
+
+ /**
+ * Returns a table containing the parameters of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {nu, lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + ": nu = " + nu + ", lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ChiSquareNoncentralDist.tex b/source/umontreal/iro/lecuyer/probdist/ChiSquareNoncentralDist.tex
new file mode 100644
index 0000000..f04f7a3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ChiSquareNoncentralDist.tex
@@ -0,0 +1,750 @@
+\defmodule {ChiSquareNoncentralDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em noncentral chi-square\/} distribution with $\nu$ degrees of freedom
+ and noncentrality parameter $\lambda$, where $\nu> 0$ and $\lambda> 0$
+ \cite[page 436]{tJOH95b}.
+Its density is
+\begin{htmlonly}
+\eq f(x) = ({e^{-(x + \lambda)/2}})/2 \left({x}/{\lambda}\right)^{(\nu-2)/4}
+ I_{\nu/2 - 1}\left(\sqrt{\lambda x}\right) \qquad\mbox{for } x > 0,
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) =\frac{e^{-(x + \lambda)/2}}2 \left(\frac{x}{\lambda}\right)^{(\nu-2)/4}
+ I_{\nu/2 - 1}\left(\sqrt{\lambda x}\right) \qquad\mbox{for } x > 0,
+ \eqlabel{eq:nc-fchi2}
+\endeq
+\end{latexonly}%
+where $I_\nu(x)$ is the modified Bessel function of the first kind of
+order $\nu$ given by
+\begin{latexonly}%
+\eq
+ I_\nu(z) = \sum_{j=0}^\infty
+ \frac {(z/2)^{\nu + 2j}}{j!\; \Gamma(\nu + j +1)},
+\endeq
+\end{latexonly}%
+\begin{htmlonly}
+\eq
+ I_\nu(z) = \sum_{j=0}^\infty
+ [(z/2)^{\nu + 2j}]/ [j!\; \Gamma(\nu + j +1)],
+\endeq
+\end{htmlonly}%
+where $\Gamma(x)$ is the gamma function.
+Notice that this distribution is more general than the {\em chi-square\/}
+distribution since its number of degrees of freedom can be any positive real
+number. For $\lambda = 0$ and $\nu$ a positive integer, we have the ordinary
+{\em chi-square\/} distribution.
+
+The cumulative probability function can be written as
+\begin{htmlonly}
+\eq
+ P[X \le x] = \sum_{j=0}^\infty ({e^{-\lambda/2}(\lambda/2)^j}/{j!})
+ P[\chi^2_{\nu + 2j} \le x],
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ P[X \le x] = \sum_{j=0}^\infty \frac {e^{-\lambda/2}(\lambda/2)^j}{j!}
+ P[\chi^2_{\nu + 2j} \le x], \eqlabel{eq:nc-cdfchi2}
+\endeq
+\end{latexonly}%
+where $\chi^2_{\nu + 2j}$ is the {\em central chi-square\/} distribution
+ with $\nu + 2j$ degrees of freedom.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: ChiSquareNoncentralDist
+ * Description: Non-central chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since March 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class ChiSquareNoncentralDist extends ContinuousDistribution\begin{hide} {
+
+ protected static final int PREC = 15; // num. decimal digits of precision
+ protected static final double EPS = Num.TEN_NEG_POW[PREC];
+
+ // To get better precision for very small probabilities
+ private static final double PROB_MIN = Double.MIN_VALUE / EPS;
+ private static final boolean DETAIL = false; // For debugging
+ private static final double PARLIM = 1000.0; // Parameters limit
+
+ private double nu;
+ private double lambda;
+ private PoissonDist pois;
+ private int jmin = -1;
+ private int jmax = -1;
+
+ private static class Function implements MathFunction {
+ protected double nu;
+ protected double lambda;
+ protected double u;
+
+ public Function (double nu, double lambda, double u) {
+ this.nu = nu;
+ this.lambda = lambda;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(nu, lambda, x);
+ }
+ }
+
+ private static double getXLIM (double nu, double lambda) {
+ // for x >= XLIM, density(x) = 0, and cdf(x) = 1
+ return (1600.0 + 20.0*(nu + lambda));
+ }
+
+ private static int calcJmin (PoissonDist pois) {
+ /* Find first lower j such that pois.cdf(j) < EPS. If EPS is chosen
+ larger, must change the 7*sig below to find jmin. */
+ double lam = pois.getLambda();
+ double sig = Math.sqrt(lam);
+ int j = (int) (lam - 7.0*sig);
+ if (j < 0) return 0;
+ while (j > 0 && pois.cdf(j) > EPS)
+ j--;
+ return j + 1;
+ }
+
+
+ private static int calcJmax (PoissonDist pois) {
+ /* Find first upper j such that pois.barF(j) < EPS. If EPS is chosen
+ larger, must change the 7*sig below to find jmax. */
+ double lam = pois.getLambda();
+ double sig = Math.sqrt(lam);
+ int j = (int) (lam + 7.0*sig);
+ while (pois.barF(j) > EPS)
+ j++;
+ return j - 1;
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ChiSquareNoncentralDist (double nu, double lambda)\begin{hide} {
+ setParams (nu, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a noncentral chi-square distribution with $\nu = $ \texttt{nu}
+ degrees of freedom and noncentrality parameter $\lambda = $ \texttt{lambda}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (nu, lambda, x);
+ }
+
+ public double cdf (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= getXLIM (nu, lambda))
+ return 1.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (false, nu, lambda, x);
+
+ return cdfExact (pois, jmax, nu, lambda, x);
+ }
+
+ public double barF (double x) {
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= getXLIM (nu, lambda))
+ return 0.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (true, nu, lambda, x);
+
+ return barFExact (pois, jmin, nu, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (nu, lambda, u);
+ }
+
+ public double getMean() {
+ return ChiSquareNoncentralDist.getMean (nu, lambda);
+ }
+
+ public double getVariance() {
+ return ChiSquareNoncentralDist.getVariance (nu, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return ChiSquareNoncentralDist.getStandardDeviation (nu, lambda);
+ }\end{hide}
+
+ public static double density (double nu, double lambda, double x)\begin{hide} {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.density (nu/2.0, 0.5, x);
+ if (x >= getXLIM (nu, lambda))
+ return 0.0;
+
+ final double a = 0.5*nu - 1.0; // a = order of Bessel I_a(x)
+ double z = lambda*x;
+
+ // The series term is maximal for j = JMAX
+ final long JMAX = (long) (0.5*z/(a + Math.sqrt(a*a + z)));
+
+ // calculer le terme pour j = JMAX --> termMax
+ double termMax = -0.5*(x + lambda) + 0.25*(nu - 2)* Math.log (x/lambda) +
+ 0.5*(a + 2*JMAX)*Math.log(0.25*x * lambda) - Num.lnFactorial(JMAX) -
+ Num.lnGamma(a + JMAX + 1) - Num.LN2;
+
+ termMax = Math.exp(termMax);
+
+ // Sum non negligible terms on each side of the max term
+ double sum = termMax;
+ double term = termMax;
+ long j = JMAX;
+ double y = 4.0/z;
+
+ while (j > 0 && term > sum*EPS) {
+ term *= y*j*(j + a);
+ sum += term;
+ j--;
+ }
+
+ term = termMax;
+ j = JMAX + 1;
+ y = z/4.0;
+ while (term > sum*EPS) {
+ term *= y/(j*(j + a));
+ sum += term;
+ j++;
+ }
+ return sum;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:nc-fchi2}) for a
+ {\em noncentral chi-square\/} distribution with $\nu = $ \texttt{nu} degrees
+ of freedom and parameter $\lambda = $ \texttt{lambda}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double nu, double lambda, double x)\begin{hide} {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.cdf (nu/2.0, 0.5, PREC, x);
+ if (x >= getXLIM (nu, lambda))
+ return 1.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (false, nu, lambda, x);
+
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int j = calcJmax (pois);
+ return cdfExact (pois, j, nu, lambda, x);
+ }
+
+
+ private static double cdfExact (PoissonDist pois, int jmax, double nu,
+ double lambda, double x) {
+ final int JMED = (int) (lambda / 2.0);
+
+ int j = jmax;
+ double chicdf = GammaDist.cdf (j + 0.5*nu, 0.5, PREC, x);
+ if (chicdf >= 1.0) return 1.0;
+
+ double chiterm = x/(j + 0.5*nu) * GammaDist.density(j + 0.5*nu, 0.5, x);
+ double prob = pois.prob(j);
+ double sum = prob * chicdf;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "----------------------------------- cdf efficace");
+ System.out.print (" j chi term chi CDF");
+ System.out.println (" Poisson p somme" +
+ PrintfFormat.NEWLINE);
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 10, chiterm) + " " +
+ PrintfFormat.g (22, 15, chicdf) + " " +
+ PrintfFormat.g (20, 10, prob) + " " +
+ PrintfFormat.g (22, 15, sum));
+ }
+
+ --j;
+ while (j >= 0 && (prob > sum*EPS || j >= JMED)) {
+ if (chicdf <= PROB_MIN) {
+ chicdf = GammaDist.cdf (j + nu/2.0, 0.5, PREC, x);
+ chiterm = x / (j + 0.5*nu) *GammaDist.density(j + nu/2.0, 0.5, x);
+ } else {
+ chiterm *= (2 + 2*j + nu) / x;
+ chicdf += chiterm;
+ }
+ if (chicdf >= 1.0 - EPS)
+ return sum + pois.cdf(j);
+ prob = pois.prob(j);
+ sum += prob * chicdf;
+ if (DETAIL) {
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 10, chiterm) + " " +
+ PrintfFormat.g (22, 15, chicdf) + " " +
+ PrintfFormat.g (20, 10, prob) + " " +
+ PrintfFormat.g (22, 15, sum));
+ }
+ --j;
+ }
+ return sum;
+ }
+
+/*******************
+ public static double cdf4 (double nu, double lambda, double x)
+ {
+ // Version naive et lente
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.cdf (nu/2.0, 0.5, PREC, x);
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int jmed = (int) (lambda / 2.0);
+ int j = 0;
+ double sum = 0.0;
+ double prob = 1.0e100;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "-------------------------------------- cdf naif");
+ System.out.print (" j Poisson p chi CDF");
+ System.out.println (" somme" + PrintfFormat.NEWLINE);
+ }
+
+ while (j <= jmed || prob > sum*EPS)
+ {
+ double chi2cdf = GammaDist.cdf (j + nu/2.0, 0.5, PREC, x);
+ prob = pois.prob(j);
+ sum += prob * chi2cdf;
+ if (DETAIL)
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 10, prob) + " " +
+ PrintfFormat.g (20, 10, chi2cdf) + " " +
+ PrintfFormat.g (20, 10, sum));
+ ++j;
+ }
+ if (DETAIL)
+ System.out.println ("");
+ return sum;
+ }
+********************/
+
+ private static double cdfPearson (boolean bar, double nu, double lambda,
+ double x) {
+ // Pearson's central chi square approximation from \cite{tPEA59a}
+ // If bar is true, then returns u = barF; else returns u = cdf
+ double t2 = (nu + 2.0*lambda);
+ double t3 = (nu + 3.0*lambda);
+ double lib = t2*t2*t2/(t3*t3);
+ double z = x + lambda*lambda/t3;
+ if (bar)
+ return GammaDist.barF(lib/2.0, 0.5, PREC, t2*z/t3);
+ else
+ return GammaDist.cdf(lib/2.0, 0.5, PREC, t2*z/t3);
+ }
+
+ private static double penevH (double y) {
+ // The h function in Penev-Raykov paper
+ if (0.0 == y)
+ return 0.0;
+ if (1.0 == y)
+ return 0.5;
+ return ((1.0 - y)*Math.log1p(-y) + y - 0.5*y*y) / (y*y);
+ }
+
+
+ private static double penevB (double mu, double s, double h1) {
+ // The B function in Penev-Raykov paper
+ final double f = 1.0 + 2.0 * mu * s;
+ final double g = 1.0 + 3.0 * mu * s;
+ final double c = (f - 2.0 * h1 - s *f) / (f - 2.0 * h1);
+ double B = -1.5 * (1.0 + 4.0 * mu * s) / (f * f);
+ B += 5.0 * g * g / (3.0 * f * f * f) + 2.0 * g / ((s - 1.0) * f * f);
+ B += 3.0 * c / ((s - 1.0) * (s - 1.0) * f);
+ B -= c * c * (1.0 + 2.0 * penevH (c)) / (2.0 * (s - 1.0) * (s - 1.0) * f);
+ return B;
+ }
+
+ private static double getPenevLim (double nu, double lam) {
+ if (lam >= 100000.0)
+ return 5.0;
+ if (nu >= 20000.0)
+ return 3.0;
+ return 2.0;
+ }
+
+ private static double cdfPenev (boolean bar, double nu, double lambda,
+ double x) {
+ /* Penev-Raykov normal approximation from \cite{tPEN00a}
+ If bar is true, then returns u = barF; else returns u = cdf.
+ This approximation is very good except very near the mean where it
+ is bad or give a NaN. Thus, near the mean, we use the Pearson approx.
+ */
+ double lim = getPenevLim(nu, lambda);
+ double mean = nu + lambda;
+ if (x >= mean - lim && x <= mean + lim)
+ return cdfPearson (bar, nu, lambda, x);
+
+ final double mu = lambda / nu;
+ final double s = (Math.sqrt(1.0 + 4.0 * x * mu / nu) - 1.0) / (2.0 * mu);
+ if (s == 1.0)
+ return 0.5;
+ final double h1 = penevH (1.0 - s);
+ final double B = penevB (mu, s, h1);
+ final double f = 1.0 + 2.0 * mu * s;
+ double z = nu * (s - 1.0) * (s - 1.0) * (0.5 / s + mu - h1 / s) -
+ Math.log(1.0 / s - 2.0 * h1 / (s * f)) + 2.0 * B / nu;
+ // If z is a NaN, then z != z
+ if (z < 0.0 || z != z)
+ return cdfPearson (bar, nu, lambda, x);
+
+ z = Math.sqrt(z);
+ if (s < 1)
+ z = -z;
+ if (bar)
+ return NormalDist.barF01(z);
+ else
+ return NormalDist.cdf01(z);
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the noncentral chi-square distribution function (\ref{eq:nc-cdfchi2})
+ with $\nu = $ \texttt{nu} degrees of freedom and parameter $\lambda = $
+ \texttt{lambda}.
+\begin{detailed} For $\lambda < 1000$, we use a reasonnably
+ efficient method
+ of summing the cumulative function (\ref{eq:nc-cdfchi2}) by using recurrence
+ relations to compute both the Poisson and the chi-square probabilities in terms
+ of neighboring probability terms. For $\lambda \ge 1000$, we use the
+ normal approximation given in \cite{tPEN00a}, except very near the mean where
+ we use the central chi square approximation from \cite{tPEA59a}. This function
+ returns at least 6 decimal degits of precision nearly everywhere.
+\end{detailed}
+\end{tabb}
+\begin{code}
+
+ public static double barF (double nu, double lambda, double x)\begin{hide} {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (lambda == 0.0)
+ return GammaDist.barF (nu/2.0, 0.5, PREC, x);
+ if (x >= getXLIM (nu, lambda))
+ return 0.0;
+
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return cdfPenev (true, nu, lambda, x);
+
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int j = calcJmin (pois);
+ return barFExact (pois, j, nu, lambda, x);
+ }
+
+/***********************
+ public static double bar4 (double nu, double lambda, double x)
+ {
+ // Version naive et lente
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (lambda == 0.0)
+ return GammaDist.barF (nu/2.0, 0.5, PREC, x);
+ PoissonDist pois = new PoissonDist (lambda / 2.0);
+ int jmed = (int) (lambda / 2.0);
+ int j = 0;
+ double sum = 0.0;
+ double pdfterm = 0.0;
+ double prob = 1.0e100;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "-------------------------------------- barF naif");
+ System.out.print (" j Poisson p chi term");
+ System.out.println (" chi barF somme" +
+ PrintfFormat.NEWLINE);
+ }
+
+ while (j <= jmed || prob > sum*EPS)
+ {
+ double chi2cdf = GammaDist.barF (j + nu/2.0, 0.5, PREC, x);
+ prob = pois.prob(j);
+ sum += prob * chi2cdf;
+ if (DETAIL)
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 12, prob) + " " +
+ PrintfFormat.g (20, 12, chi2cdf - pdfterm) + " " +
+ PrintfFormat.g (20, 12, chi2cdf) + " " +
+ PrintfFormat.g (20, 12, sum));
+ ++j;
+ pdfterm = chi2cdf;
+ }
+ if (DETAIL)
+ System.out.println ("");
+ return sum;
+ }
+********************/
+
+ private static double barFExact (PoissonDist pois, int jmin, double nu,
+ double lambda, double x) {
+ final int JMED = (int) (lambda / 2.0);
+
+ int j = jmin;
+ double chibar = GammaDist.barF (j + 0.5*nu, 0.5, PREC, x);
+ if (chibar >= 1.0) return 1.0;
+
+ double prob = pois.prob(j);
+ double sum = prob * chibar;
+ double chiterm = 2.0* GammaDist.density(j + 0.5*nu, 0.5, x);
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.NEWLINE +
+ "--------------------------------- barF efficace");
+ System.out.print (" j Poisson p chi term");
+ System.out.println (" chi barF somme" +
+ PrintfFormat.NEWLINE);
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 12, prob) + " " +
+ PrintfFormat.g (20, 12, chiterm) + " " +
+ PrintfFormat.g (22, 12, chibar) + " " +
+ PrintfFormat.g (22, 12, sum));
+ }
+
+ ++j;
+ while (prob > sum*EPS || j <= JMED)
+ {
+ if (chibar <= PROB_MIN) {
+ chibar = GammaDist.barF (j + nu/2.0, 0.5, PREC, x);
+ chiterm = 2.0 *GammaDist.density(j + 0.5*nu, 0.5, x);
+ } else {
+ chiterm *= x/(2*j - 2 + nu);
+ chibar += chiterm;
+ }
+ if (chibar >= 1.0)
+ return sum + pois.barF(j);
+ prob = pois.prob(j);
+ sum += prob * chibar;
+
+ if (DETAIL) {
+ System.out.println (PrintfFormat.d (5, j) + " " +
+ PrintfFormat.g (20, 12, prob) + " " +
+ PrintfFormat.g (20, 12, chiterm) + " " +
+ PrintfFormat.g (22, 12, chibar) + " " +
+ PrintfFormat.g (22, 12, sum));
+ }
+ ++j;
+ }
+ return sum;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary noncentral chi-square distribution function with
+ $\nu = $ \texttt{nu} degrees of freedom and parameter $\lambda = $
+ \texttt{lambda}.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double nu, double lambda, double u)\begin{hide} {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+ if (lambda == 0.0)
+ return GammaDist.inverseF (nu/2.0, 0.5, PREC, u);
+
+ double x = inverse9 (nu, lambda, u);
+ double v = cdf (nu, lambda, x);
+
+ Function f = new Function (nu, lambda, u);
+ if (v >= u)
+ x = RootFinder.brentDekker (0.0, x, f, 1.0e-10);
+ else {
+ v = getXLIM (nu, lambda);
+ x = RootFinder.brentDekker (x, v, f, 1.0e-10);
+ }
+ return x;
+ }
+
+ private static double inverse9 (double nu, double lambda, double u) {
+ // Une approximation normale \cite{tKRI06a}
+ double z = NormalDist.inverseF01 (u);
+ double a = nu + lambda;
+ double b = (nu + 2.0*lambda)/(a*a);
+ double t = z*Math.sqrt(2.0*b/9.0) - 2.0*b/9.0 + 1.0;
+ return a*t*t*t;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse of the noncentral chi-square distribution with $\nu = $
+ \texttt{nu} degrees of freedom and parameter $\lambda = $ \texttt{lambda}.
+\end{tabb}
+\begin{code}
+
+ public static double getMean (double nu, double lambda)\begin{hide} {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return nu + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \nu + \lambda$ of the
+ noncentral chi-square distribution with parameters $\nu = $
+\texttt{nu} and $\lambda = $ \texttt{lambda}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Noncentral noncentral chi-square distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double nu, double lambda)\begin{hide} {
+ if (nu <= 0.)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return 2.0 * (nu + 2.0*lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = 2(\nu +
+ 2\lambda)$ of the noncentral chi-square distribution with parameters
+ $\nu =$ \texttt{nu} and $\lambda = $ \texttt{lambda}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the noncentral chi-square distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double nu, double lambda)\begin{hide} {
+ return Math.sqrt(getVariance (nu, lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the noncentral
+ chi-square distribution with parameters $\nu =$ \texttt{nu} and $\lambda = $
+ \texttt{lambda}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the noncentral chi-square distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getNu()\begin{hide} {
+ return nu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\nu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\lambda$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public void setParams (double nu, double lambda)\begin{hide} {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (lambda == 0.0)
+ throw new IllegalArgumentException ("lambda = 0");
+ this.nu = nu;
+ this.lambda = lambda;
+ supportA = 0.0;
+ if (nu >= PARLIM || lambda >= PARLIM)
+ return;
+ pois = new PoissonDist (lambda / 2.0);
+ jmax = calcJmax (pois);
+ jmin = calcJmin (pois);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\nu =$ \texttt{nu} and $\lambda = $
+ \texttt{lambda} of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {nu, lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a table containing the parameters of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + ": nu = " + nu + ", lambda = " + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/ConstantDist.java b/source/umontreal/iro/lecuyer/probdist/ConstantDist.java
new file mode 100644
index 0000000..333fe02
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ConstantDist.java
@@ -0,0 +1,105 @@
+
+
+/*
+ * Class: ConstantDist
+ * Description: constant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Represents a <SPAN CLASS="textit">constant</SPAN> discrete distribution taking a single real
+ * value with probability 1.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = 1, for <I>x</I> = <I>c</I>,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>p</I>(<I>x</I>) = 0</SPAN> elsewhere.
+ *
+ */
+public class ConstantDist extends DiscreteDistribution {
+ private double c;
+
+
+ /**
+ * Constructs a new constant distribution with probability 1 at <TT>c</TT>.
+ *
+ */
+ public ConstantDist (double c) {
+ super (new double[] { c }, new double[] { 1.0 }, 1);
+ this.c = c;
+ }
+
+
+ /**
+ * Returns the mean <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>c</I></SPAN>.
+ *
+ * @return <SPAN CLASS="MATH"><I>c</I></SPAN>
+ *
+ */
+ @Override
+ public double getMean() {
+ return c;
+ }
+
+
+ /**
+ * Returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 0</SPAN>.
+ *
+ * @return 0
+ *
+ */
+ @Override
+ public double getVariance() {
+ return 0;
+ }
+
+
+ /**
+ * Returns the standard deviation = 0.
+ *
+ * @return 0
+ *
+ */
+ @Override
+ public double getStandardDeviation() {
+ return 0;
+ }
+
+
+ /**
+ * Returns the inverse distribution function
+ * <SPAN CLASS="MATH"><I>c</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ *
+ * @return <SPAN CLASS="MATH"><I>c</I></SPAN>
+ *
+ */
+ @Override
+ public double inverseF (double u) {
+ return c;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ConstantDist.tex b/source/umontreal/iro/lecuyer/probdist/ConstantDist.tex
new file mode 100644
index 0000000..ed9b1d2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ConstantDist.tex
@@ -0,0 +1,130 @@
+\defmodule {ConstantDist}
+
+Represents a {\emph{constant} discrete distribution taking a single real
+value with probability 1.
+Its mass function is
+\begin{latexonly}
+\eq
+ p(x) = \left\{\begin{array}{ll}
+ 1, & \qquad \mbox {for } x = c,\\[5pt]
+ 0, & \qquad\mbox {elsewhere. }
+ \end{array}\right. \eqlabel{eq:fcons}
+\endeq
+Its distribution function is
+\eq
+ F(x) = \left\{\begin{array}{ll}
+ 0, & \qquad\mbox { for } x < c\\[5pt]
+ 1, & \qquad\mbox { for } x \ge c.
+ \end{array}\right. \eqlabel{eq:cdfcons}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ p(x) = 1, \qquad \mbox {for } x = c,
+\endeq
+and $p(x) = 0$ elsewhere.
+\end{htmlonly}%
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ConstantDist
+ * Description: constant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class ConstantDist extends DiscreteDistribution \begin{hide} {
+ private double c;
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ConstantDist (double c)\begin{hide} {
+ super (new double[] { c }, new double[] { 1.0 }, 1);
+ this.c = c;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new constant distribution with probability 1 at \texttt{c}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%\subsubsection* {Methods}
+
+\begin{code}
+
+ @Override
+ public double getMean()\begin{hide} {
+ return c;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean $E[X] = c$.
+\end{tabb}
+\begin{htmlonly}
+ \return{$c$}
+\end{htmlonly}
+\begin{code}
+
+ @Override
+ public double getVariance()\begin{hide} {
+ return 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance $\mbox{Var}[X] = 0$.
+\end{tabb}
+\begin{htmlonly}
+ \return{0}
+\end{htmlonly}
+\begin{code}
+
+ @Override
+ public double getStandardDeviation()\begin{hide} {
+ return 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation = 0.
+\end{tabb}
+\begin{htmlonly}
+ \return{0}
+\end{htmlonly}
+\begin{code}
+
+ @Override
+ public double inverseF (double u)\begin{hide} {
+ return c;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inverse distribution function $c = F^{-1}(u)$.
+\end{tabb}
+\begin{htmlonly}
+ \return{$c$}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/ConstantIntDist.java b/source/umontreal/iro/lecuyer/probdist/ConstantIntDist.java
new file mode 100644
index 0000000..6e29d65
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ConstantIntDist.java
@@ -0,0 +1,59 @@
+
+
+/*
+ * Class: ConstantIntDist
+ * Description: constant integer distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Represents a <SPAN CLASS="textit">constant</SPAN> discrete distribution taking a single integer
+ * value with probability 1.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = 1, for <I>x</I> = <I>c</I>,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>p</I>(<I>x</I>) = 0</SPAN> elsewhere.
+ *
+ */
+public class ConstantIntDist extends UniformIntDist {
+
+
+ /**
+ * Constructs a new constant distribution with probability 1 at <TT>c</TT>.
+ *
+ *
+ */
+ public ConstantIntDist (int c) {
+ super (c, c);
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : c = " + i;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ConstantIntDist.tex b/source/umontreal/iro/lecuyer/probdist/ConstantIntDist.tex
new file mode 100644
index 0000000..10419f9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ConstantIntDist.tex
@@ -0,0 +1,87 @@
+\defmodule {ConstantIntDist}
+
+Represents a {\emph{constant} discrete distribution taking a single integer
+value with probability 1.
+Its mass function is
+\begin{latexonly}
+\eq
+ p(x) = \left\{\begin{array}{ll}
+ 1, & \qquad \mbox {for } x = c,\\[5pt]
+ 0, & \qquad\mbox {elsewhere. }
+ \end{array}\right. \eqlabel{eq:fconsint}
+\endeq
+Its distribution function is
+\eq
+ F(x) = \left\{\begin{array}{ll}
+ 0, & \qquad\mbox { for } x < c\\[5pt]
+ 1, & \qquad\mbox { for } x \ge c.
+ \end{array}\right. \eqlabel{eq:cdfconsint}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ p(x) = 1, \qquad \mbox {for } x = c,
+\endeq
+and $p(x) = 0$ elsewhere.
+\end{htmlonly}%
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ConstantIntDist
+ * Description: constant integer distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class ConstantIntDist extends UniformIntDist\begin{hide} {
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ConstantIntDist (int c)\begin{hide} {
+ super (c, c);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new constant distribution with probability 1 at \texttt{c}.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString () {
+ return getClass().getSimpleName() + " : c = " + i;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/probdist/ContinuousDistribution.java b/source/umontreal/iro/lecuyer/probdist/ContinuousDistribution.java
new file mode 100644
index 0000000..6b43a1f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ContinuousDistribution.java
@@ -0,0 +1,472 @@
+
+
+/*
+ * Class: ContinuousDistribution
+ * Description: continuous distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Classes implementing continuous distributions should inherit from this base
+ * class.
+ * Such distributions are characterized by a <SPAN CLASS="textit">density</SPAN> function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>,
+ * thus the signature of a <TT>density</TT> method is supplied here.
+ * This class also provides default implementations for <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>
+ * and for <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, the latter using the Brent-Dekker method to find the inverse
+ * of a generic distribution function <SPAN CLASS="MATH"><I>F</I></SPAN>.
+ */
+public abstract class ContinuousDistribution implements Distribution {
+ @Deprecated
+ public int decPrec = 15;
+
+ private int getDecPrec() {
+ return decPrec;
+ }
+
+ // x infinity for some distributions
+ protected static final double XBIG = 100.0;
+ protected static final double XBIGM = 1000.0;
+
+ // [supportA, supportB] is the support of the pdf(x)
+ protected double supportA = Double.NEGATIVE_INFINITY;
+ protected double supportB = Double.POSITIVE_INFINITY;
+
+ // EPSARRAY[j]: Epsilon required for j decimal degits of precision
+ protected static final double[] EPSARRAY = {
+ 0.5, 0.5E-1, 0.5E-2, 0.5E-3, 0.5E-4, 0.5E-5, 0.5E-6, 0.5E-7, 0.5E-8,
+ 0.5E-9, 0.5E-10, 0.5E-11, 0.5E-12, 0.5E-13, 0.5E-14, 0.5E-15, 0.5E-16,
+ 0.5E-17, 0.5E-18, 0.5E-19, 0.5E-20, 0.5E-21, 0.5E-22, 0.5E-23, 0.5E-24,
+ 0.5E-25, 0.5E-26, 0.5E-27, 0.5E-28, 0.5E-29, 0.5E-30, 0.5E-31, 0.5E-32,
+ 0.5E-33, 0.5E-34, 0.5E-35
+ };
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>, the density evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ * @param x value at which the density is evaluated
+ *
+ * @return density function evaluated at <TT>x</TT>
+ *
+ */
+ public abstract double density (double x);
+
+
+ /**
+ * Returns the complementary distribution function.
+ * The default implementation computes
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = 1 - <I>F</I>(<I>x</I>)</SPAN>.
+ *
+ * @param x value at which the complementary distribution function is evaluated
+ *
+ * @return complementary distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double barF (double x) {
+ return 1.0 - cdf (x);
+ }
+
+
+ private void findInterval (double u, double [] iv) {
+ // Finds an interval [a, b] that certainly contains x defined as
+ // u = cdf(x). The result is written in iv[0] = a and iv[1] = b.
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ final double XLIM = Double.MAX_VALUE/2.0;
+ final double B0 = 8.0;
+ double b = B0;
+ while (b < XLIM && u > cdf(b))
+ b *= 2.0;
+ if (b > B0) {
+ iv[0] = b/2.0;
+ iv[1] = Math.min (b, supportB);
+ return;
+ }
+
+ double a = -B0;
+ while (a > -XLIM && u < cdf(a))
+ a *= 2.0;
+ if (a < -B0) {
+ iv[1] = a/2.0;
+ iv[0] = Math.max (a, supportA);
+ return;
+ }
+ iv[0] = Math.max (a, supportA);
+ iv[1] = Math.min (b, supportB);
+ }
+
+ /**
+ * Computes the inverse distribution function
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>,
+ * using the Brent-Dekker method. The interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> <SPAN CLASS="textit">must</SPAN> contain
+ * the root <SPAN CLASS="MATH"><I>x</I></SPAN> such that
+ * <SPAN CLASS="MATH"><I>F</I>(<I>a</I>) <= <I>u</I> <= <I>F</I>(<I>b</I>)</SPAN>, where <SPAN CLASS="MATH"><I>u</I> = <I>F</I>(<I>x</I>)</SPAN>.
+ * The calculations are done with an approximate precision of <TT>tol</TT>.
+ * Returns
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>. Restrictions:
+ * <SPAN CLASS="MATH"><I>u</I>∈[0, 1]</SPAN>.
+ *
+ * @param a left endpoint of initial interval
+ *
+ * @param b right endpoint of initial interval
+ *
+ * @param u value at which the inverse distribution function is evaluated
+ *
+ * @param tol accuracy goal
+ *
+ * @return inverse distribution function evaluated at <TT>u</TT>
+ *
+ */
+ public double inverseBrent (double a, double b, double u, double tol) {
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (b < a) {
+ double ctemp = a; a = b; b = ctemp;
+ }
+ if (u <= 0.0) {
+ System.out.println ("********** WARNING, inverseBrent: u = 0");
+ return supportA;
+ }
+ if (u >= 1.0) {
+ System.out.println ("********** WARNING, inverseBrent: u = 1");
+ return supportB;
+ }
+ final int MAXITER = 50; // Maximum number of iterations
+ tol += EPSARRAY[decPrec] + Num.DBL_EPSILON; // in case tol is too small
+ double ua = cdf(a) - u;
+ if (ua > 0.0)
+ throw new IllegalArgumentException ("u < cdf(a)");
+ double ub = cdf(b) - u;
+ if (ub < 0.0)
+ throw new IllegalArgumentException ("u > cdf(b)");
+
+ final boolean DEBUG = false;
+ if (DEBUG) {
+ String ls = System.getProperty("line.separator");
+ System.out.println (
+ "-------------------------------------------------------------"
+ + ls + "u = " + PrintfFormat.g (20, 15, u));
+ System.out.println
+ (ls + "iter b c F(x) - u" + ls);
+ }
+ // Initialize
+ double c = a;
+ double uc = ua;
+ double len = b - a;
+ double t = len;
+ if (Math.abs(uc) < Math.abs(ub)) {
+ a = b; b = c; c = a;
+ ua = ub; ub = uc; uc = ua;
+ }
+ int i;
+ for (i = 0; i < MAXITER; ++i) {
+ double tol1 = tol + 4.0*Num.DBL_EPSILON*Math.abs(b);
+ double xm = 0.5*(c - b);
+ if (DEBUG) {
+ System.out.println (PrintfFormat.d (3, i) + " " +
+ PrintfFormat.g (18, decPrec, b) + " " +
+ PrintfFormat.g (18, decPrec, c) + " " +
+ PrintfFormat.g (14, 4, ub));
+ }
+ if (Math.abs(ub) == 0.0 || (Math.abs(xm) <= tol1)) {
+ if (b <= supportA) return supportA;
+ if (b >= supportB) return supportB;
+ return b;
+ }
+
+ double s, p, q, r;
+ if ((Math.abs(t) >= tol1) && (Math.abs(ua) > Math.abs(ub))) {
+ if (a == c) {
+ // linear interpolation
+ s = ub/ua;
+ q = 1.0 - s;
+ p = 2.0 * xm * s;
+ } else {
+ // quadratic interpolation
+ q = ua / uc;
+ r = ub / uc;
+ s = ub / ua;
+ p = s*(2.0*xm*q*(q - r) - (b - a)*(r - 1.0));
+ q = (q - 1.0)*(r - 1.0)* (s - 1.0);
+ }
+ if (p > 0.0)
+ q = -q;
+ p = Math.abs(p);
+
+ // Accept interpolation?
+ if ((2.0*p >= (3.0*xm*q - Math.abs(q*tol1))) ||
+ (p >= Math.abs(0.5*t*q))) {
+ len = xm;
+ t = len;
+ } else {
+ t = len;
+ len = p/q;
+ }
+
+ } else {
+ len = xm;
+ t = len;
+ }
+
+ a = b;
+ ua = ub;
+ if (Math.abs(len) > tol1)
+ b += len;
+ else if (xm < 0.0)
+ b -= tol1;
+ else
+ b += tol1;
+ ub = cdf(b) - u;
+
+ if (ub*(uc/Math.abs(uc)) > 0.0) {
+ c = a;
+ uc = ua;
+ len = b - a;
+ t = len;
+ } else if (Math.abs(uc) < Math.abs(ub)) {
+ a = b; b = c; c = a;
+ ua = ub; ub = uc; uc = ua;
+ }
+ }
+ if (i >= MAXITER) {
+ String lineSep = System.getProperty("line.separator");
+ System.out.println (lineSep +
+ "*********** inverseBrent: no convergence after " + MAXITER +
+ " iterations");
+ }
+ return b;
+ }
+
+
+ /**
+ * Computes and returns the inverse distribution function
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>,
+ * using bisection. Restrictions:
+ * <SPAN CLASS="MATH"><I>u</I>∈[0, 1]</SPAN>.
+ *
+ * @param u value at which the inverse distribution function is evaluated
+ *
+ * @return the inverse distribution function evaluated at <TT>u</TT>
+ * @exception IllegalArgumentException if <SPAN CLASS="MATH"><I>u</I></SPAN> is not in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ *
+ */
+ public double inverseBisection (double u) {
+ final int MAXITER = 100; // Maximum number of iterations
+ final double EPSILON = EPSARRAY[decPrec]; // Absolute precision
+ final double XLIM = Double.MAX_VALUE/2.0;
+ final boolean DEBUG = false;
+ final String lineSep = System.getProperty("line.separator");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (decPrec > Num.DBL_DIG)
+ throw new IllegalArgumentException ("decPrec too large");
+ if (decPrec <= 0)
+ throw new IllegalArgumentException ("decPrec <= 0");
+ if (DEBUG) {
+ System.out.println ("---------------------------" +
+ " -----------------------------" + lineSep +
+ PrintfFormat.f (10, 8, u));
+ }
+
+ double x = 0.0;
+ if (u <= 0.0) {
+ x = supportA;
+ if (DEBUG) {
+ System.out.println (lineSep + " x y" +
+ lineSep + PrintfFormat.g (17, 2, x) + " " +
+ PrintfFormat.f (17, decPrec, u));
+ }
+ return x;
+ }
+ if (u >= 1.0) {
+ x = supportB;
+ if (DEBUG) {
+ System.out.println (lineSep + " x y" +
+ lineSep + PrintfFormat.g (17, 2, x) + " " +
+ PrintfFormat.f (17, decPrec, u));
+ }
+ return x;
+ }
+
+ double [] iv = new double [2];
+ findInterval (u, iv);
+ double xa = iv[0];
+ double xb = iv[1];
+ double yb = cdf(xb) - u;
+ double ya = cdf(xa) - u;
+ double y;
+
+ if (DEBUG)
+ System.out.println (lineSep +
+ "iter xa xb F - u");
+
+ boolean fini = false;
+ int i = 0;
+ while (!fini) {
+ if (DEBUG)
+ System.out.println (PrintfFormat.d (3, i) + " " +
+ PrintfFormat.g (18, decPrec, xa) + " " +
+ PrintfFormat.g (18, decPrec, xb) + " " +
+ PrintfFormat.g (14, 4, y));
+ x = (xa + xb)/2.0;
+ y = cdf(x) - u;
+ if ((y == 0.0) ||
+ (Math.abs ((xb - xa)/(x + Num.DBL_EPSILON)) <= EPSILON)) {
+ fini = true;
+ if (DEBUG)
+ System.out.println (lineSep + " x" +
+ " u" + lineSep +
+ PrintfFormat.g (20, decPrec, x) + " " +
+ PrintfFormat.g (18, decPrec, y+u));
+ }
+ else if (y*ya < 0.0)
+ xb = x;
+ else
+ xa = x;
+ ++i;
+
+ if (i > MAXITER) {
+ //System.out.println (lineSep +
+ // "** inverseF:SEARCH DOES NOT SEEM TO CONVERGE");
+ fini = true;
+ }
+ }
+ return x;
+ }
+
+
+ /**
+ * Returns the inverse distribution function
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ * Restrictions:
+ * <SPAN CLASS="MATH"><I>u</I>∈[0, 1]</SPAN>.
+ *
+ * @param u value at which the inverse distribution function is evaluated
+ *
+ * @return the inverse distribution function evaluated at <TT>u</TT>
+ * @exception IllegalArgumentException if <SPAN CLASS="MATH"><I>u</I></SPAN> is not in the interval <SPAN CLASS="MATH">[0, 1]</SPAN>
+ *
+ *
+ */
+ public double inverseF (double u) {
+ double [] iv = new double [2];
+ findInterval (u, iv);
+ return inverseBrent (iv[0], iv[1], u, EPSARRAY[decPrec]);
+ }
+
+
+ /**
+ * Returns the mean.
+ *
+ * @return the mean
+ *
+ */
+ public double getMean() {
+ throw new UnsupportedOperationException("getMean is not implemented ");
+ }
+
+
+ /**
+ * Returns the variance.
+ *
+ * @return the variance
+ *
+ */
+ public double getVariance() {
+ throw new UnsupportedOperationException("getVariance is not implemented ");
+ }
+
+
+ /**
+ * Returns the standard deviation.
+ *
+ * @return the standard deviation
+ *
+ */
+ public double getStandardDeviation() {
+ throw new UnsupportedOperationException (
+ "getStandardDeviation is not implemented ");
+ }
+
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>x</I><SUB>a</SUB></SPAN> such that the probability density is 0 everywhere
+ * outside the interval
+ * <SPAN CLASS="MATH">[<I>x</I><SUB>a</SUB>, <I>x</I><SUB>b</SUB>]</SPAN>.
+ *
+ * @return lower limit of support
+ *
+ */
+ public double getXinf() {
+ return supportA;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>x</I><SUB>b</SUB></SPAN> such that the probability density is 0 everywhere
+ * outside the interval
+ * <SPAN CLASS="MATH">[<I>x</I><SUB>a</SUB>, <I>x</I><SUB>b</SUB>]</SPAN>.
+ *
+ * @return upper limit of support
+ *
+ */
+ public double getXsup() {
+ return supportB;
+ }
+
+
+
+ /**
+ * Sets the value <SPAN CLASS="MATH"><I>x</I><SUB>a</SUB> =</SPAN> <TT>xa</TT>, such that the probability
+ * density is 0 everywhere outside the interval
+ * <SPAN CLASS="MATH">[<I>x</I><SUB>a</SUB>, <I>x</I><SUB>b</SUB>]</SPAN>.
+ *
+ * @param xa lower limit of support
+ *
+ *
+ */
+ public void setXinf (double xa) {
+ supportA = xa;
+ }
+
+
+ /**
+ * Sets the value <SPAN CLASS="MATH"><I>x</I><SUB>b</SUB> =</SPAN> <TT>xb</TT>, such that the probability
+ * density is 0 everywhere outside the interval
+ * <SPAN CLASS="MATH">[<I>x</I><SUB>a</SUB>, <I>x</I><SUB>b</SUB>]</SPAN>.
+ *
+ * @param xb upper limit of support
+ *
+ */
+ public void setXsup (double xb) {
+ supportB = xb;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ContinuousDistribution.tex b/source/umontreal/iro/lecuyer/probdist/ContinuousDistribution.tex
new file mode 100644
index 0000000..5519948
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ContinuousDistribution.tex
@@ -0,0 +1,487 @@
+\defmodule {ContinuousDistribution}
+
+Classes implementing continuous distributions should inherit from this base
+ class.
+Such distributions are characterized by a \emph{density} function $f(x)$,
+thus the signature of a \texttt{density} method is supplied here.
+This class also provides default implementations for $\bar F(x)$
+and for $F^{-1}(u)$, the latter using the Brent-Dekker method to find the inverse
+of a generic distribution function $F$. % The integer \texttt{decPrec} defines
+% the target number of decimals of precision when approximating a distribution
+% function, but there is \emph{no guarantee} that this target is always attained.
+
+%% It specifies the signature of methods for computing the density
+%% $f(x)$
+%% , the distribution function $F(x)=P[X\le x]$, the
+%% complementary distribution function $\bar F(x)$,
+%% and the inverse distribution function $ F^{-1} (u)$
+%% for a random variable $X$ having a univariate continuous distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ContinuousDistribution
+ * Description: continuous distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public abstract class ContinuousDistribution implements Distribution \begin{hide} {
+ @Deprecated
+ public int decPrec = 15;
+
+ private int getDecPrec() {
+ return decPrec;
+ }\end{hide}
+\end{code}
+%% \begin{tabb} A rough target on the number of decimals of precision when
+%% approximating a distribution function.
+%% (There is \emph{no guarantee} that this target is always attained.)
+%% \end{tabb}
+\begin{code}\begin{hide}
+ // x infinity for some distributions
+ protected static final double XBIG = 100.0;
+ protected static final double XBIGM = 1000.0;
+
+ // [supportA, supportB] is the support of the pdf(x)
+ protected double supportA = Double.NEGATIVE_INFINITY;
+ protected double supportB = Double.POSITIVE_INFINITY;
+
+ // EPSARRAY[j]: Epsilon required for j decimal degits of precision
+ protected static final double[] EPSARRAY = {
+ 0.5, 0.5E-1, 0.5E-2, 0.5E-3, 0.5E-4, 0.5E-5, 0.5E-6, 0.5E-7, 0.5E-8,
+ 0.5E-9, 0.5E-10, 0.5E-11, 0.5E-12, 0.5E-13, 0.5E-14, 0.5E-15, 0.5E-16,
+ 0.5E-17, 0.5E-18, 0.5E-19, 0.5E-20, 0.5E-21, 0.5E-22, 0.5E-23, 0.5E-24,
+ 0.5E-25, 0.5E-26, 0.5E-27, 0.5E-28, 0.5E-29, 0.5E-30, 0.5E-31, 0.5E-32,
+ 0.5E-33, 0.5E-34, 0.5E-35
+ };\end{hide}
+
+ public abstract double density (double x);
+\end{code}
+\begin{tabb} Returns $f(x)$, the density evaluated at $x$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the density is evaluated}
+ \return{density function evaluated at \texttt{x}}
+\end{htmlonly}
+% \begin{code}
+%
+% public MathFunction density()\begin{hide} {
+% return new MathFunction() {
+% public double evaluate(double x) {
+% return density(x);
+% }
+% };
+% }\end{hide}
+% \end{code}
+% \begin{tabb} Returns an object allowing the computation of the density
+% at any point.
+% \end{tabb}
+% \begin{htmlonly}
+% \return{density object
+% \end{htmlonly}
+\begin{code}
+
+ public double barF (double x)\begin{hide} {
+ return 1.0 - cdf (x);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the complementary distribution function.
+ The default implementation computes $\bar F(x) = 1 - F(x)$.
+ %, which is not accurate when $F(x)\to 1$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the complementary distribution function is evaluated}
+ \return{complementary distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private void findInterval (double u, double [] iv) {
+ // Finds an interval [a, b] that certainly contains x defined as
+ // u = cdf(x). The result is written in iv[0] = a and iv[1] = b.
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ final double XLIM = Double.MAX_VALUE/2.0;
+ final double B0 = 8.0;
+ double b = B0;
+ while (b < XLIM && u > cdf(b))
+ b *= 2.0;
+ if (b > B0) {
+ iv[0] = b/2.0;
+ iv[1] = Math.min (b, supportB);
+ return;
+ }
+
+ double a = -B0;
+ while (a > -XLIM && u < cdf(a))
+ a *= 2.0;
+ if (a < -B0) {
+ iv[1] = a/2.0;
+ iv[0] = Math.max (a, supportA);
+ return;
+ }
+ iv[0] = Math.max (a, supportA);
+ iv[1] = Math.min (b, supportB);
+ }\end{hide}
+
+ public double inverseBrent (double a, double b, double u, double tol) \begin{hide} {
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (b < a) {
+ double ctemp = a; a = b; b = ctemp;
+ }
+ if (u <= 0.0) {
+ System.out.println ("********** WARNING, inverseBrent: u = 0");
+ return supportA;
+ }
+ if (u >= 1.0) {
+ System.out.println ("********** WARNING, inverseBrent: u = 1");
+ return supportB;
+ }
+ final int MAXITER = 50; // Maximum number of iterations
+ tol += EPSARRAY[decPrec] + Num.DBL_EPSILON; // in case tol is too small
+ double ua = cdf(a) - u;
+ if (ua > 0.0)
+ throw new IllegalArgumentException ("u < cdf(a)");
+ double ub = cdf(b) - u;
+ if (ub < 0.0)
+ throw new IllegalArgumentException ("u > cdf(b)");
+
+ final boolean DEBUG = false;
+ if (DEBUG) {
+ String ls = System.getProperty("line.separator");
+ System.out.println (
+ "-------------------------------------------------------------"
+ + ls + "u = " + PrintfFormat.g (20, 15, u));
+ System.out.println
+ (ls + "iter b c F(x) - u" + ls);
+ }
+ // Initialize
+ double c = a;
+ double uc = ua;
+ double len = b - a;
+ double t = len;
+ if (Math.abs(uc) < Math.abs(ub)) {
+ a = b; b = c; c = a;
+ ua = ub; ub = uc; uc = ua;
+ }
+ int i;
+ for (i = 0; i < MAXITER; ++i) {
+ double tol1 = tol + 4.0*Num.DBL_EPSILON*Math.abs(b);
+ double xm = 0.5*(c - b);
+ if (DEBUG) {
+ System.out.println (PrintfFormat.d (3, i) + " " +
+ PrintfFormat.g (18, decPrec, b) + " " +
+ PrintfFormat.g (18, decPrec, c) + " " +
+ PrintfFormat.g (14, 4, ub));
+ }
+ if (Math.abs(ub) == 0.0 || (Math.abs(xm) <= tol1)) {
+ if (b <= supportA) return supportA;
+ if (b >= supportB) return supportB;
+ return b;
+ }
+
+ double s, p, q, r;
+ if ((Math.abs(t) >= tol1) && (Math.abs(ua) > Math.abs(ub))) {
+ if (a == c) {
+ // linear interpolation
+ s = ub/ua;
+ q = 1.0 - s;
+ p = 2.0 * xm * s;
+ } else {
+ // quadratic interpolation
+ q = ua / uc;
+ r = ub / uc;
+ s = ub / ua;
+ p = s*(2.0*xm*q*(q - r) - (b - a)*(r - 1.0));
+ q = (q - 1.0)*(r - 1.0)* (s - 1.0);
+ }
+ if (p > 0.0)
+ q = -q;
+ p = Math.abs(p);
+
+ // Accept interpolation?
+ if ((2.0*p >= (3.0*xm*q - Math.abs(q*tol1))) ||
+ (p >= Math.abs(0.5*t*q))) {
+ len = xm;
+ t = len;
+ } else {
+ t = len;
+ len = p/q;
+ }
+
+ } else {
+ len = xm;
+ t = len;
+ }
+
+ a = b;
+ ua = ub;
+ if (Math.abs(len) > tol1)
+ b += len;
+ else if (xm < 0.0)
+ b -= tol1;
+ else
+ b += tol1;
+ ub = cdf(b) - u;
+
+ if (ub*(uc/Math.abs(uc)) > 0.0) {
+ c = a;
+ uc = ua;
+ len = b - a;
+ t = len;
+ } else if (Math.abs(uc) < Math.abs(ub)) {
+ a = b; b = c; c = a;
+ ua = ub; ub = uc; uc = ua;
+ }
+ }
+ if (i >= MAXITER) {
+ String lineSep = System.getProperty("line.separator");
+ System.out.println (lineSep +
+ "*********** inverseBrent: no convergence after " + MAXITER +
+ " iterations");
+ }
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse distribution function $x = F^{-1}(u)$,
+ using the Brent-Dekker method. The interval $[a, b]$ \emph{must} contain
+ the root $x$ such that $F(a) \le u \le F(b)$, where $u=F(x)$.
+ The calculations are done with an approximate precision of \texttt{tol}.
+ Returns $x = F^{-1}(u)$. Restrictions: $u \in [0,1]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{left endpoint of initial interval}
+ \param{b}{right endpoint of initial interval}
+ \param{u}{value at which the inverse distribution function is evaluated}
+ \param{tol}{accuracy goal}
+ \return{inverse distribution function evaluated at \texttt{u}}
+\end{htmlonly}
+\begin{code}
+
+ public double inverseBisection (double u)\begin{hide} {
+ final int MAXITER = 100; // Maximum number of iterations
+ final double EPSILON = EPSARRAY[decPrec]; // Absolute precision
+ final double XLIM = Double.MAX_VALUE/2.0;
+ final boolean DEBUG = false;
+ final String lineSep = System.getProperty("line.separator");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (decPrec > Num.DBL_DIG)
+ throw new IllegalArgumentException ("decPrec too large");
+ if (decPrec <= 0)
+ throw new IllegalArgumentException ("decPrec <= 0");
+ if (DEBUG) {
+ System.out.println ("---------------------------" +
+ " -----------------------------" + lineSep +
+ PrintfFormat.f (10, 8, u));
+ }
+
+ double x = 0.0;
+ if (u <= 0.0) {
+ x = supportA;
+ if (DEBUG) {
+ System.out.println (lineSep + " x y" +
+ lineSep + PrintfFormat.g (17, 2, x) + " " +
+ PrintfFormat.f (17, decPrec, u));
+ }
+ return x;
+ }
+ if (u >= 1.0) {
+ x = supportB;
+ if (DEBUG) {
+ System.out.println (lineSep + " x y" +
+ lineSep + PrintfFormat.g (17, 2, x) + " " +
+ PrintfFormat.f (17, decPrec, u));
+ }
+ return x;
+ }
+
+ double [] iv = new double [2];
+ findInterval (u, iv);
+ double xa = iv[0];
+ double xb = iv[1];
+ double yb = cdf(xb) - u;
+ double ya = cdf(xa) - u;
+ double y;
+
+ if (DEBUG)
+ System.out.println (lineSep +
+ "iter xa xb F - u");
+
+ boolean fini = false;
+ int i = 0;
+ while (!fini) {
+ if (DEBUG)
+ System.out.println (PrintfFormat.d (3, i) + " " +
+ PrintfFormat.g (18, decPrec, xa) + " " +
+ PrintfFormat.g (18, decPrec, xb) + " " +
+ PrintfFormat.g (14, 4, y));
+ x = (xa + xb)/2.0;
+ y = cdf(x) - u;
+ if ((y == 0.0) ||
+ (Math.abs ((xb - xa)/(x + Num.DBL_EPSILON)) <= EPSILON)) {
+ fini = true;
+ if (DEBUG)
+ System.out.println (lineSep + " x" +
+ " u" + lineSep +
+ PrintfFormat.g (20, decPrec, x) + " " +
+ PrintfFormat.g (18, decPrec, y+u));
+ }
+ else if (y*ya < 0.0)
+ xb = x;
+ else
+ xa = x;
+ ++i;
+
+ if (i > MAXITER) {
+ //System.out.println (lineSep +
+ // "** inverseF:SEARCH DOES NOT SEEM TO CONVERGE");
+ fini = true;
+ }
+ }
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the inverse distribution function $x = F^{-1}(u)$,
+% $F^{-1}(u) = \inf\{x\in\RR : F(x)\ge u\}$,
+ using bisection. Restrictions: $u \in [0,1]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{value at which the inverse distribution function is evaluated}
+ \return{the inverse distribution function evaluated at \texttt{u}}
+ \exception{IllegalArgumentException}{if $u$ is not in the interval $[0,1]$}
+\end{htmlonly}
+\begin{code}
+
+ public double inverseF (double u)\begin{hide} {
+ double [] iv = new double [2];
+ findInterval (u, iv);
+ return inverseBrent (iv[0], iv[1], u, EPSARRAY[decPrec]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the inverse distribution function $x = F^{-1}(u)$.
+ % by calling the method \method{inverseBrent}{}.
+ % $F^{-1}(u) = \inf\{x\in\RR : F(x)\ge u\}$,
+ Restrictions: $u \in [0,1]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{value at which the inverse distribution function is evaluated}
+ \return{the inverse distribution function evaluated at \texttt{u}}
+ \exception{IllegalArgumentException}{if $u$ is not in the interval $[0,1]$}
+\end{htmlonly}
+\begin{code}
+
+ public double getMean()\begin{hide} {
+ throw new UnsupportedOperationException("getMean is not implemented ");
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean}
+\end{htmlonly}
+\begin{code}
+
+ public double getVariance()\begin{hide} {
+ throw new UnsupportedOperationException("getVariance is not implemented ");
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance}
+\end{htmlonly}
+\begin{code}
+
+ public double getStandardDeviation()\begin{hide} {
+ throw new UnsupportedOperationException (
+ "getStandardDeviation is not implemented ");
+ }
+\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation}
+\end{htmlonly}
+\begin{code}
+
+ public double getXinf()\begin{hide} {
+ return supportA;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $x_a$ such that the probability density is 0 everywhere
+ outside the interval $[x_a, x_b]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{lower limit of support}
+\end{htmlonly}
+\begin{code}
+
+ public double getXsup()\begin{hide} {
+ return supportB;
+ }
+\end{hide}
+\end{code}
+\begin{tabb} Returns $x_b$ such that the probability density is 0 everywhere
+ outside the interval $[x_a, x_b]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{upper limit of support}
+\end{htmlonly}
+\begin{code}
+
+ public void setXinf (double xa)\begin{hide} {
+ supportA = xa;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the value $x_a=$ \texttt{xa}, such that the probability
+ density is 0 everywhere outside the interval $[x_a, x_b]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{xa}{lower limit of support}
+\end{htmlonly}
+\begin{code}
+
+ public void setXsup (double xb)\begin{hide} {
+ supportB = xb;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Sets the value $x_b=$ \texttt{xb}, such that the probability
+ density is 0 everywhere outside the interval $[x_a, x_b]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{xb}{upper limit of support}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/probdist/CramerVonMisesDist.java b/source/umontreal/iro/lecuyer/probdist/CramerVonMisesDist.java
new file mode 100644
index 0000000..c36c972
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/CramerVonMisesDist.java
@@ -0,0 +1,353 @@
+
+
+/*
+ * Class: CramerVonMisesDist
+ * Description: Cramér-von Mises distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * Cramér-von Mises distribution (see).
+ * Given a sample of <SPAN CLASS="MATH"><I>n</I></SPAN> independent uniforms <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> over <SPAN CLASS="MATH">[0, 1]</SPAN>,
+ * the Cramér-von Mises statistic <SPAN CLASS="MATH"><I>W</I><SUB>n</SUB><SUP>2</SUP></SPAN> is defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>W</I><SUB>n</SUB><SUP>2</SUP> = 1/12<I>n</I> + ∑<SUB>j=1</SUB><SUP>n</SUP>(<I>U</I><SUB>(j)</SUB> - (<I>j</I>-0.5)/<I>n</I>)<SUP>2</SUP>,
+ * </DIV><P></P>
+ * where the <SPAN CLASS="MATH"><I>U</I><SUB>(j)</SUB></SPAN> are the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> sorted in increasing order.
+ * The distribution function (the cumulative probabilities)
+ * is defined as
+ * <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>) = <I>P</I>[<I>W</I><SUB>n</SUB><SUP>2</SUP> <= <I>x</I>]</SPAN>.
+ *
+ */
+public class CramerVonMisesDist extends ContinuousDistribution {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+
+
+ /**
+ * Constructs a <EM>Cramér-von Mises</EM> distribution for a sample of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public CramerVonMisesDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return CramerVonMisesDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return CramerVonMisesDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return CramerVonMisesDist.getStandardDeviation (n);
+ }
+
+ /**
+ * Computes the density function
+ * for a <EM>Cramér-von Mises</EM> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ if (x <= 1.0/(12.0*n) || x >= n/3.0)
+ return 0.0;
+
+ if (n == 1)
+ return 1.0 / Math.sqrt (x - 1.0/12.0);
+
+ if (x <= 0.002 || x > 3.95)
+ return 0.0;
+
+ throw new UnsupportedOperationException("density not implemented.");
+ }
+
+
+ /**
+ * Computes the Cramér-von Mises distribution function with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Returns an approximation of
+ * <SPAN CLASS="MATH"><I>P</I>[<I>W</I><SUB>n</SUB><SUP>2</SUP> <= <I>x</I>]</SPAN>, where <SPAN CLASS="MATH"><I>W</I><SUB>n</SUB><SUP>2</SUP></SPAN> is the
+ * Cramér von Mises statistic (see).
+ * The approximation is based on the distribution function of
+ * <SPAN CLASS="MATH"><I>W</I><SUP>2</SUP> = lim<SUB>n -> ∞</SUB><I>W</I><SUB>n</SUB><SUP>2</SUP></SPAN>, which has the following series expansion derived
+ * by Anderson and Darling:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>(<I>W</I><SUP>2</SUP> <= <I>x</I>) = 1#1∑<SUB>j=0</SUB><SUP>∞</SUP>(- 1)<SUP>j</SUP>2#2(4j+1)<SUP>1/2</SUP> <I>exp</I>{ - 3#3}<I>K</I><SUB>1/4</SUB>([tex2html_wrap_indisplay244]),
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>K</I><SUB><I>ν</I></SUB></SPAN> is the modified Bessel function of the
+ * second kind.
+ * To correct for the deviation between
+ * <SPAN CLASS="MATH"><I>P</I>(<I>W</I><SUB>n</SUB><SUP>2</SUP> <= <I>x</I>)</SPAN> and
+ * <SPAN CLASS="MATH"><I>P</I>(<I>W</I><SUP>2</SUP> <= <I>x</I>)</SPAN>,
+ * we add a correction in <SPAN CLASS="MATH">1/<I>n</I></SPAN>, obtained empirically by simulation.
+ * For <SPAN CLASS="MATH"><I>n</I> = 10</SPAN>, 20, 40, the error is less than
+ * 0.002, 0.001, and 0.0005, respectively, while for
+ * <SPAN CLASS="MATH"><I>n</I> >= 100</SPAN> it is less than 0.0005.
+ * For
+ * <SPAN CLASS="MATH"><I>n</I> -> ∞</SPAN>, we estimate that the method returns
+ * at least 6 decimal digits of precision.
+ * For <SPAN CLASS="MATH"><I>n</I> = 1</SPAN>, the method uses the exact distribution:
+ *
+ * <SPAN CLASS="MATH"><I>P</I>(<I>W</I><SUB>1</SUB><SUP>2</SUP> <= <I>x</I>) = 2(x - 1/12)<SUP>1/2</SUP></SPAN> for
+ * <SPAN CLASS="MATH">1/12 <= <I>x</I> <= 1/3</SPAN>.
+ *
+ */
+ public static double cdf (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ if (n == 1) {
+ if (x <= 1.0/12.0)
+ return 0.0;
+ if (x >= 1.0/3.0)
+ return 1.0;
+ return 2.0*Math.sqrt (x - 1.0/12.0);
+ }
+
+ if (x <= 1.0/(12.0*n))
+ return 0.0;
+
+ if (x <= (n + 3.0)/(12.0*n*n)) {
+ double t = Num.lnFactorial(n) - Num.lnGamma (1.0 + 0.5*n) +
+ 0.5*n*Math.log (Math.PI*(x - 1.0/(12.0*n)));
+ return Math.exp(t);
+ }
+
+ if (x <= 0.002)
+ return 0.0;
+ if (x > 3.95 || x >= n/3.0)
+ return 1.0;
+
+ final double EPSILON = Num.DBL_EPSILON;
+ final int JMAX = 20;
+ int j = 0;
+ double Cor, Res, arg;
+ double termX, termS, termJ;
+
+ termX = 0.0625/x; // 1 / (16x)
+ Res = 0.0;
+
+ final double A[] = {
+ 1.0,
+ 1.11803398875,
+ 1.125,
+ 1.12673477358,
+ 1.1274116945,
+ 1.12774323743,
+ 1.1279296875,
+ 1.12804477649,
+ 1.12812074678,
+ 1.12817350091
+ };
+
+ do {
+ termJ = 4*j + 1;
+ arg = termJ*termJ*termX;
+ termS = A[j]*Math.exp (-arg)*Num.besselK025 (arg);
+ Res += termS;
+ ++j;
+ } while (!(termS < EPSILON || j > JMAX));
+
+ if (j > JMAX)
+ System.err.println ("cramerVonMises: iterations have not converged");
+ Res /= Math.PI*Math.sqrt (x);
+
+ // Empirical correction in 1/n
+ if (x < 0.0092)
+ Cor = 0.0;
+ else if (x < 0.03)
+ Cor = -0.0121763 + x*(2.56672 - 132.571*x);
+ else if (x < 0.06)
+ Cor = 0.108688 + x*(-7.14677 + 58.0662*x);
+ else if (x < 0.19)
+ Cor = -0.0539444 + x*(-2.22024 + x*(25.0407 - 64.9233*x));
+ else if (x < 0.5)
+ Cor = -0.251455 + x*(2.46087 + x*(-8.92836 + x*(14.0988 -
+ x*(5.5204 + 4.61784*x))));
+ else if (x <= 1.1)
+ Cor = 0.0782122 + x*(-0.519924 + x*(1.75148 +
+ x*(-2.72035 + x*(1.94487 - 0.524911*x))));
+ else
+ Cor = Math.exp (-0.244889 - 4.26506*x);
+
+ Res += Cor/n;
+ // This empirical correction is not very precise, so ...
+ if (Res <= 1.0)
+ return Res;
+ else
+ return 1.0;
+ }
+
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)<SUB>n</SUB>(<I>x</I>)</SPAN>
+ * with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ return 1.0 - cdf(n,x);
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUB>n</SUB><SUP>-1</SUP>(<I>u</I>)</SPAN>, where <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB></SPAN> is the
+ * <EM>Cramér-von Mises</EM> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+
+ if (u >= 1.0)
+ return n/3.0;
+ if (u <= 0.0)
+ return 1.0/(12.0*n);
+
+ if (n == 1)
+ return 1.0/12.0 + 0.25*u*u;
+
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 10.0, f, 1e-6);
+ }
+
+
+ /**
+ * Returns the mean of the distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the mean
+ *
+ */
+ public static double getMean (int n) {
+ return 1.0 / 6.0;
+ }
+
+
+ /**
+ * Returns the variance of the distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return variance
+ *
+ */
+ public static double getVariance (int n) {
+ return (4.0 * n - 3.0) / (180.0 * n );
+ }
+
+
+ /**
+ * Returns the standard deviation of the distribution with
+ * parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the standard deviation
+ *
+ */
+ public static double getStandardDeviation (int n) {
+ return Math.sqrt(getVariance(n));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public void setN (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ supportA = 1.0/(12.0*n);
+ supportB = n/3.0;
+ }
+
+
+ /**
+ * Return an array containing the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/CramerVonMisesDist.tex b/source/umontreal/iro/lecuyer/probdist/CramerVonMisesDist.tex
new file mode 100644
index 0000000..c3d2652
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/CramerVonMisesDist.tex
@@ -0,0 +1,367 @@
+\defmodule {CramerVonMisesDist}
+
+Extends the class \class{ContinuousDistribution} for the
+Cram\'er-von Mises distribution (see \cite{tDUR73a,tSTE70a,tSTE86b}).
+Given a sample of $n$ independent uniforms $U_i$ over $[0,1]$,
+the Cram\'er-von Mises statistic $W_n^2$ is defined by
+\begin{htmlonly}
+ \begin {equation}
+ W_n^2 = {1/12n} +
+ \sum_{j=1}^n \left(U_{(j)} - {(j-0.5) / n}\right)^2,
+ \end {equation}
+ where the $U_{(j)}$ are the $U_i$ sorted in increasing order.
+ The distribution function (the cumulative probabilities)
+ is defined as $F_n(x) = P[W_n^2 \le x]$.
+\end{htmlonly}%
+\begin{latexonly}%
+ \begin {equation}
+ W_n^2 = \frac{1}{12n} +
+ \sum_{j=1}^n \left(U_{(j)} - \frac{(j-0.5)}{n}\right)^2,
+ \eqlabel {eq:CraMis}
+ \end {equation}
+ where the $U_{(j)}$ are the $U_i$ sorted in increasing order.
+ The distribution function (the cumulative probabilities)
+ is defined as $F_n(x) = P[W_n^2 \le x]$.
+\end{latexonly}%
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: CramerVonMisesDist
+ * Description: Cramér-von Mises distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class CramerVonMisesDist extends ContinuousDistribution\begin{hide} {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public CramerVonMisesDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a {\em Cram\'er-von Mises\/} distribution for a sample of size $n$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return CramerVonMisesDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return CramerVonMisesDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return CramerVonMisesDist.getStandardDeviation (n);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ if (x <= 1.0/(12.0*n) || x >= n/3.0)
+ return 0.0;
+
+ if (n == 1)
+ return 1.0 / Math.sqrt (x - 1.0/12.0);
+
+ if (x <= 0.002 || x > 3.95)
+ return 0.0;
+
+ throw new UnsupportedOperationException("density not implemented.");
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function
+ for a {\em Cram\'er-von Mises\/} distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ if (n == 1) {
+ if (x <= 1.0/12.0)
+ return 0.0;
+ if (x >= 1.0/3.0)
+ return 1.0;
+ return 2.0*Math.sqrt (x - 1.0/12.0);
+ }
+
+ if (x <= 1.0/(12.0*n))
+ return 0.0;
+
+ if (x <= (n + 3.0)/(12.0*n*n)) {
+ double t = Num.lnFactorial(n) - Num.lnGamma (1.0 + 0.5*n) +
+ 0.5*n*Math.log (Math.PI*(x - 1.0/(12.0*n)));
+ return Math.exp(t);
+ }
+
+ if (x <= 0.002)
+ return 0.0;
+ if (x > 3.95 || x >= n/3.0)
+ return 1.0;
+
+ final double EPSILON = Num.DBL_EPSILON;
+ final int JMAX = 20;
+ int j = 0;
+ double Cor, Res, arg;
+ double termX, termS, termJ;
+
+ termX = 0.0625/x; // 1 / (16x)
+ Res = 0.0;
+
+ final double A[] = {
+ 1.0,
+ 1.11803398875,
+ 1.125,
+ 1.12673477358,
+ 1.1274116945,
+ 1.12774323743,
+ 1.1279296875,
+ 1.12804477649,
+ 1.12812074678,
+ 1.12817350091
+ };
+
+ do {
+ termJ = 4*j + 1;
+ arg = termJ*termJ*termX;
+ termS = A[j]*Math.exp (-arg)*Num.besselK025 (arg);
+ Res += termS;
+ ++j;
+ } while (!(termS < EPSILON || j > JMAX));
+
+ if (j > JMAX)
+ System.err.println ("cramerVonMises: iterations have not converged");
+ Res /= Math.PI*Math.sqrt (x);
+
+ // Empirical correction in 1/n
+ if (x < 0.0092)
+ Cor = 0.0;
+ else if (x < 0.03)
+ Cor = -0.0121763 + x*(2.56672 - 132.571*x);
+ else if (x < 0.06)
+ Cor = 0.108688 + x*(-7.14677 + 58.0662*x);
+ else if (x < 0.19)
+ Cor = -0.0539444 + x*(-2.22024 + x*(25.0407 - 64.9233*x));
+ else if (x < 0.5)
+ Cor = -0.251455 + x*(2.46087 + x*(-8.92836 + x*(14.0988 -
+ x*(5.5204 + 4.61784*x))));
+ else if (x <= 1.1)
+ Cor = 0.0782122 + x*(-0.519924 + x*(1.75148 +
+ x*(-2.72035 + x*(1.94487 - 0.524911*x))));
+ else
+ Cor = Math.exp (-0.244889 - 4.26506*x);
+
+ Res += Cor/n;
+ // This empirical correction is not very precise, so ...
+ if (Res <= 1.0)
+ return Res;
+ else
+ return 1.0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the Cram\'er-von Mises distribution function with parameter $n$.
+ Returns an approximation of $P[W_n^2 \le x]$, where $W_n^2$ is the
+ Cram\'er von Mises statistic (see \cite{tSTE70a,tSTE86b,tAND52a,tKNO74a}).
+ The approximation is based on the distribution function of $W^2 =
+ \lim_{n\to\infty} W_n^2$, which has the following series expansion derived
+ by Anderson and Darling \cite{tAND52a}:
+ $$ \qquad
+ P(W^2 \le x) \ = \ \frac1{\pi\sqrt x} \sum_{j=0}^\infty (-1)^j
+ \binom{-1/2}{j} \sqrt{4j+1}\;\;
+ {\rm exp}\left\{-\frac{(4j+1)^2}{16 x}\right\}
+ {\rm K}_{1/4}\left(\frac{(4j+1)^2}{16 x}\right),
+ $$
+ where ${\rm K}_{\nu}$ is the modified Bessel function of the
+ second kind.
+ To correct for the deviation between $P(W_n^2\le x)$ and $P(W^2\le x)$,
+ we add a correction in $1/n$, obtained empirically by simulation.
+ For $n = 10$, 20, 40, the error is less than
+ 0.002, 0.001, and 0.0005, respectively, while for
+ $n \ge 100$ it is less than 0.0005.
+ For $n \to\infty$, we estimate that the method returns
+ at least 6 decimal digits of precision.
+ For $n = 1$, the method uses the exact distribution:
+ $P(W_1^2 \le x) = 2 \sqrt {x - 1/12}$ for $1/12 \le x \le 1/3$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double x)\begin{hide} {
+ return 1.0 - cdf(n,x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function $\bar F_n(x)$
+ with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+
+ if (u >= 1.0)
+ return n/3.0;
+ if (u <= 0.0)
+ return 1.0/(12.0*n);
+
+ if (n == 1)
+ return 1.0/12.0 + 0.25*u*u;
+
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 10.0, f, 1e-6);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes $x = F_n^{-1}(u)$, where $F_n$ is the
+ {\em Cram\'er-von Mises\/} distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double getMean (int n)\begin{hide} {
+ return 1.0 / 6.0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean of the distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int n)\begin{hide} {
+ return (4.0 * n - 3.0) / (180.0 * n );
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance of the distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{variance}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int n)\begin{hide} {
+ return Math.sqrt(getVariance(n));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation of the distribution with
+ parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation}
+\end{htmlonly}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ supportA = 1.0/(12.0*n);
+ supportB = n/3.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameter $n$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/DiscreteDistribution.java b/source/umontreal/iro/lecuyer/probdist/DiscreteDistribution.java
new file mode 100644
index 0000000..8dd52db
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/DiscreteDistribution.java
@@ -0,0 +1,395 @@
+
+
+/*
+ * Class: DiscreteDistribution
+ * Description: discrete distributions over a set of real numbers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.util.Formatter;
+import java.util.Locale;
+
+
+/**
+ * This class implements discrete distributions over a <SPAN CLASS="textit">finite set of real numbers</SPAN>
+ * (also over <SPAN CLASS="textit">integers</SPAN> as a particular case).
+ * We assume that the random variable <SPAN CLASS="MATH"><I>X</I></SPAN> of interest can take one of the
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> values
+ * <SPAN CLASS="MATH"><I>x</I><SUB>0</SUB> < <SUP> ... </SUP> < <I>x</I><SUB>n-1</SUB></SPAN>, which <SPAN CLASS="textit">must be sorted</SPAN> by
+ * increasing order.
+ * <SPAN CLASS="MATH"><I>X</I></SPAN> can take the value <SPAN CLASS="MATH"><I>x</I><SUB>k</SUB></SPAN> with probability
+ * <SPAN CLASS="MATH"><I>p</I><SUB>k</SUB> = <I>P</I>[<I>X</I> = <I>x</I><SUB>k</SUB>]</SPAN>.
+ * In addition to the methods specified in the interface
+ * {@link umontreal.iro.lecuyer.probdist.Distribution Distribution},
+ * a method that returns the probability <SPAN CLASS="MATH"><I>p</I><SUB>k</SUB></SPAN> is supplied.
+ *
+ */
+public class DiscreteDistribution implements Distribution {
+ /*
+ For better precision in the tails, we keep the cumulative probabilities
+ (F) in cdf[x] for x <= xmed (i.e. cdf[x] is the sum off all the probabi-
+ lities pr[i] for i <= x),
+ and the complementary cumulative probabilities (1 - F) in cdf[x] for
+ x > xmed (i.e. cdf[x] is the sum off all the probabilities pr[i]
+ for i >= x).
+ */
+
+ protected double cdf[] = null; // cumulative probabilities
+ protected double pr[] = null; // probability terms or mass distribution
+ protected int xmin = 0; // pr[x] = 0 for x < xmin
+ protected int xmax = 0; // pr[x] = 0 for x > xmax
+ protected int xmed = 0; // cdf[x] = F(x) for x <= xmed, and
+ // cdf[x] = bar_F(x) for x > xmed
+ protected int nVal; // number of different values
+ protected double sortedVal[];
+ protected double supportA = Double.NEGATIVE_INFINITY;
+ protected double supportB = Double.POSITIVE_INFINITY;
+
+
+
+ protected DiscreteDistribution () {}
+ // Default constructor called by subclasses such as 'EmpiricalDist'
+
+
+ /**
+ * Constructs a discrete distribution over the <SPAN CLASS="MATH"><I>n</I></SPAN> values
+ * contained in array <TT>values</TT>, with probabilities given in array <TT>prob</TT>.
+ * Both arrays must have at least <SPAN CLASS="MATH"><I>n</I></SPAN> elements, the probabilities must
+ * sum to 1, and the values are assumed to be sorted by increasing order.
+ *
+ */
+ public DiscreteDistribution (double[] values, double[] prob, int n) {
+ init(n, values, prob);
+ }
+
+
+ /**
+ * Similar to
+ * {@link #DiscreteDistribution(double[], double[], int) DiscreteDistribution}<TT>(double[], double[], int)</TT>.
+ *
+ */
+ public DiscreteDistribution (int[] values, double[] prob, int n) {
+ double[] A = new double[n];
+ for(int i=0; i<n; i++)
+ A[i] = values[i];
+ init(n, A, prob);
+ }
+
+
+ /**
+ * Constructs a discrete distribution whose parameters are given
+ * in a single ordered array: <TT>params[0]</TT> contains <SPAN CLASS="MATH"><I>n</I></SPAN>, the number of
+ * values to consider. Then the next <SPAN CLASS="MATH"><I>n</I></SPAN> values of <TT>params</TT> are the
+ * values <SPAN CLASS="MATH"><I>x</I><SUB>j</SUB></SPAN>, and the last <SPAN CLASS="MATH"><I>n</I></SPAN> values of <TT>params</TT>
+ * are the probabilities <SPAN CLASS="MATH"><I>p</I><SUB>j</SUB></SPAN>.
+ *
+ *
+ */
+ @Deprecated
+ public DiscreteDistribution (double[] params) {
+ if (params.length != 1+params[0]*2)
+ throw new IllegalArgumentException("Wrong parameter size");
+
+ int n = (int)params[0];
+ double[] val = new double[n];
+ double[] prob = new double[n];
+
+ //int indice = 1;
+ System.arraycopy (params, 1, val, 0, n);
+ System.arraycopy (params, n+1, prob, 0, n);
+ init(n, val, prob);
+ }
+
+
+ private void init(int n, double[] val, double[] prob) {
+ int no = val.length;
+ int np = prob.length;
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (no < n || np < n)
+ throw new IllegalArgumentException
+ ("Size of arrays 'values' or 'prob' less than 'n'");
+
+ nVal = n;
+ pr = prob;
+
+ // cdf
+ sortedVal = new double[nVal];
+ System.arraycopy (val, 0, sortedVal, 0, nVal);
+
+ supportA = sortedVal[0];
+ supportB = sortedVal[nVal - 1];
+ xmin = 0;
+ xmax = nVal - 1;
+
+ /* Compute the cumulative probabilities until F >= 0.5, and keep them in
+ the lower part of cdf */
+ cdf = new double[nVal];
+ cdf[0] = pr[0];
+ int i = 0;
+ while (i < xmax && cdf[i] < 0.5) {
+ i++;
+ cdf[i] = pr[i] + cdf[i - 1];
+ }
+ // This is the boundary between F and barF in the CDF
+ xmed = i;
+
+ /* Compute the cumulative probabilities of the complementary
+ distribution and keep them in the upper part of cdf. */
+ cdf[nVal - 1] = pr[nVal - 1];
+ i = nVal - 2;
+ while (i > xmed) {
+ cdf[i] = pr[i] + cdf[i + 1];
+ i--;
+ }
+}
+
+ /**
+ * @param x value at which the distribution function is evaluated
+ *
+ * @return the distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double cdf (double x) {
+ if (x < sortedVal[0])
+ return 0.0;
+ if (x >= sortedVal[nVal-1])
+ return 1.0;
+ if ((xmax == xmed) || (x < sortedVal[xmed+1])) {
+ for (int i = 0; i <= xmed; i++)
+ if (x >= sortedVal[i] && x < sortedVal[i+1])
+ return cdf[i];
+ } else {
+ for (int i = xmed + 1; i < nVal-1; i++)
+ if (x >= sortedVal[i] && x < sortedVal[i+1])
+ return 1.0 - cdf[i+1];
+ }
+ throw new IllegalStateException();
+ }
+
+
+ /**
+ * @param x value at which the complementary distribution function is evaluated
+ *
+ * @return the complementary distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double barF (double x) {
+ if (x <= sortedVal[0])
+ return 1.0;
+ if (x > sortedVal[nVal-1])
+ return 0.0;
+ if ((xmax == xmed) || (x <= sortedVal[xmed+1])) {
+ for (int i = 0; i <= xmed; i++)
+ if (x > sortedVal[i] && x <= sortedVal[i+1])
+ return 1.0 - cdf[i];
+ } else {
+ for (int i = xmed + 1; i < nVal-1; i++)
+ if (x > sortedVal[i] && x <= sortedVal[i+1])
+ return cdf[i + 1];
+ }
+ throw new IllegalStateException();
+ }
+
+
+ public double inverseF (double u) {
+ int i, j, k;
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0)
+ return supportA;
+ if (u >= 1.0)
+ return supportB;
+
+ // Remember: the upper part of cdf contains the complementary distribu-
+ // tion for xmed < s <= xmax, and the lower part of cdf the
+ // distribution for xmin <= s <= xmed
+
+ if (u <= cdf[xmed - xmin]) {
+ // In the lower part of cdf
+ if (u <= cdf[0])
+ return sortedVal[xmin];
+ i = 0;
+ j = xmed - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u > cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ }
+ else {
+ // In the upper part of cdf
+ u = 1 - u;
+ if (u < cdf[xmax - xmin])
+ return sortedVal[xmax];
+
+ i = xmed - xmin + 1;
+ j = xmax - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u < cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ i--;
+ }
+
+ return sortedVal[i + xmin];
+ }
+
+
+ /**
+ * Computes the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = ∑<SUB>i</SUB><I>p</I><SUB>i</SUB><I>x</I><SUB>i</SUB></SPAN> of the distribution.
+ *
+ */
+ public double getMean() {
+ double mean = 0.0;
+ for (int i = 0; i < nVal; i++)
+ mean += sortedVal[i] * pr[i];
+ return mean;
+ }
+
+
+ /**
+ * Computes the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = ∑<SUB>i</SUB><I>p</I><SUB>i</SUB>(<I>x</I><SUB>i</SUB> - <I>E</I>[<I>X</I>])<SUP>2</SUP></SPAN>
+ * of the distribution.
+ *
+ */
+ public double getVariance() {
+ double mean = getMean();
+ double variance = 0.0;
+ for (int i = 0; i < nVal; i++)
+ variance += (sortedVal[i] - mean) * (sortedVal[i] - mean) * pr[i];
+ return (variance);
+ }
+
+
+ /**
+ * Computes the standard deviation of the distribution.
+ *
+ */
+ public double getStandardDeviation() {
+ return Math.sqrt (getVariance());
+ }
+
+
+ /**
+ * Returns a table containing the parameters of the current distribution.
+ * This table is built in regular order, according to constructor
+ * <TT>DiscreteDistribution(double[] params)</TT> order.
+ *
+ */
+ public double[] getParams() {
+ double[] retour = new double[1+nVal*2];
+ double sum = 0;
+ retour[0] = nVal;
+ System.arraycopy (sortedVal, 0, retour, 1, nVal);
+ for(int i = 0; i<nVal-1; i++) {
+ retour[nVal+1+i] = cdf[i] - sum;
+ sum = cdf[i];
+ }
+ retour[2*nVal] = 1.0 - sum;
+
+ return retour;
+ }
+
+
+ /**
+ * Returns the number of possible values <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN>.
+ *
+ */
+ public int getN() {
+ return nVal;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>p</I><SUB>i</SUB></SPAN>, the probability of
+ * the <SPAN CLASS="MATH"><I>i</I></SPAN>-th value, for <SPAN CLASS="MATH">0 <= <I>i</I> < <I>n</I></SPAN>.
+ *
+ * @param i value number,
+ * <SPAN CLASS="MATH">0 <= <I>i</I> < <I>n</I></SPAN>
+ *
+ * @return the probability of value <TT>i</TT>
+ *
+ */
+ public double prob (int i) {
+ if (i < 0 || i >= nVal)
+ return 0.;
+ return pr[i];
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>i</I></SPAN>-th value <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN>, for <SPAN CLASS="MATH">0 <= <I>i</I> < <I>n</I></SPAN>.
+ *
+ */
+ public double getValue (int i) {
+ return sortedVal[i];
+ }
+
+
+ /**
+ * Returns the lower limit <SPAN CLASS="MATH"><I>x</I><SUB>0</SUB></SPAN> of the support of the distribution.
+ *
+ * @return <SPAN CLASS="MATH"><I>x</I></SPAN> lower limit of support
+ *
+ */
+ public double getXinf() {
+ return supportA;
+ }
+
+
+ /**
+ * Returns the upper limit <SPAN CLASS="MATH"><I>x</I><SUB>n-1</SUB></SPAN> of the support of the distribution.
+ *
+ * @return <SPAN CLASS="MATH"><I>x</I></SPAN> upper limit of support
+ *
+ */
+ public double getXsup() {
+ return supportB;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString() {
+ StringBuilder sb = new StringBuilder ();
+ Formatter formatter = new Formatter (sb, Locale.US);
+ formatter.format ("%s%n", getClass ().getSimpleName ());
+ formatter.format ("%s : %s%n", "value", "cdf");
+ for (int i = 0; i < nVal - 1; i++)
+ formatter.format ("%f : %f%n", sortedVal[i], cdf[i]);
+ formatter.format ("%f : %f%n", sortedVal[nVal-1], 1.0);
+ return sb.toString ();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/DiscreteDistribution.tex b/source/umontreal/iro/lecuyer/probdist/DiscreteDistribution.tex
new file mode 100644
index 0000000..51ac2f7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/DiscreteDistribution.tex
@@ -0,0 +1,418 @@
+\defmodule {DiscreteDistribution}
+
+This class implements discrete distributions over a \emph{finite set of real numbers}
+(also over \emph{integers} as a particular case).
+% For discrete distributions over integers,
+% see \externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt}.
+%
+We assume that the random variable $X$ of interest can take one of the
+$n$ values $x_0 < \cdots < x_{n-1}$, which \emph{must be sorted} by
+increasing order.
+$X$ can take the value $x_k$ with probability $p_k = P[X = x_k]$.
+In addition to the methods specified in the interface
+\externalclass{umontreal.iro.lecuyer.probdist}{Distribution},
+a method that returns the probability $p_k$ is supplied.
+
+% Note that the default implementation of the complementary distribution function
+% returns \texttt{1.0 - cdf(x - 1)}, which is not accurate when $F(x)$ is near 1.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DiscreteDistribution
+ * Description: discrete distributions over a set of real numbers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+
+import java.util.Formatter;
+import java.util.Locale;\end{hide}
+
+
+public class DiscreteDistribution implements Distribution\begin{hide} {
+ /*
+ For better precision in the tails, we keep the cumulative probabilities
+ (F) in cdf[x] for x <= xmed (i.e. cdf[x] is the sum off all the probabi-
+ lities pr[i] for i <= x),
+ and the complementary cumulative probabilities (1 - F) in cdf[x] for
+ x > xmed (i.e. cdf[x] is the sum off all the probabilities pr[i]
+ for i >= x).
+ */
+
+ protected double cdf[] = null; // cumulative probabilities
+ protected double pr[] = null; // probability terms or mass distribution
+ protected int xmin = 0; // pr[x] = 0 for x < xmin
+ protected int xmax = 0; // pr[x] = 0 for x > xmax
+ protected int xmed = 0; // cdf[x] = F(x) for x <= xmed, and
+ // cdf[x] = bar_F(x) for x > xmed
+ protected int nVal; // number of different values
+ protected double sortedVal[];
+ protected double supportA = Double.NEGATIVE_INFINITY;
+ protected double supportB = Double.POSITIVE_INFINITY;
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}\begin{hide}
+
+ protected DiscreteDistribution () {}
+ // Default constructor called by subclasses such as 'EmpiricalDist'
+\end{hide}
+
+ public DiscreteDistribution (double[] values, double[] prob, int n)\begin{hide} {
+ init(n, values, prob);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a discrete distribution over the $n$ values
+ contained in array \texttt{values}, with probabilities given in array \texttt{prob}.
+ Both arrays must have at least $n$ elements, the probabilities must
+ sum to 1, and the values are assumed to be sorted by increasing order.
+\end{tabb}
+\begin{code}
+
+ public DiscreteDistribution (int[] values, double[] prob, int n)\begin{hide} {
+ double[] A = new double[n];
+ for(int i=0; i<n; i++)
+ A[i] = values[i];
+ init(n, A, prob);
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+\method{DiscreteDistribution}{double[], double[], int}\texttt{(double[], double[], int)}.
+\end{tabb}
+\begin{code}
+
+ @Deprecated
+ public DiscreteDistribution (double[] params)\begin{hide} {
+ if (params.length != 1+params[0]*2)
+ throw new IllegalArgumentException("Wrong parameter size");
+
+ int n = (int)params[0];
+ double[] val = new double[n];
+ double[] prob = new double[n];
+
+ //int indice = 1;
+ System.arraycopy (params, 1, val, 0, n);
+ System.arraycopy (params, n+1, prob, 0, n);
+ init(n, val, prob);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a discrete distribution whose parameters are given
+ in a single ordered array: \texttt{params[0]} contains $n$, the number of
+ values to consider. Then the next $n$ values of \texttt{params} are the
+ values $x_j$, and the last $n$ values of \texttt{params}
+ are the probabilities $p_j$.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ private void init(int n, double[] val, double[] prob) {
+ int no = val.length;
+ int np = prob.length;
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (no < n || np < n)
+ throw new IllegalArgumentException
+ ("Size of arrays 'values' or 'prob' less than 'n'");
+
+ nVal = n;
+ pr = prob;
+
+ // cdf
+ sortedVal = new double[nVal];
+ System.arraycopy (val, 0, sortedVal, 0, nVal);
+
+ supportA = sortedVal[0];
+ supportB = sortedVal[nVal - 1];
+ xmin = 0;
+ xmax = nVal - 1;
+
+ /* Compute the cumulative probabilities until F >= 0.5, and keep them in
+ the lower part of cdf */
+ cdf = new double[nVal];
+ cdf[0] = pr[0];
+ int i = 0;
+ while (i < xmax && cdf[i] < 0.5) {
+ i++;
+ cdf[i] = pr[i] + cdf[i - 1];
+ }
+ // This is the boundary between F and barF in the CDF
+ xmed = i;
+
+ /* Compute the cumulative probabilities of the complementary
+ distribution and keep them in the upper part of cdf. */
+ cdf[nVal - 1] = pr[nVal - 1];
+ i = nVal - 2;
+ while (i > xmed) {
+ cdf[i] = pr[i] + cdf[i + 1];
+ i--;
+ }
+}\end{code}\end{hide}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{hide}
+\begin{code}
+
+ public double cdf (double x) {
+ if (x < sortedVal[0])
+ return 0.0;
+ if (x >= sortedVal[nVal-1])
+ return 1.0;
+ if ((xmax == xmed) || (x < sortedVal[xmed+1])) {
+ for (int i = 0; i <= xmed; i++)
+ if (x >= sortedVal[i] && x < sortedVal[i+1])
+ return cdf[i];
+ } else {
+ for (int i = xmed + 1; i < nVal-1; i++)
+ if (x >= sortedVal[i] && x < sortedVal[i+1])
+ return 1.0 - cdf[i+1];
+ }
+ throw new IllegalStateException();
+ }
+\end{code}
+\begin{htmlonly}
+ \param{x}{value at which the distribution function is evaluated}
+ \return{the distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double barF (double x) {
+ if (x <= sortedVal[0])
+ return 1.0;
+ if (x > sortedVal[nVal-1])
+ return 0.0;
+ if ((xmax == xmed) || (x <= sortedVal[xmed+1])) {
+ for (int i = 0; i <= xmed; i++)
+ if (x > sortedVal[i] && x <= sortedVal[i+1])
+ return 1.0 - cdf[i];
+ } else {
+ for (int i = xmed + 1; i < nVal-1; i++)
+ if (x > sortedVal[i] && x <= sortedVal[i+1])
+ return cdf[i + 1];
+ }
+ throw new IllegalStateException();
+ }
+\end{code}
+\begin{htmlonly}
+ \param{x}{value at which the complementary distribution function is evaluated}
+ \return{the complementary distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double inverseF (double u) {
+ int i, j, k;
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0)
+ return supportA;
+ if (u >= 1.0)
+ return supportB;
+
+ // Remember: the upper part of cdf contains the complementary distribu-
+ // tion for xmed < s <= xmax, and the lower part of cdf the
+ // distribution for xmin <= s <= xmed
+
+ if (u <= cdf[xmed - xmin]) {
+ // In the lower part of cdf
+ if (u <= cdf[0])
+ return sortedVal[xmin];
+ i = 0;
+ j = xmed - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u > cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ }
+ else {
+ // In the upper part of cdf
+ u = 1 - u;
+ if (u < cdf[xmax - xmin])
+ return sortedVal[xmax];
+
+ i = xmed - xmin + 1;
+ j = xmax - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u < cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ i--;
+ }
+
+ return sortedVal[i + xmin];
+ }
+\end{code}
+\begin{htmlonly}
+ \param{u}{value in the interval $(0,1)$ for which
+ the inverse distribution function is evaluated}
+ \return{the inverse distribution function evaluated at \texttt{u}}
+ \exception{IllegalArgumentException}{if $u$ is not in the interval $(0,1)$}
+ \exception{ArithmeticException}{if the inverse cannot be computed,
+ for example if it would give infinity in a theoretical context}
+\end{htmlonly}
+\end{hide}
+\begin{code}
+
+ public double getMean()\begin{hide} {
+ double mean = 0.0;
+ for (int i = 0; i < nVal; i++)
+ mean += sortedVal[i] * pr[i];
+ return mean;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the mean $E[X] = \sum_{i}^{} p_i x_i$ of the distribution.
+\end{tabb}
+\begin{code}
+
+ public double getVariance()\begin{hide} {
+ double mean = getMean();
+ double variance = 0.0;
+ for (int i = 0; i < nVal; i++)
+ variance += (sortedVal[i] - mean) * (sortedVal[i] - mean) * pr[i];
+ return (variance);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the variance $\mbox{Var}[X] = \sum_{i}^{} p_i (x_i - E[X])^2$
+ of the distribution.
+\end{tabb}
+\begin{code}
+
+ public double getStandardDeviation()\begin{hide} {
+ return Math.sqrt (getVariance());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the standard deviation of the distribution.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams()\begin{hide} {
+ double[] retour = new double[1+nVal*2];
+ double sum = 0;
+ retour[0] = nVal;
+ System.arraycopy (sortedVal, 0, retour, 1, nVal);
+ for(int i = 0; i<nVal-1; i++) {
+ retour[nVal+1+i] = cdf[i] - sum;
+ sum = cdf[i];
+ }
+ retour[2*nVal] = 1.0 - sum;
+
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a table containing the parameters of the current distribution.
+ This table is built in regular order, according to constructor
+ \texttt{DiscreteDistribution(double[] params)} order.
+\end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return nVal;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the number of possible values $x_i$.
+\end{tabb}
+\begin{code}
+
+ public double prob (int i)\begin{hide} {
+ if (i < 0 || i >= nVal)
+ return 0.;
+ return pr[i];
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $p_i$, the probability of
+ the $i$-th value, for $0\le i<n$.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{value number, $0\le i < n$}
+ \return{the probability of value \texttt{i}}
+\end{htmlonly}
+\begin{code}
+
+ public double getValue (int i)\begin{hide} {
+ return sortedVal[i];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $i$-th value $x_i$, for $0\le i<n$.
+\end{tabb}
+\begin{code}
+
+ public double getXinf()\begin{hide} {
+ return supportA;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the lower limit $x_0$ of the support of the distribution.
+% The probability is 0 for all $x < x_0$.
+\end{tabb}
+\begin{htmlonly}
+ \return{$x$ lower limit of support}
+\end{htmlonly}
+\begin{code}
+
+ public double getXsup()\begin{hide} {
+ return supportB;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the upper limit $x_{n-1}$ of the support of the distribution.
+% The probability is 0 for all $x > x_{n-1}$.
+\end{tabb}
+\begin{htmlonly}
+ \return{$x$ upper limit of support}
+\end{htmlonly}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ StringBuilder sb = new StringBuilder ();
+ Formatter formatter = new Formatter (sb, Locale.US);
+ formatter.format ("%s%n", getClass ().getSimpleName ());
+ formatter.format ("%s : %s%n", "value", "cdf");
+ for (int i = 0; i < nVal - 1; i++)
+ formatter.format ("%f : %f%n", sortedVal[i], cdf[i]);
+ formatter.format ("%f : %f%n", sortedVal[nVal-1], 1.0);
+ return sb.toString ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/DiscreteDistributionInt.java b/source/umontreal/iro/lecuyer/probdist/DiscreteDistributionInt.java
new file mode 100644
index 0000000..e7be864
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/DiscreteDistributionInt.java
@@ -0,0 +1,279 @@
+
+
+/*
+ * Class: DiscreteDistributionInt
+ * Description: discrete distributions over the integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+/**
+ * Classes implementing discrete distributions over the integers should
+ * inherit from this class.
+ * It specifies the signatures of methods for computing the mass function
+ * (or probability)
+ * <SPAN CLASS="MATH"><I>p</I>(<I>x</I>) = <I>P</I>[<I>X</I> = <I>x</I>]</SPAN>, distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>,
+ * complementary distribution function <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>,
+ * and inverse distribution function <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, for a random variable <SPAN CLASS="MATH"><I>X</I></SPAN>
+ * with a discrete distribution over the integers.
+ *
+ * <P>
+ * <SPAN CLASS="textit">WARNING:</SPAN> the complementary distribution function is defined as
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>j</I>) = <I>P</I>[<I>X</I> >= <I>j</I>]</SPAN> (for integers <SPAN CLASS="MATH"><I>j</I></SPAN>, so that for discrete distributions
+ * in SSJ,
+ * <SPAN CLASS="MATH"><I>F</I>(<I>j</I>) + bar(F)(<I>j</I>)≠1</SPAN> since both include the term <SPAN CLASS="MATH"><I>P</I>[<I>X</I> = <I>j</I>]</SPAN>.
+ *
+ * <P>
+ * The implementing classes provide both static and non-static methods
+ * to compute the above functions.
+ * The non-static methods require the creation of an object of
+ * class {@link umontreal.iro.lecuyer.probdist.DiscreteDistributionInt DiscreteDistributionInt};
+ * all the non-negligible terms of the mass and distribution functions will be
+ * precomputed by the constructor and kept in arrays. Subsequent accesses
+ * will be very fast.
+ * The static methods do not require the construction of an object.
+ * These static methods are not specified in this abstract class because
+ * the number and types of their parameters depend on the distribution.
+ * When methods have to be called several times
+ * with the same parameters for the distributions,
+ * it is usually more efficient to create an object and use its non-static
+ * methods instead of the static ones.
+ * This trades memory for speed.
+ *
+ */
+public abstract class DiscreteDistributionInt implements Distribution {
+
+ /**
+ * Environment variable that determines what probability terms can
+ * be considered as negligible when building precomputed tables for
+ * distribution and mass functions. Probabilities smaller than <TT>EPSILON</TT>
+ * are not stored in the
+ * {@link umontreal.iro.lecuyer.probdist.DiscreteDistribution DiscreteDistribution} objects
+ * (such as those of class {@link PoissonDist}, etc.), but are computed
+ * directly each time they are needed (which should be very seldom).
+ * The default value is set to <SPAN CLASS="MATH">10<SUP>-16</SUP></SPAN>.
+ *
+ */
+ public static double EPSILON = 1.0e-16;
+ /*
+ For better precision in the tails, we keep the cumulative probabilities
+ (F) in cdf[x] for x <= xmed (i.e. cdf[x] is the sum off all the probabi-
+ lities pdf[i] for i <= x),
+ and the complementary cumulative probabilities (1 - F) in cdf[x] for
+ x > xmed (i.e. cdf[x] is the sum off all the probabilities pdf[i]
+ for i >= x).
+ */
+ protected final static double EPS_EXTRA = 1.0e-6;
+ protected double cdf[] = null; // cumulative probabilities
+ protected double pdf[] = null; // probability terms or mass distribution
+ protected int xmin = 0; // pdf[x] < EPSILON for x < xmin
+ protected int xmax = 0; // pdf[x] < EPSILON for x > xmax
+
+ // xmed is such that cdf[xmed] >= 0.5 and cdf[xmed - 1] < 0.5.
+ protected int xmed = 0; /* cdf[x] = F(x) for x <= xmed, and
+ cdf[x] = bar_F(x) for x > xmed */
+ protected int supportA = Integer.MIN_VALUE;
+ protected int supportB = Integer.MAX_VALUE;
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN>, the probability of <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ * @param x value at which the mass function must be evaluated
+ *
+ * @return the mass function evaluated at <TT>x</TT>
+ *
+ */
+ public abstract double prob (int x);
+
+
+ /**
+ * Returns the distribution function <SPAN CLASS="MATH"><I>F</I></SPAN> evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ * (see).
+ * Calls the {@link #cdf(int) cdf}<TT>(int)</TT> method.
+ *
+ * @param x value at which the distribution function must be evaluated
+ *
+ * @return the distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double cdf (double x) {
+ return cdf ((int) x);
+ }
+
+
+ /**
+ * Returns the distribution function <SPAN CLASS="MATH"><I>F</I></SPAN> evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ * (see).
+ *
+ * @param x value at which the distribution function must be evaluated
+ *
+ * @return the distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public abstract double cdf (int x);
+
+
+ /**
+ * Returns <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>, the complementary distribution function.
+ * Calls the {@link #barF(int) barF}<TT>(int)</TT> method.
+ *
+ * @param x value at which the complementary distribution function
+ * must be evaluated
+ *
+ * @return the complementary distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double barF (double x) {
+ return barF ((int) x);
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>, the complementary
+ * distribution function. <SPAN CLASS="textit">See the WARNING above</SPAN>.
+ *
+ * @param x value at which the complementary distribution function
+ * must be evaluated
+ *
+ * @return the complementary distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double barF (int x) {
+ return 1.0 - cdf (x - 1);
+ }
+
+
+ /**
+ * Returns the lower limit <SPAN CLASS="MATH"><I>x</I><SUB>a</SUB></SPAN> of the support of the probability
+ * mass function. The probability is 0 for all <SPAN CLASS="MATH"><I>x</I> < <I>x</I><SUB>a</SUB></SPAN>.
+ *
+ * @return <SPAN CLASS="MATH"><I>x</I></SPAN> lower limit of support
+ *
+ */
+ public int getXinf() {
+ return supportA;
+ }
+
+
+ /**
+ * Returns the upper limit <SPAN CLASS="MATH"><I>x</I><SUB>b</SUB></SPAN> of the support of the probability
+ * mass function. The probability is 0 for all <SPAN CLASS="MATH"><I>x</I> > <I>x</I><SUB>b</SUB></SPAN>.
+ *
+ * @return <SPAN CLASS="MATH"><I>x</I></SPAN> upper limit of support
+ *
+ */
+ public int getXsup() {
+ return supportB;
+ }
+
+
+ /**
+ * Returns the inverse distribution function
+ * <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, where
+ * <SPAN CLASS="MATH">0 <= <I>u</I> <= 1</SPAN>. Calls the <TT>inverseFInt</TT> method.
+ *
+ * @param u value in the interval <SPAN CLASS="MATH">(0, 1)</SPAN> for which
+ * the inverse distribution function is evaluated
+ *
+ * @return the inverse distribution function evaluated at <TT>u</TT>
+ * @exception IllegalArgumentException if <SPAN CLASS="MATH"><I>u</I></SPAN> is not in the interval <SPAN CLASS="MATH">(0, 1)</SPAN>
+ *
+ * @exception ArithmeticException if the inverse cannot be computed,
+ * for example if it would give infinity in a theoritical context
+ *
+ *
+ */
+ public double inverseF (double u) {
+ return inverseFInt (u);
+ }
+
+
+ /**
+ * Returns the inverse distribution function
+ * <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, where
+ * <SPAN CLASS="MATH">0 <= <I>u</I> <= 1</SPAN>.
+ * The default implementation uses binary search.
+ *
+ * @param u value in the interval <SPAN CLASS="MATH">(0, 1)</SPAN> for which
+ * the inverse distribution function is evaluated
+ *
+ * @return the inverse distribution function evaluated at <TT>u</TT>
+ * @exception IllegalArgumentException if <SPAN CLASS="MATH"><I>u</I></SPAN> is not in the interval <SPAN CLASS="MATH">(0, 1)</SPAN>
+ *
+ * @exception ArithmeticException if the inverse cannot be computed,
+ * for example if it would give infinity in a theoritical context
+ *
+ *
+ */
+ public int inverseFInt (double u) {
+ int i, j, k;
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return supportA;
+ if (u >= 1.0)
+ return supportB;
+
+
+ // Remember: the upper part of cdf contains the complementary distribu-
+ // tion for xmed < s <= xmax, and the lower part of cdf the
+ // distribution for xmin <= x <= xmed
+
+ if (u <= cdf[xmed - xmin]) {
+ // In the lower part of cdf
+ if (u <= cdf[0])
+ return xmin;
+ i = 0;
+ j = xmed - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u > cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ }
+ else {
+ // In the upper part of cdf
+ u = 1 - u;
+ if (u < cdf[xmax - xmin])
+ return xmax;
+
+ i = xmed - xmin + 1;
+ j = xmax - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u < cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ i--;
+ }
+
+ return i + xmin;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/DiscreteDistributionInt.tex b/source/umontreal/iro/lecuyer/probdist/DiscreteDistributionInt.tex
new file mode 100644
index 0000000..59f9e52
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/DiscreteDistributionInt.tex
@@ -0,0 +1,271 @@
+\defmodule {DiscreteDistributionInt}
+
+Classes implementing discrete distributions over the integers should
+inherit from this class.
+It specifies the signatures of methods for computing the mass function
+(or probability) $p(x) = P[X=x]$, distribution function $F(x)$,
+complementary distribution function $\bar F(x)$,
+and inverse distribution function $F^{-1}(u)$, for a random variable $X$
+with a discrete distribution over the integers.
+
+\emph{WARNING:} the complementary distribution function is defined as
+$\bar F(j) = P[X \ge j]$ (for integers $j$, so that for discrete distributions
+in SSJ, $F(j) + \bar F(j) \neq 1$ since both include the term $P[X = j]$.
+
+The implementing classes provide both static and non-static methods
+to compute the above functions.
+The non-static methods require the creation of an object of
+class \externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt};
+all the non-negligible terms of the mass and distribution functions will be
+precomputed by the constructor and kept in arrays. Subsequent accesses
+will be very fast.
+The static methods do not require the construction of an object.
+These static methods are not specified in this abstract class because
+the number and types of their parameters depend on the distribution.
+When methods have to be called several times
+with the same parameters for the distributions,
+it is usually more efficient to create an object and use its non-static
+methods instead of the static ones.
+This trades memory for speed.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DiscreteDistributionInt
+ * Description: discrete distributions over the integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+public abstract class DiscreteDistributionInt implements Distribution\begin{hide} {\end{hide}
+
+ public static double EPSILON = 1.0e-16;\end{code}
+ \begin{tabb} Environment variable that determines what probability terms can
+ be considered as negligible when building precomputed tables for
+ distribution and mass functions. Probabilities smaller than \texttt{EPSILON}
+ are not stored in the
+ \externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistribution} objects
+ (such as those of class \class{PoissonDist}, etc.), but are computed
+ directly each time they are needed (which should be very seldom).
+ The default value is set to $10^{-16}$.
+ \end{tabb}
+\begin{code}\begin{hide}
+ /*
+ For better precision in the tails, we keep the cumulative probabilities
+ (F) in cdf[x] for x <= xmed (i.e. cdf[x] is the sum off all the probabi-
+ lities pdf[i] for i <= x),
+ and the complementary cumulative probabilities (1 - F) in cdf[x] for
+ x > xmed (i.e. cdf[x] is the sum off all the probabilities pdf[i]
+ for i >= x).
+ */
+ protected final static double EPS_EXTRA = 1.0e-6;
+ protected double cdf[] = null; // cumulative probabilities
+ protected double pdf[] = null; // probability terms or mass distribution
+ protected int xmin = 0; // pdf[x] < EPSILON for x < xmin
+ protected int xmax = 0; // pdf[x] < EPSILON for x > xmax
+
+ // xmed is such that cdf[xmed] >= 0.5 and cdf[xmed - 1] < 0.5.
+ protected int xmed = 0; /* cdf[x] = F(x) for x <= xmed, and
+ cdf[x] = bar_F(x) for x > xmed */
+ protected int supportA = Integer.MIN_VALUE;
+ protected int supportB = Integer.MAX_VALUE;
+\end{hide}
+
+ public abstract double prob (int x);
+\end{code}
+\begin{tabb} Returns $p(x)$, the probability of $x$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the mass function must be evaluated}
+ \return{the mass function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double cdf (double x)\begin{hide} {
+ return cdf ((int) x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the distribution function $F$ evaluated at $x$
+(see (\ref{eq:FDistDisc})).
+ Calls the \method{cdf}{int}\texttt{(int)} method.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the distribution function must be evaluated}
+ \return{the distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public abstract double cdf (int x);
+\end{code}
+\begin{tabb} Returns the distribution function $F$ evaluated at $x$
+(see (\ref{eq:FDistDisc})).
+%% \eq
+%% F(x) = P[X\le x] = \sum_{s=-\infty}^x p(s).
+%% \endeq
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the distribution function must be evaluated}
+ \return{the distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double barF (double x)\begin{hide} {
+ return barF ((int) x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\bar F(x)$, the complementary distribution function.
+ Calls the \method{barF}{int}\texttt{(int)} method.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the complementary distribution function
+ must be evaluated}
+ \return{the complementary distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double barF (int x)\begin{hide} {
+ return 1.0 - cdf (x - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\bar F(x)$, the complementary
+ distribution function. \emph{See the WARNING above}.
+\begin{detailed}%
+ The default implementation returns \texttt{1.0 - cdf(x - 1)}, which
+ is not accurate when $F(x)$ is near 1.
+\end{detailed}%
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the complementary distribution function
+ must be evaluated}
+ \return{the complementary distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public int getXinf()\begin{hide} {
+ return supportA;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the lower limit $x_a$ of the support of the probability
+ mass function. The probability is 0 for all $x < x_a$.
+\end{tabb}
+\begin{htmlonly}
+ \return{$x$ lower limit of support}
+\end{htmlonly}
+\begin{code}
+
+ public int getXsup()\begin{hide} {
+ return supportB;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the upper limit $x_b$ of the support of the probability
+ mass function. The probability is 0 for all $x > x_b$.
+\end{tabb}
+\begin{htmlonly}
+ \return{$x$ upper limit of support}
+\end{htmlonly}
+\begin{code}
+
+ public double inverseF (double u)\begin{hide} {
+ return inverseFInt (u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inverse distribution function
+ $F^{-1}(u)$, where $0\le u\le 1$. Calls the \texttt{inverseFInt} method.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{value in the interval $(0,1)$ for which
+ the inverse distribution function is evaluated}
+ \return{the inverse distribution function evaluated at \texttt{u}}
+ \exception{IllegalArgumentException}{if $u$ is not in the interval $(0,1)$}
+ \exception{ArithmeticException}{if the inverse cannot be computed,
+ for example if it would give infinity in a theoritical context}
+\end{htmlonly}
+\begin{code}
+
+ public int inverseFInt (double u)\begin{hide} {
+ int i, j, k;
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return supportA;
+ if (u >= 1.0)
+ return supportB;
+
+
+ // Remember: the upper part of cdf contains the complementary distribu-
+ // tion for xmed < s <= xmax, and the lower part of cdf the
+ // distribution for xmin <= x <= xmed
+
+ if (u <= cdf[xmed - xmin]) {
+ // In the lower part of cdf
+ if (u <= cdf[0])
+ return xmin;
+ i = 0;
+ j = xmed - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u > cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ }
+ else {
+ // In the upper part of cdf
+ u = 1 - u;
+ if (u < cdf[xmax - xmin])
+ return xmax;
+
+ i = xmed - xmin + 1;
+ j = xmax - xmin;
+ while (i < j) {
+ k = (i + j) / 2;
+ if (u < cdf[k])
+ i = k + 1;
+ else
+ j = k;
+ }
+ i--;
+ }
+
+ return i + xmin;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inverse distribution function
+ $F^{-1}(u)$, where $0\le u\le 1$.
+ The default implementation uses binary search.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{value in the interval $(0,1)$ for which
+ the inverse distribution function is evaluated}
+ \return{the inverse distribution function evaluated at \texttt{u}}
+ \exception{IllegalArgumentException}{if $u$ is not in the interval $(0,1)$}
+ \exception{ArithmeticException}{if the inverse cannot be computed,
+ for example if it would give infinity in a theoritical context}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/probdist/Distribution.java b/source/umontreal/iro/lecuyer/probdist/Distribution.java
new file mode 100644
index 0000000..4cc1f1b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/Distribution.java
@@ -0,0 +1,107 @@
+
+
+/*
+ * Class: Distribution
+ * Description: interface for all discrete and continuous distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * This interface should be implemented by all classes supporting
+ * discrete and continuous distributions. It specifies the signature of methods that compute
+ * the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>,
+ * the complementary distribution function <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>,
+ * and the inverse distribution function
+ * <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ * It also specifies the signature of methods that returns the mean,
+ * the variance and the standard deviation.
+ *
+ */
+public interface Distribution {
+
+ /**
+ * Returns the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ *
+ * @param x value at which the distribution function is evaluated
+ *
+ * @return distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double cdf (double x);
+
+
+ /**
+ * Returns
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = 1 - <I>F</I>(<I>x</I>)</SPAN>.
+ *
+ * @param x value at which the complementary distribution function is evaluated
+ *
+ * @return complementary distribution function evaluated at <TT>x</TT>
+ *
+ */
+ public double barF (double x);
+
+
+ /**
+ * Returns the inverse distribution function
+ * <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, defined in.
+ *
+ * @param u value in the interval <SPAN CLASS="MATH">(0, 1)</SPAN> for which the inverse
+ * distribution function is evaluated
+ *
+ * @return the inverse distribution function evaluated at <TT>u</TT>
+ *
+ */
+ public double inverseF (double u);
+
+
+ /**
+ * Returns the mean of the distribution function.
+ *
+ */
+ public double getMean();
+
+
+ /**
+ * Returns the variance of the distribution function.
+ *
+ */
+ public double getVariance();
+
+
+ /**
+ * Returns the standard deviation of the distribution function.
+ *
+ */
+ public double getStandardDeviation();
+
+
+ /**
+ * Returns the parameters of the distribution function in the same
+ * order as in the constructors.
+ *
+ */
+ public double[] getParams();
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/Distribution.tex b/source/umontreal/iro/lecuyer/probdist/Distribution.tex
new file mode 100644
index 0000000..7666954
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/Distribution.tex
@@ -0,0 +1,125 @@
+\defmodule {Distribution}
+
+This interface should be implemented by all classes supporting
+discrete and continuous distributions. % over the \emph{real numbers}.
+It specifies the signature of methods that compute
+%%the density $f(x)$,
+the distribution function $F(x)$,
+%=P[X\le x]$,
+the complementary distribution function $\bar F(x)$,
+and the inverse distribution function $ F^{-1} (u)$.
+%for a random variable $X$ having a univariate continuous distribution.
+%
+%For distributions over integers, use the class
+%\externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt}.
+It also specifies the signature of methods that returns the mean,
+the variance and the standard deviation.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: Distribution
+ * Description: interface for all discrete and continuous distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public interface Distribution\begin{hide} {\end{hide}
+
+ public double cdf (double x);
+\end{code}
+\begin{tabb} Returns the distribution function $F(x)$.
+%% \eq
+%% F(x) = P[X\le x] = \int_{-\infty}^x f(s)ds.
+%% \endeq
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the distribution function is evaluated}
+ \return{distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double barF (double x);
+\end{code}
+\begin{tabb} Returns $\bar F(x) = 1 - F(x)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the complementary distribution function is evaluated}
+ \return{complementary distribution function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double inverseF (double u);
+\end{code}
+\begin{tabb} Returns the inverse distribution function
+ $F^{-1}(u)$, defined in (\ref{eq:inverseF}).
+%% = \inf\{x\in\RR : F(x)\ge u\}$,
+%% where $0\le u\le 1$.
+% The default implementation uses binary search to find the inverse of a
+% generic continuous distribution function $F$, evaluated at $u$.
+% The returned value has approximately \texttt{decPrec} decimal digits of
+% precision.
+%%
+% If \texttt{detail} is true, the method will print detailed information
+% about the inversion process.
+% \pierre{Va imprimer o\`u? Probablement \`a enlever.}
+% Restriction: $0 \le u \le 1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{value in the interval $(0,1)$ for which the inverse
+ distribution function is evaluated}
+ \return{the inverse distribution function evaluated at \texttt{u}}
+% \exception{IllegalArgumentException}{if $u$ is not in the interval $(0,1)$}
+% \exception{ArithmeticException}{if the inverse cannot be computed,
+% for example if it would give infinity in a theoritical context}
+\end{htmlonly}
+\begin{code}
+
+ public double getMean();
+\end{code}
+\begin{tabb} Returns the mean of the distribution function.
+\end{tabb}
+\begin{code}
+
+ public double getVariance();
+\end{code}
+\begin{tabb} Returns the variance of the distribution function.
+\end{tabb}
+\begin{code}
+
+ public double getStandardDeviation();
+\end{code}
+\begin{tabb} Returns the standard deviation of the distribution function.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams();
+\end{code}
+\begin{tabb} Returns the parameters of the distribution function in the same
+ order as in the constructors.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/DistributionFactory.java b/source/umontreal/iro/lecuyer/probdist/DistributionFactory.java
new file mode 100644
index 0000000..0db755e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/DistributionFactory.java
@@ -0,0 +1,472 @@
+
+
+/*
+ * Class: DistributionFactory
+ * Description: allows the creation of distribution objects from a string
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.lang.reflect.*;
+import java.util.StringTokenizer;
+
+/**
+ * This class implements a string API for the package <TT>probdist</TT>.
+ * It uses Java Reflection to allow the creation of probability distribution
+ * objects from a string. This permits one to obtain distribution specifications
+ * from a file or dynamically from user input during program execution.
+ * This string API is similar to that of
+ * <A NAME="tex2html1"
+ * HREF="http://statistik.wu-wien.ac.at/unuran/">UNURAN</A>.
+ *
+ * <P>
+ * The (static) methods of this class invoke the constructor specified
+ * in the string. For example,
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ * d = DistributionFactory.getContinuousDistribution ("NormalDist (0.0, 2.5)");
+ * <BR></TT>
+ * </DIV>
+ * is equivalent to
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ * d = NormalDist (0.0, 2.5);
+ * <BR></TT>
+ * </DIV>
+ *
+ * <P>
+ * The string that specifies the distribution (i.e., the formal parameter
+ * <TT>str</TT> of the methods) must be a valid call of the constructor
+ * of a class that extends {@link ContinuousDistribution} or
+ * {@link DiscreteDistribution}, and all parameter values must be numerical
+ * values (variable names are not allowed).
+ *
+ *
+ *
+ * <P>
+ * The distribution parameters can also be estimated from a set of observations
+ * instead of being passed to the constructor. In that case, one passes the
+ * vector of observations, and the constructor estimates the parameters by
+ * the maximum likelihood method.
+ *
+ */
+public class DistributionFactory {
+ private DistributionFactory() {} // ???? Utile?
+
+ public static Distribution getDistribution (String str) {
+ // Extracts the name of the distribution.
+ // If there is an open parenthesis, the name contains all the
+ // non-space characters preceeding it.If not,the name is the full string.
+
+ int i = 0;
+ str = str.trim();
+
+ int idx = str.indexOf ('(', i);
+ String distName;
+ if (idx == -1)
+ distName = str.substring (i).trim();
+ else
+ distName = str.substring (i, idx).trim();
+
+ // Try to find the class in probdist package.
+ Class<?> distClass;
+ if (distName.equals ("String"))
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ try {
+ distClass = Class.forName ("umontreal.iro.lecuyer.probdist."
+ + distName);
+ }
+ catch (ClassNotFoundException e) {
+ // Look for a fully qualified classname whose constructor
+ // matches this string.
+ try {
+ distClass = Class.forName (distName);
+ // We must check if the class implements Distribution
+ if (Distribution.class.isAssignableFrom(distClass) == false)
+ throw new IllegalArgumentException
+ ("The given class is not a Probdist distribution class.");
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ }
+ }
+
+ String paramStr = "";
+ if (idx != -1) {
+ // Get the parameters from the string.
+ int parFrom = idx;
+ int parTo = str.lastIndexOf (')');
+ // paramStr will contain the parameters without parentheses.
+ paramStr = str.substring (parFrom + 1, parTo).trim();
+ if (paramStr.indexOf ('(') != -1 || paramStr.indexOf (')') != -1)
+ //All params are numerical,so parenthesis nesting is forbidden here
+ throw new IllegalArgumentException ("Invalid parameter string: "
+ + paramStr);
+ }
+
+ if (paramStr.equals ("")) {
+ // No parameter is given to the constructor.
+ try {
+ return (Distribution) distClass.newInstance();
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalArgumentException
+ ("Default parameters not available");
+ }
+ catch (InstantiationException e) {
+ throw new IllegalArgumentException
+ ("Default parameters not available");
+ }
+ }
+
+ // Find the number of parameters and try to find a matching constructor.
+ // Within probdist, there are no constructors with the same
+ // number of arguments but with different types.
+ // This simplifies the constructor selection scheme.
+ StringTokenizer paramTok = new StringTokenizer (paramStr, ",");
+ int nparams = paramTok.countTokens();
+ Constructor[] cons = distClass.getConstructors();
+ Constructor distCons = null;
+ Class[] paramTypes = null;
+ // Find a public constructor with the correct number of parameters.
+ for (i = 0; i < cons.length; i++) {
+ if (Modifier.isPublic (cons[i].getModifiers()) &&
+ ((paramTypes = cons[i].getParameterTypes()).length == nparams)) {
+ distCons = cons[i];
+ break;
+ }
+ }
+ if (distCons == null)
+ throw new IllegalArgumentException ("Invalid parameter number");
+
+ // Create the parameters for the selected constructor.
+ Object[] instParams = new Object[nparams];
+ for (i = 0; i < nparams; i++) {
+ String par = paramTok.nextToken().trim();
+ try {
+ // We only need a limited set of parameter types here.
+ if (paramTypes[i] == int.class)
+ instParams[i] = new Integer (par);
+ else if (paramTypes[i] == long.class)
+ instParams[i] = new Long (par);
+ else if (paramTypes[i] == float.class) {
+ if (par.equalsIgnoreCase ("infinity") || par.equalsIgnoreCase
+ ("+infinity"))
+ instParams[i] = new Float (Float.POSITIVE_INFINITY);
+ else if (par.equalsIgnoreCase ("-infinity"))
+ instParams[i] = new Float (Float.NEGATIVE_INFINITY);
+ else
+ instParams[i] = new Float (par);
+ }
+ else if (paramTypes[i] == double.class) {
+ if (par.equalsIgnoreCase ("infinity") || par.equalsIgnoreCase
+ ("+infinity"))
+ instParams[i] = new Double (Double.POSITIVE_INFINITY);
+ else if (par.equalsIgnoreCase ("-infinity"))
+ instParams[i] = new Double (Double.NEGATIVE_INFINITY);
+ else
+ instParams[i] = new Double (par);
+ }
+ else
+ throw new IllegalArgumentException
+ ("Parameter " + (i+1) + " type " + paramTypes[i].getName() +
+ "not supported");
+ }
+ catch (NumberFormatException e) {
+ throw new IllegalArgumentException
+ ("Parameter " + (i+1) + " of type " +
+ paramTypes[i].getName()+" could not be converted from String");
+ }
+ }
+
+ // Try to instantiate the distribution class.
+ try {
+ return (Distribution) distCons.newInstance (instParams);
+ }
+ catch (IllegalAccessException e) {
+ return null;
+ }
+ catch (InstantiationException e) {
+ return null;
+ }
+ catch (InvocationTargetException e) {
+ return null;
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ /**
+ * Uses the Java Reflection API to construct a {@link ContinuousDistribution}
+ * object by estimating parameters of the distribution using the maximum likelihood
+ * method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param distName the name of the distribution to instanciate
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static ContinuousDistribution getDistributionMLE
+ (String distName, double[] x, int n) {
+
+ Class<?> distClass;
+ try
+ {
+ distClass = Class.forName ("umontreal.iro.lecuyer.probdist." + distName);
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ distClass = Class.forName (distName);
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ }
+ }
+
+ return getDistributionMLE ((Class<? extends ContinuousDistribution>)distClass, x, n);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ /**
+ * Uses the Java Reflection API to construct a {@link DiscreteDistributionInt}
+ * object by estimating parameters of the distribution using the maximum likelihood
+ * method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param distName the name of the distribution to instanciate
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static DiscreteDistributionInt getDistributionMLE
+ (String distName, int[] x, int n) {
+
+ Class<?> distClass;
+ try
+ {
+ distClass = Class.forName ("umontreal.iro.lecuyer.probdist." + distName);
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ distClass = Class.forName (distName);
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ }
+ }
+
+ return getDistributionMLE ((Class<? extends DiscreteDistributionInt>)distClass, x, n);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ /**
+ * Uses the Java Reflection API to construct a {@link ContinuousDistribution}
+ * object by estimating parameters of the distribution using the maximum likelihood
+ * method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param distClass the class of the distribution to instanciate
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static <T extends ContinuousDistribution> T getDistributionMLE
+ (Class<T> distClass, double[] x, int n) {
+ if (ContinuousDistribution.class.isAssignableFrom(distClass) == false)
+ throw new IllegalArgumentException
+ ("The given class is not a Probdist distribution class.");
+
+ Method m;
+ try
+ {
+ m = distClass.getMethod ("getInstanceFromMLE", double[].class, int.class);
+ }
+ catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (double[],int)");
+ }
+ if (!Modifier.isStatic (m.getModifiers()) ||
+ !distClass.isAssignableFrom (m.getReturnType()))
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (double[],int)");
+
+ try
+ {
+ return (T)m.invoke (null, x, n);
+ }
+ catch (IllegalAccessException e) {
+ return null;
+ }
+ catch (IllegalArgumentException e) {
+ return null;
+ }
+ catch (InvocationTargetException e) {
+ return null;
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ /**
+ * Uses the Java Reflection API to construct a {@link DiscreteDistributionInt}
+ * object by estimating parameters of the distribution using the maximum likelihood
+ * method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param distClass the class of the distribution to instanciate
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static <T extends DiscreteDistributionInt> T getDistributionMLE
+ (Class<T> distClass, int[] x, int n) {
+ if (DiscreteDistributionInt.class.isAssignableFrom(distClass) == false)
+ throw new IllegalArgumentException
+ ("The given class is not a discrete distribution class over integers.");
+
+ Method m;
+ try
+ {
+ m = distClass.getMethod ("getInstanceFromMLE", int[].class, int.class);
+ }
+ catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (int[],int)");
+ }
+ if (!Modifier.isStatic (m.getModifiers()) ||
+ !distClass.isAssignableFrom (m.getReturnType()))
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (int[],int)");
+
+ try
+ {
+ return (T)m.invoke (null, x, n);
+ }
+ catch (IllegalAccessException e) {
+ return null;
+ }
+ catch (IllegalArgumentException e) {
+ return null;
+ }
+ catch (InvocationTargetException e) {
+ return null;
+ }
+ }
+
+
+ /**
+ * Uses the Java Reflection API to construct a {@link ContinuousDistribution}
+ * object by executing the code contained in the string <TT>str</TT>.
+ * This code should be a valid invocation of the constructor of a
+ * {@link ContinuousDistribution} object.
+ * This method throws exceptions if it cannot parse the given string and
+ * returns <TT>null</TT> if the distribution object could not be created due to
+ * a Java-specific instantiation problem.
+ *
+ * @param str string that contains a call to the constructor of a continuous
+ * distribution
+ *
+ * @return a continuous distribution object or <TT>null</TT> if it could not
+ * be instantiated
+ * @exception IllegalArgumentException if parsing problems occured
+ * when reading <TT>str</TT>
+ *
+ * @exception ClassCastException if the distribution string does not represent
+ * a continuous distribution
+ *
+ *
+ */
+ public static ContinuousDistribution getContinuousDistribution (String str) {
+ return (ContinuousDistribution)getDistribution (str);
+ }
+
+
+ /**
+ * Same as {@link #getContinuousDistribution getContinuousDistribution}, but for discrete distributions
+ * over the real numbers.
+ *
+ * @param str string that contains a call to the constructor of a discrete
+ * distribution
+ *
+ * @return a discrete distribution object, or <TT>null</TT> if it could not
+ * be instantiated
+ * @exception IllegalArgumentException if parsing problems occured when
+ * reading <TT>str</TT>
+ *
+ * @exception ClassCastException if the distribution string does not represent
+ * a discrete distribution
+ *
+ *
+ */
+ public static DiscreteDistribution getDiscreteDistribution (String str) {
+ return (DiscreteDistribution)getDistribution (str);
+ }
+
+
+
+ /**
+ * Same as {@link #getContinuousDistribution getContinuousDistribution}, but for discrete distributions
+ * over the integers.
+ *
+ * @param str string that contains a call to the constructor of a discrete
+ * distribution
+ *
+ * @return a discrete distribution object, or <TT>null</TT> if it could not
+ * be instantiated
+ * @exception IllegalArgumentException if parsing problems occured when
+ * reading <TT>str</TT>
+ *
+ * @exception ClassCastException if the distribution string does not represent
+ * a discrete distribution
+ *
+ */
+ public static DiscreteDistributionInt getDiscreteDistributionInt (String str) {
+ return (DiscreteDistributionInt)getDistribution (str);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/DistributionFactory.tex b/source/umontreal/iro/lecuyer/probdist/DistributionFactory.tex
new file mode 100644
index 0000000..e8f2a98
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/DistributionFactory.tex
@@ -0,0 +1,490 @@
+\defmodule{DistributionFactory}
+
+This class implements a string API for the package \texttt{probdist}.
+It uses Java Reflection to allow the creation of probability distribution
+objects from a string. This permits one to obtain distribution specifications
+from a file or dynamically from user input during program execution.
+This string API is similar to that of
+\htmladdnormallink{UNURAN}{http://statistik.wu-wien.ac.at/unuran/}
+\cite{iLEY02a}.
+
+The (static) methods of this class invoke the constructor specified
+in the string. For example,
+\begin{vcode}
+ d = DistributionFactory.getContinuousDistribution ("NormalDist (0.0, 2.5)");
+\end{vcode}
+is equivalent to
+\begin{vcode}
+ d = NormalDist (0.0, 2.5);
+\end{vcode}
+
+The string that specifies the distribution (i.e., the formal parameter
+\texttt{str} of the methods) must be a valid call of the constructor
+of a class that extends \class{ContinuousDistribution} or
+\class{DiscreteDistribution}, and all parameter values must be numerical
+values (variable names are not allowed).
+\hpierre{Because reconstructing the constructor call with variable names
+ would be more difficult.}
+% Spaces in the string are ignored.
+%
+\begin{hide} %%%
+If no parentheses follow, the default parameters are used, i.e.\ a
+no-parameter constructor is searched and invoked. Distribution parameters
+are surrounded with parentheses and are separated from each other using
+commas. The order and types of the parameters should be the same
+as the corresponding class constructor. When specifying decimal values, the dot
+should be used for decimal separation, not the comma. When specifying a
+ \texttt{float} or a \texttt{double}, one can use \texttt{-infinity} or
+ \texttt{infinity} to denote $-\infty$ and $+\infty$,
+respectively. This is parsed to \texttt{Double.NEGATIVE\_INFINITY}
+and \texttt{Double.POSITIVE\_INFINITY}, respectively. However, this
+is not accepted by all probability distributions.
+\hpierre{Vraiment utile? Peut-etre pour specifier les intervalles
+ pour les distributions tronquees.}
+
+For example, if one uses the string \texttt{Normal},
+a \class{NormalDist} object with $\mu = 0$ and $\sigma = 1$ will be created.
+If one uses \texttt{Exponential(2.5)}, an \class{ExponentialDist}
+object with $\lambda=2.5$ will be constructed.
+\end{hide} %%%%%%%%%%%
+
+The distribution parameters can also be estimated from a set of observations
+instead of being passed to the constructor. In that case, one passes the
+vector of observations, and the constructor estimates the parameters by
+the maximum likelihood method.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DistributionFactory
+ * Description: allows the creation of distribution objects from a string
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import java.lang.reflect.*;
+import java.util.StringTokenizer;\end{hide}
+
+public class DistributionFactory\begin{hide} {
+ private DistributionFactory() {} // ???? Utile?
+
+ public static Distribution getDistribution (String str) {
+ // Extracts the name of the distribution.
+ // If there is an open parenthesis, the name contains all the
+ // non-space characters preceeding it.If not,the name is the full string.
+
+ int i = 0;
+ str = str.trim();
+
+ int idx = str.indexOf ('(', i);
+ String distName;
+ if (idx == -1)
+ distName = str.substring (i).trim();
+ else
+ distName = str.substring (i, idx).trim();
+
+ // Try to find the class in probdist package.
+ Class<?> distClass;
+ if (distName.equals ("String"))
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ try {
+ distClass = Class.forName ("umontreal.iro.lecuyer.probdist."
+ + distName);
+ }
+ catch (ClassNotFoundException e) {
+ // Look for a fully qualified classname whose constructor
+ // matches this string.
+ try {
+ distClass = Class.forName (distName);
+ // We must check if the class implements Distribution
+ if (Distribution.class.isAssignableFrom(distClass) == false)
+ throw new IllegalArgumentException
+ ("The given class is not a Probdist distribution class.");
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ }
+ }
+
+ String paramStr = "";
+ if (idx != -1) {
+ // Get the parameters from the string.
+ int parFrom = idx;
+ int parTo = str.lastIndexOf (')');
+ // paramStr will contain the parameters without parentheses.
+ paramStr = str.substring (parFrom + 1, parTo).trim();
+ if (paramStr.indexOf ('(') != -1 || paramStr.indexOf (')') != -1)
+ //All params are numerical,so parenthesis nesting is forbidden here
+ throw new IllegalArgumentException ("Invalid parameter string: "
+ + paramStr);
+ }
+
+ if (paramStr.equals ("")) {
+ // No parameter is given to the constructor.
+ try {
+ return (Distribution) distClass.newInstance();
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalArgumentException
+ ("Default parameters not available");
+ }
+ catch (InstantiationException e) {
+ throw new IllegalArgumentException
+ ("Default parameters not available");
+ }
+ }
+
+ // Find the number of parameters and try to find a matching constructor.
+ // Within probdist, there are no constructors with the same
+ // number of arguments but with different types.
+ // This simplifies the constructor selection scheme.
+ StringTokenizer paramTok = new StringTokenizer (paramStr, ",");
+ int nparams = paramTok.countTokens();
+ Constructor[] cons = distClass.getConstructors();
+ Constructor distCons = null;
+ Class[] paramTypes = null;
+ // Find a public constructor with the correct number of parameters.
+ for (i = 0; i < cons.length; i++) {
+ if (Modifier.isPublic (cons[i].getModifiers()) &&
+ ((paramTypes = cons[i].getParameterTypes()).length == nparams)) {
+ distCons = cons[i];
+ break;
+ }
+ }
+ if (distCons == null)
+ throw new IllegalArgumentException ("Invalid parameter number");
+
+ // Create the parameters for the selected constructor.
+ Object[] instParams = new Object[nparams];
+ for (i = 0; i < nparams; i++) {
+ String par = paramTok.nextToken().trim();
+ try {
+ // We only need a limited set of parameter types here.
+ if (paramTypes[i] == int.class)
+ instParams[i] = new Integer (par);
+ else if (paramTypes[i] == long.class)
+ instParams[i] = new Long (par);
+ else if (paramTypes[i] == float.class) {
+ if (par.equalsIgnoreCase ("infinity") || par.equalsIgnoreCase
+ ("+infinity"))
+ instParams[i] = new Float (Float.POSITIVE_INFINITY);
+ else if (par.equalsIgnoreCase ("-infinity"))
+ instParams[i] = new Float (Float.NEGATIVE_INFINITY);
+ else
+ instParams[i] = new Float (par);
+ }
+ else if (paramTypes[i] == double.class) {
+ if (par.equalsIgnoreCase ("infinity") || par.equalsIgnoreCase
+ ("+infinity"))
+ instParams[i] = new Double (Double.POSITIVE_INFINITY);
+ else if (par.equalsIgnoreCase ("-infinity"))
+ instParams[i] = new Double (Double.NEGATIVE_INFINITY);
+ else
+ instParams[i] = new Double (par);
+ }
+ else
+ throw new IllegalArgumentException
+ ("Parameter " + (i+1) + " type " + paramTypes[i].getName() +
+ "not supported");
+ }
+ catch (NumberFormatException e) {
+ throw new IllegalArgumentException
+ ("Parameter " + (i+1) + " of type " +
+ paramTypes[i].getName()+" could not be converted from String");
+ }
+ }
+
+ // Try to instantiate the distribution class.
+ try {
+ return (Distribution) distCons.newInstance (instParams);
+ }
+ catch (IllegalAccessException e) {
+ return null;
+ }
+ catch (InstantiationException e) {
+ return null;
+ }
+ catch (InvocationTargetException e) {
+ return null;
+ }
+ }\end{hide}
+\end{code}
+\begin{hide}
+ Uses the Java Reflection API to construct a \class{ContinuousDistribution}{}
+ , \class{DiscreteDistributionInt} or \class{DiscreteDistribution}{} object,
+ as specified by the string \texttt{str}.
+ This method throws exceptions
+ if it cannot parse the given string and returns \texttt{null} if
+ the distribution object simply could not be created due to a Java-specific
+ instantiation problem.
+\begin{htmlonly}
+ \param{str}{distribution specification string}
+ \return{a distribution object or \texttt{null} if it could not be instantiated}
+ \exception{IllegalArgumentException}{if parsing problems occured when
+ reading \texttt{str}}
+\end{htmlonly}
+\end{hide}
+\begin{code}
+
+\begin{hide} @SuppressWarnings("unchecked")
+\end{hide} public static ContinuousDistribution getDistributionMLE
+ (String distName, double[] x, int n)\begin{hide} {
+
+ Class<?> distClass;
+ try
+ {
+ distClass = Class.forName ("umontreal.iro.lecuyer.probdist." + distName);
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ distClass = Class.forName (distName);
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ }
+ }
+
+ return getDistributionMLE ((Class<? extends ContinuousDistribution>)distClass, x, n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses the Java Reflection API to construct a \class{ContinuousDistribution}
+ object by estimating parameters of the distribution using the maximum likelihood
+ method based on the $n$ observations in table $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{distName}{the name of the distribution to instanciate}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+\begin{hide} @SuppressWarnings("unchecked")
+\end{hide} public static DiscreteDistributionInt getDistributionMLE
+ (String distName, int[] x, int n)\begin{hide} {
+
+ Class<?> distClass;
+ try
+ {
+ distClass = Class.forName ("umontreal.iro.lecuyer.probdist." + distName);
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ distClass = Class.forName (distName);
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new IllegalArgumentException ("Invalid distribution name: "
+ + distName);
+ }
+ }
+
+ return getDistributionMLE ((Class<? extends DiscreteDistributionInt>)distClass, x, n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses the Java Reflection API to construct a \class{DiscreteDistributionInt}
+ object by estimating parameters of the distribution using the maximum likelihood
+ method based on the $n$ observations in table $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{distName}{the name of the distribution to instanciate}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+\begin{hide} @SuppressWarnings("unchecked")
+\end{hide} public static <T extends ContinuousDistribution> T getDistributionMLE
+ (Class<T> distClass, double[] x, int n)\begin{hide} {
+ if (ContinuousDistribution.class.isAssignableFrom(distClass) == false)
+ throw new IllegalArgumentException
+ ("The given class is not a Probdist distribution class.");
+
+ Method m;
+ try
+ {
+ m = distClass.getMethod ("getInstanceFromMLE", double[].class, int.class);
+ }
+ catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (double[],int)");
+ }
+ if (!Modifier.isStatic (m.getModifiers()) ||
+ !distClass.isAssignableFrom (m.getReturnType()))
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (double[],int)");
+
+ try
+ {
+ return (T)m.invoke (null, x, n);
+ }
+ catch (IllegalAccessException e) {
+ return null;
+ }
+ catch (IllegalArgumentException e) {
+ return null;
+ }
+ catch (InvocationTargetException e) {
+ return null;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses the Java Reflection API to construct a \class{ContinuousDistribution}
+ object by estimating parameters of the distribution using the maximum likelihood
+ method based on the $n$ observations in table $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{distClass}{the class of the distribution to instanciate}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+\begin{hide} @SuppressWarnings("unchecked")
+\end{hide} public static <T extends DiscreteDistributionInt> T getDistributionMLE
+ (Class<T> distClass, int[] x, int n)\begin{hide} {
+ if (DiscreteDistributionInt.class.isAssignableFrom(distClass) == false)
+ throw new IllegalArgumentException
+ ("The given class is not a discrete distribution class over integers.");
+
+ Method m;
+ try
+ {
+ m = distClass.getMethod ("getInstanceFromMLE", int[].class, int.class);
+ }
+ catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (int[],int)");
+ }
+ if (!Modifier.isStatic (m.getModifiers()) ||
+ !distClass.isAssignableFrom (m.getReturnType()))
+ throw new IllegalArgumentException
+ ("The given class does not provide the static method getInstanceFromMLE (int[],int)");
+
+ try
+ {
+ return (T)m.invoke (null, x, n);
+ }
+ catch (IllegalAccessException e) {
+ return null;
+ }
+ catch (IllegalArgumentException e) {
+ return null;
+ }
+ catch (InvocationTargetException e) {
+ return null;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses the Java Reflection API to construct a \class{DiscreteDistributionInt}
+ object by estimating parameters of the distribution using the maximum likelihood
+ method based on the $n$ observations in table $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{distClass}{the class of the distribution to instanciate}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static ContinuousDistribution getContinuousDistribution (String str)\begin{hide} {
+ return (ContinuousDistribution)getDistribution (str);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses the Java Reflection API to construct a \class{ContinuousDistribution}
+ object by executing the code contained in the string \texttt{str}.
+ This code should be a valid invocation of the constructor of a
+ \class{ContinuousDistribution} object.
+ This method throws exceptions if it cannot parse the given string and
+ returns \texttt{null} if the distribution object could not be created due to
+ a Java-specific instantiation problem.
+\hpierre{So the user must always verify if \texttt{null} was returned?}
+% Same as calling \method{getDistribution}{}
+% and casting the result to \class{ContinuousDistribution}.
+\end{tabb}
+\begin{htmlonly}
+ \param{str}{string that contains a call to the constructor of a continuous
+ distribution}
+ \return{a continuous distribution object or \texttt{null} if it could not
+ be instantiated}
+ \exception{IllegalArgumentException}{if parsing problems occured
+ when reading \texttt{str}}
+ \exception{ClassCastException}{if the distribution string does not represent
+ a continuous distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static DiscreteDistribution getDiscreteDistribution (String str)\begin{hide} {
+ return (DiscreteDistribution)getDistribution (str);
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{getContinuousDistribution}{}, but for discrete distributions
+ over the real numbers.
+\end{tabb}
+\begin{htmlonly}
+ \param{str}{string that contains a call to the constructor of a discrete
+ distribution}
+ \return{a discrete distribution object, or \texttt{null} if it could not
+ be instantiated}
+ \exception{IllegalArgumentException}{if parsing problems occured when
+ reading \texttt{str}}
+ \exception{ClassCastException}{if the distribution string does not represent
+ a discrete distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static DiscreteDistributionInt getDiscreteDistributionInt (String str)\begin{hide} {
+ return (DiscreteDistributionInt)getDistribution (str);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{getContinuousDistribution}{}, but for discrete distributions
+ over the integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{str}{string that contains a call to the constructor of a discrete
+ distribution}
+ \return{a discrete distribution object, or \texttt{null} if it could not
+ be instantiated}
+ \exception{IllegalArgumentException}{if parsing problems occured when
+ reading \texttt{str}}
+ \exception{ClassCastException}{if the distribution string does not represent
+ a discrete distribution}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/probdist/EmpiricalDist.java b/source/umontreal/iro/lecuyer/probdist/EmpiricalDist.java
new file mode 100644
index 0000000..6c478b3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/EmpiricalDist.java
@@ -0,0 +1,326 @@
+
+
+/*
+ * Class: EmpiricalDist
+ * Description: empirical discrete distribution function
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.Arrays;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.BufferedReader;
+import umontreal.iro.lecuyer.util.Misc;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * Extends {@link DiscreteDistribution} to an <SPAN CLASS="textit">empirical</SPAN>
+ * distribution function,
+ * based on the observations
+ * <SPAN CLASS="MATH"><I>X</I><SUB>(1)</SUB>,..., <I>X</I><SUB>(n)</SUB></SPAN> (sorted by increasing order).
+ * The distribution is uniform over the <SPAN CLASS="MATH"><I>n</I></SPAN> observations, so the
+ * distribution function has a jump of <SPAN CLASS="MATH">1/<I>n</I></SPAN> at each of the <SPAN CLASS="MATH"><I>n</I></SPAN> observations.
+ *
+ */
+public class EmpiricalDist extends DiscreteDistribution {
+ private int n = 0;
+ private double sampleMean;
+ private double sampleVariance;
+ private double sampleStandardDeviation;
+
+
+
+ /**
+ * Constructs a new empirical distribution using
+ * all the observations stored in <TT>obs</TT>, and which are assumed
+ * to have been sorted in increasing numerical order. <A NAME="tex2html1"
+ * HREF="#foot62"><SUP><SPAN CLASS="arabic">1</SPAN></SUP></A> These observations are copied into an internal array.
+ *
+ */
+ public EmpiricalDist (double[] obs) {
+ if (obs.length <= 1)
+ throw new IllegalArgumentException
+ ("Two or more observations are needed");
+ nVal = n = obs.length;
+ sortedVal = new double[n];
+ System.arraycopy (obs, 0, sortedVal, 0, n);
+ init();
+ }
+
+
+ /**
+ * Constructs a new empirical distribution using
+ * the observations read from the reader <TT>in</TT>. This constructor
+ * will read the first <TT>double</TT> of each line in the stream.
+ * Any line that does not start with a <TT>+</TT>, <TT>-</TT>, or a decimal digit,
+ * is ignored. One must be careful about lines starting with a blank.
+ * This format is the same as in UNURAN. The observations read are assumed
+ * to have been sorted in increasing numerical order.
+ *
+ */
+ public EmpiricalDist (Reader in) throws IOException {
+ BufferedReader inb = new BufferedReader (in);
+ double[] data = new double[5];
+ n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Double.parseDouble (li);
+ if (n >= data.length) {
+ double[] newData = new double[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {}
+ }
+ }
+ sortedVal = new double[n];
+ System.arraycopy (data, 0, sortedVal, 0, n);
+ nVal = n;
+ init();
+ }
+
+
+ public double prob (int i) {
+ if (i >= 0 && i < n)
+ return 1.0 / n;
+ throw new IllegalStateException();
+ }
+
+ public double cdf (double x) {
+ if (x < sortedVal[0])
+ return 0;
+ if (x >= sortedVal[n-1])
+ return 1;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedVal[i] && x < sortedVal[i+1])
+ return (double)(i + 1)/n;
+ }
+ throw new IllegalStateException();
+ }
+
+ public double barF (double x) {
+ if (x <= sortedVal[0])
+ return 1;
+ if (x > sortedVal[n-1])
+ return 0;
+ for (int i = 0; i < (n-1); i++) {
+ if (x > sortedVal[i] && x <= sortedVal[i+1])
+ return ((double)n-1-i)/n;
+ }
+ throw new IllegalStateException();
+ }
+
+ public double inverseF (double u) {
+ if (u < 0 || u > 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u == 1.0)
+ return sortedVal[n-1];
+ int i = (int)Math.floor ((double)n * u);
+ return sortedVal[i];
+ }
+
+ private void init() {
+ // Arrays.sort (sortedVal);
+ double sum = 0.0;
+ for (int i = 0; i < sortedVal.length; i++) {
+ sum += sortedVal[i];
+ }
+ sampleMean = sum / n;
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ double coeff = (sortedVal[i] - sampleMean);
+ sum += coeff * coeff;
+ }
+ sampleVariance = sum / (n-1);
+ sampleStandardDeviation = Math.sqrt (sampleVariance);
+ supportA = sortedVal[0];
+ supportB = sortedVal[n-1];
+ xmin = 0;
+ xmax = n - 1;
+ }
+
+ public double getMean() {
+ return sampleMean;
+ }
+
+ public double getStandardDeviation() {
+ return sampleStandardDeviation;
+ }
+
+ public double getVariance() {
+ return sampleVariance;
+ }
+
+ /**
+ * Returns the median.
+ * Returns the
+ * <SPAN CLASS="MATH"><I>n</I>/2<SUP>th</SUP></SPAN> item of the sorted observations when the number
+ * of items is odd, and the mean of the
+ * <SPAN CLASS="MATH"><I>n</I>/2<SUP>th</SUP></SPAN> and the
+ *
+ * <SPAN CLASS="MATH">(<I>n</I>/2 + 1)<SUP>th</SUP></SPAN> items when the number of items is even.
+ *
+ */
+ public double getMedian () {
+ if ((n % 2) == 0)
+ return ((sortedVal[n / 2 - 1] + sortedVal[n / 2]) / 2.0);
+ else
+ return sortedVal[(n - 1) / 2];
+ }
+
+
+ /**
+ * Returns the median.
+ * Returns the
+ * <SPAN CLASS="MATH"><I>n</I>/2<SUP>th</SUP></SPAN> item of the array <TT>obs</TT> when the number
+ * of items is odd, and the mean of the
+ * <SPAN CLASS="MATH"><I>n</I>/2<SUP>th</SUP></SPAN> and the
+ *
+ * <SPAN CLASS="MATH">(<I>n</I>/2 + 1)<SUP>th</SUP></SPAN> items when the number of items is even.
+ * The array does not have to be sorted.
+ *
+ * @param obs the array of observations
+ *
+ * @param n the number of observations
+ *
+ * @return return the median of the observations
+ *
+ */
+ public static double getMedian (double obs[], int n) {
+ return Misc.getMedian (obs, n);
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>n</I></SPAN>, the number of observations.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>X</I><SUB>(i)</SUB></SPAN>, for
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ */
+ public double getObs (int i) {
+ return sortedVal[i];
+ }
+
+
+ /**
+ * Returns the sample mean of the observations.
+ *
+ */
+ public double getSampleMean() {
+ return sampleMean;
+ }
+
+
+ /**
+ * Returns the sample variance of the observations.
+ *
+ */
+ public double getSampleVariance() {
+ return sampleVariance;
+ }
+
+
+ /**
+ * Returns the sample standard deviation of the observations.
+ *
+ */
+ public double getSampleStandardDeviation() {
+ return sampleStandardDeviation;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="textit">interquartile range</SPAN> of the observations,
+ * defined as the difference between the third and first quartiles.
+ *
+ */
+ public double getInterQuartileRange() {
+ int j = n/2;
+ double lowerqrt=0, upperqrt=0;
+ if (j % 2 == 1) {
+ lowerqrt = sortedVal[(j+1)/2-1];
+ upperqrt = sortedVal[n-(j+1)/2];
+ }
+ else {
+ lowerqrt = 0.5 * (sortedVal[j/2-1] + sortedVal[j/2+1-1]);
+ upperqrt = 0.5 * (sortedVal[n-j/2] + sortedVal[n-j/2-1]);
+ }
+ double h =upperqrt - lowerqrt;
+ if (h < 0)
+ throw new IllegalStateException("Observations MUST be sorted");
+ return h;
+ }
+
+
+ /**
+ * Return a table containing parameters of the current distribution.
+ *
+ */
+ public double[] getParams () {
+ double[] retour = new double[n];
+ System.arraycopy (sortedVal, 0, retour, 0, n);
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString () {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + PrintfFormat.NEWLINE);
+ for(int i = 0; i<n; i++) {
+ formatter.format("%f%n", sortedVal[i]);
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/EmpiricalDist.tex b/source/umontreal/iro/lecuyer/probdist/EmpiricalDist.tex
new file mode 100644
index 0000000..ea8bc73
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/EmpiricalDist.tex
@@ -0,0 +1,325 @@
+\defmodule{EmpiricalDist}
+
+Extends \class{DiscreteDistribution} to an \emph{empirical}
+distribution function,
+based on the observations $X_{(1)},\dots,X_{(n)}$ (sorted by increasing order).
+The distribution is uniform over the $n$ observations, so the
+distribution function has a jump of $1/n$ at each of the $n$ observations.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: EmpiricalDist
+ * Description: empirical discrete distribution function
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.Arrays;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.BufferedReader;
+import umontreal.iro.lecuyer.util.Misc;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class EmpiricalDist extends DiscreteDistribution\begin{hide} {
+ private int n = 0;
+ private double sampleMean;
+ private double sampleVariance;
+ private double sampleStandardDeviation;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public EmpiricalDist (double[] obs)\begin{hide} {
+ if (obs.length <= 1)
+ throw new IllegalArgumentException
+ ("Two or more observations are needed");
+ nVal = n = obs.length;
+ sortedVal = new double[n];
+ System.arraycopy (obs, 0, sortedVal, 0, n);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new empirical distribution using
+ all the observations stored in \texttt{obs}, and which are assumed
+ to have been sorted in increasing numerical order. \footnote{The method
+ \texttt{java.util.Arrays.sort} may be used to sort the observations.}
+ These observations are copied into an internal array.
+%% \pierre {They are sorted where ???}
+%% \richard {They are sorted in init with Arrays.sort}
+\end{tabb}
+\begin{code}
+
+ public EmpiricalDist (Reader in) throws IOException\begin{hide} {
+ BufferedReader inb = new BufferedReader (in);
+ double[] data = new double[5];
+ n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Double.parseDouble (li);
+ if (n >= data.length) {
+ double[] newData = new double[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {}
+ }
+ }
+ sortedVal = new double[n];
+ System.arraycopy (data, 0, sortedVal, 0, n);
+ nVal = n;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new empirical distribution using
+ the observations read from the reader \texttt{in}. This constructor
+ will read the first \texttt{double} of each line in the stream.
+ Any line that does not start with a \texttt{+}, \texttt{-}, or a decimal digit,
+ is ignored. One must be careful about lines starting with a blank.
+ This format is the same as in UNURAN. The observations read are assumed
+ to have been sorted in increasing numerical order.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double prob (int i) {
+ if (i >= 0 && i < n)
+ return 1.0 / n;
+ throw new IllegalStateException();
+ }
+
+ public double cdf (double x) {
+ if (x < sortedVal[0])
+ return 0;
+ if (x >= sortedVal[n-1])
+ return 1;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedVal[i] && x < sortedVal[i+1])
+ return (double)(i + 1)/n;
+ }
+ throw new IllegalStateException();
+ }
+
+ public double barF (double x) {
+ if (x <= sortedVal[0])
+ return 1;
+ if (x > sortedVal[n-1])
+ return 0;
+ for (int i = 0; i < (n-1); i++) {
+ if (x > sortedVal[i] && x <= sortedVal[i+1])
+ return ((double)n-1-i)/n;
+ }
+ throw new IllegalStateException();
+ }
+
+ public double inverseF (double u) {
+ if (u < 0 || u > 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u == 1.0)
+ return sortedVal[n-1];
+ int i = (int)Math.floor ((double)n * u);
+ return sortedVal[i];
+ }
+
+ private void init() {
+ // Arrays.sort (sortedVal);
+ double sum = 0.0;
+ for (int i = 0; i < sortedVal.length; i++) {
+ sum += sortedVal[i];
+ }
+ sampleMean = sum / n;
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ double coeff = (sortedVal[i] - sampleMean);
+ sum += coeff * coeff;
+ }
+ sampleVariance = sum / (n-1);
+ sampleStandardDeviation = Math.sqrt (sampleVariance);
+ supportA = sortedVal[0];
+ supportB = sortedVal[n-1];
+ xmin = 0;
+ xmax = n - 1;
+ }
+
+ public double getMean() {
+ return sampleMean;
+ }
+
+ public double getStandardDeviation() {
+ return sampleStandardDeviation;
+ }
+
+ public double getVariance() {
+ return sampleVariance;
+ }\end{hide}
+
+ public double getMedian ()\begin{hide} {
+ if ((n % 2) == 0)
+ return ((sortedVal[n / 2 - 1] + sortedVal[n / 2]) / 2.0);
+ else
+ return sortedVal[(n - 1) / 2];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the median.
+ Returns the $n/2^{\mbox{th}}$ item of the sorted observations when the number
+ of items is odd, and the mean of the $n/2^{\mbox{th}}$ and the
+ $(n/2 + 1)^{\mbox{th}}$ items when the number of items is even.
+\end{tabb}
+\begin{code}
+
+ public static double getMedian (double obs[], int n)\begin{hide} {
+ return Misc.getMedian (obs, n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the median.
+ Returns the $n/2^{\mbox{th}}$ item of the array \texttt{obs} when the number
+ of items is odd, and the mean of the $n/2^{\mbox{th}}$ and the
+ $(n/2 + 1)^{\mbox{th}}$ items when the number of items is even.
+ The array does not have to be sorted.
+\end{tabb}
+\begin{htmlonly}
+ \param{obs}{the array of observations}
+ \param{n}{the number of observations}
+ \return{return the median of the observations}
+\end{htmlonly}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $n$, the number of observations.
+\end{tabb}
+\begin{code}
+
+ public double getObs (int i)\begin{hide} {
+ return sortedVal[i];
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $X_{(i)}$, for $i=0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{code}
+
+ public double getSampleMean()\begin{hide} {
+ return sampleMean;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the sample mean of the observations.
+\end{tabb}
+\begin{code}
+
+ public double getSampleVariance()\begin{hide} {
+ return sampleVariance;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the sample variance of the observations.
+\end{tabb}
+\begin{code}
+
+ public double getSampleStandardDeviation()\begin{hide} {
+ return sampleStandardDeviation;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the sample standard deviation of the observations.
+\end{tabb}
+\begin{code}
+
+ public double getInterQuartileRange()\begin{hide} {
+ int j = n/2;
+ double lowerqrt=0, upperqrt=0;
+ if (j % 2 == 1) {
+ lowerqrt = sortedVal[(j+1)/2-1];
+ upperqrt = sortedVal[n-(j+1)/2];
+ }
+ else {
+ lowerqrt = 0.5 * (sortedVal[j/2-1] + sortedVal[j/2+1-1]);
+ upperqrt = 0.5 * (sortedVal[n-j/2] + sortedVal[n-j/2-1]);
+ }
+ double h =upperqrt - lowerqrt;
+ if (h < 0)
+ throw new IllegalStateException("Observations MUST be sorted");
+ return h;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the \emph{interquartile range} of the observations,
+ defined as the difference between the third and first quartiles.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = new double[n];
+ System.arraycopy (sortedVal, 0, retour, 0, n);
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing parameters of the current distribution.
+\end{tabb}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + PrintfFormat.NEWLINE);
+ for(int i = 0; i<n; i++) {
+ formatter.format("%f%n", sortedVal[i]);
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/probdist/ErlangDist.java b/source/umontreal/iro/lecuyer/probdist/ErlangDist.java
new file mode 100644
index 0000000..7891f8d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ErlangDist.java
@@ -0,0 +1,229 @@
+
+
+/*
+ * Class: ErlangDist
+ * Description: Erlang distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+/**
+ * Extends the class {@link GammaDist} for the special case
+ * of the <EM>Erlang</EM> distribution with
+ * shape parameter <SPAN CLASS="MATH"><I>k</I> > 0</SPAN> and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * This distribution is a special case of the gamma distribution
+ * for which the shape parameter <SPAN CLASS="MATH"><I>k</I> = <I>α</I></SPAN> is an integer.
+ *
+ */
+public class ErlangDist extends GammaDist {
+
+
+ /**
+ * Constructs a <TT>ErlangDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>k</I></SPAN> = <TT>k</TT> and <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>.
+ *
+ */
+ public ErlangDist (int k) {
+ super (k);
+ }
+
+
+ /**
+ * Constructs a <TT>ErlangDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>k</I></SPAN> = <TT>k</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>.
+ *
+ */
+ public ErlangDist (int k, double lambda) {
+ super (k, lambda);
+ }
+
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (int k, double lambda, double x) {
+ return density ((double)k, lambda, x);
+ }
+
+
+ /**
+ * Computes the distribution function using
+ * the gamma distribution function.
+ *
+ */
+ public static double cdf (int k, double lambda, int d, double x) {
+ return cdf ((double)k, d, lambda*x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (int k, double lambda, int d, double x) {
+ return barF ((double)k, d, lambda*x);
+ }
+
+
+ /**
+ * Returns the inverse distribution function.
+ *
+ */
+ public static double inverseF (int k, double lambda, int d, double u) {
+ return inverseF ((double)k, lambda, d, u);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>k</I>, <I>λ</I>)</SPAN> of the Erlang distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>k</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(k)</SPAN>,
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double parameters[] = GammaDist.getMLE (x, n);
+ parameters[0] = Math.round (parameters[0]);
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of an Erlang distribution with parameters <SPAN CLASS="MATH"><I>k</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> estimated
+ * using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static ErlangDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new ErlangDist ((int) parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean,
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>k</I>/<I>λ</I></SPAN>,
+ * of the Erlang distribution with parameters <SPAN CLASS="MATH"><I>k</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the Erlang distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>k</I>/<I>λ</I></SPAN>
+ *
+ */
+ public static double getMean (int k, double lambda) {
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return (k / lambda);
+ }
+
+
+ /**
+ * Computes and returns the variance,
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>k</I>/<I>λ</I><SUP>2</SUP></SPAN>,
+ * of the Erlang distribution with parameters <SPAN CLASS="MATH"><I>k</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the Erlang distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>k</I>/<I>λ</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (int k, double lambda) {
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (k / (lambda * lambda));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the Erlang
+ * distribution with parameters <SPAN CLASS="MATH"><I>k</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the Erlang distribution
+ *
+ */
+ public static double getStandardDeviation (int k, double lambda) {
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt (k) / lambda);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>k</I></SPAN> for this object.
+ *
+ */
+ public int getK() {
+ return (int) getAlpha();
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>k</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of the distribution for this
+ * object. Non-static methods are computed with a rough target of
+ * <TT>d</TT> decimal digits of precision.
+ *
+ */
+ public void setParams (int k, double lambda, int d) {
+ super.setParams (k, lambda, d);
+ }
+
+
+ /**
+ * Return a table containing parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>k</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ return super.getParams();
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : k = " + (int)super.getAlpha() + ", lambda = " + super.getLambda();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ErlangDist.tex b/source/umontreal/iro/lecuyer/probdist/ErlangDist.tex
new file mode 100644
index 0000000..aa32aac
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ErlangDist.tex
@@ -0,0 +1,231 @@
+\defmodule {ErlangDist}
+
+Extends the class \class{GammaDist} for the special case
+of the {\em Erlang\/} distribution with
+shape parameter $k > 0$ and scale parameter $\lambda > 0$.
+This distribution is a special case of the gamma distribution
+for which the shape parameter $k=\alpha$ is an integer.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ErlangDist
+ * Description: Erlang distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+public class ErlangDist extends GammaDist\begin{hide} {
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public ErlangDist (int k)\begin{hide} {
+ super (k);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ErlangDist} object with parameters
+ $k$ = \texttt{k} and $\lambda=1$.
+ \end{tabb}
+\begin{code}
+
+ public ErlangDist (int k, double lambda)\begin{hide} {
+ super (k, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ErlangDist} object with parameters
+ $k$ = \texttt{k} and $\lambda$ = \texttt{lambda}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static double density (int k, double lambda, double x)\begin{hide} {
+ return density ((double)k, lambda, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int k, double lambda, int d, double x)\begin{hide} {
+ return cdf ((double)k, d, lambda*x);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the distribution function using
+ the gamma distribution function.
+\end{tabb}
+\begin{code}
+
+ public static double barF (int k, double lambda, int d, double x)\begin{hide} {
+ return barF ((double)k, d, lambda*x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int k, double lambda, int d, double u)\begin{hide} {
+ return inverseF ((double)k, lambda, d, u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inverse distribution function.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = GammaDist.getMLE (x, n);
+ parameters[0] = Math.round (parameters[0]);
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(k,\lambda)$ of the Erlang distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$k$, $\lambda$].
+ \begin{detailed}
+ The equations of the maximum likelihood are the same as for
+ the gamma distribution. The $k$ parameter is the rounded value of the
+ $\alpha$ parameter of the gamma distribution, and the $\lambda$ parameter
+ is equal to the $\beta$ parameter of the gamma distribution.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{k}$, $\hat{\lambda}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static ErlangDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new ErlangDist ((int) parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of an Erlang distribution with parameters $k$ and
+ $\lambda$ estimated
+ using the maximum likelihood method based on the $n$ observations $x[i]$,
+ $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (int k, double lambda)\begin{hide} {
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return (k / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean, $E[X] = k/\lambda$,
+ of the Erlang distribution with parameters $k$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Erlang distribution $E[X] = k / \lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int k, double lambda)\begin{hide} {
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (k / (lambda * lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance, $\mbox{Var}[X] = k/\lambda^2$,
+ of the Erlang distribution with parameters $k$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Erlang distribution $\mbox{Var}[X] = k / \lambda^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int k, double lambda)\begin{hide} {
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt (k) / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the Erlang
+ distribution with parameters $k$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Erlang distribution}
+\end{htmlonly}
+\begin{code}
+
+ public int getK()\begin{hide} {
+ return (int) getAlpha();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $k$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (int k, double lambda, int d)\begin{hide} {
+ super.setParams (k, lambda, d);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $k$ and $\lambda$ of the distribution for this
+ object. Non-static methods are computed with a rough target of
+ \texttt{d} decimal digits of precision.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ return super.getParams();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing parameters of the current distribution.
+ This table is put in regular order: [$k$, $\lambda$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : k = " + (int)super.getAlpha() + ", lambda = " + super.getLambda();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/ExponentialDist.java b/source/umontreal/iro/lecuyer/probdist/ExponentialDist.java
new file mode 100644
index 0000000..81d25f0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ExponentialDist.java
@@ -0,0 +1,303 @@
+
+
+/*
+ * Class: ExponentialDist
+ * Description: exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>exponential</EM> distribution
+ * with mean <SPAN CLASS="MATH">1/<I>λ</I></SPAN> where
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>λe</I><SUP>-<I>λ</I>x</SUP> for <I>x</I> >= 0,
+ * </DIV><P></P>
+ * its distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - <I>e</I><SUP>-<I>λ</I>x</SUP>, for <I>x</I> >= 0,
+ * </DIV><P></P>
+ * and its inverse distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = - ln(1 - <I>u</I>)/<I>λ</I>, for 0 < <I>u</I> < 1.
+ * </DIV><P></P>
+ *
+ */
+public class ExponentialDist extends ContinuousDistribution {
+ private double lambda;
+
+
+
+ /**
+ * Constructs an <TT>ExponentialDist</TT> object with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> = 1.
+ *
+ */
+ public ExponentialDist() {
+ setLambda (1.0);
+ }
+
+
+ /**
+ * Constructs an <TT>ExponentialDist</TT> object with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> =
+ * <TT>lambda</TT>.
+ *
+ */
+ public ExponentialDist (double lambda) {
+ setLambda (lambda);
+ }
+
+
+ public double density (double x) {
+ return density (lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (lambda, u);
+ }
+
+ public double getMean() {
+ return ExponentialDist.getMean (lambda);
+ }
+
+ public double getVariance() {
+ return ExponentialDist.getVariance (lambda);
+ }
+
+ public double getStandardDeviation() {
+ return ExponentialDist.getStandardDeviation (lambda);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return x < 0 ? 0 : lambda*Math.exp (-lambda*x);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ double y = lambda * x;
+ if (y >= XBIG)
+ return 1.0;
+ return -Math.expm1 (-y);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (lambda*x >= XBIGM)
+ return 0.0;
+ return Math.exp (-lambda*x);
+ }
+
+
+ /**
+ * Computes the inverse distribution function.
+ *
+ */
+ public static double inverseF (double lambda, double u) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+ return -Math.log1p (-u)/lambda;
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of the exponential distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimate is returned in a one-element
+ * array, as element 0.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n)
+ {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ double sum = 0.0;
+ parameters = new double[1];
+ for (int i = 0; i < n; i++)
+ sum+= x[i];
+ parameters[0] = (double) n / sum;
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of an exponential distribution with parameter
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> estimated using
+ * the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static ExponentialDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new ExponentialDist (parameters[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean,
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = 1/<I>λ</I></SPAN>,
+ * of the exponential distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the exponential distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = 1/<I>λ</I></SPAN>
+ *
+ */
+ public static double getMean (double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (1 / lambda);
+ }
+
+
+ /**
+ * Computes and returns the variance,
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1/<I>λ</I><SUP>2</SUP></SPAN>,
+ * of the exponential distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the Exponential distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1/<I>λ</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (1 / (lambda * lambda));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the
+ * exponential distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the exponential distribution
+ *
+ */
+ public static double getStandardDeviation (double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (1 / lambda);
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+
+ /**
+ * Sets the value of <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ */
+ public void setLambda (double lambda) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lambda;
+ supportA = 0.0;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ExponentialDist.tex b/source/umontreal/iro/lecuyer/probdist/ExponentialDist.tex
new file mode 100644
index 0000000..9fab894
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ExponentialDist.tex
@@ -0,0 +1,308 @@
+\defmodule {ExponentialDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em exponential\/} distribution \cite[page 494]{tJOH95a}
+with mean $1/\lambda$ where $\lambda > 0$.
+Its density is
+\eq f(x) = \lambda e^{-\lambda x}
+ \qquad \mbox{for }x\ge 0, \eqlabel{eq:fexpon}
+\endeq
+its distribution function is
+\eq
+ F(x) = 1 - e^{-\lambda x},\qquad \mbox{for }x \ge 0,
+ \eqlabel{eq:Fexpon}
+\endeq
+and its inverse distribution function is
+\[
+ F^{-1}(u) = -\ln (1-u)/\lambda, \qquad \mbox{for } 0 < u < 1.
+\]
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ExponentialDist
+ * Description: exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;\end{hide}
+
+public class ExponentialDist extends ContinuousDistribution\begin{hide} {
+ private double lambda;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public ExponentialDist()\begin{hide} {
+ setLambda (1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs an \texttt{ExponentialDist} object with parameter $\lambda$ = 1.
+ \end{tabb}
+\begin{code}
+
+ public ExponentialDist (double lambda)\begin{hide} {
+ setLambda (lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs an \texttt{ExponentialDist} object with parameter $\lambda$ =
+ \texttt{lambda}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (lambda, u);
+ }
+
+ public double getMean() {
+ return ExponentialDist.getMean (lambda);
+ }
+
+ public double getVariance() {
+ return ExponentialDist.getVariance (lambda);
+ }
+
+ public double getStandardDeviation() {
+ return ExponentialDist.getStandardDeviation (lambda);
+ }\end{hide}
+
+ public static double density (double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ return x < 0 ? 0 : lambda*Math.exp (-lambda*x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ double y = lambda * x;
+ if (y >= XBIG)
+ return 1.0;
+ return -Math.expm1 (-y);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (lambda*x >= XBIGM)
+ return 0.0;
+ return Math.exp (-lambda*x);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double lambda, double u)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+ return -Math.log1p (-u)/lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide}
+ {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ double sum = 0.0;
+ parameters = new double[1];
+ for (int i = 0; i < n; i++)
+ sum+= x[i];
+ parameters[0] = (double) n / sum;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $\lambda$ of the exponential distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimate is returned in a one-element
+ array, as element 0.
+ \begin{detailed}
+ The equation of the maximum likelihood is defined as
+ $\hat{\lambda} = 1/\bar{x}_n$, where $\bar x_n$ is the average of
+ $x[0],\dots,x[n-1]$ (see \cite[page 506]{tJOH95a}).
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameter [$\hat{\lambda}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static ExponentialDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new ExponentialDist (parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of an exponential distribution with parameter
+ $\lambda$ estimated using
+ the maximum likelihood method based on the $n$ observations $x[i]$,
+ $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (1 / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean, $E[X] = 1/\lambda$,
+ of the exponential distribution with parameter $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the exponential distribution $E[X] = 1 / \lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (1 / (lambda * lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance, $\mbox{Var}[X] = 1/\lambda^2$,
+ of the exponential distribution with parameter $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Exponential distribution $\mbox{Var}[X] = 1 / \lambda^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (1 / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the
+ exponential distribution with parameter $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the exponential distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\lambda$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public void setLambda (double lambda)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lambda;
+ supportA = 0.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of $\lambda$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : lambda = " + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\end{hide}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/probdist/ExponentialDistFromMean.java b/source/umontreal/iro/lecuyer/probdist/ExponentialDistFromMean.java
new file mode 100644
index 0000000..4f202aa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ExponentialDistFromMean.java
@@ -0,0 +1,60 @@
+
+
+/*
+ * Class: ExponentialDistFromMean
+ * Description: exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+/**
+ * Extends the {@link ExponentialDist} class with a constructor accepting as
+ * argument the mean <SPAN CLASS="MATH">1/<I>λ</I></SPAN> instead of the rate <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+public class ExponentialDistFromMean extends ExponentialDist {
+
+
+
+ /**
+ * Constructs a new exponential distribution with mean <TT>mean</TT>.
+ *
+ * @param mean the required mean.
+ *
+ */
+ public ExponentialDistFromMean (double mean) {
+ super (1.0 / mean);
+ }
+
+
+ /**
+ * Calls
+ * {@link umontreal.iro.lecuyer.probdist.ExponentialDist#setLambda(double) setLambda}
+ * with argument <TT>1/mean</TT> to change the mean of this distribution.
+ *
+ * @param mean the new mean.
+ *
+ */
+ public void setMean (double mean) {
+ setLambda (1.0 / mean);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ExponentialDistFromMean.tex b/source/umontreal/iro/lecuyer/probdist/ExponentialDistFromMean.tex
new file mode 100644
index 0000000..b8d6e49
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ExponentialDistFromMean.tex
@@ -0,0 +1,72 @@
+\defmodule{ExponentialDistFromMean}
+
+Extends the \class{ExponentialDist} class with a constructor accepting as
+argument the mean $1/\lambda$ instead of the rate $\lambda$.
+
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ExponentialDistFromMean
+ * Description: exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+public class ExponentialDistFromMean extends ExponentialDist\begin{hide} {
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public ExponentialDistFromMean (double mean)\begin{hide} {
+ super (1.0 / mean);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new exponential distribution with mean \texttt{mean}.
+\end{tabb}
+\begin{htmlonly}
+ \param{mean}{the required mean.}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void setMean (double mean)\begin{hide} {
+ setLambda (1.0 / mean);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Calls
+\externalmethod{umontreal.iro.lecuyer.probdist}{ExponentialDist}{setLambda}{double}
+ with argument \texttt{1/mean} to change the mean of this distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{mean}{the new mean.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/probdist/ExtremeValueDist.java b/source/umontreal/iro/lecuyer/probdist/ExtremeValueDist.java
new file mode 100644
index 0000000..f738320
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ExtremeValueDist.java
@@ -0,0 +1,391 @@
+
+
+/*
+ * Class: ExtremeValueDist
+ * Description: Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+ at Deprecated
+/**
+ * <SPAN CLASS="textbf">This class has been replaced by {@link GumbelDist}</SPAN>.
+ *
+ * <P>
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>extreme value</EM> (or <SPAN CLASS="textit">Gumbel</SPAN>) distribution, with location parameter
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * It has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>λe</I><SUP>-<I>λ</I>(x-<I>α</I>)</SUP><I>e</I><SUP>-e<SUP>-<I>λ</I>(x-<I>α</I>)</SUP></SUP>, for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>e</I><SUP>-e<SUP>-<I>λ</I>(x-<I>α</I>)</SUP></SUP> for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * and inverse distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = - ln(- ln(<I>u</I>))/<I>λ</I> + <I>α</I>, for 0 <= <I>u</I> <= 1.
+ * </DIV><P></P>
+ *
+ */
+public class ExtremeValueDist extends ContinuousDistribution {
+ private double alpha;
+ private double lambda;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double mean;
+ protected double[] x;
+
+ public Function (double[] x, int n, double mean) {
+ this.n = n;
+ this.mean = mean;
+ this.x = new double[n];
+
+ System.arraycopy(x, 0, this.x, 0, n);
+ }
+
+ public double evaluate (double lambda) {
+ if (lambda <= 0.0) return 1.0e200;
+ double exp = 0.0;
+ double sumXiExp = 0.0;
+ double sumExp = 0.0;
+
+ for (int i = 0; i < n; i++)
+ {
+ exp = Math.exp (-x[i] * lambda);
+ sumExp += exp;
+ sumXiExp += x[i] * exp;
+ }
+
+ return ((mean - 1.0 / lambda) * sumExp - sumXiExp);
+ }
+ }
+
+
+
+ /**
+ * <SPAN CLASS="textbf">THIS CLASS HAS BEEN REPLACED BY {@link GumbelDist}</SPAN>.
+ * Constructs a <TT>ExtremeValueDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = 0 and <SPAN CLASS="MATH"><I>λ</I></SPAN> = 1.
+ *
+ */
+ public ExtremeValueDist() {
+ setParams (0.0, 1.0);
+ }
+
+
+ /**
+ * <SPAN CLASS="textbf">THIS CLASS HAS BEEN REPLACED BY {@link GumbelDist}</SPAN>.
+ * Constructs a <TT>ExtremeValueDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>.
+ *
+ */
+ public ExtremeValueDist (double alpha, double lambda) {
+ setParams (alpha, lambda);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, lambda, u);
+ }
+
+ public double getMean() {
+ return ExtremeValueDist.getMean (alpha, lambda);
+ }
+
+ public double getVariance() {
+ return ExtremeValueDist.getVariance (alpha, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return ExtremeValueDist.getStandardDeviation (alpha, lambda);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (double alpha, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ final double z = lambda*(x - alpha);
+ if (z <= -10.0)
+ return 0.0;
+ double t = Math.exp (-z);
+ return lambda * t * Math.exp (-t);
+ }
+
+
+ /**
+ * <SPAN CLASS="textbf">THIS CLASS HAS BEEN REPLACED BY {@link GumbelDist}</SPAN>.
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double alpha, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ final double z = lambda*(x - alpha);
+ if (z <= -10.0)
+ return 0.0;
+ if (z >= XBIG)
+ return 1.0;
+ return Math.exp (-Math.exp (-z));
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (double alpha, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ final double z = lambda*(x - alpha);
+ if (z <= -10.0)
+ return 1.0;
+ if (z >= XBIGM)
+ return 0.0;
+ return -Math.expm1 (-Math.exp (-z));
+ }
+
+
+ /**
+ * Computes the inverse distribution function.
+ *
+ */
+ public static double inverseF (double alpha, double lambda, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+
+ return -Math.log (-Math.log (u))/lambda+alpha;
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>λ</I>)</SPAN> of the extreme value distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[2];
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+ double mean = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double variance = sum / ((double) n - 1.0);
+
+ double lambda0 = Math.PI / Math.sqrt (6 * variance);
+
+ Function f = new Function (x, n, mean);
+
+ double a;
+ if ((a = lambda0 - 10.0) < 0)
+ a = 1e-15;
+ parameters[1] = RootFinder.brentDekker (a, lambda0 + 10.0, f, 1e-7);
+
+ double sumExp = 0.0;
+ for (int i = 0; i < n; i++)
+ sumExp += Math.exp (- x[i] * parameters[1]);
+ parameters[0] = - Math.log (sumExp / (double) n) / parameters[1];
+
+ return parameters;
+ }
+
+
+ /**
+ * Same as {@link #getMLE getMLE}.
+ *
+ */
+ @Deprecated
+ public static double[] getMaximumLikelihoodEstimate (double[] x, int n) {
+ return getMLE(x, n);
+ }
+
+
+ /**
+ * Creates a new instance of an extreme value distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static ExtremeValueDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new ExtremeValueDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean,
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I> + <I>γ</I>/<I>λ</I></SPAN>,
+ * of the extreme value distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>,
+ * where
+ * <SPAN CLASS="MATH"><I>γ</I> = 0.5772156649</SPAN> is the Euler-Mascheroni constant.
+ *
+ * @return the mean of the Extreme Value distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I> + <I>γ</I>/<I>λ</I></SPAN>
+ *
+ */
+ public static double getMean (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (alpha + Num.EULER / lambda);
+ }
+
+
+ /**
+ * Computes and returns the variance,
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>π</I><SUP>2</SUP>/(6<I>λ</I><SUP>2</SUP>)</SPAN>,
+ * of the extreme value distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the extreme value distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1/6<I>π</I><SUP>2</SUP>1/<I>λ</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return ((1.0 / 6.0 * Math.PI * Math.PI) * (1.0 / (lambda * lambda)));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the extreme value distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the extreme value distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt(1.0 / 6.0) * Math.PI / lambda);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double alpha, double lambda) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.alpha = alpha;
+ this.lambda = lambda;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ExtremeValueDist.tex b/source/umontreal/iro/lecuyer/probdist/ExtremeValueDist.tex
new file mode 100644
index 0000000..f593413
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ExtremeValueDist.tex
@@ -0,0 +1,396 @@
+\defmodule {ExtremeValueDist}
+
+\textbf{This class has been replaced by \class{GumbelDist}}.
+
+Extends the class \class{ContinuousDistribution} for
+the {\em extreme value\/} (or \emph{Gumbel}) distribution
+\cite[page 2]{tJOH95b}, with location parameter
+$\alpha$ and scale parameter $\lambda > 0$.
+It has density
+\eq f (x) = \lambda e^{-\lambda(x-\alpha)} e^{-e^{-\lambda(x-\alpha)}},
+ \qquad \qquad \mbox{for } -\infty < x < \infty\latex{,}\html{.}
+ \eqlabel{eq:fextremevalue}
+\endeq
+distribution function
+\eq
+ F(x) = e^{-e^{-\lambda (x - \alpha)}}
+\qquad \qquad \mbox{for } -\infty < x < \infty, \eqlabel{eq:Fextreme}
+\endeq
+and inverse distribution function
+\eq
+ F^{-1}(u) = -\ln (-\ln (u))/\lambda + \alpha,
+ \qquad\mbox{for } 0 \le u \le 1.
+\endeq
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ExtremeValueDist
+ * Description: Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+ at Deprecated
+public class ExtremeValueDist extends ContinuousDistribution\begin{hide} {
+ private double alpha;
+ private double lambda;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double mean;
+ protected double[] x;
+
+ public Function (double[] x, int n, double mean) {
+ this.n = n;
+ this.mean = mean;
+ this.x = new double[n];
+
+ System.arraycopy(x, 0, this.x, 0, n);
+ }
+
+ public double evaluate (double lambda) {
+ if (lambda <= 0.0) return 1.0e200;
+ double exp = 0.0;
+ double sumXiExp = 0.0;
+ double sumExp = 0.0;
+
+ for (int i = 0; i < n; i++)
+ {
+ exp = Math.exp (-x[i] * lambda);
+ sumExp += exp;
+ sumXiExp += x[i] * exp;
+ }
+
+ return ((mean - 1.0 / lambda) * sumExp - sumXiExp);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public ExtremeValueDist()\begin{hide} {
+ setParams (0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}\textbf{THIS CLASS HAS BEEN REPLACED BY \class{GumbelDist}}.
+ Constructs a \texttt{ExtremeValueDist} object with parameters
+ $\alpha$ = 0 and $\lambda$ = 1.
+ \end{tabb}
+\begin{code}
+
+ public ExtremeValueDist (double alpha, double lambda)\begin{hide} {
+ setParams (alpha, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb}\textbf{THIS CLASS HAS BEEN REPLACED BY \class{GumbelDist}}.
+ Constructs a \texttt{ExtremeValueDist} object with parameters
+ $\alpha$ = \texttt{alpha} and $\lambda$ = \texttt{lambda}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (alpha, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, lambda, u);
+ }
+
+ public double getMean() {
+ return ExtremeValueDist.getMean (alpha, lambda);
+ }
+
+ public double getVariance() {
+ return ExtremeValueDist.getVariance (alpha, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return ExtremeValueDist.getStandardDeviation (alpha, lambda);
+ }\end{hide}
+
+ public static double density (double alpha, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ final double z = lambda*(x - alpha);
+ if (z <= -10.0)
+ return 0.0;
+ double t = Math.exp (-z);
+ return lambda * t * Math.exp (-t);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ final double z = lambda*(x - alpha);
+ if (z <= -10.0)
+ return 0.0;
+ if (z >= XBIG)
+ return 1.0;
+ return Math.exp (-Math.exp (-z));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ \textbf{THIS CLASS HAS BEEN REPLACED BY \class{GumbelDist}}.
+ Computes the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ final double z = lambda*(x - alpha);
+ if (z <= -10.0)
+ return 1.0;
+ if (z >= XBIGM)
+ return 0.0;
+ return -Math.expm1 (-Math.exp (-z));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double lambda, double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+
+ return -Math.log (-Math.log (u))/lambda+alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[2];
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+ double mean = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double variance = sum / ((double) n - 1.0);
+
+ double lambda0 = Math.PI / Math.sqrt (6 * variance);
+
+ Function f = new Function (x, n, mean);
+
+ double a;
+ if ((a = lambda0 - 10.0) < 0)
+ a = 1e-15;
+ parameters[1] = RootFinder.brentDekker (a, lambda0 + 10.0, f, 1e-7);
+
+ double sumExp = 0.0;
+ for (int i = 0; i < n; i++)
+ sumExp += Math.exp (- x[i] * parameters[1]);
+ parameters[0] = - Math.log (sumExp / (double) n) / parameters[1];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\lambda)$ of the extreme value distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\lambda$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\alpha, \hat\lambda)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat{\lambda} & = & \bar{x}_n - \frac{\sum_{i=1}^{n} x_i\,
+ e^{- \hat{\lambda} x_i}}{\sum_{i=1}^{n} e^{-\hat{\lambda} x_i}}\\[0.5em]
+ \hat{\alpha} & = & - \frac{1}{\hat{\lambda}} \ln \left( \frac{1}{n}
+ \sum_{i=1}^{n} e^{-\hat{\lambda} x_i} \right),
+ \end{eqnarray*}
+ where $\bar x_n$ is the average of $x[0],\dots,x[n-1]$ \cite[page 89]{tEVA00a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}$, $\hat{\lambda}$]}
+\end{htmlonly}
+\begin{code}
+
+ @Deprecated
+ public static double[] getMaximumLikelihoodEstimate (double[] x, int n)\begin{hide} {
+ return getMLE(x, n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{getMLE}{}.
+\end{tabb}
+\begin{code}
+
+ public static ExtremeValueDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new ExtremeValueDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of an extreme value distribution with parameters $\alpha$ and
+ $\lambda$ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (alpha + Num.EULER / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean, $E[X] = \alpha + \gamma/\lambda$,
+ of the extreme value distribution with parameters $\alpha$ and $\lambda$,
+ where $\gamma = 0.5772156649$ is the Euler-Mascheroni constant.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Extreme Value distribution $E[X] = \alpha + \gamma / \lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return ((1.0 / 6.0 * Math.PI * Math.PI) * (1.0 / (lambda * lambda)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance, $\mbox{Var}[X] =
+ \pi^2/(6\lambda^2)$,
+ of the extreme value distribution with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the extreme value distribution $\mbox{Var}[X] = 1/6 \pi^2 1/\lambda^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt(1.0 / 6.0) * Math.PI / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the extreme value distribution with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the extreme value distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $\lambda$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.alpha = alpha;
+ this.lambda = lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the parameters $\alpha$ and $\lambda$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\lambda$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/FatigueLifeDist.java b/source/umontreal/iro/lecuyer/probdist/FatigueLifeDist.java
new file mode 100644
index 0000000..b051c30
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FatigueLifeDist.java
@@ -0,0 +1,397 @@
+
+
+/*
+ * Class: FatigueLifeDist
+ * Description: fatigue life distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>fatigue life</EM> distribution with location
+ * parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>, scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN> and shape
+ * parameter <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = [(((<I>x</I> - <I>μ</I>)/<I>β</I>)<SUP>1/2</SUP> + (<I>β</I>/(<I>x</I> - <I>μ</I>))<SUP>1/2</SUP>)/(2<I>γ</I>(<I>x</I> - <I>μ</I>))]<I>φ</I>((((<I>x</I> - <I>μ</I>)/<I>β</I>)<SUP>1/2</SUP> - (<I>β</I>/(<I>x</I> - <I>μ</I>))<SUP>1/2</SUP>)/<I>γ</I>), for <I>x</I> > <I>μ</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>φ</I></SPAN> is the probability density of the standard normal distribution.
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>Φ</I>((((<I>x</I> - <I>μ</I>)/<I>β</I>)<SUP>1/2</SUP> - (<I>β</I>/(<I>x</I> - <I>μ</I>))<SUP>1/2</SUP>)/<I>γ</I>), for <I>x</I> > <I>μ</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard normal distribution function.
+ * Restrictions: <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>,
+ * <SPAN CLASS="MATH"><I>γ</I> > 0</SPAN>.
+ *
+ * <P>
+ * The non-static versions of the methods <TT>cdf</TT>, <TT>barF</TT>,
+ * and <TT>inverseF</TT> call the static version of the same name.
+ *
+ */
+public class FatigueLifeDist extends ContinuousDistribution {
+ protected double mu;
+ protected double beta;
+ protected double gamma;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+ private double mu;
+
+ public Optim (double[] x, int n, double min)
+ {
+ this.n = n;
+ this.mu = min;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ double sum = 0.0;
+
+ if ((p[1] <= 0.0) || (p[2] <= 0.0))
+ return 1e200;
+
+ for (int i = 0; i < n; i++)
+ sum -= Math.log (density (mu, p[1], p[2], xi[i]));
+
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+
+
+
+ /**
+ * Constructs a fatigue life distribution with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public FatigueLifeDist (double mu, double beta, double gamma) {
+ setParams (mu, beta, gamma);
+ }
+
+
+ public double density (double x) {
+ return FatigueLifeDist.density (mu, beta, gamma, x);
+ }
+
+ public double cdf (double x) {
+ return FatigueLifeDist.cdf (mu, beta, gamma, x);
+ }
+
+ public double barF (double x) {
+ return FatigueLifeDist.barF (mu, beta, gamma, x);
+ }
+
+ public double inverseF (double u) {
+ return FatigueLifeDist.inverseF (mu, beta, gamma, u);
+ }
+
+ public double getMean() {
+ return FatigueLifeDist.getMean (mu, beta, gamma);
+ }
+
+ public double getVariance() {
+ return FatigueLifeDist.getVariance (mu, beta, gamma);
+ }
+
+ public double getStandardDeviation() {
+ return FatigueLifeDist.getStandardDeviation (mu, beta, gamma);
+ }
+
+ /**
+ * Computes the density for the
+ * fatigue life distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public static double density (double mu, double beta, double gamma,
+ double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (x <= mu)
+ return 0.0;
+ double y;
+ y = (Math.sqrt ((x - mu) / beta) - Math.sqrt (beta / (x - mu))) / gamma;
+
+ return (((Math.sqrt ((x - mu) / beta) + Math.sqrt (beta / (x - mu))) /
+ (2 * gamma * (x - mu))) * NormalDist.density (0.0, 1.0, y));
+ }
+
+
+ /**
+ * Computes the fatigue life distribution
+ * function with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public static double cdf (double mu, double beta, double gamma, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (x <= mu)
+ return 0.0;
+
+ return NormalDist.cdf01 ((Math.sqrt ((x - mu) / beta) - Math.sqrt (beta / (x - mu))) / gamma);
+ }
+
+
+ /**
+ * Computes the complementary distribution function of the
+ * fatigue life distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public static double barF (double mu, double beta, double gamma,
+ double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (x <= mu)
+ return 1.0;
+
+ return NormalDist.barF01 ((Math.sqrt ((x - mu) / beta) - Math.sqrt (beta / (x - mu))) / gamma);
+ }
+
+
+ /**
+ * Computes the inverse of the fatigue life distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public static double inverseF (double mu, double beta, double gamma,
+ double u) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) // if u == 0, in fact
+ return mu;
+ if (u >= 1.0) // if u == 1, in fact
+ return Double.POSITIVE_INFINITY;
+
+ double w = gamma * NormalDist.inverseF01 (u);
+ double sqrtZ = 0.5 * (w + Math.sqrt (w * w + 4.0));
+
+ return (mu + sqrtZ * sqrtZ * beta);
+ }
+
+
+ /**
+ * Estimates the parameters (<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>γ</I></SPAN>) of the fatigue life
+ * distribution using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a three-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>γ</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param mu the location parameter
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(β)</SPAN>,
+ * <SPAN CLASS="MATH">hat(γ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n, double mu) {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double[] parameters = new double[3];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] h = new double[3][3];
+ double[] udiag = new double[3];
+
+ Optim system = new Optim (x, n, mu);
+
+ double mean = 0.0;
+ for (int i = 0; i < n; i++)
+ mean += x[i];
+ mean /= (double) n;
+
+ double var = 0.0;
+ for (int i = 0; i < n; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) n;
+
+ double loc2 = (mean - mu) * (mean - mu);
+ double a = 0.25 * (var - 5 * loc2);
+ double b = (var - loc2);
+ double c = var;
+
+ double delta = b * b - 4.0 * a * c;
+
+ double gamma2 = (- b - Math.sqrt (delta)) / (2.0 * a);
+ param[2] = Math.sqrt (gamma2);
+ param[1] = (mean - mu) / (1.0 + gamma2 / 2.0);
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, h, udiag);
+
+ for (int i = 1; i < 3; i++)
+ parameters[i] = xpls[i];
+ parameters[0] = mu;
+ return parameters;
+ }
+
+
+ /**
+ * Computes and returns the mean
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I> + <I>β</I>(1 + <I>γ</I><SUP>2</SUP>/2)</SPAN>
+ * of the fatigue life distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ * @return the mean of the fatigue life distribution
+ *
+ */
+ public static double getMean (double mu, double beta, double gamma) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ return (mu + beta * (1 + 0.5 * gamma * gamma));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>β</I><SUP>2</SUP><I>γ</I><SUP>2</SUP>(1 + 5<I>γ</I><SUP>2</SUP>/4)</SPAN> of the fatigue life distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ * @return the variance of the fatigue life distribution
+ *
+ */
+ public static double getVariance (double mu, double beta, double gamma) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ return (beta * beta * gamma * gamma * (1.0 + 5.0/4.0 * gamma * gamma));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the fatigue life distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ * @return the standard deviation of the fatigue life distribution
+ *
+ */
+ public static double getStandardDeviation (double mu, double beta,
+ double gamma) {
+ return Math.sqrt (FatigueLifeDist.getVariance (mu, beta, gamma));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>γ</I></SPAN> of this object.
+ *
+ */
+ public double getGamma() {
+ return gamma;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double mu, double beta, double gamma) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ this.mu = mu;
+ this.beta = beta;
+ this.gamma = gamma;
+ supportA = mu;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>γ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, beta, gamma};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu + ", beta = " + beta + ", gamma = " + gamma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/FatigueLifeDist.tex b/source/umontreal/iro/lecuyer/probdist/FatigueLifeDist.tex
new file mode 100644
index 0000000..368107f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FatigueLifeDist.tex
@@ -0,0 +1,421 @@
+\defmodule {FatigueLifeDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em fatigue life\/} distribution \cite{tBIR69a} with location
+parameter $\mu$, scale parameter $\beta$ and shape
+parameter $\gamma$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = [(((x - \mu) / \beta)^{1/2} + (\beta / (x - \mu))^{1/2}) / (2\gamma(x - \mu))]
+ \phi((((x - \mu) / \beta)^{1/2} - (\beta / (x - \mu))^{1/2}) / \gamma),
+ \qquad \mbox{for } x>\mu,
+\endeq
+where $\phi$ is the probability density of the standard normal distribution.
+The distribution function is given by
+\eq
+ F(x) = \Phi((((x - \mu) / \beta)^{1/2} - (\beta / (x - \mu))^{1/2}) / \gamma),
+ \qquad \mbox{for } x>\mu,
+\endeq
+where $\Phi$ is the standard normal distribution function.
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \left[\frac{\sqrt{\frac{x - \mu}{\beta}} + \sqrt{\frac{\beta}{x - \mu}}}{2\gamma(x - \mu)}\right]
+ \phi\left(\frac{\sqrt{\frac{x - \mu}{\beta}} - \sqrt{\frac{\beta}{x - \mu}}}{\gamma}\right), \qquad \mbox{for } x>\mu,
+\eqlabel{eq:fFatigueLife}
+\endeq
+where $\phi$ is the probability density of the standard normal distribution.
+The distribution function is given by
+\eq
+ F(x) = \Phi\left(\frac{\sqrt{\frac{x - \mu}{\beta}} - \sqrt{\frac{\beta}{x - \mu}}}{\gamma}\right), \qquad \mbox{for } x>\mu,
+\eqlabel{eq:FFatigueLife}
+\endeq
+where $\Phi$ is the standard normal distribution function.
+\end{latexonly}%
+Restrictions: $\beta > 0$, $\gamma > 0$.
+
+The non-static versions of the methods \texttt{cdf}, \texttt{barF},
+and \texttt{inverseF} call the static version of the same name.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: FatigueLifeDist
+ * Description: fatigue life distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import optimization.*;
+\end{hide}
+
+public class FatigueLifeDist extends ContinuousDistribution\begin{hide} {
+ protected double mu;
+ protected double beta;
+ protected double gamma;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+ private double mu;
+
+ public Optim (double[] x, int n, double min)
+ {
+ this.n = n;
+ this.mu = min;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ double sum = 0.0;
+
+ if ((p[1] <= 0.0) || (p[2] <= 0.0))
+ return 1e200;
+
+ for (int i = 0; i < n; i++)
+ sum -= Math.log (density (mu, p[1], p[2], xi[i]));
+
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public FatigueLifeDist (double mu, double beta, double gamma)\begin{hide} {
+ setParams (mu, beta, gamma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a fatigue life distribution with parameters
+ $\mu$, $\beta$ and $\gamma$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return FatigueLifeDist.density (mu, beta, gamma, x);
+ }
+
+ public double cdf (double x) {
+ return FatigueLifeDist.cdf (mu, beta, gamma, x);
+ }
+
+ public double barF (double x) {
+ return FatigueLifeDist.barF (mu, beta, gamma, x);
+ }
+
+ public double inverseF (double u) {
+ return FatigueLifeDist.inverseF (mu, beta, gamma, u);
+ }
+
+ public double getMean() {
+ return FatigueLifeDist.getMean (mu, beta, gamma);
+ }
+
+ public double getVariance() {
+ return FatigueLifeDist.getVariance (mu, beta, gamma);
+ }
+
+ public double getStandardDeviation() {
+ return FatigueLifeDist.getStandardDeviation (mu, beta, gamma);
+ }\end{hide}
+
+ public static double density (double mu, double beta, double gamma,
+ double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (x <= mu)
+ return 0.0;
+ double y;
+ y = (Math.sqrt ((x - mu) / beta) - Math.sqrt (beta / (x - mu))) / gamma;
+
+ return (((Math.sqrt ((x - mu) / beta) + Math.sqrt (beta / (x - mu))) /
+ (2 * gamma * (x - mu))) * NormalDist.density (0.0, 1.0, y));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density (\ref{eq:fFatigueLife}) for the
+ fatigue life distribution with parameters $\mu$, $\beta$ and $\gamma$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu, double beta, double gamma, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (x <= mu)
+ return 0.0;
+
+ return NormalDist.cdf01 ((Math.sqrt ((x - mu) / beta) - Math.sqrt (beta / (x - mu))) / gamma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the fatigue life distribution
+ function with parameters $\mu$, $\beta$ and $\gamma$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double mu, double beta, double gamma,
+ double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (x <= mu)
+ return 1.0;
+
+ return NormalDist.barF01 ((Math.sqrt ((x - mu) / beta) - Math.sqrt (beta / (x - mu))) / gamma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function of the
+ fatigue life distribution with parameters $\mu$, $\beta$ and $\gamma$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double mu, double beta, double gamma,
+ double u)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) // if u == 0, in fact
+ return mu;
+ if (u >= 1.0) // if u == 1, in fact
+ return Double.POSITIVE_INFINITY;
+
+ double w = gamma * NormalDist.inverseF01 (u);
+ double sqrtZ = 0.5 * (w + Math.sqrt (w * w + 4.0));
+
+ return (mu + sqrtZ * sqrtZ * beta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse of the fatigue life distribution
+ with parameters $\mu$, $\beta$ and $\gamma$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n, double mu)\begin{hide} {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double[] parameters = new double[3];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] h = new double[3][3];
+ double[] udiag = new double[3];
+
+ Optim system = new Optim (x, n, mu);
+
+ double mean = 0.0;
+ for (int i = 0; i < n; i++)
+ mean += x[i];
+ mean /= (double) n;
+
+ double var = 0.0;
+ for (int i = 0; i < n; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) n;
+
+ double loc2 = (mean - mu) * (mean - mu);
+ double a = 0.25 * (var - 5 * loc2);
+ double b = (var - loc2);
+ double c = var;
+
+ double delta = b * b - 4.0 * a * c;
+
+ double gamma2 = (- b - Math.sqrt (delta)) / (2.0 * a);
+ param[2] = Math.sqrt (gamma2);
+ param[1] = (mean - mu) / (1.0 + gamma2 / 2.0);
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, h, udiag);
+
+ for (int i = 1; i < 3; i++)
+ parameters[i] = xpls[i];
+ parameters[0] = mu;
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters ($\mu$, $\beta$, $\gamma$) of the fatigue life
+ distribution using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a three-element
+ array, in regular order: [$\mu$, $\beta$, $\gamma$].
+ \begin{detailed}
+ The estimate of the parameters is given by maximizing numerically the
+ log-likelihood function, using the Uncmin package \cite{iSCHa,iVERa}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{mu}{the location parameter}
+ \return{returns the parameters [$\hat{\beta}$, $\hat{\gamma}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double beta, double gamma)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ return (mu + beta * (1 + 0.5 * gamma * gamma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+ $E[X] = \mu + \beta(1 + \gamma^2/2)$
+ of the fatigue life distribution with parameters $\mu$, $\beta$ and $\gamma$.
+\begin{htmlonly}
+ \return{the mean of the fatigue life distribution}
+\end{htmlonly}
+\end{tabb}
+\begin{code}
+
+ public static double getVariance (double mu, double beta, double gamma)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ return (beta * beta * gamma * gamma * (1.0 + 5.0/4.0 * gamma * gamma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+$\mbox{Var}[X] = \beta^2 \gamma^2 (1 + 5 \gamma^2/4)$ of the fatigue life distribution
+ with parameters $\mu$, $\beta$ and $\gamma$.
+\begin{htmlonly}
+ \return{the variance of the fatigue life distribution}
+\end{htmlonly}
+\end{tabb}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double beta,
+ double gamma)\begin{hide} {
+ return Math.sqrt (FatigueLifeDist.getVariance (mu, beta, gamma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the fatigue life distribution
+ with parameters $\mu$, $\beta$ and $\gamma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the fatigue life distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getGamma()\begin{hide} {
+ return gamma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\gamma$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double mu, double beta, double gamma)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ this.mu = mu;
+ this.beta = beta;
+ this.gamma = gamma;
+ supportA = mu;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\mu$, $\beta$ and $\gamma$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, beta, gamma};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\mu$, $\beta$, $\gamma$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu + ", beta = " + beta + ", gamma = " + gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/FisherFDist.java b/source/umontreal/iro/lecuyer/probdist/FisherFDist.java
new file mode 100644
index 0000000..b3e7166
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FisherFDist.java
@@ -0,0 +1,309 @@
+
+
+/*
+ * Class: FisherFDist
+ * Description: Fisher F-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.probdist.BetaDist;
+import umontreal.iro.lecuyer.util.*;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <SPAN CLASS="textit">Fisher F</SPAN> distribution with <SPAN CLASS="MATH"><I>n</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>n</I><SUB>2</SUB></SPAN>
+ * degrees of freedom, where <SPAN CLASS="MATH"><I>n</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>n</I><SUB>2</SUB></SPAN> are positive integers.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>Γ</I>((<I>n</I><SUB>1</SUB> + <I>n</I><SUB>2</SUB>)/2)<I>n</I><SUB>1</SUB><SUP>n<SUB>1</SUB>/2</SUP><I>n</I><SUB>2</SUB><SUP>n<SUB>2</SUB>/2</SUP>/[<I>Γ</I>(<I>n</I><SUB>1</SUB>/2)<I>Γ</I>(<I>n</I><SUB>2</SUB>/2)]<I>x</I><SUP>(n<SUB>1</SUB>-2)/2</SUP>/(<I>n</I><SUB>2</SUB> + <I>n</I><SUB>1</SUB><I>x</I>)<SUP>(n<SUB>1</SUB>+n<SUB>2</SUB>)/2</SUP>, for <I>x</I> > 0.
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined in
+ * {@link GammaDist}.
+ *
+ */
+public class FisherFDist extends ContinuousDistribution {
+ protected int n1;
+ protected int n2;
+ protected double C1;
+ private static final int DECPREC = 15; // decimal precision
+
+
+
+ /**
+ * Constructs a Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution with <TT>n1</TT> and <TT>n2</TT> degrees of freedom.
+ *
+ */
+ public FisherFDist (int n1, int n2) {
+ setParams (n1, n2);
+ }
+
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp (C1 + 0.5 * (n1 - 2) * Math.log (x) -
+ (0.5 * (n1 + n2) * Math.log (n2 + n1 * x)));
+ }
+
+ public double cdf (double x) {
+ return FisherFDist.cdf (n1, n2, x);
+ }
+
+ public double barF (double x) {
+ return FisherFDist.barF (n1, n2, x);
+ }
+
+ public double inverseF (double u) {
+ return FisherFDist.inverseF (n1, n2, u);
+ }
+
+ public double getMean() {
+ return FisherFDist.getMean (n1, n2);
+ }
+
+ public double getVariance() {
+ return FisherFDist.getVariance (n1, n2);
+ }
+
+ public double getStandardDeviation() {
+ return FisherFDist.getStandardDeviation (n1, n2);
+ }
+
+ /**
+ * Computes the density function for a Fisher
+ * <SPAN CLASS="MATH"><I>F</I></SPAN> distribution with <TT>n1</TT> and <TT>n2</TT> degrees of freedom,
+ * evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ *
+ */
+ public static double density (int n1, int n2, double x) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp (((n1/2.0) * Math.log (n1) + (n2/2.0) * Math.log(n2) +
+ ((n1 - 2) / 2.0) * Math.log (x)) -
+ (Num.lnBeta (n1/2.0, n2/2.0) +
+ ((n1 + n2) / 2.0) * Math.log (n2 + n1 * x)));
+ }
+
+
+ @Deprecated
+ public static double cdf (int n1, int n2, int d, double x) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ return BetaDist.cdf (n1/2.0, n2/2.0, d, (n1*x)/(n1*x + n2));
+ }
+
+
+ /**
+ * Computes the distribution function of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution with
+ * parameters <TT>n1</TT> and <TT>n2</TT>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ *
+ */
+ public static double cdf (int n1, int n2, double x) {
+ return cdf (n1, n2, DECPREC, x);
+ }
+
+
+ @Deprecated
+ public static double barF (int n1, int n2, int d, double x) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ return BetaDist.barF (n1/2.0, n2/2.0, d, (n1 * x) / (n1 * x + n2));
+ }
+
+
+ /**
+ * Computes the complementary distribution function of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution
+ * with parameters <TT>n1</TT> and <TT>n2</TT>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ *
+ */
+ public static double barF (int n1, int n2, double x) {
+ return barF (n1, n2, DECPREC, x);
+ }
+
+
+ @Deprecated
+ public static double inverseF (int n1, int n2, int d, double u) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u < 0 or u > 1");
+ if (u <= 0.0)
+ return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ double z = BetaDist.inverseF (n1 / 2.0, n2 / 2.0, d, u);
+ return ((n2 * z) / (n1 * (1 - z)));
+ }
+
+
+ /**
+ * Computes the inverse of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution with parameters <TT>n1</TT>
+ * and <TT>n2</TT>, evaluated at <SPAN CLASS="MATH"><I>u</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n1, int n2, double u) {
+ return inverseF (n1, n2, DECPREC, u);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>n</I><SUB>2</SUB>/(<I>n</I><SUB>2</SUB> - 2)</SPAN> of the
+ * Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution with parameters <TT>n1</TT> and <TT>n2</TT> <SPAN CLASS="MATH">= <I>n</I><SUB>2</SUB></SPAN>.
+ *
+ * @return the mean of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution
+ *
+ */
+ public static double getMean (int n1, int n2) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 2)
+ throw new IllegalArgumentException ("n2 <= 2");
+
+ return (n2 / (n2 - 2.0));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution with parameters <TT>n1</TT> <SPAN CLASS="MATH">= <I>n</I><SUB>1</SUB></SPAN>
+ * and <TT>n2</TT> <SPAN CLASS="MATH">= <I>n</I><SUB>2</SUB></SPAN>.
+ *
+ * @return the variance of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (2<I>n</I>2<SUP>2</SUP>(<I>n</I>2 + <I>n</I>1 - 2))/(<I>n</I>1(<I>n</I>2 - 2)<SUP>2</SUP>(<I>n</I>2 - 4))</SPAN>
+ *
+ */
+ public static double getVariance (int n1, int n2) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 4)
+ throw new IllegalArgumentException ("n2 <= 4");
+
+ return ((2.0 * n2 * n2 * (n2 + n1 - 2)) / (n1 * (n2 - 2.0) * (n2 - 2.0) * (n2 - 4.0)));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution with parameters <TT>n1</TT> and <TT>n2</TT>.
+ *
+ * @return the standard deviation of the Fisher <SPAN CLASS="MATH"><I>F</I></SPAN> distribution
+ *
+ *
+ */
+ public static double getStandardDeviation (int n1, int n2) {
+ return Math.sqrt (FisherFDist.getVariance (n1, n2));
+ }
+
+
+ /**
+ * Returns the parameter <TT>n1</TT> of this object.
+ *
+ */
+ @Deprecated
+ public int getN() {
+ return n1;
+ }
+
+
+ @Deprecated
+ public int getM() {
+ return n2;
+ }
+
+
+ /**
+ * Returns the parameter <TT>n1</TT> of this object.
+ *
+ */
+ public int getN1() {
+ return n1;
+ }
+
+
+ /**
+ * Returns the parameter <TT>n2</TT> of this object.
+ *
+ */
+ public int getN2() {
+ return n2;
+ }
+
+
+ /**
+ * Sets the parameters <TT>n1</TT> and <TT>n2</TT> of this object.
+ *
+ */
+ public void setParams (int n1, int n2) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+
+ this.n1 = n1;
+ this.n2 = n2;
+ supportA = 0;
+ C1 = (n1 / 2.0) * Math.log (n1) + (n2 / 2.0) * Math.log (n2) -
+ Num.lnBeta (n1 / 2.0, n2 / 2.0);
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<TT>n1</TT>, <TT>n2</TT>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n1, n2};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n1 = " + n1 + ", n2 = " + n2;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/FisherFDist.tex b/source/umontreal/iro/lecuyer/probdist/FisherFDist.tex
new file mode 100644
index 0000000..9fb98c5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FisherFDist.tex
@@ -0,0 +1,348 @@
+\defmodule {FisherFDist}
+
+Extends the class \class{ContinuousDistribution} for
+the \emph{Fisher F} distribution with $n_1$ and $n_2$
+degrees of freedom, where $n_1$ and $n_2$ are positive integers.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = \Gamma((n_1 + n_2) / 2)n_1^{n_1/2}n_2^{n_2/2} / [\Gamma(n_1/2)\Gamma(n_2/2)]
+ x^{(n_1 - 2) / 2} / (n_2 + n_1x)^{(n_1 + n_2)/2},
+\qquad\mbox{ for } x > 0.
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \frac{\Gamma(\frac{n_1 + n_2}{2})n_1^{\frac{n_1}{2}}n_2^{\frac{n_2}{2}}}{\Gamma(\frac{n_1}{2})\Gamma(\frac{n_2}{2})}
+ \frac{x^{\frac{n_1 - 2}{2}}}{(n_2 + n_1x)^{\frac{n_1 + n_2}{2}}},
+\qquad\mbox {for } x > 0
+\eqlabel{eq:FisherF}
+\endeq
+\end{latexonly}%
+where $\Gamma(x)$ is the gamma function defined in
+\latex{(\ref{eq:Gamma})}\html{\class{GammaDist}}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: FisherFDist
+ * Description: Fisher F-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.BetaDist;
+import umontreal.iro.lecuyer.util.*;\end{hide}
+
+public class FisherFDist extends ContinuousDistribution\begin{hide} {
+ protected int n1;
+ protected int n2;
+ protected double C1;
+ private static final int DECPREC = 15; // decimal precision
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public FisherFDist (int n1, int n2)\begin{hide} {
+ setParams (n1, n2);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a Fisher $F$ distribution with \texttt{n1} and \texttt{n2} degrees of freedom.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp (C1 + 0.5 * (n1 - 2) * Math.log (x) -
+ (0.5 * (n1 + n2) * Math.log (n2 + n1 * x)));
+ }
+
+ public double cdf (double x) {
+ return FisherFDist.cdf (n1, n2, x);
+ }
+
+ public double barF (double x) {
+ return FisherFDist.barF (n1, n2, x);
+ }
+
+ public double inverseF (double u) {
+ return FisherFDist.inverseF (n1, n2, u);
+ }
+
+ public double getMean() {
+ return FisherFDist.getMean (n1, n2);
+ }
+
+ public double getVariance() {
+ return FisherFDist.getVariance (n1, n2);
+ }
+
+ public double getStandardDeviation() {
+ return FisherFDist.getStandardDeviation (n1, n2);
+ }\end{hide}
+
+ public static double density (int n1, int n2, double x)\begin{hide} {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp (((n1/2.0) * Math.log (n1) + (n2/2.0) * Math.log(n2) +
+ ((n1 - 2) / 2.0) * Math.log (x)) -
+ (Num.lnBeta (n1/2.0, n2/2.0) +
+ ((n1 + n2) / 2.0) * Math.log (n2 + n1 * x)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:FisherF}) for a Fisher
+$F$ distribution with \texttt{n1} and \texttt{n2} degrees of freedom,
+ evaluated at $x$.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public static double cdf (int n1, int n2, int d, double x) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ return BetaDist.cdf (n1/2.0, n2/2.0, d, (n1*x)/(n1*x + n2));
+ }
+\end{code}
+\begin{tabb}
+Computes the distribution function of the Fisher $F$ distribution with
+parameters \texttt{n1} and \texttt{n2}, evaluated at $x$, with roughly $d$
+ decimal digits of precision.
+ \end{tabb}\end{hide}
+\begin{code}
+
+ public static double cdf (int n1, int n2, double x)\begin{hide} {
+ return cdf (n1, n2, DECPREC, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Computes the distribution function of the Fisher $F$ distribution with
+parameters \texttt{n1} and \texttt{n2}, evaluated at $x$.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public static double barF (int n1, int n2, int d, double x) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ return BetaDist.barF (n1/2.0, n2/2.0, d, (n1 * x) / (n1 * x + n2));
+ }
+\end{code}
+\begin{tabb}
+Computes the complementary distribution function of the Fisher $F$ distribution
+with parameters \texttt{n1} and \texttt{n2}, evaluated at $x$, with roughly
+ $d$ decimal digits of precision.
+\end{tabb}\end{hide}
+\begin{code}
+
+ public static double barF (int n1, int n2, double x)\begin{hide} {
+ return barF (n1, n2, DECPREC, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Computes the complementary distribution function of the Fisher $F$ distribution
+with parameters \texttt{n1} and \texttt{n2}, evaluated at $x$.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public static double inverseF (int n1, int n2, int d, double u) {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u < 0 or u > 1");
+ if (u <= 0.0)
+ return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ double z = BetaDist.inverseF (n1 / 2.0, n2 / 2.0, d, u);
+ return ((n2 * z) / (n1 * (1 - z)));
+ }
+\end{code}
+\begin{tabb}
+Computes the inverse of the Fisher $F$ distribution with parameters \texttt{n1}
+ and \texttt{n2}, evaluated at $u$, with roughly $d$ decimal digits of
+ precision.
+\end{tabb}\end{hide}
+\begin{code}
+
+ public static double inverseF (int n1, int n2, double u)\begin{hide} {
+ return inverseF (n1, n2, DECPREC, u);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Computes the inverse of the Fisher $F$ distribution with parameters \texttt{n1}
+ and \texttt{n2}, evaluated at $u$.
+\end{tabb}
+\begin{code}
+
+ public static double getMean (int n1, int n2)\begin{hide} {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 2)
+ throw new IllegalArgumentException ("n2 <= 2");
+
+ return (n2 / (n2 - 2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = n_2 / (n_2 - 2)$ of the
+Fisher $F$ distribution with parameters \texttt{n1} and \texttt{n2} $=n_2$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Fisher $F$ distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int n1, int n2)\begin{hide} {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 4)
+ throw new IllegalArgumentException ("n2 <= 4");
+
+ return ((2.0 * n2 * n2 * (n2 + n1 - 2)) / (n1 * (n2 - 2.0) * (n2 - 2.0) * (n2 - 4.0)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+\begin{latexonly}
+$$\mbox{Var}[X] = \frac{2n_2^{2} (n_2 + n_1 - 2)}{n_1 (n_2 - 2)^{2} (n_2 - 4)}$$
+\end{latexonly}
+of the Fisher $F$ distribution with parameters \texttt{n1} $=n_1$
+ and \texttt{n2} $=n_2$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Fisher $F$ distribution
+ $\mbox{Var}[X] = (2n2^2 (n2 + n1 - 2)) / (n1 (n2 - 2)^2 (n2 - 4))$
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int n1, int n2)\begin{hide} {
+ return Math.sqrt (FisherFDist.getVariance (n1, n2));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the Fisher $F$ distribution with parameters \texttt{n1} and \texttt{n2}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Fisher $F$ distribution}
+\end{htmlonly}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public int getN()\begin{hide} {
+ return n1;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter \texttt{n1} of this object.
+ \end{tabb}
+\begin{code}
+
+ @Deprecated
+ public int getM()\begin{hide} {
+ return n2;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter \texttt{n2} of this object.
+ \end{tabb}\end{hide}
+\begin{code}
+
+ public int getN1()\begin{hide} {
+ return n1;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter \texttt{n1} of this object.
+ \end{tabb}
+\begin{code}
+
+ public int getN2()\begin{hide} {
+ return n2;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter \texttt{n2} of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (int n1, int n2)\begin{hide} {
+ if (n1 <= 0)
+ throw new IllegalArgumentException ("n1 <= 0");
+ if (n2 <= 0)
+ throw new IllegalArgumentException ("n2 <= 0");
+
+ this.n1 = n1;
+ this.n2 = n2;
+ supportA = 0;
+ C1 = (n1 / 2.0) * Math.log (n1) + (n2 / 2.0) * Math.log (n2) -
+ Num.lnBeta (n1 / 2.0, n2 / 2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters \texttt{n1} and \texttt{n2} of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n1, n2};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [\texttt{n1}, \texttt{n2}].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n1 = " + n1 + ", n2 = " + n2;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/FoldedNormalDist.java b/source/umontreal/iro/lecuyer/probdist/FoldedNormalDist.java
new file mode 100644
index 0000000..21e58c5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FoldedNormalDist.java
@@ -0,0 +1,363 @@
+
+
+/*
+ * Class: FoldedNormalDist
+ * Description: folded normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>folded normal</EM> distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> >= 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>φ</I>((<I>x</I> - <I>μ</I>)/<I>σ</I>) + <I>φ</I>((- <I>x</I> - <I>μ</I>)/<I>σ</I>) for <I>x</I> >= 0,
+ * </DIV><P></P>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 0, for <I>x</I> < 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>φ</I></SPAN> denotes the density function
+ * of a standard normal distribution.
+ *
+ */
+public class FoldedNormalDist extends ContinuousDistribution {
+ protected double mu;
+ protected double sigma;
+ private static final double RACPI = 1.7724538509055160273; // Sqrt[PI]
+
+ private static class FunctionInverse implements MathFunction {
+ private double u, mu, sigma;
+
+ public FunctionInverse (double mu, double sigma, double u) {
+ this.u = u;
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(mu, sigma, x);
+ }
+ }
+
+
+
+ /**
+ * Constructs a <TT>FoldedNormalDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>.
+ *
+ */
+ public FoldedNormalDist (double mu, double sigma) {
+ setParams (mu, sigma);
+ }
+
+
+ public double density (double x) {
+ return density (mu, sigma, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return FoldedNormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return FoldedNormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return FoldedNormalDist.getStandardDeviation (mu, sigma);
+ }
+
+ /**
+ * Computes the density function of the <EM>folded normal</EM>
+ * distribution.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param x the value at which the density is evaluated
+ *
+ * @return returns the density function
+ *
+ */
+ public static double density (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (x < 0.0) return 0.0;
+ return NormalDist.density(mu,sigma,x) + NormalDist.density(mu,sigma,-x);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param x the value at which the distribution is evaluated
+ *
+ * @return returns the cdf function
+ *
+ */
+ public static double cdf (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (x <= 0.0) return 0.0;
+ return NormalDist.cdf01((x-mu)/sigma) - NormalDist.cdf01((-x-mu)/sigma);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param x the value at which the complementary distribution is evaluated
+ *
+ * @return returns the complementary distribution function
+ *
+ */
+ public static double barF (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (x <= 0.0) return 1.0;
+ return NormalDist.barF01((x-mu)/sigma) - NormalDist.barF01((-x-mu)/sigma);
+ }
+
+
+ /**
+ * Computes the inverse of the distribution function.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param u the value at which the inverse distribution is evaluated
+ *
+ * @return returns the inverse distribution function
+ *
+ */
+ public static double inverseF (double mu, double sigma, double u) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ MathFunction f = new FunctionInverse (mu, sigma, u);
+ return RootFinder.brentDekker (0.0, mu + 10.0*sigma, f, 1.0e-14);
+ }
+
+
+ /**
+ * .
+ *
+ * Computes and returns the mean
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>E</I>[<I>X</I>] = <I>σ</I>()<SUP>1/2</SUP>2<I>π</I> <I>e</I><SUP>-<I>μ</I><SUP>2</SUP>/(2<I>σ</I><SUP>2</SUP>)</SUP> + <I>μ</I> erf(1#1),
+ * </DIV><P></P>
+ * where erf<SPAN CLASS="MATH">(<I>z</I>)</SPAN> is the error function.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return returns the mean
+ *
+ */
+ public static double getMean (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+
+ return sigma * Num.RAC2 / RACPI * Math.exp(-mu*mu/(2.0*sigma*sigma))
+ + mu * Num.erf(mu/(sigma*Num.RAC2));
+ }
+
+
+ /**
+ * .
+ *
+ * Computes and returns the variance
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * Var[<I>X</I>] = <I>μ</I><SUP>2</SUP> + <I>σ</I><SUP>2</SUP> - <I>E</I>[<I>X</I>]<SUP>2</SUP>.
+ * </DIV><P></P>
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return returns the variance
+ *
+ */
+ public static double getVariance (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ double mean = sigma * Num.RAC2 / RACPI * Math.exp(-mu*mu/(2.0*sigma*sigma))
+ + mu * Num.erf(mu/(sigma*Num.RAC2));
+ return mu*mu + sigma*sigma - mean*mean;
+ }
+
+
+ /**
+ * Computes the standard deviation of the folded normal distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return returns the standard deviation
+ *
+ */
+ public static double getStandardDeviation (double mu, double sigma) {
+ return Math.sqrt (FoldedNormalDist.getVariance (mu, sigma));
+ }
+
+
+ /**
+ * NOT IMPLEMENTED. Les formules pour le MLE sont données dans.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(σ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ throw new UnsupportedOperationException("getMLE is not implemented ");
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ * @return returns the parameter mu
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ * @return returns the parameter sigma
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> for this object.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ *
+ */
+ public void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>]
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, sigma};
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ * @return returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/FoldedNormalDist.tex b/source/umontreal/iro/lecuyer/probdist/FoldedNormalDist.tex
new file mode 100644
index 0000000..a188e42
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FoldedNormalDist.tex
@@ -0,0 +1,353 @@
+\defmodule {FoldedNormalDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em folded normal\/} distribution with
+parameters $\mu \ge 0$ and $\sigma > 0$.
+The density is
+\begin{htmlonly}
+\eq
+ f(x) = \phi \left((x-\mu)/\sigma\right) + \phi \left((-x-\mu)/\sigma\right)
+ \qquad \mbox {for } x \ge 0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \phi \left(\frac{x-\mu}{\sigma}\right) +
+ \phi \left(\frac{-x-\mu}{\sigma}\right)
+ \qquad \mbox {for } x \ge 0, \eqlabel{eq:fFoldedNormal}
+\endeq
+\end{latexonly}
+$$
+f(x) = 0, \qquad \mbox{ for } x < 0,
+$$
+where $ \phi $ denotes the density function
+of a standard normal distribution.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: FoldedNormalDist
+ * Description: folded normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+\end{hide}
+
+public class FoldedNormalDist extends ContinuousDistribution\begin{hide} {
+ protected double mu;
+ protected double sigma;
+ private static final double RACPI = 1.7724538509055160273; // Sqrt[PI]
+
+ private static class FunctionInverse implements MathFunction {
+ private double u, mu, sigma;
+
+ public FunctionInverse (double mu, double sigma, double u) {
+ this.u = u;
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(mu, sigma, x);
+ }
+ }
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public FoldedNormalDist (double mu, double sigma)\begin{hide} {
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{FoldedNormalDist} object with parameters
+ $\mu =$ \texttt{mu} and $\sigma =$ \texttt{sigma}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (mu, sigma, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return FoldedNormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return FoldedNormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return FoldedNormalDist.getStandardDeviation (mu, sigma);
+ }\end{hide}
+
+ public static double density (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (x < 0.0) return 0.0;
+ return NormalDist.density(mu,sigma,x) + NormalDist.density(mu,sigma,-x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function of the {\em folded normal\/}
+distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{x}{the value at which the density is evaluated}
+ \return{returns the density function}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (x <= 0.0) return 0.0;
+ return NormalDist.cdf01((x-mu)/sigma) - NormalDist.cdf01((-x-mu)/sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{x}{the value at which the distribution is evaluated}
+ \return{returns the cdf function}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (x <= 0.0) return 1.0;
+ return NormalDist.barF01((x-mu)/sigma) - NormalDist.barF01((-x-mu)/sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{x}{the value at which the complementary distribution is evaluated}
+ \return{returns the complementary distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double mu, double sigma, double u)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ MathFunction f = new FunctionInverse (mu, sigma, u);
+ return RootFinder.brentDekker (0.0, mu + 10.0*sigma, f, 1.0e-14);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse of the distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{u}{the value at which the inverse distribution is evaluated}
+ \return{returns the inverse distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+
+ return sigma * Num.RAC2 / RACPI * Math.exp(-mu*mu/(2.0*sigma*sigma))
+ + mu * Num.erf(mu/(sigma*Num.RAC2));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+$$
+E[X] = \sigma \sqrt\frac2\pi\; e^{-\mu^2/(2\sigma^2)}
+ + \mu\,\mbox{erf}\left(\frac\mu{\sigma\sqrt 2}\right),
+$$
+where erf$(z)$ is the error function.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{returns the mean}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ double mean = sigma * Num.RAC2 / RACPI * Math.exp(-mu*mu/(2.0*sigma*sigma))
+ + mu * Num.erf(mu/(sigma*Num.RAC2));
+ return mu*mu + sigma*sigma - mean*mean;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+ $$
+\mbox{Var}[X] = \mu^2 + \sigma^2 - E[X]^2.
+$$
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{returns the variance}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double sigma) \begin{hide} {
+ return Math.sqrt (FoldedNormalDist.getVariance (mu, sigma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard deviation of the folded normal distribution
+with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{returns the standard deviation}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ throw new UnsupportedOperationException("getMLE is not implemented ");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+NOT IMPLEMENTED. Les formules pour le MLE sont donn\'ees dans \cite{tLEO61a}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\mu}$, $\hat{\sigma}$]}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{returns the parameter mu}
+\end{htmlonly}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{returns the parameter sigma}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double mu, double sigma) \begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the parameters $\mu$ and $\sigma$ for this object.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, sigma};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\mu$, $\sigma$].
+\end{tabb}
+\begin{htmlonly}
+ \return{returns the parameters [$\mu$, $\sigma$]}
+\end{htmlonly}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{returns a \texttt{String} containing information about the current distribution.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/FrechetDist.java b/source/umontreal/iro/lecuyer/probdist/FrechetDist.java
new file mode 100644
index 0000000..a8a6aca
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FrechetDist.java
@@ -0,0 +1,455 @@
+
+
+/*
+ * Class: FrechetDist
+ * Description: Fréchet distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import optimization.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the <SPAN CLASS="textit">Fréchet</SPAN>
+ * distribution, with location parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>, scale
+ * parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>, and shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>, where we use
+ * the notation
+ * <SPAN CLASS="MATH"><I>z</I> = (<I>x</I> - <I>δ</I>)/<I>β</I></SPAN>. It has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>αe</I><SUP>-z<SUP>-<I>α</I></SUP></SUP>/(<I>βz</I><SUP><I>α</I>+1</SUP>), for <I>x</I> > <I>δ</I>
+ * </DIV><P></P>
+ * and distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>e</I><SUP>-z<SUP>-<I>α</I></SUP></SUP>, for <I>x</I> > <I>δ</I>.
+ * </DIV><P></P>
+ * Both the density and the distribution are 0 for
+ * <SPAN CLASS="MATH"><I>x</I> <= <I>δ</I></SPAN>.
+ *
+ * <P>
+ * The mean is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>E</I>[<I>X</I>] = <I>δ</I> + <I>βΓ</I>(1 - 1/<I>α</I>),
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function. The variance is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * Var[<I>X</I>] = <I>β</I><SUP>2</SUP>[<I>Γ</I>(1 - 2/<I>α</I>) - (<I>Γ</I>(1 - 1/<I>α</I>))<SUP>2</SUP>].
+ * </DIV><P></P>
+ *
+ */
+public class FrechetDist extends ContinuousDistribution {
+ private double delta;
+ private double beta;
+ private double alpha;
+
+
+ private static class Optim implements Lmder_fcn {
+ protected double[] x;
+ protected int n;
+
+ public Optim (double[] x, int n) {
+ this.n = n;
+ this.x = x;
+ }
+
+ public void fcn (int m, int n, double[] par, double[] fvec, double[][] fjac, int iflag[])
+ {
+ if (par[1] <= 0.0 || par[2] <= 0.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fvec[3] = BIG;
+ return;
+ }
+
+ double sum1, sum2, sumb, sum4, sum5;
+ double z, w, v;
+ double alpha = par[1];
+ double beta = par[2];
+ double mu = par[3];
+
+ if (iflag[1] == 1) {
+ sum1 = sum2 = sumb = sum4 = sum5 = 0;
+ for (int i = 0; i < n; i++) {
+ z = (x[i] - mu) / beta;
+ sum1 += 1.0 / z;
+ v = Math.pow(z, -alpha);
+ sum2 += v / z;
+ sumb += v;
+ w = Math.log(z);
+ sum4 += w;
+ sum5 += v * w;
+ }
+
+ fvec[2] = sumb - n; // eq. for beta
+ fvec[3] = (alpha + 1) * sum1 - alpha * sum2; // eq. for mu
+ fvec[1] = n / alpha + sum5 - sum4; // eq. for alpha
+
+ } else if (iflag[1] == 2) {
+ throw new IllegalArgumentException ("iflag = 2");
+ // The 3 X 3 Jacobian must be calculated and put in fjac
+ }
+ }
+ }
+
+
+ private static class Function implements MathFunction {
+ private int n;
+ private double[] x;
+ private double delta;
+ public double sumxi;
+ public double dif;
+
+ public Function (double[] y, int n, double delta) {
+ this.n = n;
+ this.x = y;
+ this.delta = delta;
+ double xmin = Double.MAX_VALUE;
+ for (int i = 0; i < n; i++) {
+ if ((y[i] < xmin) && (y[i] > delta))
+ xmin = y[i];
+ }
+ dif = xmin - delta;
+ }
+
+ public double evaluate (double alpha) {
+ if (alpha <= 0.0) return 1.0e100;
+ double v, w;
+ double sum1 = 0, sum2 = 0, sum3 = 0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] <= delta)
+ continue;
+ v = Math.log(x[i] - delta);
+ w = Math.pow(dif / (x[i] - delta), alpha);
+ sum1 += v;
+ sum2 += w;
+ sum3 += v * w;
+ }
+
+ sum1 /= n;
+ sumxi = sum2 / n;
+ return 1 / alpha + sum3 / sum2 - sum1;
+ }
+ }
+
+
+ /**
+ * Constructor for the standard <SPAN CLASS="textit">Fréchet</SPAN>
+ * distribution with parameters <SPAN CLASS="MATH"><I>β</I></SPAN> = 1 and <SPAN CLASS="MATH"><I>δ</I></SPAN> = 0.
+ *
+ */
+ public FrechetDist (double alpha) {
+ setParams (alpha, 1.0, 0.0);
+ }
+
+
+ /**
+ * Constructs a <TT>FrechetDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT>.
+ *
+ */
+ public FrechetDist (double alpha, double beta, double delta) {
+ setParams (alpha, beta, delta);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, beta, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, delta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, delta, u);
+ }
+
+ public double getMean() {
+ return getMean (alpha, beta, delta);
+ }
+
+ public double getVariance() {
+ return getVariance (alpha, beta, delta);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (alpha, beta, delta);
+ }
+
+ /**
+ * Computes and returns the density function.
+ *
+ */
+ public static double density (double alpha, double beta, double delta,
+ double x) {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ final double z = (x - delta)/beta;
+ if (z <= 0.0)
+ return 0.0;
+ double t = Math.pow (z, -alpha);
+ return alpha * t * Math.exp (-t) / (z * beta);
+ }
+
+
+ /**
+ * Computes and returns the distribution function.
+ *
+ */
+ public static double cdf (double alpha, double beta, double delta,
+ double x) {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ final double z = (x - delta)/beta;
+ if (z <= 0.0)
+ return 0.0;
+ double t = Math.pow (z, -alpha);
+ return Math.exp (-t);
+ }
+
+
+ /**
+ * Computes and returns the complementary distribution function <SPAN CLASS="MATH">1 - <I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double barF (double alpha, double beta, double delta,
+ double x) {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ final double z = (x - delta)/beta;
+ if (z <= 0.0)
+ return 1.0;
+ double t = Math.pow (z, -alpha);
+ return -Math.expm1 (-t);
+ }
+
+
+ /**
+ * Computes and returns the inverse distribution function.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double delta,
+ double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return delta;
+ double t = Math.pow (-Math.log (u), 1.0/alpha);
+ if (t <= Double.MIN_NORMAL)
+ return Double.MAX_VALUE;
+ return delta + beta / t;
+ }
+
+
+ /**
+ * Given <SPAN CLASS="MATH"><I>δ</I> =</SPAN> <TT>delta</TT>, estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>β</I>)</SPAN>
+ * of the <SPAN CLASS="textit">Fréchet</SPAN> distribution
+ * using the maximum likelihood method with the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @param delta location parameter
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n, double delta) {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n <= 1");
+
+ Function func = new Function (x, n, delta);
+ double a = 1e-4;
+ double b = 1.0e12;
+ double alpha = RootFinder.brentDekker (a, b, func, 1e-12);
+ double par[] = new double[2];
+ par[0] = alpha;
+ par[1] = func.dif * Math.pow (func.sumxi, -1.0/alpha);
+ return par;
+ }
+
+
+ /**
+ * Given <SPAN CLASS="MATH"><I>δ</I> =</SPAN> <TT>delta</TT>, creates a new instance of a <SPAN CLASS="textit">Fréchet</SPAN>
+ * distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> estimated using the
+ * maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param delta location parameter
+ *
+ */
+ public static FrechetDist getInstanceFromMLE (double[] x, int n,
+ double delta) {
+ double par[] = getMLE (x, n, delta);
+ return new FrechetDist (par[0], par[1], delta);
+ }
+
+
+ /**
+ * Returns the mean of the <SPAN CLASS="textit">Fréchet</SPAN> distribution with
+ * parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the mean
+ *
+ */
+ public static double getMean (double alpha, double beta, double delta) {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 1)
+ throw new IllegalArgumentException ("alpha <= 1");
+ double t = Num.lnGamma(1.0 - 1.0/alpha);
+ return delta + beta * Math.exp(t);
+ }
+
+
+ /**
+ * Returns the variance of the <SPAN CLASS="textit">Fréchet</SPAN> distribution with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the variance
+ *
+ */
+ public static double getVariance (double alpha, double beta,
+ double delta) {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 2)
+ throw new IllegalArgumentException ("alpha <= 2");
+ double t = Num.lnGamma(1.0 - 1.0/alpha);
+ double mu = Math.exp(t);
+ double v = Math.exp(Num.lnGamma(1.0 - 2.0/alpha));
+ return beta * beta * (v - mu * mu);
+ }
+
+
+ /**
+ * Returns the standard deviation of the <SPAN CLASS="textit">Fréchet</SPAN> distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the standard deviation
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta,
+ double delta) {
+ return Math.sqrt(getVariance (alpha, beta, delta));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double alpha, double beta, double delta) {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }
+
+
+ /**
+ * Return an array containing the parameters of the current object
+ * in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams() {
+ double[] retour = {alpha, beta, delta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta + ", delta = " + delta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/FrechetDist.tex b/source/umontreal/iro/lecuyer/probdist/FrechetDist.tex
new file mode 100644
index 0000000..6640a70
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/FrechetDist.tex
@@ -0,0 +1,551 @@
+\defmodule {FrechetDist}
+
+Extends the class \class{ContinuousDistribution} for the \emph{Fr\'echet}
+distribution \cite[page 3]{tJOH95b}, with location parameter $\delta$, scale
+ parameter $\beta > 0$, and shape parameter $\alpha > 0$, where we use
+ the notation $z = (x-\delta)/\beta$. It has density
+\begin{htmlonly}
+\eq
+ f (x) = \alpha e^{-z^{-\alpha}} / ( \beta z^{\alpha +1} ),
+ \qquad \mbox{for } x > \delta
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\[
+f (x) =
+ \frac{\alpha e^{-z^{-\alpha}}} {\beta z^{\alpha +1}},
+ \qquad \mbox{for } x > \delta
+\]
+\end{latexonly}
+and distribution function
+\[
+F(x) =
+ e^{-z^{-\alpha}}, \qquad \mbox{for } x > \delta.
+\]
+Both the density and the distribution are 0 for $x \le \delta$.
+
+The mean is given by
+\begin{htmlonly}
+\eq
+E[X] = \delta + \beta \Gamma(1 - 1/\alpha),
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\[
+E[X] = \delta + \beta \Gamma\!\left(1 - \frac1\alpha\right),
+\]
+\end{latexonly}
+where $\Gamma(x)$ is the gamma function. The variance is
+\begin{htmlonly}
+\eq
+\mbox{Var}[X] = \beta^2 [\Gamma(1 - 2/\alpha) - (\Gamma(1 - 1/\alpha))^2].
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\[
+\mbox{Var}[X] = \beta^2 \left[\Gamma\!\left(1 - \frac2\alpha\right) -
+ \Gamma^2\!\left(1 - \frac1\alpha\right)\right].
+\]
+\end{latexonly}
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: FrechetDist
+ * Description: Fréchet distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import optimization.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+public class FrechetDist extends ContinuousDistribution\begin{hide} {
+ private double delta;
+ private double beta;
+ private double alpha;
+
+
+ private static class Optim implements Lmder_fcn {
+ protected double[] x;
+ protected int n;
+
+ public Optim (double[] x, int n) {
+ this.n = n;
+ this.x = x;
+ }
+
+ public void fcn (int m, int n, double[] par, double[] fvec, double[][] fjac, int iflag[])
+ {
+ if (par[1] <= 0.0 || par[2] <= 0.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fvec[3] = BIG;
+ return;
+ }
+
+ double sum1, sum2, sumb, sum4, sum5;
+ double z, w, v;
+ double alpha = par[1];
+ double beta = par[2];
+ double mu = par[3];
+
+ if (iflag[1] == 1) {
+ sum1 = sum2 = sumb = sum4 = sum5 = 0;
+ for (int i = 0; i < n; i++) {
+ z = (x[i] - mu) / beta;
+ sum1 += 1.0 / z;
+ v = Math.pow(z, -alpha);
+ sum2 += v / z;
+ sumb += v;
+ w = Math.log(z);
+ sum4 += w;
+ sum5 += v * w;
+ }
+
+ fvec[2] = sumb - n; // eq. for beta
+ fvec[3] = (alpha + 1) * sum1 - alpha * sum2; // eq. for mu
+ fvec[1] = n / alpha + sum5 - sum4; // eq. for alpha
+
+ } else if (iflag[1] == 2) {
+ throw new IllegalArgumentException ("iflag = 2");
+ // The 3 X 3 Jacobian must be calculated and put in fjac
+ }
+ }
+ }
+
+
+ private static class Function implements MathFunction {
+ private int n;
+ private double[] x;
+ private double delta;
+ public double sumxi;
+ public double dif;
+
+ public Function (double[] y, int n, double delta) {
+ this.n = n;
+ this.x = y;
+ this.delta = delta;
+ double xmin = Double.MAX_VALUE;
+ for (int i = 0; i < n; i++) {
+ if ((y[i] < xmin) && (y[i] > delta))
+ xmin = y[i];
+ }
+ dif = xmin - delta;
+ }
+
+ public double evaluate (double alpha) {
+ if (alpha <= 0.0) return 1.0e100;
+ double v, w;
+ double sum1 = 0, sum2 = 0, sum3 = 0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] <= delta)
+ continue;
+ v = Math.log(x[i] - delta);
+ w = Math.pow(dif / (x[i] - delta), alpha);
+ sum1 += v;
+ sum2 += w;
+ sum3 += v * w;
+ }
+
+ sum1 /= n;
+ sumxi = sum2 / n;
+ return 1 / alpha + sum3 / sum2 - sum1;
+ }
+ }\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public FrechetDist (double alpha)\begin{hide} {
+ setParams (alpha, 1.0, 0.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructor for the standard \emph{Fr\'echet}
+ distribution with parameters $\beta$ = 1 and $\delta$ = 0.
+ \end{tabb}
+\begin{code}
+
+ public FrechetDist (double alpha, double beta, double delta)\begin{hide} {
+ setParams (alpha, beta, delta);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{FrechetDist} object with parameters
+ $\alpha$ = \texttt{alpha}, $\beta$ = \texttt{beta} and $\delta$ = \texttt{delta}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (alpha, beta, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, delta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, delta, u);
+ }
+
+ public double getMean() {
+ return getMean (alpha, beta, delta);
+ }
+
+ public double getVariance() {
+ return getVariance (alpha, beta, delta);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (alpha, beta, delta);
+ }\end{hide}
+
+ public static double density (double alpha, double beta, double delta,
+ double x)\begin{hide} {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ final double z = (x - delta)/beta;
+ if (z <= 0.0)
+ return 0.0;
+ double t = Math.pow (z, -alpha);
+ return alpha * t * Math.exp (-t) / (z * beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta, double delta,
+ double x)\begin{hide} {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ final double z = (x - delta)/beta;
+ if (z <= 0.0)
+ return 0.0;
+ double t = Math.pow (z, -alpha);
+ return Math.exp (-t);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes and returns the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double delta,
+ double x)\begin{hide} {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ final double z = (x - delta)/beta;
+ if (z <= 0.0)
+ return 1.0;
+ double t = Math.pow (z, -alpha);
+ return -Math.expm1 (-t);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes and returns the complementary distribution function $1 - F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double beta, double delta,
+ double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return delta;
+ double t = Math.pow (-Math.log (u), 1.0/alpha);
+ if (t <= Double.MIN_NORMAL)
+ return Double.MAX_VALUE;
+ return delta + beta / t;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes and returns the inverse distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n, double delta)\begin{hide} {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n <= 1");
+
+ Function func = new Function (x, n, delta);
+ double a = 1e-4;
+ double b = 1.0e12;
+ double alpha = RootFinder.brentDekker (a, b, func, 1e-12);
+ double par[] = new double[2];
+ par[0] = alpha;
+ par[1] = func.dif * Math.pow (func.sumxi, -1.0/alpha);
+ return par;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Given $\delta =$ \texttt{delta}, estimates the parameters $(\alpha, \beta)$
+ of the \emph{Fr\'echet} distribution
+ using the maximum likelihood method with the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\beta$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\alpha, \hat\beta)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat\beta & = & \left(\frac1n \sum_{i=0}^{n-1} (x_i - \delta)^{-\hat\alpha}\right)^{\!\!-1/\hat\alpha} \\[0.5em]
+ \frac1n \sum_{i=0}^{n-1} \ln(x_i - \delta) & = & \frac 1 {\hat\alpha} +
+ \frac{\sum_{i=0}^{n-1} (x_i - \delta)^{-\hat\alpha}\ln(x_i - \delta)}
+ {\sum_{i=0}^{n-1} (x_i - \delta)^{-\hat\alpha}}.
+ \end{eqnarray*}
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \param{delta}{location parameter}
+ \return{returns the parameters [$\hat{\alpha}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static FrechetDist getInstanceFromMLE (double[] x, int n,
+ double delta)\begin{hide} {
+ double par[] = getMLE (x, n, delta);
+ return new FrechetDist (par[0], par[1], delta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Given $\delta =$ \texttt{delta}, creates a new instance of a \emph{Fr\'echet}
+distribution with parameters $\alpha$ and $\beta$ estimated using the
+maximum likelihood method based on the $n$ observations $x[i]$,
+$i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{delta}{location parameter}
+\end{htmlonly}
+% \begin{code}
+%
+% public static double[] getMLE (double[] x, int n)\begin{hide} {
+% if (n <= 1)
+% throw new IllegalArgumentException ("n <= 1");
+% throw new UnsupportedOperationException("NOT IMPLEMENTED");
+% // TO DO: The Jacobian must be calculated and put in class Optim above
+% // Then it will be finished.
+% final int m = 3;
+% double[] param = new double[m + 1];
+% param[1] = param[2] = param[3] = 1;
+%
+% double[] fvec = new double [m + 1];
+% double[][] fjac = new double[m + 1][m + 1];
+% int[] iflag = new int[2];
+% int[] info = new int[2];
+% int[] ipvt = new int[m + 1];
+% Optim system = new Optim (x, n);
+%
+% Minpack_f77.lmder1_f77 (system, m, m, param, fvec, fjac, 1e-5, info, ipvt);
+%
+% double par[] = new double[m];
+% par[0] = param[1];
+% par[1] = param[2];
+% par[2] = param[3];
+%
+% return par;
+% }\end{hide}
+% \end{code}
+% \begin{tabb}
+% Estimates the parameters $(\alpha, \beta, \delta)$
+% of the \emph{Fr\'echet} distribution
+% using the maximum likelihood method with the $n$ observations
+% $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a three-element
+% array, in regular order: [$\alpha$, $\beta$, $\delta$].
+% \begin{detailed}
+% The maximum likelihood estimators are the values $(\hat\alpha, \hat\beta, \hat{\delta})$
+% that satisfy the equations:
+% \begin{eqnarray*}
+% n & = & \sum_{i=0}^{n-1} z_i^{-\hat\alpha}, \\[0.5em]
+% (\hat\alpha + 1)\sum_{i=0}^{n-1} z_i^{-1} & = &
+% \hat\alpha \sum_{i=0}^{n-1} z_i^{-\hat\alpha - 1}, \\[0.5em]
+% \sum_{i=0}^{n-1} \ln (z_i) & = & \frac n {\hat\alpha} +
+% {\sum_{i=0}^{n-1} z_i^{-\hat\alpha}\ln(z_i)},
+% \end{eqnarray*}
+% where we use the notation $z_i = (x_i - \hat\delta) / \hat\beta$.
+% \end{detailed}
+% \end{tabb}
+% \begin{htmlonly}
+% \param{x}{the list of observations used to evaluate parameters}
+% \param{n}{the number of observations used to evaluate parameters}
+% \return{returns the parameters [$\hat{\alpha}$, $\hat{\beta}$, $\hat{\delta}$]}
+% \end{htmlonly}
+% \begin{code}
+%
+% public static FrechetDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+% double par[] = getMLE (x, n);
+% return new FrechetDist (par[0], par[1], par[2]);
+% }\end{hide}
+% \end{code}
+% \begin{tabb}
+% Creates a new instance of a \emph{Fr\'echet}
+% distribution with parameters $\alpha$, $\beta$ and $\delta$ estimated using the
+% maximum likelihood method based on the $n$ observations $x[i]$,
+% $i = 0, 1, \ldots, n-1$.
+% \end{tabb}
+% \begin{htmlonly}
+% \param{x}{the list of observations to use to evaluate parameters}
+% \param{n}{the number of observations to use to evaluate parameters}
+% \end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta, double delta)\begin{hide} {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 1)
+ throw new IllegalArgumentException ("alpha <= 1");
+ double t = Num.lnGamma(1.0 - 1.0/alpha);
+ return delta + beta * Math.exp(t);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean of the \emph{Fr\'echet} distribution with
+ parameters $\alpha$, $\beta$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta,
+ double delta)\begin{hide} {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 2)
+ throw new IllegalArgumentException ("alpha <= 2");
+ double t = Num.lnGamma(1.0 - 1.0/alpha);
+ double mu = Math.exp(t);
+ double v = Math.exp(Num.lnGamma(1.0 - 2.0/alpha));
+ return beta * beta * (v - mu * mu);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance of the \emph{Fr\'echet} distribution with parameters
+ $\alpha$, $\beta$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta,
+ double delta)\begin{hide} {
+ return Math.sqrt(getVariance (alpha, beta, delta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation of the \emph{Fr\'echet} distribution
+with parameters $\alpha$, $\beta$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $\delta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double beta, double delta)\begin{hide} {
+ if (beta <= 0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the parameters $\alpha$, $\beta$ and $\delta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams()\begin{hide} {
+ double[] retour = {alpha, beta, delta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameters of the current object
+ in regular order: [$\alpha$, $\beta$, $\delta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta + ", delta = " + delta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/GammaDist.java b/source/umontreal/iro/lecuyer/probdist/GammaDist.java
new file mode 100644
index 0000000..67108e7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GammaDist.java
@@ -0,0 +1,600 @@
+
+
+/*
+ * Class: GammaDist
+ * Description: gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import umontreal.iro.lecuyer.util.RootFinder;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>gamma</EM> distribution with
+ * shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter
+ *
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>λ</I><SUP><I>α</I></SUP><I>x</I><SUP><I>α</I>-1</SUP><I>e</I><SUP>-<I>λ</I>x</SUP>/<I>Γ</I>(<I>α</I>), for <I>x</I> > 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function, defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>Γ</I>(<I>α</I>) = ∫<SUB>0</SUB><SUP>∞</SUP><I>x</I><SUP><I>α</I>-1</SUP><I>e</I><SUP>-x</SUP><I>dx</I>.
+ * </DIV><P></P>
+ * In particular,
+ * <SPAN CLASS="MATH"><I>Γ</I>(<I>n</I>) = (<I>n</I> - 1)!</SPAN> when <SPAN CLASS="MATH"><I>n</I></SPAN> is a positive integer.
+ *
+ */
+public class GammaDist extends ContinuousDistribution {
+ private double alpha;
+ private double lambda;
+ private double logFactor; // Log (lambda^alpha / Gamma (alpha))
+ private static final double ALIM = 1.0E5;
+
+ private static class Function implements MathFunction {
+ // For MLE
+ private int n;
+ private double empiricalMean;
+ private double sumLn;
+
+ public Function (int n, double empiricalMean, double sumLn) {
+ this.n = n;
+ this.empiricalMean = empiricalMean;
+ this.sumLn = sumLn;
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0) return 1.0e200;
+ return (n * Math.log (empiricalMean / x) + n * Num.digamma (x) - sumLn);
+ }
+ }
+
+
+ private static class myFunc implements MathFunction {
+ // For inverseF
+ protected int d;
+ protected double alp, u;
+
+ public myFunc (double alp, int d, double u) {
+ this.alp = alp;
+ this.d = d;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - GammaDist.cdf(alp, d, x);
+ }
+ }
+
+
+
+
+ /**
+ * Constructs a <TT>GammaDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>.
+ *
+ */
+ public GammaDist (double alpha) {
+ setParams (alpha, 1.0, decPrec);
+ }
+
+
+ /**
+ * Constructs a <TT>GammaDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>.
+ *
+ */
+ public GammaDist (double alpha, double lambda) {
+ setParams (alpha, lambda, decPrec);
+ }
+
+
+ /**
+ * Constructs a <TT>GammaDist</TT> object with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> =
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>, and approximations of
+ * roughly <TT>d</TT> decimal digits of precision when computing functions.
+ *
+ */
+ public GammaDist (double alpha, double lambda, int d) {
+ setParams (alpha, lambda, d);
+ }
+
+
+ static double mybelog (double x)
+ /*
+ * This is the function 1 + (1 - x*x + 2*x*log(x)) / ((1 - x)*(1 - x))
+ */
+ {
+ if (x < 1.0e-30)
+ return 0.0;
+ if (x > 1.0e30)
+ return 2.0*(Math.log(x) - 1.0) / x;
+ if (x == 1.0)
+ return 1.0;
+
+ double t = 1.0 - x;
+ if (x < 0.9 || x > 1.1) {
+ double w = (t + x*Math.log(x)) / (t*t);
+ return 2.0 * w;
+ }
+
+ // For x near 1, use a series expansion to avoid loss of precision.
+ double term;
+ final double EPS = 1.0e-12;
+ double tpow = 1.0;
+ double sum = 0.5;
+ int j = 3;
+ do {
+ tpow *= t;
+ term = tpow / (j * (j - 1));
+ sum += term;
+ j++;
+ } while (Math.abs (term / sum) > EPS);
+ return 2.0*sum;
+ }
+
+
+ public double density (double x) {
+ if (x <= 0)
+ return 0.0;
+ double z = logFactor + (alpha - 1.0) * Math.log(x) - lambda * x;
+ if (z > -XBIGM)
+ return Math.exp (z);
+ else
+ return 0.0;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, decPrec, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, decPrec, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, decPrec, u)/lambda;
+ }
+
+ public double getMean() {
+ return GammaDist.getMean (alpha, lambda);
+ }
+
+ public double getVariance() {
+ return GammaDist.getVariance (alpha, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return GammaDist.getStandardDeviation (alpha, lambda);
+ }
+
+
+ /**
+ * Computes the density function at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double density (double alpha, double lambda, double x) {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ if (x <= 0)
+ return 0.0;
+ double z = alpha * Math.log (lambda*x) - lambda*x - Num.lnGamma (alpha);
+ if (z > -XBIGM)
+ return Math.exp (z) / x;
+ else
+ return 0.0;
+ }
+
+
+ /**
+ * Returns an approximation of the gamma distribution
+ * function with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>.
+ * The function tries to return <SPAN CLASS="MATH"><I>d</I></SPAN> decimals digits of precision.
+ * For <SPAN CLASS="MATH"><I>α</I></SPAN> not too large (e.g.,
+ * <SPAN CLASS="MATH"><I>α</I> <= 1000</SPAN>),
+ * <SPAN CLASS="MATH"><I>d</I></SPAN> gives a good idea of the precision attained.
+ *
+ */
+ public static double cdf (double alpha, double lambda, int d, double x) {
+ return cdf (alpha, d, lambda*x);
+ }
+
+
+ /**
+ * Equivalent to <TT>cdf (alpha, 1.0, d, x)</TT>.
+ *
+ */
+ public static double cdf (double alpha, int d, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (1.0 == alpha)
+ return ExponentialDist.cdf (1.0, x);
+
+ if (alpha > 10.0) {
+ if (x > alpha * 10.0)
+ return 1.0;
+ } else {
+ if (x > XBIG)
+ return 1.0;
+ }
+
+ if (alpha >= ALIM) {
+ double d2 = x + 1.0/3.0 - alpha - 0.02/alpha;
+ double S = alpha - 1.0/2.0;
+ double w = mybelog(S/x);
+ double y = d2 * Math.sqrt(w/x);
+ return NormalDist.cdf01 (y);
+ }
+
+ if (x <= 1.0 || x < alpha) {
+ double factor, z, rn, term;
+ factor = Math.exp (alpha*Math.log (x) - x - Num.lnGamma (alpha));
+ final double EPS = EPSARRAY[d];
+ z = 1.0;
+ term = 1.0;
+ rn = alpha;
+ do {
+ rn += 1.0;
+ term *= x/rn;
+ z += term;
+ } while (term >= EPS * z);
+ return z*factor/alpha;
+
+ } else
+ return 1.0 - barF (alpha, d, x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (double alpha, double lambda, int d, double x) {
+ return barF (alpha, d, lambda*x);
+ }
+
+
+ /**
+ * Same as {@link #barF(double,double,int,double) barF} <TT>(alpha, 1.0, d, x)</TT>.
+ *
+ */
+ public static double barF (double alpha, int d, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (1.0 == alpha)
+ return ExponentialDist.barF (1.0, x);
+
+ if (alpha >= 70.0) {
+ if (x >= alpha * XBIG)
+ return 0.0;
+ } else {
+ if (x >= XBIGM)
+ return 0.0;
+ }
+
+ if (alpha >= ALIM) {
+ double d2 = x + 1.0/3.0 - alpha - 0.02/alpha;
+ double S = alpha - 1.0/2.0;
+ double w = mybelog(S/x);
+ double y = d2 * Math.sqrt(w/x);
+ return NormalDist.barF01 (y);
+ }
+
+ if (x <= 1.0 || x < alpha)
+ return 1.0 - cdf (alpha, d, x);
+
+ double[] V = new double[6];
+ final double EPS = EPSARRAY[d];
+ final double RENORM = 1.0E100;
+ double R, dif;
+ int i;
+ double factor = Math.exp (alpha*Math.log (x) - x - Num.lnGamma (alpha));
+
+ double A = 1.0 - alpha;
+ double B = A + x + 1.0;
+ double term = 0.0;
+ V[0] = 1.0;
+ V[1] = x;
+ V[2] = x + 1.0;
+ V[3] = x * B;
+ double res = V[2]/V[3];
+
+ do {
+ A += 1.0;
+ B += 2.0;
+ term += 1.0;
+ V[4] = B * V[2] - A * term * V[0];
+ V[5] = B * V[3] - A * term * V[1];
+ if (V[5] != 0.0) {
+ R = V[4]/V[5];
+ dif = Math.abs (res - R);
+ if (dif <= EPS*R)
+ return factor*res;
+ res = R;
+ }
+ for (i = 0; i < 4; i++)
+ V[i] = V[i + 2];
+ if (Math.abs (V[4]) >= RENORM) {
+ for (i = 0; i < 4; i++)
+ V[i] /= RENORM;
+ }
+ } while (true);
+ }
+
+
+ /**
+ * Computes the inverse distribution function.
+ *
+ */
+ public static double inverseF (double alpha, double lambda, int d,
+ double u) {
+ return inverseF (alpha, d, u)/lambda;
+ }
+
+
+ /**
+ * Same as
+ * {@link #inverseF(double,double,int,double) inverseF} <TT>(alpha, 1, d, u)</TT>.
+ *
+ */
+ public static double inverseF (double alpha, int d, double u) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0)
+ return 0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (d > 15)
+ d = 15;
+ final double EPS = Math.pow (10.0, -d);
+
+ double sigma = GammaDist.getStandardDeviation (alpha, 1.0);
+ double x = NormalDist.inverseF (alpha, sigma, u);
+ if (x < 0.)
+ x = 0.;
+ double v = GammaDist.cdf (alpha, d, x);
+ double xmax;
+ if (alpha < 1.0)
+ xmax = 100.0;
+ else
+ xmax = alpha + 40.0 * sigma;
+ myFunc f = new myFunc (alpha, d, u);
+
+ if (u <= 1.0e-8 || alpha <= 1.5) {
+ if (v < u)
+ return RootFinder.bisection (x, xmax, f, EPS);
+ else
+ return RootFinder.bisection (0, x, f, EPS);
+ } else {
+ if (v < u)
+ return RootFinder.brentDekker (x, xmax, f, EPS);
+ else
+ return RootFinder.brentDekker (0, x, f, EPS);
+ }
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>λ</I>)</SPAN> of the gamma distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double parameters[];
+ double sum = 0.0;
+ double sumLn = 0.0;
+ double empiricalMean;
+ double alphaMME;
+ double a;
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+
+ parameters = new double[2];
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ for (int i = 0; i < n; i++)
+ {
+ sum += x[i];
+ if (x[i] <= 0.0)
+ sumLn += LN_EPS;
+ else
+ sumLn += Math.log (x[i]);
+ }
+ empiricalMean = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += (x[i] - empiricalMean) * (x[i] - empiricalMean);
+ }
+
+ alphaMME = (empiricalMean * empiricalMean * (double) n) / sum;
+ if ((a = alphaMME - 10.0) <= 0) {
+ a = 1.0e-5;
+ }
+
+ Function f = new Function (n, empiricalMean, sumLn);
+ parameters[0] = RootFinder.brentDekker (a, alphaMME + 10.0, f, 1e-7);
+ parameters[1] = parameters[0] / empiricalMean;
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a gamma distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static GammaDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new GammaDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I>/<I>λ</I></SPAN>
+ * of the gamma distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the gamma distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I>/<I>λ</I></SPAN>
+ *
+ */
+ public static double getMean (double alpha, double lambda) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (alpha / lambda);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>α</I>/<I>λ</I><SUP>2</SUP></SPAN>
+ * of the gamma distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the gamma distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>α</I>/<I>λ</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double alpha, double lambda) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (alpha / (lambda * lambda));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the gamma
+ * distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the gamma distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double lambda) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt(alpha) / lambda);
+ }
+
+
+ /**
+ * Return the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> for this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Return the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ public void setParams (double alpha, double lambda, int d) {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ this.alpha = alpha;
+ this.lambda = lambda;
+ this.decPrec = d;
+ logFactor = alpha * Math.log(lambda) - Num.lnGamma (alpha);
+ supportA = 0.0;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/GammaDist.tex b/source/umontreal/iro/lecuyer/probdist/GammaDist.tex
new file mode 100644
index 0000000..143fbef
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GammaDist.tex
@@ -0,0 +1,619 @@
+\defmodule {GammaDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em gamma\/} distribution \cite[page 337]{tJOH95a} with
+shape parameter $\alpha > 0$ and scale parameter
+$\lambda > 0$.
+The density is
+\begin{htmlonly}
+\eq
+ f(x) = \lambda^{\alpha} x^{\alpha - 1}e^{-\lambda x}/\Gamma(\alpha),
+ \qquad \mbox {for } x > 0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac{\lambda^\alpha x^{\alpha - 1}e^{-\lambda x}}
+ {\Gamma(\alpha)},
+ \qquad \mbox {for } x > 0,\eqlabel{eq:fgamma}
+\endeq
+\end{latexonly}
+where $\Gamma$ is the gamma function, defined by
+\eq
+ \Gamma (\alpha) = \int_0^\infty x^{\alpha-1} e^{-x} dx.
+ \eqlabel{eq:Gamma}
+\endeq
+In particular, $\Gamma(n) = (n-1)!$ when $n$ is a positive integer.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaDist
+ * Description: gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import umontreal.iro.lecuyer.util.RootFinder;
+\end{hide}
+
+public class GammaDist extends ContinuousDistribution\begin{hide} {
+ private double alpha;
+ private double lambda;
+ private double logFactor; // Log (lambda^alpha / Gamma (alpha))
+ private static final double ALIM = 1.0E5;
+
+ private static class Function implements MathFunction {
+ // For MLE
+ private int n;
+ private double empiricalMean;
+ private double sumLn;
+
+ public Function (int n, double empiricalMean, double sumLn) {
+ this.n = n;
+ this.empiricalMean = empiricalMean;
+ this.sumLn = sumLn;
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0) return 1.0e200;
+ return (n * Math.log (empiricalMean / x) + n * Num.digamma (x) - sumLn);
+ }
+ }
+
+
+ private static class myFunc implements MathFunction {
+ // For inverseF
+ protected int d;
+ protected double alp, u;
+
+ public myFunc (double alp, int d, double u) {
+ this.alp = alp;
+ this.d = d;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - GammaDist.cdf(alp, d, x);
+ }
+ }
+
+
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public GammaDist (double alpha)\begin{hide} {
+ setParams (alpha, 1.0, decPrec);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{GammaDist} object with parameters
+ $\alpha$ = \texttt{alpha} and $\lambda=1$.
+ \end{tabb}
+\begin{code}
+
+ public GammaDist (double alpha, double lambda)\begin{hide} {
+ setParams (alpha, lambda, decPrec);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{GammaDist} object with parameters
+ $\alpha$ = \texttt{alpha} and $\lambda$ = \texttt{lambda}.
+ \end{tabb}
+\begin{code}
+
+ public GammaDist (double alpha, double lambda, int d)\begin{hide} {
+ setParams (alpha, lambda, d);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{GammaDist} object with parameters $\alpha$ =
+ \texttt{alpha} and $\lambda$ = \texttt{lambda}, and approximations of
+ roughly \texttt{d} decimal digits of precision when computing functions.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}
+\begin{code}
+
+ static double mybelog (double x)
+ /*
+ * This is the function 1 + (1 - x*x + 2*x*log(x)) / ((1 - x)*(1 - x))
+ */
+ {
+ if (x < 1.0e-30)
+ return 0.0;
+ if (x > 1.0e30)
+ return 2.0*(Math.log(x) - 1.0) / x;
+ if (x == 1.0)
+ return 1.0;
+
+ double t = 1.0 - x;
+ if (x < 0.9 || x > 1.1) {
+ double w = (t + x*Math.log(x)) / (t*t);
+ return 2.0 * w;
+ }
+
+ // For x near 1, use a series expansion to avoid loss of precision.
+ double term;
+ final double EPS = 1.0e-12;
+ double tpow = 1.0;
+ double sum = 0.5;
+ int j = 3;
+ do {
+ tpow *= t;
+ term = tpow / (j * (j - 1));
+ sum += term;
+ j++;
+ } while (Math.abs (term / sum) > EPS);
+ return 2.0*sum;
+ }
+
+
+ public double density (double x) {
+ if (x <= 0)
+ return 0.0;
+ double z = logFactor + (alpha - 1.0) * Math.log(x) - lambda * x;
+ if (z > -XBIGM)
+ return Math.exp (z);
+ else
+ return 0.0;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, decPrec, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, decPrec, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, decPrec, u)/lambda;
+ }
+
+ public double getMean() {
+ return GammaDist.getMean (alpha, lambda);
+ }
+
+ public double getVariance() {
+ return GammaDist.getVariance (alpha, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return GammaDist.getStandardDeviation (alpha, lambda);
+ }
+\end{code}
+\end{hide}\begin{code}
+
+ public static double density (double alpha, double lambda, double x)\begin{hide} {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ if (x <= 0)
+ return 0.0;
+ double z = alpha * Math.log (lambda*x) - lambda*x - Num.lnGamma (alpha);
+ if (z > -XBIGM)
+ return Math.exp (z) / x;
+ else
+ return 0.0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:fgamma}) at $x$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double lambda, int d, double x)\begin{hide} {
+ return cdf (alpha, d, lambda*x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns an approximation of the gamma distribution
+function with parameters $\alpha$ = \texttt{alpha} and
+$\lambda$ = \texttt{lambda}\html{.}\latex{, whose density is given
+by (\ref{eq:fgamma}).
+% One has $\Gamma (\alpha) = (\alpha-1)!$ when $\alpha$ is an integer.
+ The approximation is an improved version of the algorithm in \cite{tBAT70a}.}
+ The function tries to return $d$ decimals digits of precision.
+%, but there is no guarantee.
+ For $\alpha$ not too large (e.g., $\alpha \le 1000$),
+ $d$ gives a good idea of the precision attained.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, int d, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (1.0 == alpha)
+ return ExponentialDist.cdf (1.0, x);
+
+ if (alpha > 10.0) {
+ if (x > alpha * 10.0)
+ return 1.0;
+ } else {
+ if (x > XBIG)
+ return 1.0;
+ }
+
+ if (alpha >= ALIM) {
+ double d2 = x + 1.0/3.0 - alpha - 0.02/alpha;
+ double S = alpha - 1.0/2.0;
+ double w = mybelog(S/x);
+ double y = d2 * Math.sqrt(w/x);
+ return NormalDist.cdf01 (y);
+ }
+
+ if (x <= 1.0 || x < alpha) {
+ double factor, z, rn, term;
+ factor = Math.exp (alpha*Math.log (x) - x - Num.lnGamma (alpha));
+ final double EPS = EPSARRAY[d];
+ z = 1.0;
+ term = 1.0;
+ rn = alpha;
+ do {
+ rn += 1.0;
+ term *= x/rn;
+ z += term;
+ } while (term >= EPS * z);
+ return z*factor/alpha;
+
+ } else
+ return 1.0 - barF (alpha, d, x);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Equivalent to \texttt{cdf (alpha, 1.0, d, x)}.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double lambda, int d, double x)\begin{hide} {
+ return barF (alpha, d, lambda*x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, int d, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (1.0 == alpha)
+ return ExponentialDist.barF (1.0, x);
+
+ if (alpha >= 70.0) {
+ if (x >= alpha * XBIG)
+ return 0.0;
+ } else {
+ if (x >= XBIGM)
+ return 0.0;
+ }
+
+ if (alpha >= ALIM) {
+ double d2 = x + 1.0/3.0 - alpha - 0.02/alpha;
+ double S = alpha - 1.0/2.0;
+ double w = mybelog(S/x);
+ double y = d2 * Math.sqrt(w/x);
+ return NormalDist.barF01 (y);
+ }
+
+ if (x <= 1.0 || x < alpha)
+ return 1.0 - cdf (alpha, d, x);
+
+ double[] V = new double[6];
+ final double EPS = EPSARRAY[d];
+ final double RENORM = 1.0E100;
+ double R, dif;
+ int i;
+ double factor = Math.exp (alpha*Math.log (x) - x - Num.lnGamma (alpha));
+
+ double A = 1.0 - alpha;
+ double B = A + x + 1.0;
+ double term = 0.0;
+ V[0] = 1.0;
+ V[1] = x;
+ V[2] = x + 1.0;
+ V[3] = x * B;
+ double res = V[2]/V[3];
+
+ do {
+ A += 1.0;
+ B += 2.0;
+ term += 1.0;
+ V[4] = B * V[2] - A * term * V[0];
+ V[5] = B * V[3] - A * term * V[1];
+ if (V[5] != 0.0) {
+ R = V[4]/V[5];
+ dif = Math.abs (res - R);
+ if (dif <= EPS*R)
+ return factor*res;
+ res = R;
+ }
+ for (i = 0; i < 4; i++)
+ V[i] = V[i + 2];
+ if (Math.abs (V[4]) >= RENORM) {
+ for (i = 0; i < 4; i++)
+ V[i] /= RENORM;
+ }
+ } while (true);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Same as \method{barF}{double,double,int,double}~\texttt{(alpha, 1.0, d, x)}.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double lambda, int d,
+ double u)\begin{hide} {
+ return inverseF (alpha, d, u)/lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse distribution function.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, int d, double u)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0)
+ return 0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+ if (d > 15)
+ d = 15;
+ final double EPS = Math.pow (10.0, -d);
+
+ double sigma = GammaDist.getStandardDeviation (alpha, 1.0);
+ double x = NormalDist.inverseF (alpha, sigma, u);
+ if (x < 0.)
+ x = 0.;
+ double v = GammaDist.cdf (alpha, d, x);
+ double xmax;
+ if (alpha < 1.0)
+ xmax = 100.0;
+ else
+ xmax = alpha + 40.0 * sigma;
+ myFunc f = new myFunc (alpha, d, u);
+
+ if (u <= 1.0e-8 || alpha <= 1.5) {
+ if (v < u)
+ return RootFinder.bisection (x, xmax, f, EPS);
+ else
+ return RootFinder.bisection (0, x, f, EPS);
+ } else {
+ if (v < u)
+ return RootFinder.brentDekker (x, xmax, f, EPS);
+ else
+ return RootFinder.brentDekker (0, x, f, EPS);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as
+\method{inverseF}{double,double,int,double}~\texttt{(alpha, 1, d, u)}.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double parameters[];
+ double sum = 0.0;
+ double sumLn = 0.0;
+ double empiricalMean;
+ double alphaMME;
+ double a;
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+
+ parameters = new double[2];
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ for (int i = 0; i < n; i++)
+ {
+ sum += x[i];
+ if (x[i] <= 0.0)
+ sumLn += LN_EPS;
+ else
+ sumLn += Math.log (x[i]);
+ }
+ empiricalMean = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += (x[i] - empiricalMean) * (x[i] - empiricalMean);
+ }
+
+ alphaMME = (empiricalMean * empiricalMean * (double) n) / sum;
+ if ((a = alphaMME - 10.0) <= 0) {
+ a = 1.0e-5;
+ }
+
+ Function f = new Function (n, empiricalMean, sumLn);
+ parameters[0] = RootFinder.brentDekker (a, alphaMME + 10.0, f, 1e-7);
+ parameters[1] = parameters[0] / empiricalMean;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\lambda)$ of the gamma distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\lambda$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\alpha , \hat\lambda)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \frac{1}{n} \sum_{i=1}^{n} \ln(x_i) - \ln(\bar{x}_n) & = &
+ \psi(\hat{\alpha}) - \ln(\hat{\alpha})\\
+ \hat{\lambda} \bar{x}_n & = & \hat{\alpha}
+ \end{eqnarray*}
+ where $\bar x_n$ is the average of $x[0],\dots,x[n-1]$, and
+ $\psi$ is the logarithmic derivative of the Gamma function
+ $\psi(x) = \Gamma'(x) / \Gamma(x)$ (\cite[page 361]{tJOH95a}).
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}$, $\hat{\lambda}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static GammaDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new GammaDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a gamma distribution with parameters $\alpha$ and $\lambda$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double lambda)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (alpha / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \alpha/\lambda$
+ of the gamma distribution with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the gamma distribution $E[X] = \alpha / \lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double lambda)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (alpha / (lambda * lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = \alpha/\lambda^2$
+ of the gamma distribution with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the gamma distribution $\mbox{Var}[X] = \alpha / \lambda^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double lambda)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt(alpha) / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the gamma
+ distribution with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the gamma distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Return the parameter $\alpha$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Return the parameter $\lambda$ for this object.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double lambda, int d)\begin{hide} {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ this.alpha = alpha;
+ this.lambda = lambda;
+ this.decPrec = d;
+ logFactor = alpha * Math.log(lambda) - Num.lnGamma (alpha);
+ supportA = 0.0;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\lambda$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/GammaDistFromMoments.java b/source/umontreal/iro/lecuyer/probdist/GammaDistFromMoments.java
new file mode 100644
index 0000000..97c6d56
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GammaDistFromMoments.java
@@ -0,0 +1,76 @@
+
+
+/*
+ * Class: GammaDistFromMoments
+ * Description: gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Extends the {@link GammaDist} distribution with constructors accepting the
+ * mean <SPAN CLASS="MATH"><I>μ</I></SPAN> and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN> as arguments instead of a shape parameter
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> and a scale parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ * Since
+ * <SPAN CLASS="MATH"><I>μ</I> = <I>α</I>/<I>λ</I></SPAN>, and
+ * <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP> = <I>α</I>/<I>λ</I><SUP>2</SUP></SPAN>,
+ * the shape and scale parameters are
+ * <SPAN CLASS="MATH"><I>α</I> = <I>μ</I><SUP>2</SUP>/<I>σ</I><SUP>2</SUP></SPAN>, and
+ *
+ * <SPAN CLASS="MATH"><I>λ</I> = <I>μ</I>/<I>σ</I><SUP>2</SUP></SPAN>, respectively.
+ *
+ */
+public class GammaDistFromMoments extends GammaDist {
+
+
+
+ /**
+ * Constructs a gamma distribution with mean <TT>mean</TT>,
+ * variance <TT>var</TT>, and <TT>d</TT> decimal of precision.
+ *
+ * @param mean the desired mean.
+ *
+ * @param var the desired variance.
+ *
+ * @param d the number of decimals of precision.
+ *
+ *
+ */
+ public GammaDistFromMoments (double mean, double var, int d) {
+ super (mean * mean / var, mean / var, d);
+ }
+
+
+ /**
+ * Constructs a gamma distribution with
+ * mean <TT>mean</TT>, and variance <TT>var</TT>.
+ *
+ * @param mean the desired mean.
+ *
+ * @param var the desired variance.
+ *
+ */
+ public GammaDistFromMoments (double mean, double var) {
+ super (mean * mean / var, mean / var);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/GammaDistFromMoments.tex b/source/umontreal/iro/lecuyer/probdist/GammaDistFromMoments.tex
new file mode 100644
index 0000000..bfcf883
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GammaDistFromMoments.tex
@@ -0,0 +1,74 @@
+\defmodule{GammaDistFromMoments}
+
+Extends the \class{GammaDist} distribution with constructors accepting the
+mean $\mu$ and variance $\sigma^2$ as arguments instead of a shape parameter
+$\alpha$ and a scale parameter $\lambda$.
+Since $\mu=\alpha / \lambda$, and $\sigma^2=\alpha / \lambda^2$,
+the shape and scale parameters are $\alpha=\mu^2 / \sigma^2$, and
+$\lambda=\mu / \sigma^2$, respectively.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaDistFromMoments
+ * Description: gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class GammaDistFromMoments extends GammaDist\begin{hide} {
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public GammaDistFromMoments (double mean, double var, int d)\begin{hide} {
+ super (mean * mean / var, mean / var, d);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a gamma distribution with mean \texttt{mean},
+ variance \texttt{var}, and \texttt{d} decimal of precision.
+\end{tabb}
+\begin{htmlonly}
+ \param{mean}{the desired mean.}
+ \param{var}{the desired variance.}
+ \param{d}{the number of decimals of precision.}
+\end{htmlonly}
+\begin{code}
+
+ public GammaDistFromMoments (double mean, double var)\begin{hide} {
+ super (mean * mean / var, mean / var);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Constructs a gamma distribution with
+ mean \texttt{mean}, and variance \texttt{var}.
+\end{tabb}
+\begin{htmlonly}
+ \param{mean}{the desired mean.}
+ \param{var}{the desired variance.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/probdist/GeometricDist.java b/source/umontreal/iro/lecuyer/probdist/GeometricDist.java
new file mode 100644
index 0000000..15cd13d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GeometricDist.java
@@ -0,0 +1,322 @@
+
+
+/*
+ * Class: GeometricDist
+ * Description: geometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for
+ * the <EM>geometric</EM> distribution with parameter
+ * <SPAN CLASS="MATH"><I>p</I></SPAN>, where <SPAN CLASS="MATH">0 < <I>p</I> < 1</SPAN>.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = <I>p</I> (1 - <I>p</I>)<SUP>x</SUP>, for <I>x</I> = 0, 1, 2,…
+ * </DIV><P></P>
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - (1 - <I>p</I>)<SUP>x+1</SUP>, for <I>x</I> = 0, 1, 2,…
+ * </DIV><P></P>
+ * and its inverse is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = floor(ln(1 - <I>u</I>)/ln(1 - <I>p</I>)), for 0 <= <I>u</I> < 1.
+ * </DIV><P></P>
+ *
+ */
+public class GeometricDist extends DiscreteDistributionInt {
+
+ private double p;
+ private double vp;
+
+
+ /**
+ * Constructs a geometric distribution with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ */
+ public GeometricDist (double p) {
+ setP(p);
+ }
+
+
+ public double prob (int x) {
+ return prob (p, x);
+ }
+
+ public double cdf (int x) {
+ return cdf (p, x);
+ }
+
+ public double barF (int x) {
+ return barF (p, x);
+ }
+
+ public int inverseFInt (double u) {
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (p >= 1.0)
+ return 0;
+ if (u <= p)
+ return 0;
+ if (u >= 1.0 || p <= 0.0)
+ return Integer.MAX_VALUE;
+
+ return (int)Math.floor (Math.log1p(-u)/vp);
+ }
+
+ public double getMean() {
+ return GeometricDist.getMean (p);
+ }
+
+ public double getVariance() {
+ return GeometricDist.getVariance (p);
+ }
+
+ public double getStandardDeviation() {
+ return GeometricDist.getStandardDeviation (p);
+ }
+
+
+
+ /**
+ * Computes the geometric probability <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double prob (double p, int x) {
+ if (p < 0 || p > 1)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+ if (p <= 0)
+ return 0;
+ if (p >= 1)
+ return 0;
+ if (x < 0)
+ return 0;
+ return p*Math.pow (1 - p, x);
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double cdf (double p, int x) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 1.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+ return 1.0 - Math.pow (1.0 - p, (double)x + 1.0);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ * <SPAN CLASS="textit">WARNING:</SPAN> The complementary distribution function is defined as
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>.
+ *
+ */
+ public static double barF (double p, int x) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x < 0)
+ return 1.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 0.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 1.0;
+
+ return Math.pow (1.0 - p, x);
+ }
+
+
+ /**
+ * Computes the inverse of the geometric
+ * distribution.
+ *
+ */
+ public static int inverseF (double p, double u) {
+ if (p > 1.0 || p < 0.0)
+ throw new IllegalArgumentException ( "p not in [0,1]");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (p >= 1.0)
+ return 0;
+ if (u <= p)
+ return 0;
+ if (u >= 1.0 || p <= 0.0)
+ return Integer.MAX_VALUE;
+
+ double v = Math.log1p (-p);
+ return (int)Math.floor (Math.log1p (-u)/v);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of the geometric distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimate is returned in element 0
+ * of the returned array.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH">hat(p)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[1];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+
+ parameters[0] = 1.0 / (((double) sum / (double) n) + 1.0);
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a geometric distribution with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static GeometricDist getInstanceFromMLE (int[] x, int n) {
+
+ double parameters[] = getMLE (x, n);
+
+ return new GeometricDist (parameters[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (1 - <I>p</I>)/<I>p</I></SPAN> of the
+ * geometric distribution with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the mean of the geometric distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (1 - <I>p</I>)/<I>p</I></SPAN>
+ *
+ */
+ public static double getMean (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+
+ return (1 - p) / p;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (1 - <I>p</I>)/<I>p</I><SUP>2</SUP></SPAN>
+ * of the geometric distribution with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the variance of the Geometric distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (1 - <I>p</I>)/<I>p</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+
+ return ((1 - p) / (p * p));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the geometric
+ * distribution with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the standard deviation of the geometric distribution
+ *
+ */
+ public static double getStandardDeviation (double p) {
+ return Math.sqrt (GeometricDist.getVariance (p));
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>p</I></SPAN> associated with this object.
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+ /**
+ * Resets the value of <SPAN CLASS="MATH"><I>p</I></SPAN> associated with this object.
+ *
+ */
+ public void setP (double p) {
+ if (p < 0 || p > 1)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+ vp = Math.log1p (-p);
+ this.p = p;
+ supportA = 0;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {p};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : p = " + p;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/GeometricDist.tex b/source/umontreal/iro/lecuyer/probdist/GeometricDist.tex
new file mode 100644
index 0000000..eb1766e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GeometricDist.tex
@@ -0,0 +1,334 @@
+\defmodule {GeometricDist}
+
+Extends the class \class{DiscreteDistributionInt} for
+the {\em geometric\/} distribution
+\cite[page 322]{sLAW00a} with parameter
+$p$, where $0 < p < 1$.
+Its mass function is
+\eq
+ p (x) = p\, (1-p)^x, \qquad \mbox{for } x = 0, 1, 2, \ldots
+ \eqlabel{eq:fgeom}
+\endeq
+The distribution function is given by
+\eq
+ F (x) = 1 - (1-p)^{x+1}, \qquad \mbox{for } x = 0, 1, 2, \ldots
+ \eqlabel{eq:Fgeom}
+\endeq
+and its inverse is
+\eq
+ F^{-1}(u) = \left\lfloor \latex{\frac{\ln (1 - u)}{\ln (1 - p)}}
+ \html{\ln(1-u)/\ln(1-p)}\right\rfloor,
+ \qquad \mbox{for } 0 \le u < 1.
+ \eqlabel{eq:FInvgeom}
+\endeq
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GeometricDist
+ * Description: geometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;\end{hide}
+
+public class GeometricDist extends DiscreteDistributionInt\begin{hide} {
+
+ private double p;
+ private double vp;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public GeometricDist (double p)\begin{hide} {
+ setP(p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a geometric distribution with parameter $p$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}\begin{code}
+
+ public double prob (int x) {
+ return prob (p, x);
+ }
+
+ public double cdf (int x) {
+ return cdf (p, x);
+ }
+
+ public double barF (int x) {
+ return barF (p, x);
+ }
+
+ public int inverseFInt (double u) {
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (p >= 1.0)
+ return 0;
+ if (u <= p)
+ return 0;
+ if (u >= 1.0 || p <= 0.0)
+ return Integer.MAX_VALUE;
+
+ return (int)Math.floor (Math.log1p(-u)/vp);
+ }
+
+ public double getMean() {
+ return GeometricDist.getMean (p);
+ }
+
+ public double getVariance() {
+ return GeometricDist.getVariance (p);
+ }
+
+ public double getStandardDeviation() {
+ return GeometricDist.getStandardDeviation (p);
+ }
+
+\end{code}\end{hide}
+\begin{code}
+
+ public static double prob (double p, int x)\begin{hide} {
+ if (p < 0 || p > 1)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+ if (p <= 0)
+ return 0;
+ if (p >= 1)
+ return 0;
+ if (x < 0)
+ return 0;
+ return p*Math.pow (1 - p, x);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the geometric probability $p(x)$%
+\latex{ given in (\ref{eq:fgeom}) }.
+ \end{tabb}
+\begin{code}
+
+ public static double cdf (double p, int x)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 1.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+ return 1.0 - Math.pow (1.0 - p, (double)x + 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the distribution function $F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double p, int x)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0,1]");
+ if (x < 0)
+ return 1.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 0.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 1.0;
+
+ return Math.pow (1.0 - p, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function.
+\emph{WARNING:} The complementary distribution function is defined as
+$\bar F(x) = P[X \ge x]$.
+\end{tabb}
+\begin{code}
+
+ public static int inverseF (double p, double u)\begin{hide} {
+ if (p > 1.0 || p < 0.0)
+ throw new IllegalArgumentException ( "p not in [0,1]");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (p >= 1.0)
+ return 0;
+ if (u <= p)
+ return 0;
+ if (u >= 1.0 || p <= 0.0)
+ return Integer.MAX_VALUE;
+
+ double v = Math.log1p (-p);
+ return (int)Math.floor (Math.log1p (-u)/v);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse of the geometric
+ distribution\latex{, given by (\ref{eq:FInvgeom})}.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[1];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+
+ parameters[0] = 1.0 / (((double) sum / (double) n) + 1.0);
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $p$ of the geometric distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$. The estimate is returned in element 0
+ of the returned array.
+ \begin{detailed}
+ The maximum likelihood estimator $\hat{p}$ satisfies the equation
+ (see \cite[page 323]{sLAW00a})
+ \begin{eqnarray*}
+ \hat{p} = \frac{1}{\bar{x}_n + 1\rule{0pt}{1em}}
+ \end{eqnarray*}
+ where $\bar{x}_n$ is the average of $x[0], \ldots, x[n-1]$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameter [$\hat{p}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static GeometricDist getInstanceFromMLE (int[] x, int n)\begin{hide} {
+
+ double parameters[] = getMLE (x, n);
+
+ return new GeometricDist (parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a geometric distribution with parameter $p$
+ estimated using the maximum likelihood method based on the $n$
+ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double p)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+
+ return (1 - p) / p;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = (1 - p)/p$ of the
+ geometric distribution with parameter $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the geometric distribution $E[X] = (1 - p) / p$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double p)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+
+ return ((1 - p) / (p * p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = (1 - p)/p^2$
+ of the geometric distribution with parameter $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Geometric distribution
+ $\mbox{Var}[X] = (1 - p) / p^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double p)\begin{hide} {
+ return Math.sqrt (GeometricDist.getVariance (p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the geometric
+ distribution with parameter $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the geometric distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $p$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public void setP (double p)\begin{hide} {
+ if (p < 0 || p > 1)
+ throw new IllegalArgumentException ("p not in range (0,1)");
+ vp = Math.log1p (-p);
+ this.p = p;
+ supportA = 0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Resets the value of $p$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {p};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString () {
+ return getClass().getSimpleName() + " : p = " + p;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/GumbelDist.java b/source/umontreal/iro/lecuyer/probdist/GumbelDist.java
new file mode 100644
index 0000000..68dadce
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GumbelDist.java
@@ -0,0 +1,487 @@
+
+
+/*
+ * Class: GumbelDist
+ * Description: Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <SPAN CLASS="textit">Gumbel</SPAN> distribution, with location parameter
+ * <SPAN CLASS="MATH"><I>δ</I></SPAN> and scale parameter
+ * <SPAN CLASS="MATH"><I>β</I>≠ 0</SPAN>. Using the notation
+ * <SPAN CLASS="MATH"><I>z</I> = (<I>x</I> - <I>δ</I>)/<I>β</I></SPAN>,
+ * it has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>e</I><SUP>-z</SUP><I>e</I><SUP>-e<SUP>-z</SUP></SUP>/| <I>β</I>|, for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * and distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>e</I><SUP>-e<SUP>-z</SUP></SUP>, for <I>β</I> > 0
+ * </DIV><P></P>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - <I>e</I><SUP>-e<SUP>-z</SUP></SUP>, for <I>β</I> < 0.
+ * </DIV><P></P>
+ *
+ */
+public class GumbelDist extends ContinuousDistribution {
+ private double delta;
+ private double beta;
+
+ private static class FunctionPlus implements MathFunction {
+ // when beta > 0
+ protected int n;
+ protected double mean;
+ protected double[] x;
+ private double minx; // min of all {x[i]}
+
+ public FunctionPlus (double[] y, int n, double mean, double minx) {
+ this.n = n;
+ this.mean = mean;
+ this.x = y;
+ this.minx = minx;
+ }
+
+ public double evaluate (double lam) {
+ if (lam <= 0.0) return 1.0e100;
+ double tem;
+ double sum2 = 0.0;
+ double sum1 = 0.0;
+
+ for (int i = 0; i < n; i++) {
+ tem = Math.exp (-(x[i] - minx)* lam);
+ sum1 += tem;
+ sum2 += x[i] * tem;
+ }
+
+ return (mean - 1/lam) * sum1 - sum2;
+ }
+ }
+
+
+ private static class FunctionMinus implements MathFunction {
+ // when beta < 0
+ protected int n;
+ protected double mean;
+ protected double[] x;
+ protected double xmax;
+
+ public FunctionMinus (double[] y, int n, double mean, double xmax) {
+ this.n = n;
+ this.mean = mean;
+ this.x = y;
+ this.xmax = xmax;
+ }
+
+ public double evaluate (double lam) {
+ if (lam >= 0.0) return 1.0e100;
+ double tem;
+ double sum2 = 0.0;
+ double sum1 = 0.0;
+
+ for (int i = 0; i < n; i++) {
+ tem = Math.exp ((xmax - x[i]) * lam);
+ sum1 += tem;
+ sum2 += x[i] * tem;
+ }
+
+ return (mean - 1/lam) * sum1 - sum2;
+ }
+ }
+
+
+ /**
+ * Constructor for the standard
+ * Gumbel distribution with parameters <SPAN CLASS="MATH"><I>β</I></SPAN> = 1 and <SPAN CLASS="MATH"><I>δ</I></SPAN> = 0.
+ *
+ */
+ public GumbelDist() {
+ setParams (1.0, 0.0);
+ }
+
+
+ /**
+ * Constructs a <TT>GumbelDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT>.
+ *
+ */
+ public GumbelDist (double beta, double delta) {
+ setParams (beta, delta);
+ }
+
+
+ public double density (double x) {
+ return density (beta, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (beta, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (beta, delta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (beta, delta, u);
+ }
+
+ public double getMean() {
+ return GumbelDist.getMean (beta, delta);
+ }
+
+ public double getVariance() {
+ return GumbelDist.getVariance (beta, delta);
+ }
+
+ public double getStandardDeviation() {
+ return GumbelDist.getStandardDeviation (beta, delta);
+ }
+
+ /**
+ * Computes and returns the density function.
+ *
+ */
+ public static double density (double beta, double delta, double x) {
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ final double z = (x - delta)/beta;
+ if (z <= -10.0)
+ return 0.0;
+ double t = Math.exp (-z);
+ return t * Math.exp (-t)/Math.abs(beta);
+ }
+
+
+ /**
+ * Computes and returns the distribution function.
+ *
+ */
+ public static double cdf (double beta, double delta, double x) {
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ final double z = (x - delta)/beta;
+ if (beta > 0.) {
+ if (z <= -7.0)
+ return 0.0;
+ return Math.exp (-Math.exp (-z));
+ } else { // beta < 0
+ if (z <= -7.0)
+ return 1.0;
+ return -Math.expm1 (-Math.exp (-z));
+ }
+ }
+
+
+ /**
+ * Computes and returns the complementary distribution function <SPAN CLASS="MATH">1 - <I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double barF (double beta, double delta, double x) {
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ final double z = (x - delta)/beta;
+ if (beta > 0.) {
+ if (z <= -7.0)
+ return 1.0;
+ return -Math.expm1 (-Math.exp (-z));
+ } else { // beta < 0
+ if (z <= -7.0)
+ return 0.0;
+ return Math.exp (-Math.exp (-z));
+ }
+ }
+
+
+ /**
+ * Computes and returns the inverse distribution function.
+ *
+ */
+ public static double inverseF (double beta, double delta, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (beta > 0.)
+ return delta - Math.log (-Math.log (u))*beta;
+ else
+ return delta - Math.log (-Math.log1p(-u))*beta;
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>β</I>, <I>δ</I>)</SPAN> of the Gumbel distribution,
+ * <SPAN CLASS="textit">assuming that <SPAN CLASS="MATH"><I>β</I> > 0</SPAN></SPAN>, and
+ * using the maximum likelihood method with the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(δ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n <= 1");
+ int i;
+ double par[] = new double[2];
+
+ double xmin = Double.MAX_VALUE;
+ double sum = 0;
+ for (i = 0; i < n; i++) {
+ sum += x[i];
+ if (x[i] < xmin)
+ xmin = x[i];
+ }
+ double mean = sum / (double) n;
+
+ sum = 0;
+ for (i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double variance = sum / (n - 1.0);
+
+ FunctionPlus func = new FunctionPlus (x, n, mean, xmin);
+
+ double lam = 1.0 / (0.7797*Math.sqrt (variance));
+ final double EPS = 0.02;
+ double a = (1.0 - EPS)*lam - 5.0;
+ if (a <= 0)
+ a = 1e-15;
+ double b = (1.0 + EPS)*lam + 5.0;
+ lam = RootFinder.brentDekker (a, b, func, 1e-8);
+ par[0] = 1.0 / lam;
+
+ sum = 0;
+ for (i = 0; i < n; i++)
+ sum += Math.exp (-(x[i] - xmin) * lam);
+ par[1] = xmin - Math.log (sum/n) / lam;
+ return par;
+ }
+
+
+ /**
+ * Similar to {@link #getMLE getMLE}, but <SPAN CLASS="textit">for the case <SPAN CLASS="MATH"><I>β</I> < 0</SPAN></SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(δ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLEmin (double[] x, int n) {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n <= 1");
+
+ int i;
+ double par[] = new double[2];
+ double xmax = -Double.MAX_VALUE;
+ double sum = 0.0;
+ for (i = 0; i < n; i++) {
+ sum += x[i];
+ if (x[i] > xmax)
+ xmax = x[i];
+ }
+ double mean = sum / (double) n;
+
+ sum = 0.0;
+ for (i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double variance = sum / (n - 1.0);
+
+ FunctionMinus func = new FunctionMinus (x, n, mean, xmax);
+
+ double lam = -1.0 / (0.7797*Math.sqrt (variance));
+ final double EPS = 0.02;
+ double a = (1.0 + EPS)*lam - 2.0;
+ double b = (1.0 - EPS)*lam + 2.0;
+ if (b >= 0)
+ b = -1e-15;
+ lam = RootFinder.brentDekker (a, b, func, 1e-12);
+ par[0] = 1.0 / lam;
+
+ sum = 0.0;
+ for (i = 0; i < n; i++)
+ sum += Math.exp ((xmax - x[i]) * lam);
+ par[0] = 1.0 / lam;
+ par[1] = xmax - Math.log (sum / n) / lam;
+
+ return par;
+ }
+
+
+ /**
+ * Creates a new instance of an Gumbel distribution with parameters
+ * <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> estimated using the maximum likelihood method based
+ * on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>, <SPAN CLASS="textit">assuming that <SPAN CLASS="MATH"><I>β</I> > 0</SPAN></SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static GumbelDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new GumbelDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Similar to {@link #getInstanceFromMLE getInstanceFromMLE}, but <SPAN CLASS="textit">for the case <SPAN CLASS="MATH"><I>β</I> < 0</SPAN></SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static GumbelDist getInstanceFromMLEmin (double[] x, int n) {
+ double parameters[] = getMLEmin (x, n);
+ return new GumbelDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Returns the mean,
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>δ</I> + <I>γβ</I></SPAN>,
+ * of the Gumbel distribution with parameters <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>,
+ * where
+ * <SPAN CLASS="MATH"><I>γ</I> = 0.5772156649015329</SPAN> is the Euler-Mascheroni constant.
+ *
+ * @return the mean of the Extreme Value distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>δ</I> + <I>γ</I>*<I>β</I></SPAN>
+ *
+ */
+ public static double getMean (double beta, double delta) {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+
+ return delta + Num.EULER * beta;
+ }
+
+
+ /**
+ * Returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>π</I><SUP>2</SUP><I>β</I><SUP>2</SUP>/6</SPAN> of the Gumbel distribution with parameters <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the variance of the Gumbel distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = ()<I>πβ</I>)<SUP>2</SUP>/6</SPAN>
+ *
+ */
+ public static double getVariance (double beta, double delta) {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+
+ return Math.PI * Math.PI * beta * beta / 6.0;
+ }
+
+
+ /**
+ * Returns the standard deviation
+ * of the Gumbel distribution with parameters <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the standard deviation of the Gumbel distribution
+ *
+ */
+ public static double getStandardDeviation (double beta, double delta) {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+
+ return Math.sqrt(getVariance (beta, delta));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double beta, double delta) {
+ if (beta == 0)
+ throw new IllegalArgumentException ("beta = 0");
+ this.delta = delta;
+ this.beta = beta;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {beta, delta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : beta = " + beta + ", delta = " + delta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/GumbelDist.tex b/source/umontreal/iro/lecuyer/probdist/GumbelDist.tex
new file mode 100644
index 0000000..e4d12a7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/GumbelDist.tex
@@ -0,0 +1,508 @@
+\defmodule {GumbelDist}
+
+Extends the class \class{ContinuousDistribution} for
+the \emph{Gumbel} distribution
+\cite[page 2]{tJOH95b}, with location parameter
+$\delta$ and scale parameter $\beta \neq 0$. Using the notation $z = (x-\delta)/\beta$,
+it has density
+\eq
+ \begin{htmlonly}
+f (x) = e^{-z} e^{-e^{-z}}/|\beta|,
+ \qquad \mbox{for } -\infty < x < \infty.
+\end{htmlonly}
+\begin{latexonly}
+f (x) = \frac{e^{-z} e^{-e^{-z}}}{|\beta|},
+ \qquad \mbox{for } -\infty < x < \infty
+\end{latexonly}
+ \eqlabel{eq:densgumbel}
+ \endeq
+and distribution function
+\begin{latexonly}
+\[
+F(x) =
+\left\{
+\begin{array}{ll}
+ e^{-e^{-z}}, \qquad & \mbox{for } \beta > 0 \\
+ 1 - e^{-e^{-z}}, \qquad & \mbox{for } \beta < 0.
+\end{array}
+\right.
+\]
+\end{latexonly}
+\begin{htmlonly}
+\[
+ F(x) = e^{-e^{-z}}, \qquad \mbox{for } \beta > 0
+\]
+\[
+ F(x) = 1 - e^{-e^{-z}}, \qquad \mbox{for } \beta < 0.
+\]
+\end{htmlonly}
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GumbelDist
+ * Description: Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+public class GumbelDist extends ContinuousDistribution\begin{hide} {
+ private double delta;
+ private double beta;
+
+ private static class FunctionPlus implements MathFunction {
+ // when beta > 0
+ protected int n;
+ protected double mean;
+ protected double[] x;
+ private double minx; // min of all {x[i]}
+
+ public FunctionPlus (double[] y, int n, double mean, double minx) {
+ this.n = n;
+ this.mean = mean;
+ this.x = y;
+ this.minx = minx;
+ }
+
+ public double evaluate (double lam) {
+ if (lam <= 0.0) return 1.0e100;
+ double tem;
+ double sum2 = 0.0;
+ double sum1 = 0.0;
+
+ for (int i = 0; i < n; i++) {
+ tem = Math.exp (-(x[i] - minx)* lam);
+ sum1 += tem;
+ sum2 += x[i] * tem;
+ }
+
+ return (mean - 1/lam) * sum1 - sum2;
+ }
+ }
+
+
+ private static class FunctionMinus implements MathFunction {
+ // when beta < 0
+ protected int n;
+ protected double mean;
+ protected double[] x;
+ protected double xmax;
+
+ public FunctionMinus (double[] y, int n, double mean, double xmax) {
+ this.n = n;
+ this.mean = mean;
+ this.x = y;
+ this.xmax = xmax;
+ }
+
+ public double evaluate (double lam) {
+ if (lam >= 0.0) return 1.0e100;
+ double tem;
+ double sum2 = 0.0;
+ double sum1 = 0.0;
+
+ for (int i = 0; i < n; i++) {
+ tem = Math.exp ((xmax - x[i]) * lam);
+ sum1 += tem;
+ sum2 += x[i] * tem;
+ }
+
+ return (mean - 1/lam) * sum1 - sum2;
+ }
+ }\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public GumbelDist()\begin{hide} {
+ setParams (1.0, 0.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructor for the standard
+ Gumbel distribution with parameters $\beta$ = 1 and $\delta$ = 0.
+ \end{tabb}
+\begin{code}
+
+ public GumbelDist (double beta, double delta)\begin{hide} {
+ setParams (beta, delta);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{GumbelDist} object with parameters
+ $\beta$ = \texttt{beta} and $\delta$ = \texttt{delta}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (beta, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (beta, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (beta, delta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (beta, delta, u);
+ }
+
+ public double getMean() {
+ return GumbelDist.getMean (beta, delta);
+ }
+
+ public double getVariance() {
+ return GumbelDist.getVariance (beta, delta);
+ }
+
+ public double getStandardDeviation() {
+ return GumbelDist.getStandardDeviation (beta, delta);
+ }\end{hide}
+
+ public static double density (double beta, double delta, double x)\begin{hide} {
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ final double z = (x - delta)/beta;
+ if (z <= -10.0)
+ return 0.0;
+ double t = Math.exp (-z);
+ return t * Math.exp (-t)/Math.abs(beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double beta, double delta, double x)\begin{hide} {
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ final double z = (x - delta)/beta;
+ if (beta > 0.) {
+ if (z <= -7.0)
+ return 0.0;
+ return Math.exp (-Math.exp (-z));
+ } else { // beta < 0
+ if (z <= -7.0)
+ return 1.0;
+ return -Math.expm1 (-Math.exp (-z));
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes and returns the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double beta, double delta, double x)\begin{hide} {
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ final double z = (x - delta)/beta;
+ if (beta > 0.) {
+ if (z <= -7.0)
+ return 1.0;
+ return -Math.expm1 (-Math.exp (-z));
+ } else { // beta < 0
+ if (z <= -7.0)
+ return 0.0;
+ return Math.exp (-Math.exp (-z));
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes and returns the complementary distribution function $1 - F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double beta, double delta, double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (beta == 0.)
+ throw new IllegalArgumentException ("beta = 0");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (beta > 0.)
+ return delta - Math.log (-Math.log (u))*beta;
+ else
+ return delta - Math.log (-Math.log1p(-u))*beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes and returns the inverse distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n <= 1");
+ int i;
+ double par[] = new double[2];
+
+ double xmin = Double.MAX_VALUE;
+ double sum = 0;
+ for (i = 0; i < n; i++) {
+ sum += x[i];
+ if (x[i] < xmin)
+ xmin = x[i];
+ }
+ double mean = sum / (double) n;
+
+ sum = 0;
+ for (i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double variance = sum / (n - 1.0);
+
+ FunctionPlus func = new FunctionPlus (x, n, mean, xmin);
+
+ double lam = 1.0 / (0.7797*Math.sqrt (variance));
+ final double EPS = 0.02;
+ double a = (1.0 - EPS)*lam - 5.0;
+ if (a <= 0)
+ a = 1e-15;
+ double b = (1.0 + EPS)*lam + 5.0;
+ lam = RootFinder.brentDekker (a, b, func, 1e-8);
+ par[0] = 1.0 / lam;
+
+ sum = 0;
+ for (i = 0; i < n; i++)
+ sum += Math.exp (-(x[i] - xmin) * lam);
+ par[1] = xmin - Math.log (sum/n) / lam;
+ return par;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\beta,\delta)$ of the Gumbel distribution,
+ \emph{assuming that $\beta > 0$}, and
+ using the maximum likelihood method with the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\beta$, $\delta$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\beta, \hat\delta)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat\beta & = & \bar{x}_n - \frac{\sum_{i=1}^{n} x_i\,
+ e^{- x_i/\hat{\beta}}}{\sum_{i=1}^{n} e^{- x_i / \hat{\beta}}}\\[0.5em]
+ \hat{\delta} & = & -{\hat{\beta}} \ln \left( \frac{1}{n}
+ \sum_{i=1}^{n} e^{-x_i/\hat{\beta}} \right),
+ \end{eqnarray*}
+ where $\bar x_n$ is the average of $x[0],\dots,x[n-1]$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\delta}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLEmin (double[] x, int n)\begin{hide} {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n <= 1");
+
+ int i;
+ double par[] = new double[2];
+ double xmax = -Double.MAX_VALUE;
+ double sum = 0.0;
+ for (i = 0; i < n; i++) {
+ sum += x[i];
+ if (x[i] > xmax)
+ xmax = x[i];
+ }
+ double mean = sum / (double) n;
+
+ sum = 0.0;
+ for (i = 0; i < n; i++)
+ sum += (x[i] - mean) * (x[i] - mean);
+ double variance = sum / (n - 1.0);
+
+ FunctionMinus func = new FunctionMinus (x, n, mean, xmax);
+
+ double lam = -1.0 / (0.7797*Math.sqrt (variance));
+ final double EPS = 0.02;
+ double a = (1.0 + EPS)*lam - 2.0;
+ double b = (1.0 - EPS)*lam + 2.0;
+ if (b >= 0)
+ b = -1e-15;
+ lam = RootFinder.brentDekker (a, b, func, 1e-12);
+ par[0] = 1.0 / lam;
+
+ sum = 0.0;
+ for (i = 0; i < n; i++)
+ sum += Math.exp ((xmax - x[i]) * lam);
+ par[0] = 1.0 / lam;
+ par[1] = xmax - Math.log (sum / n) / lam;
+
+ return par;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Similar to \method{getMLE}{}, but \emph{for the case $\beta < 0$}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\delta}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static GumbelDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new GumbelDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of an Gumbel distribution with parameters
+ $\beta$ and $\delta$ estimated using the maximum likelihood method based
+ on the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$, \emph{assuming that $\beta > 0$}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static GumbelDist getInstanceFromMLEmin (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLEmin (x, n);
+ return new GumbelDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Similar to \method{getInstanceFromMLE}{}, but \emph{for the case $\beta < 0$}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double beta, double delta)\begin{hide} {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+
+ return delta + Num.EULER * beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean, $E[X] = \delta + \gamma\beta$,
+ of the Gumbel distribution with parameters $\beta$ and $\delta$,
+ where $\gamma = 0.5772156649015329$ is the Euler-Mascheroni constant.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Extreme Value distribution $E[X] = \delta + \gamma * \beta$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double beta, double delta)\begin{hide} {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+
+ return Math.PI * Math.PI * beta * beta / 6.0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance $\mbox{Var}[X] =
+ \pi^2 \beta^2\!/6$ of the Gumbel distribution with parameters $\beta$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Gumbel distribution $\mbox{Var}[X] = ()\pi\beta)^2/6$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double beta, double delta)\begin{hide} {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+
+ return Math.sqrt(getVariance (beta, delta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation
+ of the Gumbel distribution with parameters $\beta$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Gumbel distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $\delta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double beta, double delta)\begin{hide} {
+ if (beta == 0)
+ throw new IllegalArgumentException ("beta = 0");
+ this.delta = delta;
+ this.beta = beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the parameters $\beta$ and $\delta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {beta, delta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\beta$, $\delta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : beta = " + beta + ", delta = " + delta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/HalfNormalDist.java b/source/umontreal/iro/lecuyer/probdist/HalfNormalDist.java
new file mode 100644
index 0000000..015cfc9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HalfNormalDist.java
@@ -0,0 +1,368 @@
+
+
+/*
+ * Class: HalfNormalDist
+ * Description: half-normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the <EM>half-normal</EM>
+ * distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = ((2/π)<SUP>1/2</SUP>/<I>σ</I>)<I>e</I><SUP>-(x-<I>μ</I>)<SUP>2</SUP>/(2<I>σ</I><SUP>2</SUP>)</SUP>, for <I>x</I> > = <I>μ</I>,
+ * </DIV><P></P>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 0 for <I>x</I> < <I>μ</I>,
+ * </DIV><P></P>
+ *
+ */
+public class HalfNormalDist extends ContinuousDistribution {
+ protected double mu;
+ protected double sigma;
+ protected double C1;
+
+
+
+ /**
+ * Constructs a <TT>HalfNormalDist</TT> object with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN>
+ * <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>.
+ *
+ */
+ public HalfNormalDist (double mu, double sigma) {
+ setParams (mu, sigma);
+ }
+
+
+ public double density (double x) {
+ final double z = (x-mu)/sigma;
+ if (z < 0.0)
+ return 0.0;
+ return C1 * Math.exp(-z*z/2.0);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return HalfNormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return HalfNormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return HalfNormalDist.getStandardDeviation (mu, sigma);
+ }
+
+ /**
+ * Computes the density function of the <EM>half-normal</EM> distribution.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param x the value at which the density is evaluated
+ *
+ * @return returns the density function
+ *
+ */
+ public static double density (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ final double Z = (x-mu)/sigma;
+ if (Z < 0.0) return 0.0;
+ return Math.sqrt(2.0/Math.PI) / sigma * Math.exp(-Z*Z/2.0);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param x the value at which the distribution is evaluated
+ *
+ * @return returns the cdf function
+ *
+ */
+ public static double cdf (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ final double Z = (x-mu)/sigma;
+ if (Z <= 0.0) return 0.0;
+ return Num.erf(Z/Num.RAC2);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param x the value at which the complementary distribution is evaluated
+ *
+ * @return returns the complementary distribution function
+ *
+ */
+ public static double barF (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ final double Z = (x-mu)/sigma;
+ if (Z <= 0.0) return 1.0;
+ return Num.erfc(Z/Num.RAC2);
+ }
+
+
+ /**
+ * Computes the inverse of the distribution function.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @param u the value at which the inverse distribution is evaluated
+ *
+ * @return returns the inverse distribution function
+ *
+ */
+ public static double inverseF (double mu, double sigma, double u) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) return mu;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ final double Z = Num.RAC2 * Num.erfInv(u);
+ return mu + sigma * Z;
+ }
+
+
+ /**
+ * Estimates the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> of the half-normal distribution
+ * using the maximum likelihood method from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double mu = Double.MAX_VALUE;
+ for (int i = 0 ; i < n ; ++i)
+ if (x[i] < mu)
+ mu = x[i];
+
+ double sigma = 0.0;
+ for (int i = 0 ; i < n ; ++i)
+ sigma += (x[i]-mu)*(x[i]-mu);
+
+ double[] parametres = new double [2];
+ parametres[0] = mu;
+ parametres[1] = Math.sqrt(sigma/n);
+ return parametres;
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of the half-normal distribution using the
+ * maximum likelihood method from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN> and the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT>. The estimate is
+ * returned in a one-element array: [<SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameter
+ *
+ * @param mu the parameter mu
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH"><I>σ</I></SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n, double mu) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sigma = 0.0;
+ for (int i = 0 ; i < n ; ++i)
+ sigma += (x[i]-mu)*(x[i]-mu);
+
+ double[] parametres = new double [1];
+ parametres[0] = Math.sqrt(sigma/n);
+ return parametres;
+ }
+
+
+ /**
+ * Computes and returns the mean
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I> + <I>σ</I>(2 / π)<SUP>1/2</SUP>.</SPAN>
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return returns the mean
+ *
+ */
+ public static double getMean (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu + sigma*Math.sqrt(2.0/Math.PI);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (1 - 2/<I>π</I>)<I>σ</I><SUP>2</SUP>.</SPAN>
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return returns the variance
+ *
+ */
+ public static double getVariance (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return (1.0 - 2.0/Math.PI)*sigma*sigma;
+ }
+
+
+ /**
+ * Computes the standard deviation of the half-normal distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return returns the standard deviation
+ *
+ */
+ public static double getStandardDeviation (double mu, double sigma) {
+ return Math.sqrt (HalfNormalDist.getVariance (mu, sigma));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ * @return returns the parameter mu
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ * @return returns the parameter sigma
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ *
+ */
+ public void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ C1 = Math.sqrt(2.0/Math.PI) / sigma;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>]
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, sigma};
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ * @return returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/HalfNormalDist.tex b/source/umontreal/iro/lecuyer/probdist/HalfNormalDist.tex
new file mode 100644
index 0000000..4c06da6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HalfNormalDist.tex
@@ -0,0 +1,380 @@
+\defmodule {HalfNormalDist}
+
+Extends the class \class{ContinuousDistribution} for the {\em half-normal\/}
+distribution with parameters $\mu$ and $\sigma > 0$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = (\sqrt{2/\pi}/\sigma) e^{-(x-\mu)^2/(2\sigma^2)},
+ \qquad \mbox {for } x >= \mu,
+\endeq
+$$
+f(x) = 0 \qquad \mbox {for } x < \mu,
+$$
+\end{htmlonly}
+\begin{latexonly}
+\begin{eqnarray}
+ f(x) &=& \frac{1}{\sigma}\sqrt{\frac2\pi}\; e^{-(x-\mu)^2/2\sigma^2},
+ \qquad \mbox {for } x \ge \mu. \\[6pt] \eqlabel{eq:fHalfNormal}
+f(x) &=& 0, \qquad \mbox {for } x < \mu. \nonumber
+\end{eqnarray}
+\end{latexonly}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: HalfNormalDist
+ * Description: half-normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+\end{hide}
+
+public class HalfNormalDist extends ContinuousDistribution\begin{hide} {
+ protected double mu;
+ protected double sigma;
+ protected double C1;
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public HalfNormalDist (double mu, double sigma)\begin{hide} {
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{HalfNormalDist} object with parameters $\mu =$
+ \texttt{mu} and $\sigma =$ \texttt{sigma}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ final double z = (x-mu)/sigma;
+ if (z < 0.0)
+ return 0.0;
+ return C1 * Math.exp(-z*z/2.0);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return HalfNormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return HalfNormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return HalfNormalDist.getStandardDeviation (mu, sigma);
+ }\end{hide}
+
+ public static double density (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ final double Z = (x-mu)/sigma;
+ if (Z < 0.0) return 0.0;
+ return Math.sqrt(2.0/Math.PI) / sigma * Math.exp(-Z*Z/2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function of the {\em half-normal\/} distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{x}{the value at which the density is evaluated}
+ \return{returns the density function}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ final double Z = (x-mu)/sigma;
+ if (Z <= 0.0) return 0.0;
+ return Num.erf(Z/Num.RAC2);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{x}{the value at which the distribution is evaluated}
+ \return{returns the cdf function}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ final double Z = (x-mu)/sigma;
+ if (Z <= 0.0) return 1.0;
+ return Num.erfc(Z/Num.RAC2);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{x}{the value at which the complementary distribution is evaluated}
+ \return{returns the complementary distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double mu, double sigma, double u)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) return mu;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ final double Z = Num.RAC2 * Num.erfInv(u);
+ return mu + sigma * Z;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse of the distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \param{u}{the value at which the inverse distribution is evaluated}
+ \return{returns the inverse distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double mu = Double.MAX_VALUE;
+ for (int i = 0 ; i < n ; ++i)
+ if (x[i] < mu)
+ mu = x[i];
+
+ double sigma = 0.0;
+ for (int i = 0 ; i < n ; ++i)
+ sigma += (x[i]-mu)*(x[i]-mu);
+
+ double[] parametres = new double [2];
+ parametres[0] = mu;
+ parametres[1] = Math.sqrt(sigma/n);
+ return parametres;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $\mu$ and $\sigma$ of the half-normal distribution
+ using the maximum likelihood method from the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$. The estimates are returned in a two-element
+ array: [$\mu$, $\sigma$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values
+ $\hat{\mu}$ and $\hat{\sigma}$ that satisfy the equation
+ \begin{eqnarray*}
+ \hat \mu = \min_j \{x_j\}, \\[6pt]
+ \hat \sigma = \sqrt{\frac{1}{n}\Sigma_j(x_j-\hat{\mu})^2}.
+ \end{eqnarray*}
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\mu$, $\sigma$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n, double mu)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sigma = 0.0;
+ for (int i = 0 ; i < n ; ++i)
+ sigma += (x[i]-mu)*(x[i]-mu);
+
+ double[] parametres = new double [1];
+ parametres[0] = Math.sqrt(sigma/n);
+ return parametres;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $\sigma$ of the half-normal distribution using the
+ maximum likelihood method from the $n$ observations $x[i]$, $i = 0, 1,
+ \ldots, n-1$ and the parameter $\mu$ = \texttt{mu}. The estimate is
+ returned in a one-element array: [$\sigma$].
+ \begin{detailed}
+ The maximum likelihood estimator is the value
+ $\hat{\sigma}$ that satisfies the equation
+ \begin{eqnarray*}
+ \hat \sigma = \sqrt{\frac{1}{n}\Sigma_j(x_j-\mu)^2}.
+ \end{eqnarray*}
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameter}
+ \param{mu}{the parameter mu}
+ \return{returns the parameter [$\sigma$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu + sigma*Math.sqrt(2.0/Math.PI);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+$
+E[X] = \mu + \sigma \sqrt{2 / \pi}.
+$
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{returns the mean}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return (1.0 - 2.0/Math.PI)*sigma*sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+$
+\mbox{Var}[X] = \left(1-2/\pi\right)\sigma^2.
+$
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{returns the variance}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double sigma) \begin{hide} {
+ return Math.sqrt (HalfNormalDist.getVariance (mu, sigma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard deviation of the half-normal distribution with
+ parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{returns the standard deviation}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{returns the parameter mu}
+\end{htmlonly}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{returns the parameter sigma}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double mu, double sigma) \begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ C1 = Math.sqrt(2.0/Math.PI) / sigma;
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, sigma};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\mu$, $\sigma$].
+\end{tabb}
+\begin{htmlonly}
+ \return{returns the parameters [$\mu$, $\sigma$]}
+\end{htmlonly}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{returns a \texttt{String} containing information about the current distribution.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/HyperbolicSecantDist.java b/source/umontreal/iro/lecuyer/probdist/HyperbolicSecantDist.java
new file mode 100644
index 0000000..e4fe030
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HyperbolicSecantDist.java
@@ -0,0 +1,373 @@
+
+
+/*
+ * Class: HyperbolicSecantDist
+ * Description: hyperbolic secant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>hyperbolic secant</EM> distribution with location
+ * parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> and scale parameter
+ * <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 1/(2<I>σ</I>) sech(<I>π</I>/2(<I>x</I> - <I>μ</I>)/<I>σ</I>)
+ * </DIV><P></P>
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 2/<I>πtan</I><SUP>-1</SUP>[<I>exp</I>(<I>π</I>/2(<I>x</I> - <I>μ</I>)/<I>σ</I>)]
+ * </DIV><P></P>
+ *
+ * <P>
+ * The non-static versions of the methods <TT>cdf</TT>, <TT>barF</TT>,
+ * and <TT>inverseF</TT> call the static version of the same name.
+ *
+ */
+public class HyperbolicSecantDist extends ContinuousDistribution {
+ protected double mu;
+ protected double sigma;
+ private static final double ZLIMB = 500.0;
+ private static final double ZLIMS = 50.0;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+
+ public Optim (double[] x, int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ double sum = 0.0;
+
+ if (p[2] <= 0.0)
+ return 1e200;
+
+ for (int i = 0; i < n; i++)
+ sum -= Math.log (density (p[1], p[2], xi[i]));
+
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+
+
+
+ /**
+ * Constructs a hyperbolic secant distribution with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public HyperbolicSecantDist (double mu, double sigma) {
+ setParams (mu, sigma);
+ }
+
+
+ public double density (double x) {
+ return HyperbolicSecantDist.density (mu, sigma, x);
+ }
+
+ public double cdf (double x) {
+ return HyperbolicSecantDist.cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return HyperbolicSecantDist.barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return HyperbolicSecantDist.inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return HyperbolicSecantDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return HyperbolicSecantDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return HyperbolicSecantDist.getStandardDeviation (mu, sigma);
+ }
+
+ /**
+ * Computes the density function
+ * for a hyperbolic secant distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public static double density (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ double y = (x - mu) / sigma;
+ if (Math.abs(y) >= ZLIMB)
+ return 0.0;
+ else
+ return (1.0 / (Math.cosh (Math.PI * y / 2.0) * 2.0 * sigma));
+ }
+
+
+ /**
+ * Computes the distribution function of the hyperbolic secant distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public static double cdf (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ double y = (x - mu) / sigma;
+ if (y >= ZLIMS)
+ return 1.0;
+ else if (y <= -ZLIMB)
+ return 0.0;
+ else
+ return (2.0 * Math.atan (Math.exp (Math.PI * y / 2.0))) / Math.PI;
+ }
+
+
+ /**
+ * Computes the complementary distribution function of the
+ * hyperbolic secant distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public static double barF (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ double y = (x - mu) / sigma;
+ if (y >= ZLIMB)
+ return 0.0;
+ else if (y <= -ZLIMS)
+ return 1.0;
+ else
+ return 2.0 / Math.PI * Math.atan (Math.exp (-Math.PI * y / 2.0));
+ }
+
+
+ /**
+ * Computes the inverse of the hyperbolic secant distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public static double inverseF (double mu, double sigma, double u) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ else if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ else
+ return (mu + (2.0 * sigma / Math.PI * Math.log (Math.tan (Math.PI / 2.0 * u))));
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>μ</I>, <I>σ</I>)</SPAN> of the hyperbolic secant distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(σ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double sum;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ Optim system = new Optim (x, n);
+
+ double[] parameters = new double[2];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[3][3];
+ double[] udiag = new double[3];
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+ param[1] = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (x[i] - param[1]) * (x[i] - param[1]);
+ param[2] = Math.sqrt (sum / (double) n);
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (int i = 0; i < 2; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a hyperbolic secant distribution with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> estimated using the maximum likelihood method based on
+ * the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static HyperbolicSecantDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new HyperbolicSecantDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN> of the
+ * hyperbolic secant distribution with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the mean of the hyperbolic secant distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN>
+ *
+ */
+ public static double getMean (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>σ</I><SUP>2</SUP></SPAN>
+ * of the hyperbolic secant distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the variance of the hyperbolic secant distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>σ</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (sigma * sigma);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the hyperbolic secant distribution with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the standard deviation of the hyperbolic secant distribution
+ *
+ */
+ public static double getStandardDeviation (double mu, double sigma) {
+ return Math.sqrt (HyperbolicSecantDist.getVariance (mu, sigma));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, sigma};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/HyperbolicSecantDist.tex b/source/umontreal/iro/lecuyer/probdist/HyperbolicSecantDist.tex
new file mode 100644
index 0000000..39ec3d1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HyperbolicSecantDist.tex
@@ -0,0 +1,388 @@
+\defmodule {HyperbolicSecantDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em hyperbolic secant\/} distribution with location
+parameter $\mu$ and scale parameter $\sigma > 0$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = 1/(2\sigma) \mbox{ sech}(\pi/2 (x - \mu) / \sigma)
+\endeq
+The distribution function is given by
+\eq
+ F(x) = 2/\pi tan^{-1}[exp(\pi/2 (x - \mu) / \sigma)]
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \frac{1}{2 \sigma} \mbox{ sech}\left(\frac{\pi}{2} \frac{(x - \mu)}{\sigma}\right)
+\eqlabel{eq:fHyperbolicSecant}
+\endeq
+The distribution function is given by
+\eq
+ F(x) = \frac{2}{\pi} \tan^{-1}\left[\exp{\left(\frac{\pi}{2} \frac{(x - \mu)}{\sigma}\right)}\right]
+\eqlabel{eq:FHyperbolicSecant}
+\endeq
+\end{latexonly}%
+
+The non-static versions of the methods \texttt{cdf}, \texttt{barF},
+and \texttt{inverseF} call the static version of the same name.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: HyperbolicSecantDist
+ * Description: hyperbolic secant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import optimization.*;
+\end{hide}
+
+public class HyperbolicSecantDist extends ContinuousDistribution\begin{hide} {
+ protected double mu;
+ protected double sigma;
+ private static final double ZLIMB = 500.0;
+ private static final double ZLIMS = 50.0;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+
+ public Optim (double[] x, int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ double sum = 0.0;
+
+ if (p[2] <= 0.0)
+ return 1e200;
+
+ for (int i = 0; i < n; i++)
+ sum -= Math.log (density (p[1], p[2], xi[i]));
+
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public HyperbolicSecantDist (double mu, double sigma)\begin{hide} {
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a hyperbolic secant distribution with parameters
+ $\mu$ and $\sigma$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return HyperbolicSecantDist.density (mu, sigma, x);
+ }
+
+ public double cdf (double x) {
+ return HyperbolicSecantDist.cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return HyperbolicSecantDist.barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return HyperbolicSecantDist.inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return HyperbolicSecantDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return HyperbolicSecantDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return HyperbolicSecantDist.getStandardDeviation (mu, sigma);
+ }\end{hide}
+
+ public static double density (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ double y = (x - mu) / sigma;
+ if (Math.abs(y) >= ZLIMB)
+ return 0.0;
+ else
+ return (1.0 / (Math.cosh (Math.PI * y / 2.0) * 2.0 * sigma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:fHyperbolicSecant})
+ for a hyperbolic secant distribution with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ double y = (x - mu) / sigma;
+ if (y >= ZLIMS)
+ return 1.0;
+ else if (y <= -ZLIMB)
+ return 0.0;
+ else
+ return (2.0 * Math.atan (Math.exp (Math.PI * y / 2.0))) / Math.PI;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the distribution function of the hyperbolic secant distribution
+ with parameters $\mu$ and $\sigma$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ double y = (x - mu) / sigma;
+ if (y >= ZLIMB)
+ return 0.0;
+ else if (y <= -ZLIMS)
+ return 1.0;
+ else
+ return 2.0 / Math.PI * Math.atan (Math.exp (-Math.PI * y / 2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function of the
+ hyperbolic secant distribution with parameters $\mu$ and $\sigma$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double mu, double sigma, double u)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ else if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ else
+ return (mu + (2.0 * sigma / Math.PI * Math.log (Math.tan (Math.PI / 2.0 * u))));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse of the hyperbolic secant distribution
+ with parameters $\mu$ and $\sigma$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double sum;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ Optim system = new Optim (x, n);
+
+ double[] parameters = new double[2];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[3][3];
+ double[] udiag = new double[3];
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+ param[1] = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (x[i] - param[1]) * (x[i] - param[1]);
+ param[2] = Math.sqrt (sum / (double) n);
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (int i = 0; i < 2; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\mu, \sigma)$ of the hyperbolic secant distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\mu$, $\sigma$].
+ \begin{detailed}
+ The estimate of the parameters is given by maximizing numerically the
+ log-likelihood function, using the Uncmin package \cite{iSCHa,iVERa}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\mu}$, $\hat{\sigma}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static HyperbolicSecantDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new HyperbolicSecantDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a hyperbolic secant distribution with parameters
+ $\mu$ and $\sigma$ estimated using the maximum likelihood method based on
+ the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \mu$ of the
+ hyperbolic secant distribution with parameters
+ $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the hyperbolic secant distribution $E[X] = \mu$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (sigma * sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = \sigma^2$
+ of the hyperbolic secant distribution with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the hyperbolic secant distribution
+ $\mbox{Var}[X] = \sigma^2$
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double sigma)\begin{hide} {
+ return Math.sqrt (HyperbolicSecantDist.getVariance (mu, sigma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the hyperbolic secant distribution with parameters
+ $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the hyperbolic secant distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ this.mu = mu;
+ this.sigma = sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\mu$ and $\sigma$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, sigma};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\mu$, $\sigma$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/HypergeometricDist.java b/source/umontreal/iro/lecuyer/probdist/HypergeometricDist.java
new file mode 100644
index 0000000..c2751b1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypergeometricDist.java
@@ -0,0 +1,461 @@
+
+
+/*
+ * Class: HypergeometricDist
+ * Description: hypergeometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for
+ * the <EM>hypergeometric</EM> distribution with
+ * <SPAN CLASS="MATH"><I>k</I></SPAN> elements chosen among <SPAN CLASS="MATH"><I>l</I></SPAN>, <SPAN CLASS="MATH"><I>m</I></SPAN> being
+ * of one type, and <SPAN CLASS="MATH"><I>l</I> - <I>m</I></SPAN> of the other.
+ * The parameters <SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>k</I></SPAN> and <SPAN CLASS="MATH"><I>l</I></SPAN> are positive integers
+ * where
+ * <SPAN CLASS="MATH">1 <= <I>m</I> <= <I>l</I></SPAN> and
+ * <SPAN CLASS="MATH">1 <= <I>k</I> <= <I>l</I></SPAN>.
+ * Its mass function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = nCr(<I>m</I>, <I>x</I>)nCr(<I>l</I> - <I>m</I>, <I>k</I> - <I>x</I>)/nCr(<I>l</I>, <I>k</I>), for max(0, <I>k</I> - <I>l</I> + <I>m</I>) <= <I>x</I> <= min(<I>k</I>, <I>m</I>),
+ * </DIV><P></P>
+ * where nCr is defined in {@link BinomialDist}.
+ *
+ */
+public class HypergeometricDist extends DiscreteDistributionInt {
+
+ private int m;
+ private int l;
+ private int k;
+ private double p0;
+
+
+ public static double MAXN = 100000;
+
+
+ /**
+ * Constructs an hypergeometric distribution with parameters
+ * <SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>l</I></SPAN> and <SPAN CLASS="MATH"><I>k</I></SPAN>.
+ *
+ */
+ public HypergeometricDist (int m, int l, int k) {
+ setParams (m, l, k);
+ }
+
+
+ public double prob (int x) {
+ if (x < supportA || x > supportB)
+ return 0.0;
+ if (pdf == null || x < xmin || x > xmax)
+ return prob (m, l, k, x);
+ return pdf[x - xmin];
+ }
+
+ public double cdf (int x) {
+ if (x < supportA)
+ return 0.0;
+ if (x >= supportB)
+ return 1.0;
+ if (cdf != null) {
+ if (x >= xmax)
+ return 1.0;
+ if (x < xmin)
+ return cdf (m, l, k, x);
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+ }
+ else
+ return cdf (m, l, k, x);
+ }
+
+ public double barF (int x) {
+ if (x <= supportA)
+ return 1.0;
+ if (x > supportB)
+ return 0.0;
+ if (cdf != null) {
+ if (x > xmax)
+ return barF (m, l, k, x);
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ }
+ else
+ return barF (m, l, k, x);
+ }
+
+ public int inverseFInt (double u) {
+ if (u < 0 || u > 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return Math.max (0, k - l + m);
+ if (u >= 1.0)
+ return Math.min (k, m);
+ double p = p0;
+ // Empirical correction, the original algorithm sets x=0.
+ int x = Math.max (0, k - l + m);
+ if (u <= p) return x;
+ do {
+ u = u - p;
+ p = p*(m - x)*(k - x)/((x+1)*(l - m - k + 1.0 + x));
+ x++;
+ } while (u > p);
+ return x;
+ }
+
+ public double getMean() {
+ return HypergeometricDist.getMean (m, l, k);
+ }
+
+ public double getVariance() {
+ return HypergeometricDist.getVariance (m, l, k);
+ }
+
+ public double getStandardDeviation() {
+ return HypergeometricDist.getStandardDeviation (m, l, k);
+ }
+
+ /**
+ * Computes the hypergeometric probability
+ * <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double prob (int m, int l, int k, int x) {
+ final int SLIM = 70;
+ final double MAXEXP = (Num.DBL_MAX_EXP - 1)*Num.LN2;// To avoid overflow
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 <= m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 <= k < l");
+ if (x < Math.max (0, k - l + m) || x > Math.min (k, m))
+ return 0;
+
+ if (l <= SLIM)
+ return Num.combination (m, x)
+ * Num.combination (l - m, k - x)/Num.combination (l, k);
+ else {
+ double res =
+ Num.lnFactorial (m) + Num.lnFactorial (l-m) - Num.lnFactorial (l)
+ - Num.lnFactorial (x) - Num.lnFactorial (k - x) + Num.lnFactorial (k)
+ - Num.lnFactorial (m - x) - Num.lnFactorial (l - m - k + x)
+ + Num.lnFactorial (l - k);
+ if (res >= MAXEXP)
+ throw new IllegalArgumentException ("term overflow");
+ return Math.exp (res);
+ }
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double cdf (int m, int l, int k, int x) {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 <= m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 <= k < l");
+ int imin = Math.max (0, k - l + m);
+ int imax = Math.min (k, m);
+ if (x < imin)
+ return 0.0;
+ if (x >= imax)
+ return 1.0;
+ // Very inefficient
+ double res = 0.0;
+ for (int i = imin; i <= x; i++)
+ res += prob (m, l, k, i);
+ if (res >= 1.0)
+ return 1.0;
+ return res;
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ * <SPAN CLASS="textit">WARNING:</SPAN> The complementary distribution function is defined as
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>.
+ *
+ */
+ public static double barF (int m, int l, int k, int x) {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 < =m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 < =k < l");
+ int imin = Math.max (0, k - l + m);
+ int imax = Math.min (k, m);
+ if (x <= imin)
+ return 1.0;
+ if (x > imax)
+ return 0.0;
+ // Very inefficient
+ double res = 0.0;
+ for (int i = imax; i >= x; i--)
+ res += prob (m, l, k, i);
+ if (res >= 1.0)
+ return 1.0;
+ return res;
+ }
+
+
+ /**
+ * Computes <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN> for the hypergeometric distribution without
+ * using precomputed tables. The inversion is computed
+ * using the chop-down algorithm.
+ *
+ */
+ public static int inverseF (int m, int l, int k, double u) {
+ // algo hin dans Kachitvichyanukul
+ if (u < 0 || u >= 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return Math.max (0, k - l + m);
+ if (u >= 1.0)
+ return Math.min (k, m);
+ double p = 0;
+ if (k < l - m)
+ p = Math.exp (Num.lnFactorial (l - m) + Num.lnFactorial (l - k)
+ -Num.lnFactorial (l) - Num.lnFactorial (l - m - k));
+ else
+ p = Math.exp (Num.lnFactorial (m) + Num.lnFactorial (k)
+ -Num.lnFactorial (k- l + m) - Num.lnFactorial (l));
+
+ // Empirical correction, the original algorithm sets x=0.
+ int x = Math.max (0, k - l + m);
+ if (u <= p) return x;
+
+ do {
+ u = u - p;
+ p = p*(m - x)*(k - x)/((x+1)*(l - m - k + 1.0 + x));
+ x++;
+ } while (u > p && p > 0);
+ return x;
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>km</I>/<I>l</I></SPAN>
+ * of the Hypergeometric distribution with parameters <SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>l</I></SPAN> and <SPAN CLASS="MATH"><I>k</I></SPAN>.
+ *
+ * @return the mean of the hypergeometric distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>km</I>/<I>l</I></SPAN>
+ *
+ */
+ public static double getMean (int m, int l, int k) {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1<=m<l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1<=k<l");
+
+ return ((double) k * (double) m / (double) l);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * of the hypergeometric distribution with parameters <SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>l</I></SPAN> and <SPAN CLASS="MATH"><I>k</I></SPAN>.
+ *
+ * @return the variance of the Hypergeometric distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (<I>km</I>/<I>l</I> )(1 - <I>m</I>/<I>l</I> )(<I>l</I> - <I>k</I>)/(<I>l</I> - 1)</SPAN>
+ *
+ */
+ public static double getVariance (int m, int l, int k) {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1<=m<l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1<=k<l");
+
+ return (((double) k * (double) m / (double) l) *
+ ( 1 - ((double) m / (double) l)) * ((double) l - (double) k) /
+ ((double) l - 1));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the hypergeometric distribution
+ * with parameters <SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>l</I></SPAN> and <SPAN CLASS="MATH"><I>k</I></SPAN>.
+ *
+ * @return the standard deviation of the hypergeometric distribution
+ *
+ */
+ public static double getStandardDeviation (int m, int l, int k) {
+ return Math.sqrt (HypergeometricDist.getVariance (m, l, k));
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>m</I></SPAN> associated with this object.
+ *
+ */
+ public int getM() {
+ return m;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>l</I></SPAN> associated with this object.
+ *
+ */
+ public int getL() {
+ return l;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>k</I></SPAN> associated with this object.
+ *
+ */
+ public int getK() {
+ return k;
+ }
+
+ private void setHypergeometric() {
+ int imin = Math.max (0, k - l + m);
+ int imax = Math.min (k, m);
+ supportA = imin;
+ supportB = imax;
+ int ns = imax - imin + 1;
+ if (ns > MAXN) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ int offset = imin;
+ imin = 0;
+ imax -= offset;
+ double[] P = new double[ns];
+ double[] F = new double[ns];
+
+ // Computes the mode (taken from UNURAN)
+ int mode = (int)((k + 1.0)*(m + 1.0)/(l + 2.0));
+ int imid = mode - offset;
+
+ P[imid] = prob (m, l, k, mode);
+
+ int i = imid;
+ while (i > imin && Math.abs (P[i]) > EPSILON) {
+ P[i-1] = P[i]*(i + offset)/(m-i-offset+1)
+ * (l - m - k + i + offset)/(k - i - offset + 1);
+ i--;
+ }
+ imin = i;
+
+ i = imid;
+ while (i < imax && Math.abs (P[i]) > EPSILON) {
+ P[i+1] = P[i]*(m - i - offset)/(i + offset + 1)
+ * (k - i - offset)/(l - m - k + i + offset + 1);
+ i++;
+ }
+ imax = i;
+
+ F[imin] = P[imin];
+ i = imin;
+ while (i < imax && F[i] < 0.5) {
+ i++;
+ F[i] = F[i-1] + P[i];
+ }
+ xmed = i;
+
+ F[imax] = P[imax];
+ i = imax - 1;
+ while (i > xmed) {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ }
+
+ xmin = imin + offset;
+ xmax = imax + offset;
+ xmed += offset;
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax+1-imin);
+ System.arraycopy (F, imin, cdf, 0, imax+1-imin);
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>l</I></SPAN>, <SPAN CLASS="MATH"><I>k</I></SPAN>].
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {m, l, k};
+ return retour;
+ }
+
+
+ /**
+ * Resets the parameters of this object to <SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>l</I></SPAN> and <SPAN CLASS="MATH"><I>k</I></SPAN>.
+ *
+ *
+ */
+ public void setParams (int m, int l, int k) {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 <= m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 <= k < l");
+ this.m = m;
+ this.l = l;
+ this.k = k;
+ setHypergeometric();
+ if (k < l - m)
+ p0 = Math.exp (Num.lnFactorial (l - m) + Num.lnFactorial (l - k)
+ -Num.lnFactorial (l) - Num.lnFactorial (l - m - k));
+ else
+ p0 = Math.exp (Num.lnFactorial (m) + Num.lnFactorial (k)
+ -Num.lnFactorial (k- l + m) - Num.lnFactorial (l));
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : m = " + m + ", l = " + l + ", k = " + k;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/probdist/HypergeometricDist.tex b/source/umontreal/iro/lecuyer/probdist/HypergeometricDist.tex
new file mode 100644
index 0000000..9585d06
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypergeometricDist.tex
@@ -0,0 +1,490 @@
+\defmodule {HypergeometricDist}
+
+Extends the class \class{DiscreteDistributionInt} for
+the {\em hypergeometric\/} distribution \cite[page 101]{rGEN98a} with
+$k$ elements chosen among $l$, $m$ being
+of one type, and $l-m$ of the other.
+The parameters $m$, $k$ and $l$ are positive integers
+where $1\le m\le l$ and $1\le k\le l$.
+Its mass function is given by
+\begin{htmlonly}
+\eq
+ p(x) =
+ \mbox{nCr}(m, x) \mbox{nCr}(l - m, k-x)/\mbox{nCr}(l, k),
+ \qquad \mbox{for } \max(0,k-l+m)\le x\le \min(k, m),
+\endeq
+where nCr is defined in \class{BinomialDist}.
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ p(x) =
+ \frac{ \binom{m}{x} \binom{l - m}{k-x}}{\binom{l}{k}}
+ \qquad \mbox{for } \max(0,k-l+m)\le x\le \min(k, m). \eqlabel{eq:fheperg}
+\endeq
+\end{latexonly}
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: HypergeometricDist
+ * Description: hypergeometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class HypergeometricDist extends DiscreteDistributionInt\begin{hide} {
+
+ private int m;
+ private int l;
+ private int k;
+ private double p0;
+\end{hide}\end{code}
+
+\unmoved\begin{detailed}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constant}
+\begin{code}
+
+ public static double MAXN = 100000;
+\end{code}
+ \begin{tabb}
+ If the number of integers in the interval $[$max$(0,k-l+m),\,$min$(k,m)]$
+ is larger than this constant, the tables will {\em not\/}
+ be precomputed by the constructor.
+\end{tabb}
+\end{detailed}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+\begin{code}
+
+ public HypergeometricDist (int m, int l, int k)\begin{hide} {
+ setParams (m, l, k);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs an hypergeometric distribution with parameters
+ $m$, $l$ and $k$.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double prob (int x) {
+ if (x < supportA || x > supportB)
+ return 0.0;
+ if (pdf == null || x < xmin || x > xmax)
+ return prob (m, l, k, x);
+ return pdf[x - xmin];
+ }
+
+ public double cdf (int x) {
+ if (x < supportA)
+ return 0.0;
+ if (x >= supportB)
+ return 1.0;
+ if (cdf != null) {
+ if (x >= xmax)
+ return 1.0;
+ if (x < xmin)
+ return cdf (m, l, k, x);
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+ }
+ else
+ return cdf (m, l, k, x);
+ }
+
+ public double barF (int x) {
+ if (x <= supportA)
+ return 1.0;
+ if (x > supportB)
+ return 0.0;
+ if (cdf != null) {
+ if (x > xmax)
+ return barF (m, l, k, x);
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ }
+ else
+ return barF (m, l, k, x);
+ }
+
+ public int inverseFInt (double u) {
+ if (u < 0 || u > 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return Math.max (0, k - l + m);
+ if (u >= 1.0)
+ return Math.min (k, m);
+ double p = p0;
+ // Empirical correction, the original algorithm sets x=0.
+ int x = Math.max (0, k - l + m);
+ if (u <= p) return x;
+ do {
+ u = u - p;
+ p = p*(m - x)*(k - x)/((x+1)*(l - m - k + 1.0 + x));
+ x++;
+ } while (u > p);
+ return x;
+ }
+
+ public double getMean() {
+ return HypergeometricDist.getMean (m, l, k);
+ }
+
+ public double getVariance() {
+ return HypergeometricDist.getVariance (m, l, k);
+ }
+
+ public double getStandardDeviation() {
+ return HypergeometricDist.getStandardDeviation (m, l, k);
+ }\end{hide}
+
+ public static double prob (int m, int l, int k, int x)\begin{hide} {
+ final int SLIM = 70;
+ final double MAXEXP = (Num.DBL_MAX_EXP - 1)*Num.LN2;// To avoid overflow
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 <= m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 <= k < l");
+ if (x < Math.max (0, k - l + m) || x > Math.min (k, m))
+ return 0;
+
+ if (l <= SLIM)
+ return Num.combination (m, x)
+ * Num.combination (l - m, k - x)/Num.combination (l, k);
+ else {
+ double res =
+ Num.lnFactorial (m) + Num.lnFactorial (l-m) - Num.lnFactorial (l)
+ - Num.lnFactorial (x) - Num.lnFactorial (k - x) + Num.lnFactorial (k)
+ - Num.lnFactorial (m - x) - Num.lnFactorial (l - m - k + x)
+ + Num.lnFactorial (l - k);
+ if (res >= MAXEXP)
+ throw new IllegalArgumentException ("term overflow");
+ return Math.exp (res);
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the hypergeometric probability
+ $p(x)$\latex{ given by (\ref{eq:fheperg})}.
+ \end{tabb}
+\begin{code}
+
+ public static double cdf (int m, int l, int k, int x)\begin{hide} {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 <= m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 <= k < l");
+ int imin = Math.max (0, k - l + m);
+ int imax = Math.min (k, m);
+ if (x < imin)
+ return 0.0;
+ if (x >= imax)
+ return 1.0;
+ // Very inefficient
+ double res = 0.0;
+ for (int i = imin; i <= x; i++)
+ res += prob (m, l, k, i);
+ if (res >= 1.0)
+ return 1.0;
+ return res;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the distribution function $F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int m, int l, int k, int x)\begin{hide} {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 < =m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 < =k < l");
+ int imin = Math.max (0, k - l + m);
+ int imax = Math.min (k, m);
+ if (x <= imin)
+ return 1.0;
+ if (x > imax)
+ return 0.0;
+ // Very inefficient
+ double res = 0.0;
+ for (int i = imax; i >= x; i--)
+ res += prob (m, l, k, i);
+ if (res >= 1.0)
+ return 1.0;
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function.
+\emph{WARNING:} The complementary distribution function is defined as
+$\bar F(x) = P[X \ge x]$.
+\end{tabb}
+\begin{code}
+
+ public static int inverseF (int m, int l, int k, double u)\begin{hide} {
+ // algo hin dans Kachitvichyanukul
+ if (u < 0 || u >= 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return Math.max (0, k - l + m);
+ if (u >= 1.0)
+ return Math.min (k, m);
+ double p = 0;
+ if (k < l - m)
+ p = Math.exp (Num.lnFactorial (l - m) + Num.lnFactorial (l - k)
+ -Num.lnFactorial (l) - Num.lnFactorial (l - m - k));
+ else
+ p = Math.exp (Num.lnFactorial (m) + Num.lnFactorial (k)
+ -Num.lnFactorial (k- l + m) - Num.lnFactorial (l));
+
+ // Empirical correction, the original algorithm sets x=0.
+ int x = Math.max (0, k - l + m);
+ if (u <= p) return x;
+
+ do {
+ u = u - p;
+ p = p*(m - x)*(k - x)/((x+1)*(l - m - k + 1.0 + x));
+ x++;
+ } while (u > p && p > 0);
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes $F^{-1}(u)$ for the hypergeometric distribution without
+ using precomputed tables. The inversion is computed
+ using the chop-down algorithm \cite{sKAC85a}.
+\end{tabb}
+\begin{code}
+
+ public static double getMean (int m, int l, int k)\begin{hide} {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1<=m<l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1<=k<l");
+
+ return ((double) k * (double) m / (double) l);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = km/l$
+ of the Hypergeometric distribution with parameters $m$, $l$ and $k$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the hypergeometric distribution $E[X] = km / l$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int m, int l, int k)\begin{hide} {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1<=m<l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1<=k<l");
+
+ return (((double) k * (double) m / (double) l) *
+ ( 1 - ((double) m / (double) l)) * ((double) l - (double) k) /
+ ((double) l - 1));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+\begin{latexonly}
+ $\mbox{Var}[X] = \frac{(km/l)(1 - m/l)(l - k)}{l - 1}$
+\end{latexonly}
+ of the hypergeometric distribution with parameters $m$, $l$ and $k$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Hypergeometric distribution
+ $\mbox{Var}[X] = (km / l)(1 - m / l)(l - k) / (l - 1)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int m, int l, int k)\begin{hide} {
+ return Math.sqrt (HypergeometricDist.getVariance (m, l, k));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the hypergeometric distribution
+ with parameters $m$, $l$ and $k$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the hypergeometric distribution}
+\end{htmlonly}
+\begin{code}
+
+ public int getM()\begin{hide} {
+ return m;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $m$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public int getL()\begin{hide} {
+ return l;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $l$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public int getK()\begin{hide} {
+ return k;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $k$ associated with this object.
+\end{tabb}
+\begin{code}\begin{hide}
+ private void setHypergeometric() {
+ int imin = Math.max (0, k - l + m);
+ int imax = Math.min (k, m);
+ supportA = imin;
+ supportB = imax;
+ int ns = imax - imin + 1;
+ if (ns > MAXN) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ int offset = imin;
+ imin = 0;
+ imax -= offset;
+ double[] P = new double[ns];
+ double[] F = new double[ns];
+
+ // Computes the mode (taken from UNURAN)
+ int mode = (int)((k + 1.0)*(m + 1.0)/(l + 2.0));
+ int imid = mode - offset;
+
+ P[imid] = prob (m, l, k, mode);
+
+ int i = imid;
+ while (i > imin && Math.abs (P[i]) > EPSILON) {
+ P[i-1] = P[i]*(i + offset)/(m-i-offset+1)
+ * (l - m - k + i + offset)/(k - i - offset + 1);
+ i--;
+ }
+ imin = i;
+
+ i = imid;
+ while (i < imax && Math.abs (P[i]) > EPSILON) {
+ P[i+1] = P[i]*(m - i - offset)/(i + offset + 1)
+ * (k - i - offset)/(l - m - k + i + offset + 1);
+ i++;
+ }
+ imax = i;
+
+ F[imin] = P[imin];
+ i = imin;
+ while (i < imax && F[i] < 0.5) {
+ i++;
+ F[i] = F[i-1] + P[i];
+ }
+ xmed = i;
+
+ F[imax] = P[imax];
+ i = imax - 1;
+ while (i > xmed) {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ }
+
+ xmin = imin + offset;
+ xmax = imax + offset;
+ xmed += offset;
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax+1-imin);
+ System.arraycopy (F, imin, cdf, 0, imax+1-imin);
+ }
+\end{hide}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {m, l, k};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$m$, $l$, $k$].
+\end{tabb}
+\begin{code}
+
+ public void setParams (int m, int l, int k)\begin{hide} {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1 <= m < l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1 <= k < l");
+ this.m = m;
+ this.l = l;
+ this.k = k;
+ setHypergeometric();
+ if (k < l - m)
+ p0 = Math.exp (Num.lnFactorial (l - m) + Num.lnFactorial (l - k)
+ -Num.lnFactorial (l) - Num.lnFactorial (l - m - k));
+ else
+ p0 = Math.exp (Num.lnFactorial (m) + Num.lnFactorial (k)
+ -Num.lnFactorial (k- l + m) - Num.lnFactorial (l));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Resets the parameters of this object to $m$, $l$ and $k$.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString () {
+ return getClass().getSimpleName() + " : m = " + m + ", l = " + l + ", k = " + k;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/probdist/HypoExponentialDist.java b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDist.java
new file mode 100644
index 0000000..a3b9892
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDist.java
@@ -0,0 +1,432 @@
+
+/*
+ * Class: HypoExponentialDist
+ * Description: Hypo-exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.util.Formatter;
+import java.util.Locale;
+import cern.colt.matrix.*;
+import cern.colt.matrix.impl.*;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * This class implements the <SPAN CLASS="textit">hypoexponential</SPAN> distribution,
+ * also called the generalized Erlang distribution. Let the <SPAN CLASS="MATH"><I>X</I><SUB>j</SUB></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>j</I> = 1,…, <I>k</I></SPAN>, be <SPAN CLASS="MATH"><I>k</I></SPAN> independent exponential random variables with different
+ * rates <SPAN CLASS="MATH"><I>λ</I><SUB>j</SUB></SPAN>, i.e. assume that
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>j</SUB>≠<I>λ</I><SUB>i</SUB></SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I>≠<I>j</I></SPAN>. Then the sum
+ * <SPAN CLASS="MATH">∑<SUB>j=1</SUB><SUP>k</SUP><I>X</I><SUB>j</SUB></SPAN> is called a <SPAN CLASS="textit">hypoexponential</SPAN>
+ * random variable.
+ *
+ * <P>
+ * Let the <SPAN CLASS="MATH"><I>k</I>×<I>k</I></SPAN> upper triangular bidiagonal matrix
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:tail-hypomatrix"></A>
+ * <B>A</B> = 1#1
+ * </DIV><P></P>
+ * with <SPAN CLASS="MATH"><I>λ</I><SUB>j</SUB></SPAN> the rates of the <SPAN CLASS="MATH"><I>k</I></SPAN> exponential random variables;
+ * then the cumulative complementary probability of the hypoexponential
+ * distribution is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:tail-hypoexp"></A>
+ * bar(F)(<I>x</I>) = <I>P</I>[<I>X</I><SUB>1</SUB> + <SUP> ... </SUP> + <I>X</I><SUB>k</SUB> > <I>x</I>] = ∑<SUB>j=1</SUB><SUP>k</SUP>(<I>e</I><SUP><B>A</B>x</SUP>)<SUB>1j</SUB>,
+ * </DIV><P></P>
+ * i.e., it is the sum of the elements of the first row of matrix <SPAN CLASS="MATH"><I>e</I><SUP><B>A</B>x</SUP></SPAN>.
+ * The density of the hypoexponential distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (- <I>e</I><SUP><B>A</B>x</SUP><B>A</B>)<SUB>1k</SUB> = <I>λ</I><SUB>k</SUB>(<I>e</I><SUP><B>A</B>x</SUP>)<SUB>1k</SUB>,
+ * </DIV><P></P>
+ * i.e., it is element <SPAN CLASS="MATH">(1, <I>k</I>)</SPAN> of matrix
+ * <SPAN CLASS="MATH">- <I>e</I><SUP><B>A</B>x</SUP><B>A</B></SPAN>.
+ * The distribution function is as usual
+ * <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = 1 - bar(F)(<I>x</I>)</SPAN>.
+ *
+ * <P>
+ * See the class {@link HypoExponentialDistQuick} for alternative formulae
+ * for the probabilities.
+ *
+ */
+public class HypoExponentialDist extends ContinuousDistribution {
+ protected double[] m_lambda;
+
+ protected static void testLambda (double[] lambda) {
+ int m = lambda.length;
+ for (int j = 0; j < m; ++j) {
+ if (lambda[j] <= 0)
+ throw new IllegalArgumentException ("lambda_j <= 0");
+ }
+ }
+
+
+ // Builds the bidiagonal matrix A out of the lambda
+ private static DoubleMatrix2D buildMatrix (double[] lambda, double x) {
+ int m = lambda.length;
+ testLambda (lambda);
+ DoubleFactory2D F2 = DoubleFactory2D.dense;
+ DoubleMatrix2D A = F2.make(m, m);
+ for (int j = 0; j < m-1; j++) {
+ A.setQuick(j, j, -lambda[j]*x);
+ A.setQuick(j, j + 1, lambda[j]*x);
+ }
+ A.setQuick(m-1, m-1, -lambda[m-1]*x);
+ return A;
+ }
+
+
+ private static class myFunc implements MathFunction {
+ // For inverseF
+ private double[] m_lam;
+ private double m_u;
+
+ public myFunc (double[] lam, double u) {
+ m_lam = lam;
+ m_u = u;
+ }
+
+ public double evaluate (double x) {
+ return m_u - HypoExponentialDist.cdf(m_lam, x);
+ }
+ }
+
+
+
+ /**
+ * Constructs a <TT>HypoExponentialDist</TT> object,
+ * with rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ */
+ public HypoExponentialDist (double[] lambda) {
+ supportA = 0.0;
+ setLambda (lambda);
+ }
+
+
+ public double density (double x) {
+ return density (m_lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (m_lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (m_lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (m_lambda, u);
+ }
+
+ public double getMean() {
+ return getMean (m_lambda);
+ }
+
+ public double getVariance() {
+ return getVariance (m_lambda);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (m_lambda);
+ }
+
+ /**
+ * Computes the density function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>, with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN>
+ * <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param x value at which the density is evaluated
+ *
+ * @return density at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double density (double[] lambda, double x) {
+ if (x < 0)
+ return 0;
+ DoubleMatrix2D Ax = buildMatrix (lambda, x);
+ DoubleMatrix2D T = DMatrix.expBidiagonal(Ax);
+ int m = lambda.length;
+ return lambda[m-1]*T.getQuick(0, m-1);
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>, with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN>
+ * <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param x value at which the distribution is evaluated
+ *
+ * @return distribution at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double cdf (double[] lambda, double x) {
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= Double.MAX_VALUE)
+ return 1.0;
+ double mean = HypoExponentialDist.getMean (lambda);
+ double std = HypoExponentialDist.getStandardDeviation(lambda);
+ double LOW = mean - 1.5*std;
+ if (x > LOW) {
+ double p = 1.0 - HypoExponentialDist.barF(lambda, x);
+ double LIMIT = 1.0e-3;
+ if (p > LIMIT)
+ return p;
+ }
+
+ DoubleMatrix2D T = buildMatrix (lambda, x);
+ DoubleFactory1D fac1 = DoubleFactory1D.dense;
+ int m = lambda.length;
+ DoubleMatrix1D C = fac1.make(m, 1.0);
+ DoubleMatrix1D B = DMatrix.expmiBidiagonal(T, C);
+ return Math.abs(B.getQuick(0));
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>, with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN>
+ * <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>. Returns <SPAN CLASS="MATH">1 -</SPAN><TT>barF(lambda, x)</TT>,
+ * which is much faster than <TT>cdf</TT> but loses precision
+ * in the lower tail.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param x value at which the distribution is evaluated
+ *
+ * @return distribution at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double cdf2 (double[] lambda, double x) {
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= Double.MAX_VALUE)
+ return 1.0;
+ return (1.0 - HypoExponentialDist.barF(lambda, x));
+ }
+
+
+ /**
+ * Computes the complementary distribution <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>,
+ * with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param x value at which the complementary distribution is evaluated
+ *
+ * @return complementary distribution at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double barF (double[] lambda, double x) {
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= Double.MAX_VALUE)
+ return 0.0;
+ DoubleMatrix2D T = buildMatrix (lambda, x);
+ DoubleFactory1D fac1 = DoubleFactory1D.dense;
+ int m = lambda.length;
+ DoubleMatrix1D C = fac1.make(m, 1.0);
+ DoubleMatrix1D B = DMatrix.expBidiagonal(T, C);
+ return B.getQuick(0);
+ }
+
+
+ /**
+ * Computes the inverse distribution function <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>,
+ * with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param u value at which the inverse distribution is evaluated
+ *
+ * @return inverse distribution at <SPAN CLASS="MATH"><I>u</I></SPAN>
+ *
+ */
+ public static double inverseF (double[] lambda, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ final double EPS = 1.0e-12;
+ myFunc fonc = new myFunc (lambda, u);
+ double x1 = getMean (lambda);
+ double v = cdf (lambda, x1);
+ if (u <= v)
+ return RootFinder.brentDekker (0, x1, fonc, EPS);
+
+ // u > v
+ double x2 = 4.0*x1 + 1.0;
+ v = cdf (lambda, x2);
+ while (v < u) {
+ x1 = x2;
+ x2 = 4.0*x2;
+ v = cdf (lambda, x2);
+ }
+ return RootFinder.brentDekker (x1, x2, fonc, EPS);
+ }
+
+
+ /**
+ * Returns the mean,
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = ∑<SUB>i=1</SUB><SUP>k</SUP>1/<I>λ</I><SUB>i</SUB></SPAN>,
+ * of the hypoexponential distribution with rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN>
+ * <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @return mean of the hypoexponential distribution
+ *
+ */
+ public static double getMean (double[] lambda) {
+ testLambda (lambda);
+ int k = lambda.length;
+ double sum = 0;
+ for (int j = 0; j < k; j++)
+ sum += 1.0 / lambda[j];
+ return sum;
+ }
+
+
+ /**
+ * Returns the variance,
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = ∑<SUB>i=1</SUB><SUP>k</SUP>1/<I>λ</I><SUB>i</SUB><SUP>2</SUP></SPAN>,
+ * of the hypoexponential distribution with rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN>
+ * <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @return variance of the hypoexponential distribution
+ *
+ */
+ public static double getVariance (double[] lambda) {
+ testLambda (lambda);
+ int k = lambda.length;
+ double sum = 0;
+ for (int j = 0; j < k; j++)
+ sum += 1.0 / (lambda[j]*lambda[j]);
+ return sum;
+ }
+
+
+ /**
+ * Returns the standard deviation
+ * of the hypoexponential distribution with rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN>
+ * <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @return standard deviation of the hypoexponential distribution
+ *
+ */
+ public static double getStandardDeviation (double[] lambda) {
+ double s = getVariance (lambda);
+ return Math.sqrt(s);
+ }
+
+
+ /**
+ * Returns the values <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN> for this object.
+ *
+ */
+ public double[] getLambda() {
+ return m_lambda;
+ }
+
+
+
+ /**
+ * Sets the values
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN><TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN> for this object.
+ *
+ */
+ public void setLambda (double[] lambda) {
+ if (lambda == null)
+ return;
+ int k = lambda.length;
+ m_lambda = new double[k];
+ testLambda (lambda);
+ System.arraycopy (lambda, 0, m_lambda, 0, k);
+ }
+
+
+ /**
+ * Same as {@link #getLambda getLambda}.
+ *
+ *
+ */
+ public double[] getParams() {
+ return m_lambda;
+ }
+
+
+ public String toString () {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + " : lambda = {" +
+ PrintfFormat.NEWLINE);
+ int k = m_lambda.length;
+ for(int i = 0; i < k; i++) {
+ formatter.format(" %g%n", m_lambda[i]);
+ }
+ formatter.format("}%n");
+ return sb.toString();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/HypoExponentialDist.tex b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDist.tex
new file mode 100644
index 0000000..1bda001
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDist.tex
@@ -0,0 +1,442 @@
+\defmodule {HypoExponentialDist}
+
+This class implements the \emph{hypoexponential} distribution,
+also called the generalized Erlang distribution. Let the $X_j$,
+$j=1,\ldots,k$, be $k$ independent exponential random variables with different
+rates $\lambda_j$, i.e. assume that $\lambda_j \neq \lambda_i$ for
+$i \neq j$. Then the sum $\sum_{j=1}^kX_j$ is called a \emph{hypoexponential}
+random variable.
+
+Let the $k\times k$ upper triangular bidiagonal matrix
+%
+\begin{latexonly}%
+\begin{equation}
+\label{eq:tail-hypomatrix}
+ \bA = \begin{pmatrix}
+ -\lambda_1 & \lambda_1 & 0 & \ldots & 0 \\
+ 0 & -\lambda_2& \lambda_2 & \ldots & 0 \\
+ \vdots & \vdots & \ddots & ~~\ddots & \vdots \\
+ 0 & \ldots & 0 & -\lambda_{k-1} & \lambda_{k-1} \\
+ 0 & \ldots & 0 & 0 & -\lambda_k
+ \end{pmatrix}
+\end{equation}
+\end{latexonly}%
+%
+\begin{htmlonly}%
+\begin{equation}
+\label{eq:tail-hypomatrix}
+ \bA = \begin{array}{ccccc}
+ -\lambda_1 & \lambda_1 & 0 & \ldots & 0 \\
+ 0 & -\lambda_2& \lambda_2 & \ldots & 0 \\
+ \vdots & \vdots & \ddots & ~~\ddots & \vdots \\
+ 0 & \ldots & 0 & -\lambda_{k-1} & \lambda_{k-1} \\
+ 0 & \ldots & 0 & 0 & -\lambda_k
+ \end{array}
+\end{equation}
+\end{htmlonly}%
+%
+with $\lambda_j$ the rates of the $k$ exponential random variables;
+then the cumulative complementary probability of the hypoexponential
+distribution is given by \cite{pNEU81a,pLAT99a}
+\begin{equation}
+\label{eq:tail-hypoexp}
+\bar F(x) =
+\begin{latexonly} \mathbb{P}
+\end{latexonly}
+\begin{htmlonly} \mathcal{P}
+\end{htmlonly}
+\left[X_1 + \cdots + X_k > x \right] =
+\sum_{j=1}^k \left(e^{\bA x}\right)_{1j},
+\end{equation}
+%
+i.e., it is the sum of the elements of the first row of matrix $e^{\bA x}$.
+The density of the hypoexponential distribution is
+\eq f(x) = \left(-e^{\bA x}\bA\right)_{1k} =
+ \lambda_k \left(e^{\bA x}\right)_{1k},
+ \eqlabel{eq:fhypoexp}
+\endeq
+%
+i.e., it is element $(1,k)$ of matrix $-e^{\bA x}\bA$.
+The distribution function is as usual $F(x) = 1 - \bar F(x)$.
+
+See the class \class{HypoExponentialDistQuick} for alternative formulae
+for the probabilities.
+
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: HypoExponentialDist
+ * Description: Hypo-exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import java.util.Formatter;
+import java.util.Locale;
+import cern.colt.matrix.*;
+import cern.colt.matrix.impl.*;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class HypoExponentialDist extends ContinuousDistribution\begin{hide} {
+ protected double[] m_lambda;
+
+ protected static void testLambda (double[] lambda) {
+ int m = lambda.length;
+ for (int j = 0; j < m; ++j) {
+ if (lambda[j] <= 0)
+ throw new IllegalArgumentException ("lambda_j <= 0");
+ }
+ }
+
+
+ // Builds the bidiagonal matrix A out of the lambda
+ private static DoubleMatrix2D buildMatrix (double[] lambda, double x) {
+ int m = lambda.length;
+ testLambda (lambda);
+ DoubleFactory2D F2 = DoubleFactory2D.dense;
+ DoubleMatrix2D A = F2.make(m, m);
+ for (int j = 0; j < m-1; j++) {
+ A.setQuick(j, j, -lambda[j]*x);
+ A.setQuick(j, j + 1, lambda[j]*x);
+ }
+ A.setQuick(m-1, m-1, -lambda[m-1]*x);
+ return A;
+ }
+
+
+ private static class myFunc implements MathFunction {
+ // For inverseF
+ private double[] m_lam;
+ private double m_u;
+
+ public myFunc (double[] lam, double u) {
+ m_lam = lam;
+ m_u = u;
+ }
+
+ public double evaluate (double x) {
+ return m_u - HypoExponentialDist.cdf(m_lam, x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public HypoExponentialDist (double[] lambda)\begin{hide} {
+ supportA = 0.0;
+ setLambda (lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{HypoExponentialDist} object,
+with rates $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (m_lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (m_lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (m_lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (m_lambda, u);
+ }
+
+ public double getMean() {
+ return getMean (m_lambda);
+ }
+
+ public double getVariance() {
+ return getVariance (m_lambda);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (m_lambda);
+ }\end{hide}
+
+ public static double density (double[] lambda, double x)\begin{hide} {
+ if (x < 0)
+ return 0;
+ DoubleMatrix2D Ax = buildMatrix (lambda, x);
+ DoubleMatrix2D T = DMatrix.expBidiagonal(Ax);
+ int m = lambda.length;
+ return lambda[m-1]*T.getQuick(0, m-1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function $f(x)$, with $\lambda_i = $
+\texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{x}{value at which the density is evaluated}
+ \return{density at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double[] lambda, double x)\begin{hide} {
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= Double.MAX_VALUE)
+ return 1.0;
+ double mean = HypoExponentialDist.getMean (lambda);
+ double std = HypoExponentialDist.getStandardDeviation(lambda);
+ double LOW = mean - 1.5*std;
+ if (x > LOW) {
+ double p = 1.0 - HypoExponentialDist.barF(lambda, x);
+ double LIMIT = 1.0e-3;
+ if (p > LIMIT)
+ return p;
+ }
+
+ DoubleMatrix2D T = buildMatrix (lambda, x);
+ DoubleFactory1D fac1 = DoubleFactory1D.dense;
+ int m = lambda.length;
+ DoubleMatrix1D C = fac1.make(m, 1.0);
+ DoubleMatrix1D B = DMatrix.expmiBidiagonal(T, C);
+ return Math.abs(B.getQuick(0));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function $F(x)$, with $\lambda_i = $
+\texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{x}{value at which the distribution is evaluated}
+ \return{distribution at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf2 (double[] lambda, double x)\begin{hide} {
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= Double.MAX_VALUE)
+ return 1.0;
+ return (1.0 - HypoExponentialDist.barF(lambda, x));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function $F(x)$, with $\lambda_i = $
+\texttt{lambda[$i-1$]}, $i = 1,\ldots,k$. Returns $1 - $\texttt{barF(lambda, x)},
+ which is much faster than \texttt{cdf} but loses precision
+ in the lower tail.
+ \end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{x}{value at which the distribution is evaluated}
+ \return{distribution at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double[] lambda, double x)\begin{hide} {
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= Double.MAX_VALUE)
+ return 0.0;
+ DoubleMatrix2D T = buildMatrix (lambda, x);
+ DoubleFactory1D fac1 = DoubleFactory1D.dense;
+ int m = lambda.length;
+ DoubleMatrix1D C = fac1.make(m, 1.0);
+ DoubleMatrix1D B = DMatrix.expBidiagonal(T, C);
+ return B.getQuick(0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution $\bar F(x)$,
+ with $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{x}{value at which the complementary distribution is evaluated}
+ \return{complementary distribution at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double[] lambda, double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ final double EPS = 1.0e-12;
+ myFunc fonc = new myFunc (lambda, u);
+ double x1 = getMean (lambda);
+ double v = cdf (lambda, x1);
+ if (u <= v)
+ return RootFinder.brentDekker (0, x1, fonc, EPS);
+
+ // u > v
+ double x2 = 4.0*x1 + 1.0;
+ v = cdf (lambda, x2);
+ while (v < u) {
+ x1 = x2;
+ x2 = 4.0*x2;
+ v = cdf (lambda, x2);
+ }
+ return RootFinder.brentDekker (x1, x2, fonc, EPS);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse distribution function $F^{-1}(u)$,
+with $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+% It uses a root-finding method and is very slow.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{u}{value at which the inverse distribution is evaluated}
+ \return{inverse distribution at $u$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double[] lambda)\begin{hide} {
+ testLambda (lambda);
+ int k = lambda.length;
+ double sum = 0;
+ for (int j = 0; j < k; j++)
+ sum += 1.0 / lambda[j];
+ return sum;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean, $E[X] = \sum_{i=1}^k 1/\lambda_i$,
+ of the hypoexponential distribution with rates $\lambda_i = $
+\texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \return{mean of the hypoexponential distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double[] lambda)\begin{hide} {
+ testLambda (lambda);
+ int k = lambda.length;
+ double sum = 0;
+ for (int j = 0; j < k; j++)
+ sum += 1.0 / (lambda[j]*lambda[j]);
+ return sum;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance,
+$\mbox{Var}[X] = \sum_{i=1}^k 1/\lambda_i^2$,
+of the hypoexponential distribution with rates $\lambda_i = $
+\texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \return{variance of the hypoexponential distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double[] lambda)\begin{hide} {
+ double s = getVariance (lambda);
+ return Math.sqrt(s);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation
+of the hypoexponential distribution with rates $\lambda_i = $
+\texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \return{standard deviation of the hypoexponential distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getLambda()\begin{hide} {
+ return m_lambda;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the values $\lambda_i$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public void setLambda (double[] lambda)\begin{hide} {
+ if (lambda == null)
+ return;
+ int k = lambda.length;
+ m_lambda = new double[k];
+ testLambda (lambda);
+ System.arraycopy (lambda, 0, m_lambda, 0, k);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the values $\lambda_i = $\texttt{lambda[$i-1$]},
+ $i = 1,\ldots,k$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams()\begin{hide} {
+ return m_lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{getLambda}{}.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + " : lambda = {" +
+ PrintfFormat.NEWLINE);
+ int k = m_lambda.length;
+ for(int i = 0; i < k; i++) {
+ formatter.format(" %g%n", m_lambda[i]);
+ }
+ formatter.format("}%n");
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current
+ distribution.
+\end{tabb}
+\end{hide}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistEqual.java b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistEqual.java
new file mode 100644
index 0000000..08977be
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistEqual.java
@@ -0,0 +1,228 @@
+
+/*
+ * Class: HypoExponentialDistEqual
+ * Description: Hypo-exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since February 2014
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.util.Formatter;
+import java.util.Locale;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+/**
+ * This class implements the <SPAN CLASS="textit">hypoexponential</SPAN> distribution for the case of equidistant
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> = (<I>n</I> + 1 - <I>i</I>)<I>h</I></SPAN>. We have
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i+1</SUB> - <I>λ</I><SUB>i</SUB> = <I>h</I></SPAN>, with <SPAN CLASS="MATH"><I>h</I></SPAN> a constant, and <SPAN CLASS="MATH"><I>n</I> >= <I>k</I></SPAN> are integers.
+ *
+ * <P>
+ * The formula becomes
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * bar(F)(<I>x</I>) = <I>P</I>[<I>X</I><SUB>1</SUB> + <SUP> ... </SUP> + <I>X</I><SUB>k</SUB> > <I>x</I>] = ∑<SUB>i=1</SUB><SUP>k</SUP><I>e</I><SUP>-(n+1-i)hx</SUP>∏<SUB>1#1j=1<tex2html_row_mark>j 2#2i</SUB><SUP>k</SUP><I>n</I>+1-<I>j</I>/<I>i</I>-<I>j</I>.
+ * </DIV><P></P>
+ * The formula for the density becomes
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = ∑<SUB>i=1</SUB><SUP>k</SUP>(<I>n</I> + 1 - <I>i</I>)<I>he</I><SUP>-(n+1-i)hx</SUP>∏<SUB>[tex2html_wrap_indisplay193]j=1<tex2html_row_mark>j [tex2html_wrap_indisplay194]i</SUB><SUP>k</SUP><I>n</I>+1-<I>j</I>/<I>i</I>-<I>j</I>.
+ * </DIV><P></P>
+ *
+ */
+public class HypoExponentialDistEqual extends HypoExponentialDist {
+ private double m_h;
+ private int m_n;
+ private int m_k;
+
+
+
+ /**
+ * Constructor for equidistant rates.
+ * The rates are
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> = (<I>n</I> + 1 - <I>i</I>)<I>h</I></SPAN>, for
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param n largest rate is <SPAN CLASS="MATH"><I>nh</I></SPAN>
+ *
+ * @param k number of rates
+ *
+ * @param h difference between adjacent rates
+ *
+ */
+ public HypoExponentialDistEqual (int n, int k, double h) {
+ super (null);
+ setParams (n, k, h);
+ }
+
+ public double density (double x) {
+ return density (m_n, m_k, m_h, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (m_n, m_k, m_h, x);
+ }
+
+ public double barF (double x) {
+ return barF (m_n, m_k, m_h, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (m_n, m_k, m_h, u);
+ }
+
+ /**
+ * Computes the density function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>, with the same arguments
+ * as in the constructor.
+ *
+ * @param n max possible number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param k effective number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param h step between two successive <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param x value at which the distribution is evaluated
+ *
+ * @return density at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double density (int n, int k, double h, double x) {
+ if (x < 0)
+ return 0;
+ double r = -Math.expm1(-h*x);
+ double v = BetaDist.density(k, n - k + 1, r);
+ return h*v*Math.exp(-h*x);
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>, with arguments
+ * as in the constructor.
+ *
+ * @param n max possible number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param k effective number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param h step between two successive <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param x value at which the distribution is evaluated
+ *
+ * @return value of distribution at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double cdf (int n, int k, double h, double x) {
+ if (x <= 0)
+ return 0;
+ double r = -Math.expm1(-h*x);
+ double u = BetaDist.cdf(k, n - k + 1, r);
+ return u;
+ }
+
+
+ /**
+ * Computes the complementary distribution <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>,
+ * as in formula.
+ *
+ * @param n max possible number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param k effective number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param h step between two successive <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param x value at which the complementary distribution is evaluated
+ *
+ * @return value of complementary distribution at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double barF (int n, int k, double h, double x) {
+ if (x <= 0)
+ return 1.0;
+ double r = Math.exp(-h*x);
+ double v = BetaDist.cdf(n - k + 1, k, r);
+ return v;
+ }
+
+
+ /**
+ * Computes the inverse distribution
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>,
+ * with arguments as in the constructor.
+ *
+ * @param n max possible number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param k effective number of <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param h step between two successive <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ *
+ * @param u value at which the inverse distribution is evaluated
+ *
+ * @return inverse distribution at <SPAN CLASS="MATH"><I>u</I></SPAN>
+ *
+ */
+ public static double inverseF (int n, int k, double h, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ double z = BetaDist.inverseF(k, n - k + 1, u);
+ return -Math.log1p(-z) / h;
+ }
+
+
+ /**
+ * Returns the three parameters
+ * of this hypoexponential distribution as array <SPAN CLASS="MATH">(<I>n</I>, <I>k</I>, <I>h</I>)</SPAN>.
+ *
+ * @return parameters of the hypoexponential distribution
+ *
+ */
+ public double[] getParams() {
+ double[] par = new double[]{m_n, m_k, m_h};
+ return par;
+ }
+
+
+ public void setParams (int n, int k, double h) {
+ m_n = n;
+ m_k = k;
+ m_h = h;
+ m_lambda = new double[k];
+ for(int i = 0; i < k; i++) {
+ m_lambda[i] = (n - i)*h;
+ }
+ }
+
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + " : params = {" +
+ PrintfFormat.NEWLINE);
+ formatter.format(" %d, %d, %f", m_n, m_k, m_h);
+ formatter.format("}%n");
+ return sb.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistEqual.tex b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistEqual.tex
new file mode 100644
index 0000000..2fe53ed
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistEqual.tex
@@ -0,0 +1,234 @@
+\defmodule {HypoExponentialDistEqual}
+
+This class implements the \emph{hypoexponential} distribution for the case of equidistant $\lambda_{i} = (n+1-i)h$. We have $\lambda_{i+1} - \lambda_i =
+h$, with $h$ a constant, and $n \ge k$ are integers.
+
+\noindent The formula (\ref{eq:convolution-hypo}) becomes
+\begin{equation}
+\begin{latexonly}%
+\bar F(x) = \mathbb{P}\left[X_1 + \cdots + X_k > x \right]
+ = \sum_{i=1}^k e^{-(n+1-i)h x} \prod_{\substack{j=1\\ j\not=i}}^k
+ \frac{n+1-j}{i - j}.
+\label{eq:conv-hypo-equal}
+\end{latexonly}%
+\begin{htmlonly}%
+\bar F(x) = \mathcal{P}\left[X_1 + \cdots + X_k > x \right]
+ = \sum_{i=1}^k e^{-(n+1-i)h x} \prod_{\substack{j=1\\ j\not=i}}^k
+ {n+1-j}/{i - j}.
+\end{htmlonly}%
+\end{equation}
+%
+The formula (\ref{eq:fhypoexp2}) for the density becomes
+\begin{equation}
+\begin{latexonly}%
+ f(x) = \sum_{i=1}^k (n+1-i)h e^{-(n+1-i)h x} \prod_{\substack{j=1\\ j\not=i}}^k
+ \frac{n+1-j}{i - j}.
+\eqlabel{eq:fhypoexp3}
+\end{latexonly}%
+\begin{htmlonly}%
+ f(x) =\sum_{i=1}^k (n+1-i)h e^{-(n+1-i)h x}
+ \prod_{\substack{j=1\\ j\not=i}}^k {n+1-j}/{i - j}.
+\end{htmlonly}%
+\end{equation}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: HypoExponentialDistEqual
+ * Description: Hypo-exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since February 2014
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import java.util.Formatter;
+import java.util.Locale;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;\end{hide}
+
+public class HypoExponentialDistEqual extends HypoExponentialDist\begin{hide} {
+ private double m_h;
+ private int m_n;
+ private int m_k;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public HypoExponentialDistEqual (int n, int k, double h)\begin{hide} {
+ super (null);
+ setParams (n, k, h);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructor for equidistant rates.
+ The rates are $\lambda_i = (n+1-i)h$, for $i = 1,\ldots,k$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{largest rate is $nh$}
+ \param{k}{number of rates}
+ \param{h}{difference between adjacent rates}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ public double density (double x) {
+ return density (m_n, m_k, m_h, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (m_n, m_k, m_h, x);
+ }
+
+ public double barF (double x) {
+ return barF (m_n, m_k, m_h, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (m_n, m_k, m_h, u);
+ }\end{hide}
+
+ public static double density (int n, int k, double h, double x)\begin{hide} {
+ if (x < 0)
+ return 0;
+ double r = -Math.expm1(-h*x);
+ double v = BetaDist.density(k, n - k + 1, r);
+ return h*v*Math.exp(-h*x);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the density function $f(x)$, with the same arguments
+ as in the constructor.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{max possible number of $\lambda_i$}
+ \param{k}{effective number of $\lambda_i$}
+ \param{h}{step between two successive $\lambda_i$}
+ \param{x}{value at which the distribution is evaluated}
+ \return{density at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (int n, int k, double h, double x)\begin{hide} {
+ if (x <= 0)
+ return 0;
+ double r = -Math.expm1(-h*x);
+ double u = BetaDist.cdf(k, n - k + 1, r);
+ return u;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the distribution function $F(x)$, with arguments
+ as in the constructor.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{max possible number of $\lambda_i$}
+ \param{k}{effective number of $\lambda_i$}
+ \param{h}{step between two successive $\lambda_i$}
+ \param{x}{value at which the distribution is evaluated}
+ \return{value of distribution at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (int n, int k, double h, double x)\begin{hide} {
+ if (x <= 0)
+ return 1.0;
+ double r = Math.exp(-h*x);
+ double v = BetaDist.cdf(n - k + 1, k, r);
+ return v;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution $\bar F(x)$,
+ as in formula (\ref{eq:conv-hypo-equal}).
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{max possible number of $\lambda_i$}
+ \param{k}{effective number of $\lambda_i$}
+ \param{h}{step between two successive $\lambda_i$}
+ \param{x}{value at which the complementary distribution is evaluated}
+ \return{value of complementary distribution at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (int n, int k, double h, double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ double z = BetaDist.inverseF(k, n - k + 1, u);
+ return -Math.log1p(-z) / h;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse distribution $x=F^{-1}(u)$,
+ with arguments as in the constructor.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{max possible number of $\lambda_i$}
+ \param{k}{effective number of $\lambda_i$}
+ \param{h}{step between two successive $\lambda_i$}
+ \param{u}{value at which the inverse distribution is evaluated}
+ \return{inverse distribution at $u$}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getParams()\begin{hide} {
+ double[] par = new double[]{m_n, m_k, m_h};
+ return par;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the three parameters
+of this hypoexponential distribution as array $(n, k, h)$.
+\end{tabb}
+\begin{htmlonly}
+ \return{parameters of the hypoexponential distribution}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void setParams (int n, int k, double h) {
+ m_n = n;
+ m_k = k;
+ m_h = h;
+ m_lambda = new double[k];
+ for(int i = 0; i < k; i++) {
+ m_lambda[i] = (n - i)*h;
+ }
+ }
+
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + " : params = {" +
+ PrintfFormat.NEWLINE);
+ formatter.format(" %d, %d, %f", m_n, m_k, m_h);
+ formatter.format("}%n");
+ return sb.toString();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistQuick.java b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistQuick.java
new file mode 100644
index 0000000..cdca26e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistQuick.java
@@ -0,0 +1,325 @@
+
+/*
+ * Class: HypoExponentialDistQuick
+ * Description: Hypo-exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.util.Formatter;
+import java.util.Locale;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+/**
+ * This class is a subclass of {@link HypoExponentialDist}
+ * and also implements the <SPAN CLASS="textit">hypoexponential</SPAN> distribution. It uses
+ * different algorithms to compute the probabilities.
+ * The formula for the complementary distribution
+ * is mathematically equivalent to (see and)
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * bar(F)(<I>x</I>) = <I>P</I>[<I>X</I><SUB>1</SUB> + <SUP> ... </SUP> + <I>X</I><SUB>k</SUB> > <I>x</I>] = ∑<SUB>i=1</SUB><SUP>k</SUP><I>e</I><SUP>-<I>λ</I><SUB>i</SUB>x</SUP>∏<SUB>j=1, j≠i</SUB><SUP>k</SUP><I>λ</I><SUB>j</SUB>/(<I>λ</I><SUB>j</SUB>-<I>λ</I><SUB>i</SUB>).
+ * </DIV><P></P>
+ *
+ * <P>
+ * The expression is much faster to compute than the
+ * matrix exponential formula, but it becomes numerically
+ * unstable when <SPAN CLASS="MATH"><I>k</I></SPAN> gets large and/or the differences between the <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ * are too small, because it is an alternating sum with relatively large terms
+ * of similar size. When the <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN> are close, many of the
+ * factors
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>j</SUB> - <I>λ</I><SUB>i</SUB></SPAN> in are small,
+ * and the effect of this is amplified when <SPAN CLASS="MATH"><I>k</I></SPAN> is large. This gives rise to
+ * large terms of opposite sign in the sum and the formula becomes unstable
+ * due to subtractive cancellation.
+ * For example, with the computations done in standard 64-bit floating-point
+ * arithmetic, if the <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN> are regularly spaced with differences
+ * of
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i+1</SUB> - <I>λ</I><SUB>i</SUB> = 0.1</SPAN> for all <SPAN CLASS="MATH"><I>i</I></SPAN>, the formula breaks down already for
+ * <SPAN CLASS="MATH"><I>k</I> = 15</SPAN>, while if
+ * the differences
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i+1</SUB> - <I>λ</I><SUB>i</SUB> = 3</SPAN>, it gives a few decimal
+ * digits of precision for <SPAN CLASS="MATH"><I>k</I></SPAN> up to
+ * <SPAN CLASS="MATH"> = 300</SPAN>.
+ *
+ * <P>
+ * The formula for the density is mathematically equivalent to
+ * the much faster formula
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = ∑<SUB>i=1</SUB><SUP>k</SUP><I>λ</I><SUB>i</SUB><I>e</I><SUP>-<I>λ</I><SUB>i</SUB>x</SUP>∏<SUB>j=1, j≠i</SUB><SUP>k</SUP><I>λ</I><SUB>j</SUB>/(<I>λ</I><SUB>j</SUB>-<I>λ</I><SUB>i</SUB>),
+ * </DIV><P></P>
+ * which is also numerically
+ * unstable when <SPAN CLASS="MATH"><I>k</I></SPAN> gets large and/or the differences between the <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN>
+ * are too small.
+ *
+ */
+public class HypoExponentialDistQuick extends HypoExponentialDist {
+ private double[] m_H;
+
+ private static double[] computeH (double[] lambda) {
+ int k = lambda.length;
+ double[] H = new double[k];
+ double tem;
+ int j;
+ for (int i = 0; i < k; i++) {
+ tem = 1.0;
+ for (j = 0; j < i; j++)
+ tem *= lambda[j] / (lambda[j] - lambda[i]);
+ for (j = i + 1; j < k; j++)
+ tem *= lambda[j] / (lambda[j] - lambda[i]);
+ H[i] = tem;
+ }
+ return H;
+ }
+
+
+ private static double m_density(double[] lambda, double[] H, double x) {
+ if (x < 0)
+ return 0;
+
+ int k = lambda.length;
+ double tem;
+ double prob = 0;
+ for (int j = 0; j < k; j++) {
+ tem = Math.exp (-lambda[j] * x);
+ if (tem > 0)
+ prob += lambda[j] * H[j] * tem;
+ }
+
+ return prob;
+ }
+
+
+ private static double m_barF(double[] lambda, double[] H, double x) {
+ if (x <= 0.)
+ return 1.;
+
+ int k = lambda.length;
+ double tem;
+ double prob = 0; // probability
+ for (int j = 0; j < k; j++) {
+ tem = Math.exp (-lambda[j] * x);
+ if (tem > 0)
+ prob += H[j] * tem;
+ }
+ return prob;
+ }
+
+
+ private static double m_cdf(double[] lambda, double[] H, double x) {
+ if (x <= 0.)
+ return 0.;
+
+ int k = lambda.length;
+ double tem = Math.exp(-lambda[0] * x);
+ if (tem <= 0)
+ return 1.0 - HypoExponentialDistQuick.m_barF(lambda, H, x);
+
+ double prob = 0; // cumulative probability
+ for (int j = 0; j < k; j++) {
+ tem = Math.expm1 (-lambda[j] * x);
+ prob += H[j] * tem;
+ }
+ return -prob;
+ }
+
+
+ private static class myFunc implements MathFunction {
+ // For inverseF
+ private double[] m_lam;
+ private double m_u;
+
+ public myFunc (double[] lam, double u) {
+ m_lam = lam;
+ m_u = u;
+ }
+
+ public double evaluate (double x) {
+ return m_u - HypoExponentialDistQuick.cdf(m_lam, x);
+ }
+ }
+
+
+
+ /**
+ * Constructs a <TT>HypoExponentialDistQuick</TT> object,
+ * with rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ */
+ public HypoExponentialDistQuick (double[] lambda) {
+ super(lambda);
+ }
+
+ /* These public methods are necessary so that methods cdf,
+ * barF and inverseF used are those of the present
+ * class and not those of the mother class.
+ */
+ public double density (double x) {
+ return m_density (m_lambda, m_H, x);
+ }
+
+ public double cdf (double x) {
+ return m_cdf (m_lambda, m_H, x);
+ }
+
+ public double barF (double x) {
+ return m_barF (m_lambda, m_H, x);
+ }
+
+ public double inverseF (double u) {
+ return m_inverseF (m_lambda, m_H, u);
+ }
+
+ /**
+ * Computes the density function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>, with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN>
+ * <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param x value at which the density is evaluated
+ *
+ * @return density at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double density (double[] lambda, double x) {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_density(lambda, H, x);
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>,
+ * with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param x value at which the distribution is evaluated
+ *
+ * @return value of distribution at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double cdf (double[] lambda, double x) {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_cdf(lambda, H, x);
+ }
+
+
+ /**
+ * Computes the complementary distribution <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>,
+ * with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param x value at which the complementary distribution is evaluated
+ *
+ * @return value of complementary distribution at <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double barF (double[] lambda, double x) {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_barF(lambda, H, x);
+ }
+
+
+ /**
+ * Computes the inverse distribution function <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>,
+ * with
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param lambda rates of the hypoexponential distribution
+ *
+ * @param u value at which the inverse distribution is evaluated
+ *
+ * @return inverse distribution at <SPAN CLASS="MATH"><I>u</I></SPAN>
+ *
+ */
+ public static double inverseF (double[] lambda, double u) {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_inverseF(lambda, H, u);
+ }
+
+ private static double m_inverseF(double[] lambda, double[] H, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ final double EPS = 1.0e-12;
+ myFunc fonc = new myFunc (lambda, u);
+ double x1 = getMean (lambda);
+ double v = m_cdf(lambda, H, x1);
+ if (u <= v)
+ return RootFinder.brentDekker (0, x1, fonc, EPS);
+
+ // u > v
+ double x2 = 4.0*x1 + 1.0;
+ v = m_cdf(lambda, H, x2);
+ while (v < u) {
+ x1 = x2;
+ x2 = 4.0*x2;
+ v = m_cdf(lambda, H, x2);
+ }
+ return RootFinder.brentDekker (x1, x2, fonc, EPS);
+ }
+
+
+ public void setLambda (double[] lambda) {
+ if (lambda == null)
+ return;
+ super.setLambda (lambda);
+ m_H = computeH (lambda);
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + " : lambda = {" +
+ PrintfFormat.NEWLINE);
+ int k = m_lambda.length;
+ for(int i = 0; i < k; i++) {
+ formatter.format(" %f%n", m_lambda[i]);
+ }
+ formatter.format("}%n");
+ return sb.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistQuick.tex b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistQuick.tex
new file mode 100644
index 0000000..cb03541
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/HypoExponentialDistQuick.tex
@@ -0,0 +1,335 @@
+\defmodule {HypoExponentialDistQuick}
+
+This class is a subclass of \class{HypoExponentialDist}
+and also implements the \emph{hypoexponential} distribution. It uses
+different algorithms to compute the probabilities.
+The formula (\ref{eq:tail-hypoexp}) for the complementary distribution
+is mathematically equivalent to (see
+\cite[page 299]{pROS07b} and \cite[Appendix B]{pGER10a})
+\begin{equation}
+\begin{latexonly}%
+\bar F(x) = \mathbb{P}\left[X_1 + \cdots + X_k > x \right]
+ = \sum_{i=1}^k e^{-\lambda_i x} \prod_{\substack{j=1\\ j\not=i}}^k \frac{\lambda_j}{\lambda_
+j - \lambda_i}.
+\label{eq:convolution-hypo}
+\end{latexonly}%
+\begin{htmlonly}%
+\bar F(x) = \mathcal{P}\left[X_1 + \cdots + X_k > x \right]
+ = \sum_{i=1}^k e^{-\lambda_i x} \prod_{j=1,\; j\neq i}^k
+ {\lambda_j}/{(\lambda_j - \lambda_i)}.
+\end{htmlonly}%
+\end{equation}
+
+
+The expression (\ref{eq:convolution-hypo}) is much faster to compute than the
+matrix exponential formula (\ref{eq:tail-hypoexp}), but it becomes numerically
+unstable when $k$ gets large and/or the differences between the $\lambda_i$
+are too small, because it is an alternating sum with relatively large terms
+of similar size. When the $\lambda_i$ are close, many of the
+factors $\lambda_{j} - \lambda_{i}$ in (\ref{eq:convolution-hypo}) are small,
+and the effect of this is amplified when $k$ is large. This gives rise to
+large terms of opposite sign in the sum and the formula becomes unstable
+due to subtractive cancellation.
+%
+For example, with the computations done in standard 64-bit floating-point
+arithmetic, if the $\lambda_i$ are regularly spaced with differences
+of $\lambda_{i+1} - \lambda_{i} = 0.1$ for all $i$, the formula
+(\ref{eq:convolution-hypo}) breaks down already for $k \approx 15$, while if
+the differences $\lambda_{i+1} - \lambda_{i} = 3$, it gives a few decimal
+digits of precision for $k$ up to $\approx 300$.
+
+
+The formula (\ref{eq:fhypoexp}) for the density is mathematically equivalent to
+the much faster formula
+\begin{equation}
+\begin{latexonly}%
+ f(x) = \sum_{i=1}^k\lambda_i e^{-\lambda_i x}
+ \prod_{\substack{j=1\\ j\not=i}}^k \frac{\lambda_j}{\lambda_j - \lambda_i},
+\eqlabel{eq:fhypoexp2}
+\end{latexonly}%
+\begin{htmlonly}%
+ f(x) = \sum_{i=1}^k\lambda_i e^{-\lambda_i x}
+ \prod_{j=1,\; j\neq i}^k {\lambda_j}/{(\lambda_j - \lambda_i)},
+\end{htmlonly}%
+\end{equation}
+which is also numerically
+unstable when $k$ gets large and/or the differences between the $\lambda_i$
+are too small.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: HypoExponentialDistQuick
+ * Description: Hypo-exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import java.util.Formatter;
+import java.util.Locale;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;\end{hide}
+
+public class HypoExponentialDistQuick extends HypoExponentialDist\begin{hide} {
+ private double[] m_H;
+
+ private static double[] computeH (double[] lambda) {
+ int k = lambda.length;
+ double[] H = new double[k];
+ double tem;
+ int j;
+ for (int i = 0; i < k; i++) {
+ tem = 1.0;
+ for (j = 0; j < i; j++)
+ tem *= lambda[j] / (lambda[j] - lambda[i]);
+ for (j = i + 1; j < k; j++)
+ tem *= lambda[j] / (lambda[j] - lambda[i]);
+ H[i] = tem;
+ }
+ return H;
+ }
+
+
+ private static double m_density(double[] lambda, double[] H, double x) {
+ if (x < 0)
+ return 0;
+
+ int k = lambda.length;
+ double tem;
+ double prob = 0;
+ for (int j = 0; j < k; j++) {
+ tem = Math.exp (-lambda[j] * x);
+ if (tem > 0)
+ prob += lambda[j] * H[j] * tem;
+ }
+
+ return prob;
+ }
+
+
+ private static double m_barF(double[] lambda, double[] H, double x) {
+ if (x <= 0.)
+ return 1.;
+
+ int k = lambda.length;
+ double tem;
+ double prob = 0; // probability
+ for (int j = 0; j < k; j++) {
+ tem = Math.exp (-lambda[j] * x);
+ if (tem > 0)
+ prob += H[j] * tem;
+ }
+ return prob;
+ }
+
+
+ private static double m_cdf(double[] lambda, double[] H, double x) {
+ if (x <= 0.)
+ return 0.;
+
+ int k = lambda.length;
+ double tem = Math.exp(-lambda[0] * x);
+ if (tem <= 0)
+ return 1.0 - HypoExponentialDistQuick.m_barF(lambda, H, x);
+
+ double prob = 0; // cumulative probability
+ for (int j = 0; j < k; j++) {
+ tem = Math.expm1 (-lambda[j] * x);
+ prob += H[j] * tem;
+ }
+ return -prob;
+ }
+
+
+ private static class myFunc implements MathFunction {
+ // For inverseF
+ private double[] m_lam;
+ private double m_u;
+
+ public myFunc (double[] lam, double u) {
+ m_lam = lam;
+ m_u = u;
+ }
+
+ public double evaluate (double x) {
+ return m_u - HypoExponentialDistQuick.cdf(m_lam, x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public HypoExponentialDistQuick (double[] lambda)\begin{hide} {
+ super(lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{HypoExponentialDistQuick} object,
+with rates $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ /* These public methods are necessary so that methods cdf,
+ * barF and inverseF used are those of the present
+ * class and not those of the mother class.
+ */
+ public double density (double x) {
+ return m_density (m_lambda, m_H, x);
+ }
+
+ public double cdf (double x) {
+ return m_cdf (m_lambda, m_H, x);
+ }
+
+ public double barF (double x) {
+ return m_barF (m_lambda, m_H, x);
+ }
+
+ public double inverseF (double u) {
+ return m_inverseF (m_lambda, m_H, u);
+ }\end{hide}
+
+ public static double density (double[] lambda, double x)\begin{hide} {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_density(lambda, H, x);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the density function $f(x)$, with $\lambda_i = $
+\texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{x}{value at which the density is evaluated}
+ \return{density at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double[] lambda, double x)\begin{hide} {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_cdf(lambda, H, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the distribution function $F(x)$,
+with $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{x}{value at which the distribution is evaluated}
+ \return{value of distribution at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double[] lambda, double x)\begin{hide} {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_barF(lambda, H, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution $\bar F(x)$,
+with $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{x}{value at which the complementary distribution is evaluated}
+ \return{value of complementary distribution at $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double[] lambda, double u)\begin{hide} {
+ testLambda (lambda);
+ double[] H = computeH (lambda);
+ return m_inverseF(lambda, H, u);
+ }
+
+ private static double m_inverseF(double[] lambda, double[] H, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ final double EPS = 1.0e-12;
+ myFunc fonc = new myFunc (lambda, u);
+ double x1 = getMean (lambda);
+ double v = m_cdf(lambda, H, x1);
+ if (u <= v)
+ return RootFinder.brentDekker (0, x1, fonc, EPS);
+
+ // u > v
+ double x2 = 4.0*x1 + 1.0;
+ v = m_cdf(lambda, H, x2);
+ while (v < u) {
+ x1 = x2;
+ x2 = 4.0*x2;
+ v = m_cdf(lambda, H, x2);
+ }
+ return RootFinder.brentDekker (x1, x2, fonc, EPS);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse distribution function $F^{-1}(u)$,
+with $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$.
+% It uses a root-finding method and is very slow.
+\end{tabb}
+\begin{htmlonly}
+ \param{lambda}{rates of the hypoexponential distribution}
+ \param{u}{value at which the inverse distribution is evaluated}
+ \return{inverse distribution at $u$}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void setLambda (double[] lambda) {
+ if (lambda == null)
+ return;
+ super.setLambda (lambda);
+ m_H = computeH (lambda);
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + " : lambda = {" +
+ PrintfFormat.NEWLINE);
+ int k = m_lambda.length;
+ for(int i = 0; i < k; i++) {
+ formatter.format(" %f%n", m_lambda[i]);
+ }
+ formatter.format("}%n");
+ return sb.toString();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/InverseDistFromDensity.java b/source/umontreal/iro/lecuyer/probdist/InverseDistFromDensity.java
new file mode 100644
index 0000000..2f0db2d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/InverseDistFromDensity.java
@@ -0,0 +1,966 @@
+
+
+/*
+ * Class: InverseDistFromDensity
+ * Description: computing the inverse of an arbitrary continuous distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+ import umontreal.iro.lecuyer.functions.MathFunction;
+import umontreal.iro.lecuyer.util.Misc;
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+
+
+
+/**
+ * Implements a method for computing the inverse of an <SPAN CLASS="textit">arbitrary
+ * continuous</SPAN> distribution function when only the probability density
+ * is known. The cumulative probabilities (cdf) are pre-computed by
+ * numerical quadrature of the
+ * density using Gauss-Lobatto integration over suitably small intervals to
+ * satisfy the required precision, and these values are kept in tables. Then the
+ * algorithm uses polynomial interpolation over the tabulated values to get
+ * the inverse cdf. The user can select the
+ * desired precision and the degree of the interpolating polynomials.
+ *
+ * <P>
+ * The algorithm may fail for some distributions for which the density
+ * becomes infinite at a point (for ex. the Gamma and the Beta distributions
+ * with
+ * <SPAN CLASS="MATH"><I>α</I> < 1</SPAN>) if one chooses too high a precision
+ * (a too small <TT>eps</TT>, for ex.
+ * <SPAN CLASS="MATH"><I>ε</I>∼10<SUP>-15</SUP></SPAN>).
+ * However, it should work also for continuous densities with finite discontinuities.
+ *
+ * <P>
+ * While the setup time for this class is relatively slow, the numerical inversion
+ * is extremely fast and practically independent of the required precision
+ * and of the specific distribution. For comparisons between the times
+ * of standard inversion and inversion from this class as well as
+ * comparisons between setup times, see the introduction in class
+ * {@link umontreal.iro.lecuyer.randvar.InverseFromDensityGen InverseFromDensityGen}
+ * from package <TT>randvar</TT>.
+ *
+ * <P>
+ * Thus if only a few inverses are needed, then using this class
+ * is not efficient because of the slow set-up. But if one wants to call
+ * <TT>inverseF</TT> thousands of times or more, then using this class will
+ * be very efficient.
+ *
+ */
+public class InverseDistFromDensity extends ContinuousDistribution {
+ private final boolean DEBUG = false;
+ protected static final double HALF_PI = Math.PI/2.0;
+ private final double epsc = 1.0e-5; // for small values of lc in tails
+ private final double HUGE = Double.MAX_VALUE / 2.0;
+ private double epsu0; // initial u-resolution
+ private double xc; // mode, mean, or median of distribution
+ // xc is an x where the density is high
+ MathFunction m_dens; // probability density
+ private String name; // Name of class
+
+ private final int K0 = 128; // initial size of tables A, F, ...
+ private int Kmax; // final size of tables A, F, ... is (Kmax + 1)
+ private double[] A; // x-values
+ private double[] F; // corresponding u-values (the CDF)
+ private double[][] X; // interpolation x-values in [A[k], A[k+1]]
+ private double[][] U; // interpolation u-values in [A[k], A[k+1]]
+ private double[][] C; // interpolation coefficients in [A[k], A[k+1]]
+ private int order; // order of interpolation polynomial in each [A[k], A[k+1]]
+ private int[] Index; // for indexed search in F[k]
+ private int Imax; // final size of Index is (Imax + 1)
+
+ private double bleft; // computational left limit of density
+ private double bright; // computational right limit of density
+ private double bl; // left border of the computational domain
+ private double br; // right border of the computational domain
+ private double llc; // absolute local concavity in left tail
+ private double rlc; // absolute local concavity in right tail
+/*
+ For NormalDist(0,1), local concavity c = 1/z^2
+ For CauchyDist(0,1), local concavity c = -1/2 + 1/(2z^2)
+ For GammaDist(a,lam), local concavity c = (a-1)/(1 - a +lam*x)^2
+*/
+ private double lc1; // left, c1 = f(p)/f'(p) or f(p) / (c*f'(p))
+ private double lc2; // left, c2 = |f'(p)|(1 + c) / (f(p)^2)
+ private double lc3; // left, c3 = c/(1 + c)
+ private double rc1; // right, c1 = f(p) / f'(p) or f(p) / (c*f'(p))
+ private double rc2; // right, c2 = |f'(p)|(1 + c) / (f(p)^2)
+ private double rc3; // right, c3 = c/(1 + c)
+ private double epstail; // = 0.05*epsu*I0;
+ private boolean lcutF = false; // cut-off flag, left tail
+ private boolean rcutF = false; // cut-off flag, right tail
+
+
+ protected void printArray (double[] U) {
+ System.out.print(" Tableau = (");
+ for (int j = 0; j < U.length; j++)
+ System.out.printf(" %f", U[j]);
+ System.out.println(" )");
+ }
+
+
+ private class MaDensite implements MathFunction {
+ private ContinuousDistribution cdist;
+
+ public MaDensite(ContinuousDistribution dist) {
+ cdist = dist;
+ supportA = cdist.getXinf();
+ supportB = cdist.getXsup();
+ }
+
+ public double evaluate (double x) {
+ return cdist.density (x);
+ }
+ }
+
+
+ private void init (double xc, double epsu, int n) {
+ double[] zs = new double[n + 1];
+ double[] ys = new double[n + 1]; // ksi[]
+ double[] xs = new double[n + 1];
+ double[] vs = new double[n + 1];
+ double[] us = new double[n + 1];
+ double[] cs = new double[n + 1];
+ epsu = 0.9*epsu;
+ findSupport(xc);
+
+ double I0 = MathFunctionUtil.gaussLobatto (m_dens, bleft, bright, 1.0e-6);
+ if (I0 > 1.05 || I0 < 0.95)
+ throw new IllegalStateException(" NOT a probability density");
+ epstail = 0.05*epsu*I0;
+ epstail = Math.min(epstail, 1.e-10);
+ epstail = Math.max(epstail, 1.e-15);
+ double tol = epstail;
+ findCutoff (bleft, epstail, false); // left tail
+ findCutoff (bright, epstail, true); // right tail
+ if (DEBUG)
+ System.out.println("lcutF = " + lcutF + "\nrcutF = " + rcutF + "\n") ;
+
+ reserve(0, n);
+ A[0] = bl;
+ if (lcutF)
+ F[0] = epstail;
+ else
+ F[0] = 0;
+ ys[0] = 0;
+ final double HMIN = 1.0e-12; // smallest integration step h
+ double h = (br - bl) / K0;
+ int j;
+ int k = 0;
+ calcChebyZ(zs, n);
+ double eps = 0;
+ if (DEBUG)
+ System.out.println(
+ " k a_k F_k h");
+
+ while (A[k] < br) {
+ while (h >= HMIN) {
+ calcChebyX(zs, xs, n, h);
+ calcU(m_dens, A[k], xs, us, n, tol);
+ Misc.interpol(n, us, xs, cs);
+ NTest(us, vs, n);
+ // Evaluate Newton interpolating polynomial at vs[j].
+ for (j = 1; j <= n; j++)
+ ys[j] = Misc.evalPoly(n, us, cs, vs[j]);
+ // NEval (cs, us, vs, ys, n);
+ try {
+ eps = calcEps(m_dens, A[k], ys, vs, n, tol);
+ } catch (IllegalArgumentException e) {
+ h = 0.5 * h;
+ continue;
+ }
+ if (eps <= epsu)
+ break;
+ else
+ h = 0.8 * h;
+ }
+ if (k + 1 >= A.length)
+ reserve(k, n);
+ copy (k, cs, us, xs, n);
+ if (DEBUG)
+ System.out.printf(
+ " %d %16.12f %20.16g %g%n", k, A[k], F[k], h);
+ if (F[k] > 1.01)
+ throw new IllegalStateException("Unable to compute CDF");
+ k++;
+
+ if (eps < epsu / 3.0)
+ h = 1.3 * h;
+ if (h < HMIN)
+ h = HMIN;
+ if (A[k] > br) {
+ A[k] = br;
+ F[k] = 1;
+ }
+ }
+
+ if (DEBUG) {
+ System.out.printf(
+ " %d %16.12f %20.16g %g%n",
+ k, A[k], F[k], A[k] - A[k - 1]);
+ System.out.println("\nFin du tableau");
+ }
+ Kmax = k;
+ while (k > 0 && F[k] >= 1.) {
+ F[k] = 1.;
+ k--;
+ }
+ reserve(-Kmax, n);
+ createIndex (Kmax);
+ }
+
+ /**
+ * Given a continuous distribution <TT>dist</TT> with a well-defined
+ * density method, this class will compute tables for the numerical inverse of
+ * the distribution. The user may wish to set the left and the right boundaries
+ * between which the density is non-zero by calling methods
+ * {@link umontreal.iro.lecuyer.probdist.ContinuousDistribution#setXinf setXinf}
+ * and
+ * {@link umontreal.iro.lecuyer.probdist.ContinuousDistribution#setXsup setXsup}
+ * of <TT>dist</TT>, for better efficiency.
+ * Argument <TT>xc</TT> can be the mean,
+ * the mode or any other <SPAN CLASS="MATH"><I>x</I></SPAN> for which the density is relatively large.
+ * The <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>eps</TT> is the required absolute error in the cdf,
+ * and <TT>order</TT> is the degree of the
+ * Newton interpolating polynomial over each interval.
+ * An <TT>order</TT> of 3 or 5, and an <TT>eps</TT> of <SPAN CLASS="MATH">10<SUP>-6</SUP></SPAN> to <SPAN CLASS="MATH">10<SUP>-12</SUP></SPAN>
+ * are usually good choices.
+ * Restrictions:
+ * <SPAN CLASS="MATH">3 <= <texttt>order</texttt> <= 12</SPAN>.
+ *
+ */
+ public InverseDistFromDensity (ContinuousDistribution dist, double xc,
+ double eps, int order) {
+ setParams (dist, null, xc, eps, order);
+ init (xc, eps, order);
+ }
+
+
+ /**
+ * Given a continuous probability density <TT>dens</TT>,
+ * this class will compute tables for the numerical inverse of
+ * the distribution. The left and the right boundaries of the density are
+ * <TT>xleft</TT> and <TT>xright</TT> (the density is 0 outside the
+ * interval <TT>[xleft, xright]</TT>).
+ * See the description of the other constructor.
+ *
+ */
+ public InverseDistFromDensity (MathFunction dens, double xc, double eps,
+ int order, double xleft, double xright) {
+ supportA = xleft;
+ supportB = xright;
+ setParams (null, dens, xc, eps, order);
+ init (xc, eps, order);
+ }
+
+
+ /**
+ * Computes the probability density at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public double density (double x) {
+ return m_dens.evaluate (x);
+ }
+
+
+ /**
+ * Computes the distribution function at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public double cdf (double x) {
+ throw new UnsupportedOperationException("cdf not implemented");
+ }
+
+
+ /**
+ * Computes the inverse distribution function at <SPAN CLASS="MATH"><I>u</I></SPAN>.
+ *
+ */
+ public double inverseF (double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return supportB;
+ if (u <= 0.0)
+ return supportA;
+ if ((u < epstail) && lcutF)
+ return uinvLeftTail (u);
+ if ((u > 1.0 - epstail) && rcutF)
+ return uinvRightTail (u);
+
+ int k = searchIndex(u);
+ double x = A[k] + Misc.evalPoly(order, U[k], C[k], u - F[k]);
+ if (x <= supportA)
+ return supportA;
+ if (x >= supportB)
+ return supportB;
+ return x;
+ }
+
+
+ /**
+ * Returns the <TT>xc</TT> given in the constructor.
+ *
+ */
+ public double getXc() {
+ return xc;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>eps</TT> associated with this object.
+ *
+ */
+ public double getEpsilon() {
+ return epsu0;
+ }
+
+
+ /**
+ * Returns the order associated with this object.
+ *
+ */
+ public int getOrder() {
+ return order;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is returned as: [<TT>xc</TT>, <TT>eps</TT>, <TT>order</TT>].
+ *
+ */
+ public double[] getParams() {
+ double[] retour = {xc, epsu0, order};
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ *
+ */
+ public String toString() {
+ return name;
+ }
+
+
+ private void createIndex (int Kmax) {
+ // create table for indexed search
+ Imax = 2*Kmax;
+ Index = new int [Imax + 1];
+ Index[0] = 0;
+ Index[Imax] = Kmax - 1;
+ double u;
+ int k = 1;
+ for (int i = 1; i < Imax; i++) {
+ u = (double) i / Imax;
+ while (u >= F[k])
+ k++;
+ Index[i] = k-1;
+ }
+ }
+
+
+ private int searchIndex (double u) {
+ // search index of interval for interpolation in [F[k], F[k+1]]
+ int i = (int) (Imax*u);
+ int k = Index[i];
+ while (u >= F[k] && k < Kmax)
+ k++;
+ if (k <= 0)
+ return 0;
+ return k-1;
+ }
+
+
+ private void copy (int k, double[] cs, double[] us, double[] xs, int n) {
+ // Copy parameters in interval [A[k], A[k+1]]
+ for (int j = 0; j <= n; j++) {
+ X[k][j] = xs[j];
+ C[k][j] = cs[j];
+ U[k][j] = us[j];
+ }
+ A[k+1] = A[k] + xs[n];
+ F[k+1] = F[k] + us[n];
+ }
+
+
+ private double calcEps (MathFunction dens, double a, double[] Y,
+ double[] V, int n, double tol) {
+ // Test if precision at test points Y is good enough
+ // a is beginning of interval
+ // Y are test points
+ // V are values of CDF to compare with
+ // n is order of interpolation
+ // returns max eps
+ // throw exception if Y[j] < Y[j-1] in gaussLobatto
+ double eps = 0;
+ double dif;
+ double u = 0;
+ for (int j = 1; j <= n; j++) {
+ u += MathFunctionUtil.gaussLobatto (dens, a + Y[j-1], a + Y[j], tol);
+ dif = Math.abs(u - V[j]);
+ if (dif > eps)
+ eps = dif;
+ }
+ return eps;
+ }
+
+
+ private void NEval (double[] C, double[] U, double[] T, double[] Y, int n) {
+ // Evaluate Newton interpolating polynomial at T[j].
+ // U are interpolation points
+ // C are interpolation coefficients
+ // Returns results in Y[j]
+ int j;
+ boolean fail = false;
+ Y[0] = 0;
+ for (j = 1; j <= n; j++) {
+ Y[j] = Misc.evalPoly(n, U, C, T[j]);
+ if (Y[j] < Y[j-1])
+ fail = true;
+ }
+
+ if (fail) {
+// System.out.println("NEval");
+ for (j = 1; j <= n; j++)
+ Y[j] = Misc.evalPoly(1, U, C, T[j]);
+ }
+ }
+
+
+ private void calcU (MathFunction dens, double a, double[] X, double[] U,
+ int n, double tol) {
+ // compute CDF over n sub-intervals in [A[k], A[k+1]]
+ // a is beginning of interval
+ // X are x-values
+ // U are values of CDF
+ // precision is tol
+
+ U[0] = 0;
+ for (int j = 1; j <= n; j++)
+ U[j] = U[j-1] +
+ MathFunctionUtil.gaussLobatto (dens, a + X[j-1], a + X[j], tol);
+ }
+
+
+ private void reserve (int m, int n) {
+ // Reserve memory for object
+ A = reserve (A, m);
+ F = reserve (F, m);
+ C = reserve (C, m, n);
+ U = reserve (U, m, n);
+ X = reserve (X, m, n);
+ }
+
+
+ private double[] reserve (double[] T, int m) {
+ if (m == 0) {
+ // first call, just reserve memory.
+ T = new double[K0 + 1];
+
+ } else if (m < 0) {
+ // Computation of table is complete. Table capacity is larger than
+ // size: Resize table to exact size (-m + 1) and keep old values.
+ m = -m;
+ double[] tem = new double[m + 1];
+ for (int i = 0; i <= m; i++)
+ tem[i] = T[i];
+ T = tem;
+
+ } else {
+ // Array is too short: reserve more memory and keep old values
+ double[] tem = new double[2*m + 1];
+ for (int i = 0; i <= m; i++)
+ tem[i] = T[i];
+ T = tem;
+ }
+ return T;
+ }
+
+
+ private double[][] reserve (double[][] T, int m, int n) {
+ if (m == 0) {
+ // first call, just reserve memory.
+ T = new double[K0 + 1][n+1];
+
+ } else if (m < 0) {
+ // Computation of table is complete. Table capacity is larger than
+ // size: Resize table to exact size (-m + 1) and keep old values.
+ m = -m;
+ double[][] tem = new double[m + 1][n+1];
+ int j;
+ for (int i = 0; i <= m; i++) {
+ for (j = 0; j <= n; j++)
+ tem[i][j] = T[i][j];
+ }
+ T = tem;
+
+ } else {
+ // Array is too short: reserve more memory and keep old values
+ double[][] tem = new double[2*m + 1][n+1];
+ int j;
+ for (int i = 0; i <= m; i++) {
+ for (j = 0; j <= n; j++)
+ tem[i][j] = T[i][j];
+ }
+ T = tem;
+ }
+ return T;
+ }
+
+
+ private void NTest (double[] U, double[] T, int n) {
+ // Routine 3 NTest in cite{rDER09a}
+ // Compute test points T, given U
+ int i, j, k;
+ double s, sq, tem;
+ T[0] = 0;
+ for (k = 1; k <= n; k++) {
+ T[k] = (U[k-1] + U[k]) / 2.;
+ for (j = 0; j < 2 ; j++) {
+ s = 0;
+ sq = 0;
+ for (i = 0; i <= n; i++) {
+ tem = T[k] - U[i];
+ if (tem == 0.)
+ break;
+ tem = 1.0/tem;
+ s += tem;
+ sq += tem*tem;
+ }
+ if (sq != 0.)
+ T[k] += s/sq;
+ }
+ }
+ }
+
+
+ private void calcChebyZ (double[] Z, int n) {
+ // Eq. (3) in cite{rDER09a}. z_j = sin(j*phi)*sin((j+1)*phi)/cos(phi)
+ // Compute normalized Chebyshev points in [0, 1]
+ double phi = HALF_PI/(n+1);
+ double c = Math.cos(phi);
+ double y;
+ double temp = 0;
+ for (int j = 0; j < n; j++) {
+ y = temp;
+ temp = Math.sin((j+1)*phi);
+ y *= temp;
+ Z[j] = y/c;
+ }
+ Z[n] = 1;
+ }
+
+
+ private void calcChebyX (double[] Z, double[] X, int n, double h) {
+ // Compute Chebyshev points in [0, h]
+ for (int j = 1; j < n; j++)
+ X[j] = h*Z[j];
+ X[0] = 0;
+ X[n] = h;
+ }
+
+
+ private double binSearch (double xa, double xb, double eps,
+ boolean right) {
+ /*
+ * Binary search:
+ * find x such that fa*epslow < f < fb*eps in the left tail
+ * find x such that fa*eps > f > fb*epslow in the right tail
+ * where fa = density(xa), fb = density(xb), f = density(x).
+ * We find an x such that density(x) is a little smaller than eps
+ */
+ final double epslow = 0.1 * eps;
+ double x = 0, y = 0;
+ boolean fini = false;
+
+ if (right) { // right tail
+ while (!fini) {
+ x = 0.5 * (xa + xb);
+ if ((xb - xa) < eps*Math.abs(x) || (xb - xa) < eps) {
+ fini = true;
+ if (x > supportB)
+ x = supportB;
+ }
+ y = m_dens.evaluate(x);
+ if (y < epslow) {
+ xb = x;
+ } else if (y > eps) {
+ xa = x;
+ } else
+ fini = true;
+ }
+
+ } else { // left tail
+ while (!fini) {
+ x = 0.5 * (xa + xb);
+ if ((xb - xa) < eps*Math.abs(x) || (xb - xa) < eps) {
+ fini = true;
+ if (x < supportA)
+ x = supportA;
+ }
+ y = m_dens.evaluate(x);
+ if (y < epslow) {
+ xa = x;
+ } else if (y > eps) {
+ xb = x;
+ } else
+ fini = true;
+ }
+ }
+ if (DEBUG)
+ System.out.printf(
+ "binSearch x = %g f = %g r = %g%n", x, y, y/eps);
+
+ return x;
+ }
+
+
+ private void findSupport (double xc) {
+ /*
+ * Find interval where density is non-negligible (above some epsilon):
+ * find points bleft < xc < bright such that
+ * density(bleft) ~ density(bright) ~ 10^(-13)*density(xc)
+ */
+ boolean flagL = false;
+ boolean flagR = false;
+ final double DELTA = 1.0e-100;
+ final double DELTAR = 1.0e-14;
+ double x, y;
+ double bl = supportA;
+ double br = supportB;
+
+ if (bl > Double.NEGATIVE_INFINITY) {
+ // Density is 0 for finite x < bl
+ y = m_dens.evaluate(bl);
+ x = bl;
+ if (y >= HUGE || y <= 0.0) {
+ // density is infinite or 0 at bl; choose bl --> bl(1 + epsilon)
+ x = bl + DELTAR * Math.abs(bl);
+ if (x == 0)
+ // bl is 0 --> choose bl = DELTA
+ x = DELTA;
+ y = m_dens.evaluate(x);
+ }
+
+ if (y >= HUGE)
+ throw new UnsupportedOperationException
+ ("Infinite density at left boundary");
+
+ if (y >= 1.0e-50) {
+ // f(bl) is large enough; we have found bl
+ flagL = true;
+ bl = x;
+ }
+ }
+
+ if (br < Double.POSITIVE_INFINITY) {
+ // Density is 0 for finite x > br
+ y = m_dens.evaluate(br);
+ x = br;
+ if (y >= HUGE || y <= 0.0) {
+ // density is infinite or 0 at br; choose br --> br(1 - epsilon)
+ x = br - DELTAR * Math.abs(br);
+ if (x == 0)
+ // br is 0 --> choose br = -DELTA
+ x = -DELTA;
+ y = m_dens.evaluate(x);
+ }
+
+ if (y >= HUGE)
+ throw new UnsupportedOperationException
+ ("Infinite density at right boundary");
+
+ if (y >= 1.0e-50) {
+ // f(br) is large enough; we have found br
+ flagR = true;
+ br = x;
+ }
+ }
+
+ bleft = bl;
+ bright = br;
+ if (flagL && flagR)
+ return;
+
+ // We have not found bl or br
+ double h;
+ y = m_dens.evaluate(xc);
+ double epsy = 1.0e-13*y;
+ double xa, xb;
+
+
+ if (!flagR) {
+ // Find br: start at xc; increase x until density is very small
+ h = 1;
+ xa = xc;
+ xb = xc + h;
+ while (m_dens.evaluate(xb) >= epsy) {
+ xa = xb;
+ h *= 2.0;
+ xb += h;
+ }
+ // Now we have density(xa) > epsy > density(xb)
+
+ if (xb > supportB) {
+ // density = 0 outside [supportA, supportB]
+ xb = supportB;
+ }
+ x = binSearch (xa, xb, epsy, true);
+ bright = x; // Have found br
+ }
+
+ if (!flagL) {
+ h = 1;
+ xb = xc;
+ xa = xc - h;
+ while (m_dens.evaluate(xa) >= epsy) {
+ xb = xa;
+ h *= 2.0;
+ xa -= h;
+ }
+ // Now we have density(xa) < epsy < density(xb)
+
+ if (xa < supportA) {
+ // density = 0 outside [supportA, supportB]
+ xa = supportA;
+ }
+ x = binSearch (xa, xb, epsy, false);
+ bleft = x; // Have found bl
+ }
+ }
+
+
+ protected void setParams (ContinuousDistribution dist, MathFunction dens,
+ double xc, double eps, int order) {
+ // Sets the parameter of this object
+ if (eps < 1.0e-15)
+ throw new IllegalArgumentException ("eps < 10^{-15}");
+ if (eps > 1.0e-3)
+ throw new IllegalArgumentException ("eps > 10^{-3}");
+ if (order < 3)
+ throw new IllegalArgumentException ("order < 3");
+ if (order > 12)
+ throw new IllegalArgumentException ("order > 12");
+ epsu0 = eps;
+ this.xc = xc;
+ this.order = order;
+
+ StringBuffer sb = new StringBuffer ("InverseDistFromDensity: ");
+ if (dist == null) {
+ m_dens = dens;
+ } else {
+ m_dens = new MaDensite(dist);
+ sb.append (dist.toString());
+ }
+ name = sb.toString();
+ }
+
+
+ private double uinvLeftTail (double u) {
+ // Returns x = inverseF(u) in left tail
+
+ double x = 0;
+ if (llc <= epsc)
+ x = bl + lc1 * Math.log (u*lc2);
+ else
+ x = bl + lc1 * (Math.pow (u*lc2, lc3) - 1.);
+ if (x <= supportA)
+ return supportA;
+ return x;
+ }
+
+
+ private double uinvRightTail (double u) {
+ // Returns x = inverseF(u) in right tail
+
+ double x = 0;
+ double v = 1. - u;
+ if (rlc <= epsc)
+ x = br + rc1 * Math.log (v*rc2);
+ else
+ x = br + rc1 * (Math.pow (v*rc2, rc3) - 1.);
+ if (x >= supportB)
+ return supportB;
+ return x;
+ }
+
+
+ private void findCutoff (double x0, double eps, boolean right) {
+ /*
+ * Find cut-off points for the computational domain.
+ * Find cut-off x in the tails such that cdf(x) = eps in the left
+ * tail, and eps = 1 - cdf(x) in the right tail.
+ * Uses successive approximations starting at x = x0.
+ * If right is true, case of the right tail; otherwise the left tail.
+ * The program uses T_c-concavity of densities as described in
+ * Leydold et al.
+ */
+ final double epsx = 1.0e-3;
+ final double range = bright - bleft;
+ double del;
+
+ if (right) {
+ del = m_dens.evaluate(bright) - m_dens.evaluate(bright - epsx);
+ if ((supportB < Double.POSITIVE_INFINITY) &&
+ (supportB - bright <= epsx ||
+ supportB - bright <= Math.abs(supportB)*epsx)) {
+ // If density is non-negligible right up to domain limit supportB,
+ // then cutoff is bright. There is no right tail. We want cutoff
+ // at bright in case density(supportB) = infinite.
+ if (del < 0) {
+ // density decreases toward supportB;
+ br = supportB;
+ } else {
+ // density increases toward supportB; may be infinite
+ br = bright;
+ }
+ rcutF = false;
+ return;
+ } else {
+ rcutF = true; // There is a right tail
+ }
+
+ } else {
+ del = m_dens.evaluate(bleft + epsx) - m_dens.evaluate(bleft);
+ if ((supportA > Double.NEGATIVE_INFINITY) &&
+ (bleft - supportA <= epsx ||
+ bleft - supportA <= Math.abs(supportA)*epsx)) {
+ // If density is non-negligible right down to domain limit supportA,
+ // then cutoff is bleft. There is no left tail. We want cutoff
+ // at bleft in case density(supportA) = infinite.
+ if (del > 0) {
+ // density decreases toward supportA
+ bl = supportA;
+ } else {
+ // density increases toward supportA; may be infinite
+ bl = bleft;
+ }
+ lcutF = false;
+ return;
+ } else {
+ lcutF = true; // There is a left tail
+ }
+ }
+
+ double c = 0;
+ double h = 1.0/64.0; // step to compute derivative
+ h = Math.max (h, (bright - bleft) / (1024));
+ double x = x0, xnew;
+ double y = 0, yl = 0, yr = 0, yprime = 0;
+ double tem = 0;
+ int iter = 0;
+ final int ITERMAX = 30;
+ boolean fini = false;
+
+ while (!fini && iter < ITERMAX) {
+ iter++;
+ boolean ended = false;
+ int it = 0;
+
+ while (!ended && it < 10) {
+ it++;
+ if (x + h > supportB)
+ h = supportB - x;
+ if (x - h < supportA)
+ h = x - supportA;
+ yr = m_dens.evaluate(x + h);
+ y = m_dens.evaluate(x);
+ yl = m_dens.evaluate(x - h);
+ if (!(yl == 0 || yr == 0 || y == 0))
+ ended = true;
+ else
+ h /= 2;
+ }
+
+ c = yr / (yr - y) + yl / (yl - y) - 1.; // the local concavity lc
+ yprime = (yr - yl) / (2. * h); // first derivative
+ tem = Math.abs (y * y / ((c + 1.) * yprime)); // tail area of CDF
+
+ if (Double.isNaN (tem))
+ break;
+ if (Math.abs (tem / eps - 1.) < 1.e-4) // accuracy is good?
+ break;
+ if (Math.abs(c) <= epsc) {
+ tem = eps * Math.abs(yprime) / (y * y); // formula (10)
+ if (tem <= 0)
+ break;
+ xnew = x + y / yprime * Math.log(tem);
+ } else {
+ tem = (1. + c) * eps * Math.abs(yprime) / (y * y); // formula(10)
+ if (tem < 0)
+ break;
+ xnew = x + y / (c*yprime) * (Math.pow(tem, c / (1. + c)) - 1.);
+ }
+
+ if (DEBUG)
+ System.out.printf(
+ "Cutoff x = %g y = %g c = %g%n", xnew, y, c);
+
+ if ((Math.abs(xnew - x) <= Math.abs(x)*epsx) ||
+ (Math.abs(xnew - x) <= epsx))
+ fini = true; // found cut-off x
+ else
+ x = xnew;
+
+ // Given good x, precompute some parameters in formula (10)
+ if (right) {
+ rlc = Math.abs(c);
+ br = x;
+ rc3 = c / (1 + c);
+ rc2 = tem / eps;
+ rc1 = y / yprime;
+ if (Math.abs(c) > epsc)
+ rc1 /= c;
+
+ } else {
+ llc = Math.abs(c);
+ bl = x;
+ lc3 = c / (1 + c);
+ lc2 = tem / eps;
+ lc1 = y / yprime;
+ if (Math.abs(c) > epsc)
+ lc1 /= c;
+ }
+
+ if (Math.abs(xnew - x) >= range)
+ fini = true;
+ }
+
+ if (right) {
+ if ((rc1 == 0 && rc2 == 0 && rc3 == 0)) {
+ br = bright;
+ rcutF = false;
+ }
+ } else {
+ if ((lc1 == 0 && lc2 == 0 && lc3 == 0)) {
+ bl = bleft;
+ lcutF = false;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/probdist/InverseDistFromDensity.tex b/source/umontreal/iro/lecuyer/probdist/InverseDistFromDensity.tex
new file mode 100644
index 0000000..a685198
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/InverseDistFromDensity.tex
@@ -0,0 +1,966 @@
+\defmodule {InverseDistFromDensity}
+
+Implements a method for computing the inverse of an \emph{arbitrary
+continuous} distribution function when only the probability density
+is known \cite{rDER09a}. The cumulative probabilities (cdf) are pre-computed by
+ numerical quadrature of the
+density using Gauss-Lobatto integration over suitably small intervals to
+satisfy the required precision, and these values are kept in tables. Then the
+ algorithm uses polynomial interpolation over the tabulated values to get
+ the inverse cdf. The user can select the
+ desired precision and the degree of the interpolating polynomials.
+
+The algorithm may fail for some distributions for which the density
+ becomes infinite at a point (for ex. the Gamma and the Beta distributions
+ with $\alpha < 1$) if one chooses too high a precision
+(a too small \texttt{eps}, for ex. $\epsilon \sim 10^{-15}$).
+However, it should work also for continuous densities with finite discontinuities.
+
+
+While the setup time for this class is relatively slow, the numerical inversion
+ is extremely fast and practically independent of the required precision
+and of the specific distribution. For comparisons between the times
+of standard inversion and inversion from this class as well as
+comparisons between setup times, see the introduction in class
+\externalclass{umontreal.iro.lecuyer.randvar}{InverseFromDensityGen}
+from package \texttt{randvar}.
+
+Thus if only a few inverses are needed, then using this class
+ is not efficient because of the slow set-up. But if one wants to call
+ \texttt{inverseF} thousands of times or more, then using this class will
+be very efficient.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseDistFromDensity
+ * Description: computing the inverse of an arbitrary continuous distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+ import umontreal.iro.lecuyer.functions.MathFunction;\begin{hide}
+import umontreal.iro.lecuyer.util.Misc;
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+\end{hide}
+
+
+public class InverseDistFromDensity extends ContinuousDistribution \begin{hide} {
+ private final boolean DEBUG = false;
+ protected static final double HALF_PI = Math.PI/2.0;
+ private final double epsc = 1.0e-5; // for small values of lc in tails
+ private final double HUGE = Double.MAX_VALUE / 2.0;
+ private double epsu0; // initial u-resolution
+ private double xc; // mode, mean, or median of distribution
+ // xc is an x where the density is high
+ MathFunction m_dens; // probability density
+ private String name; // Name of class
+
+ private final int K0 = 128; // initial size of tables A, F, ...
+ private int Kmax; // final size of tables A, F, ... is (Kmax + 1)
+ private double[] A; // x-values
+ private double[] F; // corresponding u-values (the CDF)
+ private double[][] X; // interpolation x-values in [A[k], A[k+1]]
+ private double[][] U; // interpolation u-values in [A[k], A[k+1]]
+ private double[][] C; // interpolation coefficients in [A[k], A[k+1]]
+ private int order; // order of interpolation polynomial in each [A[k], A[k+1]]
+ private int[] Index; // for indexed search in F[k]
+ private int Imax; // final size of Index is (Imax + 1)
+
+ private double bleft; // computational left limit of density
+ private double bright; // computational right limit of density
+ private double bl; // left border of the computational domain
+ private double br; // right border of the computational domain
+ private double llc; // absolute local concavity in left tail
+ private double rlc; // absolute local concavity in right tail
+/*
+ For NormalDist(0,1), local concavity c = 1/z^2
+ For CauchyDist(0,1), local concavity c = -1/2 + 1/(2z^2)
+ For GammaDist(a,lam), local concavity c = (a-1)/(1 - a +lam*x)^2
+*/
+ private double lc1; // left, c1 = f(p)/f'(p) or f(p) / (c*f'(p))
+ private double lc2; // left, c2 = |f'(p)|(1 + c) / (f(p)^2)
+ private double lc3; // left, c3 = c/(1 + c)
+ private double rc1; // right, c1 = f(p) / f'(p) or f(p) / (c*f'(p))
+ private double rc2; // right, c2 = |f'(p)|(1 + c) / (f(p)^2)
+ private double rc3; // right, c3 = c/(1 + c)
+ private double epstail; // = 0.05*epsu*I0;
+ private boolean lcutF = false; // cut-off flag, left tail
+ private boolean rcutF = false; // cut-off flag, right tail
+
+
+ protected void printArray (double[] U) {
+ System.out.print(" Tableau = (");
+ for (int j = 0; j < U.length; j++)
+ System.out.printf(" %f", U[j]);
+ System.out.println(" )");
+ }
+
+
+ private class MaDensite implements MathFunction {
+ private ContinuousDistribution cdist;
+
+ public MaDensite(ContinuousDistribution dist) {
+ cdist = dist;
+ supportA = cdist.getXinf();
+ supportB = cdist.getXsup();
+ }
+
+ public double evaluate (double x) {
+ return cdist.density (x);
+ }
+ }
+
+
+ private void init (double xc, double epsu, int n) {
+ double[] zs = new double[n + 1];
+ double[] ys = new double[n + 1]; // ksi[]
+ double[] xs = new double[n + 1];
+ double[] vs = new double[n + 1];
+ double[] us = new double[n + 1];
+ double[] cs = new double[n + 1];
+ epsu = 0.9*epsu;
+ findSupport(xc);
+
+ double I0 = MathFunctionUtil.gaussLobatto (m_dens, bleft, bright, 1.0e-6);
+ if (I0 > 1.05 || I0 < 0.95)
+ throw new IllegalStateException(" NOT a probability density");
+ epstail = 0.05*epsu*I0;
+ epstail = Math.min(epstail, 1.e-10);
+ epstail = Math.max(epstail, 1.e-15);
+ double tol = epstail;
+ findCutoff (bleft, epstail, false); // left tail
+ findCutoff (bright, epstail, true); // right tail
+ if (DEBUG)
+ System.out.println("lcutF = " + lcutF + "\nrcutF = " + rcutF + "\n") ;
+
+ reserve(0, n);
+ A[0] = bl;
+ if (lcutF)
+ F[0] = epstail;
+ else
+ F[0] = 0;
+ ys[0] = 0;
+ final double HMIN = 1.0e-12; // smallest integration step h
+ double h = (br - bl) / K0;
+ int j;
+ int k = 0;
+ calcChebyZ(zs, n);
+ double eps = 0;
+ if (DEBUG)
+ System.out.println(
+ " k a_k F_k h");
+
+ while (A[k] < br) {
+ while (h >= HMIN) {
+ calcChebyX(zs, xs, n, h);
+ calcU(m_dens, A[k], xs, us, n, tol);
+ Misc.interpol(n, us, xs, cs);
+ NTest(us, vs, n);
+ // Evaluate Newton interpolating polynomial at vs[j].
+ for (j = 1; j <= n; j++)
+ ys[j] = Misc.evalPoly(n, us, cs, vs[j]);
+ // NEval (cs, us, vs, ys, n);
+ try {
+ eps = calcEps(m_dens, A[k], ys, vs, n, tol);
+ } catch (IllegalArgumentException e) {
+ h = 0.5 * h;
+ continue;
+ }
+ if (eps <= epsu)
+ break;
+ else
+ h = 0.8 * h;
+ }
+ if (k + 1 >= A.length)
+ reserve(k, n);
+ copy (k, cs, us, xs, n);
+ if (DEBUG)
+ System.out.printf(
+ " %d %16.12f %20.16g %g%n", k, A[k], F[k], h);
+ if (F[k] > 1.01)
+ throw new IllegalStateException("Unable to compute CDF");
+ k++;
+
+ if (eps < epsu / 3.0)
+ h = 1.3 * h;
+ if (h < HMIN)
+ h = HMIN;
+ if (A[k] > br) {
+ A[k] = br;
+ F[k] = 1;
+ }
+ }
+
+ if (DEBUG) {
+ System.out.printf(
+ " %d %16.12f %20.16g %g%n",
+ k, A[k], F[k], A[k] - A[k - 1]);
+ System.out.println("\nFin du tableau");
+ }
+ Kmax = k;
+ while (k > 0 && F[k] >= 1.) {
+ F[k] = 1.;
+ k--;
+ }
+ reserve(-Kmax, n);
+ createIndex (Kmax);
+ }\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseDistFromDensity (ContinuousDistribution dist, double xc,
+ double eps, int order) \begin{hide} {
+ setParams (dist, null, xc, eps, order);
+ init (xc, eps, order);
+ } \end{hide}
+\end{code}
+\begin{tabb} Given a continuous distribution \texttt{dist} with a well-defined
+density method, this class will compute tables for the numerical inverse of
+the distribution. The user may wish to set the left and the right boundaries
+ between which the density is non-zero by calling methods
+\externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{setXinf}{}
+and
+\externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{setXsup}{}
+of \texttt{dist}, for better efficiency.
+Argument \texttt{xc} can be the mean,
+the mode or any other $x$ for which the density is relatively large.
+The $u$-resolution \texttt{eps} is the required absolute error in the cdf,
+and \texttt{order} is the degree of the
+Newton interpolating polynomial over each interval.
+An \texttt{order} of 3 or 5, and an \texttt{eps} of $10^{-6}$ to $10^{-12}$
+are usually good choices.
+ Restrictions: $3 \le \texttt{order} \le 12$.
+\end{tabb}
+\begin{code}
+
+ public InverseDistFromDensity (MathFunction dens, double xc, double eps,
+ int order, double xleft, double xright) \begin{hide} {
+ supportA = xleft;
+ supportB = xright;
+ setParams (null, dens, xc, eps, order);
+ init (xc, eps, order);
+ } \end{hide}
+\end{code}
+\begin{tabb} Given a continuous probability density \texttt{dens},
+this class will compute tables for the numerical inverse of
+the distribution. The left and the right boundaries of the density are
+\texttt{xleft} and \texttt{xright} (the density is 0 outside the
+interval \texttt{[xleft, xright]}).
+See the description of the other constructor.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public double density (double x) \begin{hide} {
+ return m_dens.evaluate (x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the probability density at $x$.
+\end{tabb}
+\begin{code}
+
+ public double cdf (double x) \begin{hide} {
+ throw new UnsupportedOperationException("cdf not implemented");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the distribution function at $x$.
+\end{tabb}
+\begin{code}
+
+ public double inverseF (double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u >= 1.0)
+ return supportB;
+ if (u <= 0.0)
+ return supportA;
+ if ((u < epstail) && lcutF)
+ return uinvLeftTail (u);
+ if ((u > 1.0 - epstail) && rcutF)
+ return uinvRightTail (u);
+
+ int k = searchIndex(u);
+ double x = A[k] + Misc.evalPoly(order, U[k], C[k], u - F[k]);
+ if (x <= supportA)
+ return supportA;
+ if (x >= supportB)
+ return supportB;
+ return x;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the inverse distribution function at $u$.
+ \end{tabb}
+\begin{code}
+
+ public double getXc()\begin{hide} {
+ return xc;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{xc} given in the constructor.
+\end{tabb}
+\begin{code}
+
+ public double getEpsilon()\begin{hide} {
+ return epsu0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $u$-resolution \texttt{eps} associated with this object.
+\end{tabb}
+\begin{code}
+
+ public int getOrder()\begin{hide} {
+ return order;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the order associated with this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams()\begin{hide} {
+ double[] retour = {xc, epsu0, order};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is returned as: [\texttt{xc}, \texttt{eps}, \texttt{order}].
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ return name;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ private void createIndex (int Kmax) {
+ // create table for indexed search
+ Imax = 2*Kmax;
+ Index = new int [Imax + 1];
+ Index[0] = 0;
+ Index[Imax] = Kmax - 1;
+ double u;
+ int k = 1;
+ for (int i = 1; i < Imax; i++) {
+ u = (double) i / Imax;
+ while (u >= F[k])
+ k++;
+ Index[i] = k-1;
+ }
+ }
+
+
+ private int searchIndex (double u) {
+ // search index of interval for interpolation in [F[k], F[k+1]]
+ int i = (int) (Imax*u);
+ int k = Index[i];
+ while (u >= F[k] && k < Kmax)
+ k++;
+ if (k <= 0)
+ return 0;
+ return k-1;
+ }
+
+
+ private void copy (int k, double[] cs, double[] us, double[] xs, int n) {
+ // Copy parameters in interval [A[k], A[k+1]]
+ for (int j = 0; j <= n; j++) {
+ X[k][j] = xs[j];
+ C[k][j] = cs[j];
+ U[k][j] = us[j];
+ }
+ A[k+1] = A[k] + xs[n];
+ F[k+1] = F[k] + us[n];
+ }
+
+
+ private double calcEps (MathFunction dens, double a, double[] Y,
+ double[] V, int n, double tol) {
+ // Test if precision at test points Y is good enough
+ // a is beginning of interval
+ // Y are test points
+ // V are values of CDF to compare with
+ // n is order of interpolation
+ // returns max eps
+ // throw exception if Y[j] < Y[j-1] in gaussLobatto
+ double eps = 0;
+ double dif;
+ double u = 0;
+ for (int j = 1; j <= n; j++) {
+ u += MathFunctionUtil.gaussLobatto (dens, a + Y[j-1], a + Y[j], tol);
+ dif = Math.abs(u - V[j]);
+ if (dif > eps)
+ eps = dif;
+ }
+ return eps;
+ }
+
+
+ private void NEval (double[] C, double[] U, double[] T, double[] Y, int n) {
+ // Evaluate Newton interpolating polynomial at T[j].
+ // U are interpolation points
+ // C are interpolation coefficients
+ // Returns results in Y[j]
+ int j;
+ boolean fail = false;
+ Y[0] = 0;
+ for (j = 1; j <= n; j++) {
+ Y[j] = Misc.evalPoly(n, U, C, T[j]);
+ if (Y[j] < Y[j-1])
+ fail = true;
+ }
+
+ if (fail) {
+// System.out.println("NEval");
+ for (j = 1; j <= n; j++)
+ Y[j] = Misc.evalPoly(1, U, C, T[j]);
+ }
+ }
+
+
+ private void calcU (MathFunction dens, double a, double[] X, double[] U,
+ int n, double tol) {
+ // compute CDF over n sub-intervals in [A[k], A[k+1]]
+ // a is beginning of interval
+ // X are x-values
+ // U are values of CDF
+ // precision is tol
+
+ U[0] = 0;
+ for (int j = 1; j <= n; j++)
+ U[j] = U[j-1] +
+ MathFunctionUtil.gaussLobatto (dens, a + X[j-1], a + X[j], tol);
+ }
+
+
+ private void reserve (int m, int n) {
+ // Reserve memory for object
+ A = reserve (A, m);
+ F = reserve (F, m);
+ C = reserve (C, m, n);
+ U = reserve (U, m, n);
+ X = reserve (X, m, n);
+ }
+
+
+ private double[] reserve (double[] T, int m) {
+ if (m == 0) {
+ // first call, just reserve memory.
+ T = new double[K0 + 1];
+
+ } else if (m < 0) {
+ // Computation of table is complete. Table capacity is larger than
+ // size: Resize table to exact size (-m + 1) and keep old values.
+ m = -m;
+ double[] tem = new double[m + 1];
+ for (int i = 0; i <= m; i++)
+ tem[i] = T[i];
+ T = tem;
+
+ } else {
+ // Array is too short: reserve more memory and keep old values
+ double[] tem = new double[2*m + 1];
+ for (int i = 0; i <= m; i++)
+ tem[i] = T[i];
+ T = tem;
+ }
+ return T;
+ }
+
+
+ private double[][] reserve (double[][] T, int m, int n) {
+ if (m == 0) {
+ // first call, just reserve memory.
+ T = new double[K0 + 1][n+1];
+
+ } else if (m < 0) {
+ // Computation of table is complete. Table capacity is larger than
+ // size: Resize table to exact size (-m + 1) and keep old values.
+ m = -m;
+ double[][] tem = new double[m + 1][n+1];
+ int j;
+ for (int i = 0; i <= m; i++) {
+ for (j = 0; j <= n; j++)
+ tem[i][j] = T[i][j];
+ }
+ T = tem;
+
+ } else {
+ // Array is too short: reserve more memory and keep old values
+ double[][] tem = new double[2*m + 1][n+1];
+ int j;
+ for (int i = 0; i <= m; i++) {
+ for (j = 0; j <= n; j++)
+ tem[i][j] = T[i][j];
+ }
+ T = tem;
+ }
+ return T;
+ }
+
+
+ private void NTest (double[] U, double[] T, int n) {
+ // Routine 3 NTest in cite{rDER09a}
+ // Compute test points T, given U
+ int i, j, k;
+ double s, sq, tem;
+ T[0] = 0;
+ for (k = 1; k <= n; k++) {
+ T[k] = (U[k-1] + U[k]) / 2.;
+ for (j = 0; j < 2 ; j++) {
+ s = 0;
+ sq = 0;
+ for (i = 0; i <= n; i++) {
+ tem = T[k] - U[i];
+ if (tem == 0.)
+ break;
+ tem = 1.0/tem;
+ s += tem;
+ sq += tem*tem;
+ }
+ if (sq != 0.)
+ T[k] += s/sq;
+ }
+ }
+ }
+
+
+ private void calcChebyZ (double[] Z, int n) {
+ // Eq. (3) in cite{rDER09a}. z_j = sin(j*phi)*sin((j+1)*phi)/cos(phi)
+ // Compute normalized Chebyshev points in [0, 1]
+ double phi = HALF_PI/(n+1);
+ double c = Math.cos(phi);
+ double y;
+ double temp = 0;
+ for (int j = 0; j < n; j++) {
+ y = temp;
+ temp = Math.sin((j+1)*phi);
+ y *= temp;
+ Z[j] = y/c;
+ }
+ Z[n] = 1;
+ }
+
+
+ private void calcChebyX (double[] Z, double[] X, int n, double h) {
+ // Compute Chebyshev points in [0, h]
+ for (int j = 1; j < n; j++)
+ X[j] = h*Z[j];
+ X[0] = 0;
+ X[n] = h;
+ }
+
+
+ private double binSearch (double xa, double xb, double eps,
+ boolean right) {
+ /*
+ * Binary search:
+ * find x such that fa*epslow < f < fb*eps in the left tail
+ * find x such that fa*eps > f > fb*epslow in the right tail
+ * where fa = density(xa), fb = density(xb), f = density(x).
+ * We find an x such that density(x) is a little smaller than eps
+ */
+ final double epslow = 0.1 * eps;
+ double x = 0, y = 0;
+ boolean fini = false;
+
+ if (right) { // right tail
+ while (!fini) {
+ x = 0.5 * (xa + xb);
+ if ((xb - xa) < eps*Math.abs(x) || (xb - xa) < eps) {
+ fini = true;
+ if (x > supportB)
+ x = supportB;
+ }
+ y = m_dens.evaluate(x);
+ if (y < epslow) {
+ xb = x;
+ } else if (y > eps) {
+ xa = x;
+ } else
+ fini = true;
+ }
+
+ } else { // left tail
+ while (!fini) {
+ x = 0.5 * (xa + xb);
+ if ((xb - xa) < eps*Math.abs(x) || (xb - xa) < eps) {
+ fini = true;
+ if (x < supportA)
+ x = supportA;
+ }
+ y = m_dens.evaluate(x);
+ if (y < epslow) {
+ xa = x;
+ } else if (y > eps) {
+ xb = x;
+ } else
+ fini = true;
+ }
+ }
+ if (DEBUG)
+ System.out.printf(
+ "binSearch x = %g f = %g r = %g%n", x, y, y/eps);
+
+ return x;
+ }
+
+
+ private void findSupport (double xc) {
+ /*
+ * Find interval where density is non-negligible (above some epsilon):
+ * find points bleft < xc < bright such that
+ * density(bleft) ~ density(bright) ~ 10^(-13)*density(xc)
+ */
+ boolean flagL = false;
+ boolean flagR = false;
+ final double DELTA = 1.0e-100;
+ final double DELTAR = 1.0e-14;
+ double x, y;
+ double bl = supportA;
+ double br = supportB;
+
+ if (bl > Double.NEGATIVE_INFINITY) {
+ // Density is 0 for finite x < bl
+ y = m_dens.evaluate(bl);
+ x = bl;
+ if (y >= HUGE || y <= 0.0) {
+ // density is infinite or 0 at bl; choose bl --> bl(1 + epsilon)
+ x = bl + DELTAR * Math.abs(bl);
+ if (x == 0)
+ // bl is 0 --> choose bl = DELTA
+ x = DELTA;
+ y = m_dens.evaluate(x);
+ }
+
+ if (y >= HUGE)
+ throw new UnsupportedOperationException
+ ("Infinite density at left boundary");
+
+ if (y >= 1.0e-50) {
+ // f(bl) is large enough; we have found bl
+ flagL = true;
+ bl = x;
+ }
+ }
+
+ if (br < Double.POSITIVE_INFINITY) {
+ // Density is 0 for finite x > br
+ y = m_dens.evaluate(br);
+ x = br;
+ if (y >= HUGE || y <= 0.0) {
+ // density is infinite or 0 at br; choose br --> br(1 - epsilon)
+ x = br - DELTAR * Math.abs(br);
+ if (x == 0)
+ // br is 0 --> choose br = -DELTA
+ x = -DELTA;
+ y = m_dens.evaluate(x);
+ }
+
+ if (y >= HUGE)
+ throw new UnsupportedOperationException
+ ("Infinite density at right boundary");
+
+ if (y >= 1.0e-50) {
+ // f(br) is large enough; we have found br
+ flagR = true;
+ br = x;
+ }
+ }
+
+ bleft = bl;
+ bright = br;
+ if (flagL && flagR)
+ return;
+
+ // We have not found bl or br
+ double h;
+ y = m_dens.evaluate(xc);
+ double epsy = 1.0e-13*y;
+ double xa, xb;
+
+
+ if (!flagR) {
+ // Find br: start at xc; increase x until density is very small
+ h = 1;
+ xa = xc;
+ xb = xc + h;
+ while (m_dens.evaluate(xb) >= epsy) {
+ xa = xb;
+ h *= 2.0;
+ xb += h;
+ }
+ // Now we have density(xa) > epsy > density(xb)
+
+ if (xb > supportB) {
+ // density = 0 outside [supportA, supportB]
+ xb = supportB;
+ }
+ x = binSearch (xa, xb, epsy, true);
+ bright = x; // Have found br
+ }
+
+ if (!flagL) {
+ h = 1;
+ xb = xc;
+ xa = xc - h;
+ while (m_dens.evaluate(xa) >= epsy) {
+ xb = xa;
+ h *= 2.0;
+ xa -= h;
+ }
+ // Now we have density(xa) < epsy < density(xb)
+
+ if (xa < supportA) {
+ // density = 0 outside [supportA, supportB]
+ xa = supportA;
+ }
+ x = binSearch (xa, xb, epsy, false);
+ bleft = x; // Have found bl
+ }
+ }
+
+
+ protected void setParams (ContinuousDistribution dist, MathFunction dens,
+ double xc, double eps, int order) {
+ // Sets the parameter of this object
+ if (eps < 1.0e-15)
+ throw new IllegalArgumentException ("eps < 10^{-15}");
+ if (eps > 1.0e-3)
+ throw new IllegalArgumentException ("eps > 10^{-3}");
+ if (order < 3)
+ throw new IllegalArgumentException ("order < 3");
+ if (order > 12)
+ throw new IllegalArgumentException ("order > 12");
+ epsu0 = eps;
+ this.xc = xc;
+ this.order = order;
+
+ StringBuffer sb = new StringBuffer ("InverseDistFromDensity: ");
+ if (dist == null) {
+ m_dens = dens;
+ } else {
+ m_dens = new MaDensite(dist);
+ sb.append (dist.toString());
+ }
+ name = sb.toString();
+ }
+
+
+ private double uinvLeftTail (double u) {
+ // Returns x = inverseF(u) in left tail
+
+ double x = 0;
+ if (llc <= epsc)
+ x = bl + lc1 * Math.log (u*lc2);
+ else
+ x = bl + lc1 * (Math.pow (u*lc2, lc3) - 1.);
+ if (x <= supportA)
+ return supportA;
+ return x;
+ }
+
+
+ private double uinvRightTail (double u) {
+ // Returns x = inverseF(u) in right tail
+
+ double x = 0;
+ double v = 1. - u;
+ if (rlc <= epsc)
+ x = br + rc1 * Math.log (v*rc2);
+ else
+ x = br + rc1 * (Math.pow (v*rc2, rc3) - 1.);
+ if (x >= supportB)
+ return supportB;
+ return x;
+ }
+
+
+ private void findCutoff (double x0, double eps, boolean right) {
+ /*
+ * Find cut-off points for the computational domain.
+ * Find cut-off x in the tails such that cdf(x) = eps in the left
+ * tail, and eps = 1 - cdf(x) in the right tail.
+ * Uses successive approximations starting at x = x0.
+ * If right is true, case of the right tail; otherwise the left tail.
+ * The program uses T_c-concavity of densities as described in
+ * Leydold et al.
+ */
+ final double epsx = 1.0e-3;
+ final double range = bright - bleft;
+ double del;
+
+ if (right) {
+ del = m_dens.evaluate(bright) - m_dens.evaluate(bright - epsx);
+ if ((supportB < Double.POSITIVE_INFINITY) &&
+ (supportB - bright <= epsx ||
+ supportB - bright <= Math.abs(supportB)*epsx)) {
+ // If density is non-negligible right up to domain limit supportB,
+ // then cutoff is bright. There is no right tail. We want cutoff
+ // at bright in case density(supportB) = infinite.
+ if (del < 0) {
+ // density decreases toward supportB;
+ br = supportB;
+ } else {
+ // density increases toward supportB; may be infinite
+ br = bright;
+ }
+ rcutF = false;
+ return;
+ } else {
+ rcutF = true; // There is a right tail
+ }
+
+ } else {
+ del = m_dens.evaluate(bleft + epsx) - m_dens.evaluate(bleft);
+ if ((supportA > Double.NEGATIVE_INFINITY) &&
+ (bleft - supportA <= epsx ||
+ bleft - supportA <= Math.abs(supportA)*epsx)) {
+ // If density is non-negligible right down to domain limit supportA,
+ // then cutoff is bleft. There is no left tail. We want cutoff
+ // at bleft in case density(supportA) = infinite.
+ if (del > 0) {
+ // density decreases toward supportA
+ bl = supportA;
+ } else {
+ // density increases toward supportA; may be infinite
+ bl = bleft;
+ }
+ lcutF = false;
+ return;
+ } else {
+ lcutF = true; // There is a left tail
+ }
+ }
+
+ double c = 0;
+ double h = 1.0/64.0; // step to compute derivative
+ h = Math.max (h, (bright - bleft) / (1024));
+ double x = x0, xnew;
+ double y = 0, yl = 0, yr = 0, yprime = 0;
+ double tem = 0;
+ int iter = 0;
+ final int ITERMAX = 30;
+ boolean fini = false;
+
+ while (!fini && iter < ITERMAX) {
+ iter++;
+ boolean ended = false;
+ int it = 0;
+
+ while (!ended && it < 10) {
+ it++;
+ if (x + h > supportB)
+ h = supportB - x;
+ if (x - h < supportA)
+ h = x - supportA;
+ yr = m_dens.evaluate(x + h);
+ y = m_dens.evaluate(x);
+ yl = m_dens.evaluate(x - h);
+ if (!(yl == 0 || yr == 0 || y == 0))
+ ended = true;
+ else
+ h /= 2;
+ }
+
+ c = yr / (yr - y) + yl / (yl - y) - 1.; // the local concavity lc
+ yprime = (yr - yl) / (2. * h); // first derivative
+ tem = Math.abs (y * y / ((c + 1.) * yprime)); // tail area of CDF
+
+ if (Double.isNaN (tem))
+ break;
+ if (Math.abs (tem / eps - 1.) < 1.e-4) // accuracy is good?
+ break;
+ if (Math.abs(c) <= epsc) {
+ tem = eps * Math.abs(yprime) / (y * y); // formula (10)
+ if (tem <= 0)
+ break;
+ xnew = x + y / yprime * Math.log(tem);
+ } else {
+ tem = (1. + c) * eps * Math.abs(yprime) / (y * y); // formula(10)
+ if (tem < 0)
+ break;
+ xnew = x + y / (c*yprime) * (Math.pow(tem, c / (1. + c)) - 1.);
+ }
+
+ if (DEBUG)
+ System.out.printf(
+ "Cutoff x = %g y = %g c = %g%n", xnew, y, c);
+
+ if ((Math.abs(xnew - x) <= Math.abs(x)*epsx) ||
+ (Math.abs(xnew - x) <= epsx))
+ fini = true; // found cut-off x
+ else
+ x = xnew;
+
+ // Given good x, precompute some parameters in formula (10)
+ if (right) {
+ rlc = Math.abs(c);
+ br = x;
+ rc3 = c / (1 + c);
+ rc2 = tem / eps;
+ rc1 = y / yprime;
+ if (Math.abs(c) > epsc)
+ rc1 /= c;
+
+ } else {
+ llc = Math.abs(c);
+ bl = x;
+ lc3 = c / (1 + c);
+ lc2 = tem / eps;
+ lc1 = y / yprime;
+ if (Math.abs(c) > epsc)
+ lc1 /= c;
+ }
+
+ if (Math.abs(xnew - x) >= range)
+ fini = true;
+ }
+
+ if (right) {
+ if ((rc1 == 0 && rc2 == 0 && rc3 == 0)) {
+ br = bright;
+ rcutF = false;
+ }
+ } else {
+ if ((lc1 == 0 && lc2 == 0 && lc3 == 0)) {
+ bl = bleft;
+ lcutF = false;
+ }
+ }
+ }
+
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/probdist/InverseGammaDist.java b/source/umontreal/iro/lecuyer/probdist/InverseGammaDist.java
new file mode 100644
index 0000000..8c14634
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/InverseGammaDist.java
@@ -0,0 +1,310 @@
+
+
+/*
+ * Class: InverseGammaDist
+ * Description: inverse gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <SPAN CLASS="textit">inverse gamma</SPAN> distribution with shape parameter
+ *
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>β</I><SUP><I>α</I></SUP>exp<SUP>-<I>β</I>/x</SUP>)/(<I>x</I><SUP><I>α</I>+1</SUP><I>Γ</I>(<I>α</I>)) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> otherwise,
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function.
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - <I>F</I><SUB>G</SUB>(1/<I>x</I>), for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = 0</SPAN> otherwise, where <SPAN CLASS="MATH"><I>F</I><SUB>G</SUB>(<I>x</I>)</SPAN> is the distribution function
+ * of a gamma
+ * distribution with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+public class InverseGammaDist extends ContinuousDistribution {
+ protected double alpha;
+ protected double beta;
+ protected double logam; // Ln (Gamma(alpha))
+
+
+
+ /**
+ * Constructs an <TT>InverseGammaDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>.
+ *
+ */
+ public InverseGammaDist (double alpha, double beta) {
+ setParam (alpha, beta);
+ }
+
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - logam) / x;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean () {
+ return getMean (alpha, beta);
+ }
+
+ public double getVariance () {
+ return getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation () {
+ return getStandardDeviation (alpha, beta);
+ }
+
+ /**
+ * Computes the density function of the inverse gamma distribution with shape
+ * parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double density (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - Num.lnGamma (alpha)) / x;
+ }
+
+
+ /**
+ * Computes the cumulative probability function of the inverse gamma distribution
+ * with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double cdf (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return GammaDist.barF (alpha, beta, 15, 1.0 / x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function of the inverse gamma distribution
+ * with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double barF (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+
+ return GammaDist.cdf (alpha, beta, 15, 1.0 / x);
+ }
+
+
+ /**
+ * Computes the inverse distribution function of the inverse gamma distribution
+ * with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double u) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return 1.0 / GammaDist.inverseF (alpha, beta, 15, 1 - u);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>β</I>)</SPAN> of the inverse gamma distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α), hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double[] y = new double[n];
+
+ for (int i = 0; i < n; i++) {
+ if(x[i] > 0)
+ y[i] = 1.0 / x[i];
+ else
+ y[i] = 1.0E100;
+ }
+
+ return GammaDist.getMLE (y, n);
+ }
+
+
+ /**
+ * Creates a new instance of the inverse gamma distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>β</I></SPAN> estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static InverseGammaDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new InverseGammaDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>β</I>/(<I>α</I> - 1)</SPAN> of the inverse gamma
+ * distribution with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getMean (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return (beta / (alpha - 1.0));
+ }
+
+
+ /**
+ * Returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>β</I><SUP>2</SUP>/((<I>α</I> -1)<SUP>2</SUP>(<I>α</I> - 2))</SPAN>
+ * of the inverse gamma distribution with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale
+ * parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getVariance (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return ((beta * beta) / ((alpha - 1.0) * (alpha - 1.0) * (alpha - 2.0)));
+ }
+
+
+ /**
+ * Returns the standard deviation of the inverse gamma distribution with
+ * shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta) {
+ return Math.sqrt (getVariance (alpha, beta));
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>α</I></SPAN> parameter of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>β</I></SPAN> parameter of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public void setParam (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ supportA = 0.0;
+ this.alpha = alpha;
+ this.beta = beta;
+ logam = Num.lnGamma (alpha);
+ }
+
+
+ /**
+ * Returns a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/InverseGammaDist.tex b/source/umontreal/iro/lecuyer/probdist/InverseGammaDist.tex
new file mode 100644
index 0000000..b4f1f30
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/InverseGammaDist.tex
@@ -0,0 +1,338 @@
+\defmodule{InverseGammaDist}
+
+Extends the class \class{ContinuousDistribution} for
+the \emph{inverse gamma} distribution with shape parameter
+$\alpha > 0$ and scale parameter $\beta > 0$.
+The density function is given by
+\begin{htmlonly}
+\eq
+ f(x) = (\beta^{\alpha}\exp^{-\beta / x}) / (x^{\alpha+1} \Gamma(\alpha))
+ \qquad \mbox{for } x > 0,
+\endeq
+ and $f(x) = 0$ otherwise,
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \left\{\begin{array}{ll} \displaystyle
+ \frac{\beta^{\alpha}e^{-\beta / x}}{x^{\alpha+ 1} \Gamma(\alpha)}
+ & \quad \mbox{for } x > 0 \\[12pt]
+ 0 & \quad \mbox{otherwise,}
+ \end{array} \right.
+ \eqlabel{eq:dinvgam}
+\endeq
+\end{latexonly}
+where $\Gamma$ is the gamma function.
+The distribution function is given by
+\begin{htmlonly}
+\eq
+ F(x) = 1 - F_{G}(1 / x),
+ \quad \mbox{for } x > 0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ F(x) = 1 - F_{G}\left(\frac{1}{x}\right)
+ \qquad \mbox{for } x > 0,
+ \eqlabel{eq:Finvgam}
+\endeq
+\end{latexonly}
+ and $F(x) = 0$ otherwise, where $F_{G}(x)$ is the distribution function
+of a gamma
+distribution with shape parameter $\alpha$ and scale parameter $\beta$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGammaDist
+ * Description: inverse gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class InverseGammaDist extends ContinuousDistribution\begin{hide} {
+ protected double alpha;
+ protected double beta;
+ protected double logam; // Ln (Gamma(alpha))
+
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public InverseGammaDist (double alpha, double beta)\begin{hide} {
+ setParam (alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs an \texttt{InverseGammaDist} object with parameters
+ $\alpha$ = \texttt{alpha} and $\beta$ = \texttt{beta}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - logam) / x;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean () {
+ return getMean (alpha, beta);
+ }
+
+ public double getVariance () {
+ return getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation () {
+ return getStandardDeviation (alpha, beta);
+ }\end{hide}
+
+ public static double density (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - Num.lnGamma (alpha)) / x;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the density function of the inverse gamma distribution with shape
+ parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return GammaDist.barF (alpha, beta, 15, 1.0 / x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Computes the cumulative probability function of the inverse gamma distribution
+ with shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+
+ return GammaDist.cdf (alpha, beta, 15, 1.0 / x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function of the inverse gamma distribution
+ with shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double beta, double u)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return 1.0 / GammaDist.inverseF (alpha, beta, 15, 1 - u);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse distribution function of the inverse gamma distribution
+ with shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double[] y = new double[n];
+
+ for (int i = 0; i < n; i++) {
+ if(x[i] > 0)
+ y[i] = 1.0 / x[i];
+ else
+ y[i] = 1.0E100;
+ }
+
+ return GammaDist.getMLE (y, n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\beta)$ of the inverse gamma distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\beta$].
+ \begin{detailed}
+ The equations of the maximum likelihood are the same as the equations of
+ the gamma distribution, with the sample $y_i = 1/x_i$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}, \hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static InverseGammaDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new InverseGammaDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Creates a new instance of the inverse gamma distribution with parameters $\alpha$
+and $\beta$ estimated using the maximum likelihood method based on the $n$
+observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return (beta / (alpha - 1.0));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the mean $E[X] = \beta / (\alpha - 1)$ of the inverse gamma
+ distribution with shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return ((beta * beta) / ((alpha - 1.0) * (alpha - 1.0) * (alpha - 2.0)));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the variance $\mbox{Var}[X] = \beta^2 / ((\alpha - 1)^2(\alpha - 2))$
+ of the inverse gamma distribution with shape parameter $\alpha$ and scale
+parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta)\begin{hide} {
+ return Math.sqrt (getVariance (alpha, beta));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the standard deviation of the inverse gamma distribution with
+ shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\alpha$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\beta$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParam (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ supportA = 0.0;
+ this.alpha = alpha;
+ this.beta = beta;
+ logam = Num.lnGamma (alpha);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\alpha$ and $\beta$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\beta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/InverseGaussianDist.java b/source/umontreal/iro/lecuyer/probdist/InverseGaussianDist.java
new file mode 100644
index 0000000..02574ad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/InverseGaussianDist.java
@@ -0,0 +1,362 @@
+
+
+/*
+ * Class: InverseGaussianDist
+ * Description: inverse Gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>inverse Gaussian</EM> distribution with location parameter
+ * <SPAN CLASS="MATH"><I>μ</I> > 0</SPAN> and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (λ/ (2πx^3))<SUP>1/2</SUP><I>e</I><SUP>-<I>λ</I>(x-<I>μ</I>)<SUP>2</SUP>/(2<I>μ</I><SUP>2</SUP>x)</SUP>, for <I>x</I> > 0.
+ * </DIV><P></P>
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>Φ</I>((λ/x)<SUP>1/2</SUP>(<I>x</I>/<I>μ</I> -1)) + <I>e</I><SUP>2<I>λ</I>/<I>μ</I></SUP><I>Φ</I>(- (λ/x)<SUP>1/2</SUP>(<I>x</I>/<I>μ</I> + 1)),
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard normal distribution function.
+ *
+ * <P>
+ * The non-static versions of the methods <TT>cdf</TT>, <TT>barF</TT>,
+ * and <TT>inverseF</TT> call the static version of the same name.
+ *
+ */
+public class InverseGaussianDist extends ContinuousDistribution {
+ protected double mu;
+ protected double lambda;
+
+ private static class Function implements MathFunction {
+ protected double mu;
+ protected double lambda;
+ protected double u;
+
+ public Function (double mu, double lambda, double u) {
+ this.mu = mu;
+ this.lambda = lambda;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(mu, lambda, x);
+ }
+ }
+
+
+
+ /**
+ * Constructs the <EM>inverse Gaussian</EM> distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public InverseGaussianDist (double mu, double lambda) {
+ setParams (mu, lambda);
+ }
+
+
+ public double density (double x) {
+ return density (mu, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, lambda, u);
+ }
+
+ public double getMean() {
+ return getMean (mu, lambda);
+ }
+
+ public double getVariance() {
+ return getVariance (mu, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (mu, lambda);
+ }
+
+ /**
+ * Computes the density function for the
+ * inverse gaussian distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>,
+ * evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double density (double mu, double lambda, double x) {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ double sqrtX = Math.sqrt (x);
+
+ return (Math.sqrt (lambda / (2 * Math.PI)) / (sqrtX * sqrtX * sqrtX) *
+ Math.exp (-lambda * (x - 2 * mu + (mu * mu / x)) / (2 * mu * mu)));
+ }
+
+
+ /**
+ * Computes the distribution function
+ * of the inverse gaussian distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double cdf (double mu, double lambda, double x) {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ double temp = Math.sqrt (lambda / x);
+ double z = temp * (x / mu - 1.0);
+ double w = temp * (x / mu + 1.0);
+
+ // C'est bien un + dans exp (2 * lambda / mu)
+ return (NormalDist.cdf01 (z) +
+ Math.exp (2 * lambda / mu) * NormalDist.cdf01 (-w));
+ }
+
+
+ /**
+ * Computes the complementary distribution function of the inverse gaussian distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double barF (double mu, double lambda, double x) {
+ return 1.0 - cdf (mu, lambda, x);
+ }
+
+
+ /**
+ * Computes the inverse of the inverse gaussian distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public static double inverseF (double mu, double lambda, double u) {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+
+ Function f = new Function (mu, lambda, u);
+
+ // Find interval containing root = x*
+ double sig = getStandardDeviation(mu, lambda);
+ double x0 = 0.0;
+ double x = mu;
+ double v = cdf(mu, lambda, x);
+ while (v < u) {
+ x0 = x;
+ x += 3.0*sig;
+ v = cdf(mu, lambda, x);
+ }
+
+ return RootFinder.brentDekker (x0, x, f, 1e-12);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>μ</I>, <I>λ</I>)</SPAN> of the inverse gaussian distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[2];
+ double sum = 0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+ parameters[0] = sum / (double) n;
+
+ sum = 0;
+ for (int i = 0; i < n; i++) {
+ sum += ((1.0 / (double) x[i]) - (1.0 / parameters[0]));
+ }
+ parameters[1] = (double) n / (double) sum;
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of an inverse gaussian distribution with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> estimated using the maximum likelihood method based on
+ * the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static InverseGaussianDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new InverseGaussianDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN> of the
+ * inverse gaussian distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the inverse gaussian distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN>
+ *
+ */
+ public static double getMean (double mu, double lambda) {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return mu;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>μ</I><SUP>3</SUP>/<I>λ</I></SPAN> of
+ * the inverse gaussian distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the inverse gaussian distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>μ</I><SUP>3</SUP>/<I>λ</I></SPAN>
+ *
+ */
+ public static double getVariance (double mu, double lambda) {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (mu * mu * mu / lambda);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the inverse gaussian distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the inverse gaussian distribution
+ *
+ */
+ public static double getStandardDeviation (double mu, double lambda) {
+ return Math.sqrt (getVariance (mu, lambda));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double mu, double lambda) {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ this.mu = mu;
+ this.lambda = lambda;
+ supportA = 0.0;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu + ", lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/InverseGaussianDist.tex b/source/umontreal/iro/lecuyer/probdist/InverseGaussianDist.tex
new file mode 100644
index 0000000..fc4211c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/InverseGaussianDist.tex
@@ -0,0 +1,388 @@
+\defmodule {InverseGaussianDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em inverse Gaussian\/} distribution with location parameter
+$\mu > 0$ and scale parameter $\lambda > 0$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = \sqrt{\lambda / (2\pi x^{3})} e^{-\lambda(x - \mu)^2 / (2\mu^2x)},
+\qquad\mbox{ for } x > 0.
+\endeq
+The distribution function is given by
+\eq
+ F(x) = \Phi\left(\sqrt{\lambda/{x}}({x}/{\mu} - 1)\right) +
+ e^{{2\lambda}/{\mu}}\Phi\left(-\sqrt{{\lambda}/{x}}({x}/{\mu} + 1)\right),
+\endeq
+where $\Phi$ is the standard normal distribution function.
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \sqrt{\frac{\lambda}{2\pi x^{3}}}\; e^{{-\lambda(x - \mu)^2}/{(2\mu^2x)}},
+\qquad\mbox {for } x > 0.
+\eqlabel{eq:fInverseGaussian}
+\endeq
+The distribution function is given by
+\eq
+ F(x) = \Phi\left(\sqrt{\frac{\lambda}{x}}\left(\frac{x}{\mu} - 1\right)\right) +
+ e^{({2\lambda}/{\mu})}\Phi\left(-\sqrt{\frac{\lambda}{x}}\left(\frac{x}{\mu} + 1\right)\right),
+\eqlabel{eq:FInverseGaussian}
+\endeq
+where $\Phi$ is the standard normal distribution function.
+\end{latexonly}%
+
+The non-static versions of the methods \texttt{cdf}, \texttt{barF},
+and \texttt{inverseF} call the static version of the same name.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGaussianDist
+ * Description: inverse Gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+ \end{hide}
+
+public class InverseGaussianDist extends ContinuousDistribution\begin{hide} {
+ protected double mu;
+ protected double lambda;
+
+ private static class Function implements MathFunction {
+ protected double mu;
+ protected double lambda;
+ protected double u;
+
+ public Function (double mu, double lambda, double u) {
+ this.mu = mu;
+ this.lambda = lambda;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(mu, lambda, x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public InverseGaussianDist (double mu, double lambda)\begin{hide} {
+ setParams (mu, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs the {\em inverse Gaussian\/} distribution with parameters $\mu$ and $\lambda$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (mu, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, lambda, u);
+ }
+
+ public double getMean() {
+ return getMean (mu, lambda);
+ }
+
+ public double getVariance() {
+ return getVariance (mu, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (mu, lambda);
+ }\end{hide}
+
+ public static double density (double mu, double lambda, double x)\begin{hide} {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ double sqrtX = Math.sqrt (x);
+
+ return (Math.sqrt (lambda / (2 * Math.PI)) / (sqrtX * sqrtX * sqrtX) *
+ Math.exp (-lambda * (x - 2 * mu + (mu * mu / x)) / (2 * mu * mu)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:fInverseGaussian}) for the
+ inverse gaussian distribution with parameters $\mu$ and $\lambda$,
+ evaluated at $x$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu, double lambda, double x)\begin{hide} {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ double temp = Math.sqrt (lambda / x);
+ double z = temp * (x / mu - 1.0);
+ double w = temp * (x / mu + 1.0);
+
+ // C'est bien un + dans exp (2 * lambda / mu)
+ return (NormalDist.cdf01 (z) +
+ Math.exp (2 * lambda / mu) * NormalDist.cdf01 (-w));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the distribution function (\ref{eq:FInverseGaussian})
+ of the inverse gaussian distribution with parameters $\mu$ and
+ $\lambda$, evaluated at $x$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double mu, double lambda, double x)\begin{hide} {
+ return 1.0 - cdf (mu, lambda, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function of the inverse gaussian distribution
+ with parameters $\mu$ and $\lambda$, evaluated at $x$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double mu, double lambda, double u)\begin{hide} {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+
+ Function f = new Function (mu, lambda, u);
+
+ // Find interval containing root = x*
+ double sig = getStandardDeviation(mu, lambda);
+ double x0 = 0.0;
+ double x = mu;
+ double v = cdf(mu, lambda, x);
+ while (v < u) {
+ x0 = x;
+ x += 3.0*sig;
+ v = cdf(mu, lambda, x);
+ }
+
+ return RootFinder.brentDekker (x0, x, f, 1e-12);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse of the inverse gaussian distribution
+ with parameters $\mu$ and $\lambda$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[2];
+ double sum = 0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+ parameters[0] = sum / (double) n;
+
+ sum = 0;
+ for (int i = 0; i < n; i++) {
+ sum += ((1.0 / (double) x[i]) - (1.0 / parameters[0]));
+ }
+ parameters[1] = (double) n / (double) sum;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\mu, \lambda)$ of the inverse gaussian distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\mu$, $\lambda$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\mu , \hat\lambda)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat{\mu} & = & \bar{x}_n \\[5pt]
+ \frac{1}{\hat{\lambda}} & = & \frac{1}{n} \sum_{i=1}^{n}
+ \left(\frac{1}{x_i} - \frac{1}{\hat{\mu}}\right),
+ \end{eqnarray*}
+ where $\bar x_n$ is the average of $x[0],\dots,x[n-1]$,
+ \cite[page 271]{tJOH95a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\mu}$, $\hat{\lambda}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static InverseGaussianDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new InverseGaussianDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of an inverse gaussian distribution with parameters
+ $\mu$ and $\lambda$ estimated using the maximum likelihood method based on
+ the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double lambda)\begin{hide} {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean $E[X] = \mu$ of the
+ inverse gaussian distribution with parameters $\mu$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the inverse gaussian distribution $E[X] = \mu$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double mu, double lambda)\begin{hide} {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (mu * mu * mu / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = \mu^3/\lambda$ of
+ the inverse gaussian distribution with parameters $\mu$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the inverse gaussian distribution
+ $\mbox{Var}[X] = \mu^3 / \lambda$
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double lambda)\begin{hide} {
+ return Math.sqrt (getVariance (mu, lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the inverse gaussian distribution with parameters $\mu$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the inverse gaussian distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\lambda$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double mu, double lambda)\begin{hide} {
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ this.mu = mu;
+ this.lambda = lambda;
+ supportA = 0.0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\mu$ and $\lambda$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\mu$, $\lambda$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu + ", lambda = " + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSBDist.java b/source/umontreal/iro/lecuyer/probdist/JohnsonSBDist.java
new file mode 100644
index 0000000..2f4db3a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSBDist.java
@@ -0,0 +1,485 @@
+
+
+/*
+ * Class: JohnsonSBDist
+ * Description: Johnson S_B distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN></EM> distribution
+ * with shape parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> > 0</SPAN>, location parameter <SPAN CLASS="MATH"><I>ξ</I></SPAN>,
+ * and scale parameter <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Denoting
+ * <SPAN CLASS="MATH"><I>t</I> = (<I>x</I> - <I>ξ</I>)/<I>λ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>z</I> = <I>γ</I> + <I>δ</I>ln(<I>t</I>/(1 - <I>t</I>))</SPAN>, the density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>δe</I><SUP>-z<SUP>2</SUP>/2</SUP>/(<I>λt</I>(1-<I>t</I>)(2π)<SUP>1/2</SUP>), for <I>ξ</I> < <I>x</I> < <I>ξ</I> + <I>λ</I>,
+ * </DIV><P></P>
+ * and 0 elsewhere. The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>Φ</I>(<I>z</I>), for <I>ξ</I> < <I>x</I> < <I>ξ</I> + <I>λ</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard normal distribution function.
+ * The inverse distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>ξ</I> + <I>λ</I>(1/(1 + <I>e</I><SUP>-v(u)</SUP>)) for 0 <= <I>u</I> <= 1,
+ * </DIV><P></P>
+ * where
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>v</I>(<I>u</I>) = [<I>Φ</I><SUP>-1</SUP>(<I>u</I>) - <I>γ</I>]/<I>δ</I>.
+ * </DIV><P></P>
+ *
+ * <P>
+ * This class relies on the methods {@link NormalDist#cdf01 NormalDist.cdf01} and
+ * {@link NormalDist#inverseF01 NormalDist.inverseF01}
+ * of {@link NormalDist} to approximate <SPAN CLASS="MATH"><I>Φ</I></SPAN> and <SPAN CLASS="MATH"><I>Φ</I><SUP>-1</SUP></SPAN>.
+ *
+ */
+public class JohnsonSBDist extends JohnsonSystem {
+ // m_psi is used in computing the mean and the variance
+ private double m_psi = -1.0e100;
+
+
+ private static double getMeanPsi (double gamma, double delta,
+ double xi, double lambda, double[] tpsi) {
+ // Returns the theoretical mean of t = (x - xi)/lambda;
+ // also compute psi and returns it in tpsi[0], since
+ // it is used in computing the mean and the variance
+
+ final int NMAX = 10000;
+ final double EPS = 1.0e-15;
+
+ double a1 = 1.0/(2*delta*delta);
+ double a2 = (1.0 - 2*delta*gamma)/(2*delta*delta);
+ double a3 = (gamma - 1./delta)/delta;
+ int n = 0;
+ double tem = 1;
+ double sum = 0;
+ double v;
+ while (Math.abs(tem) > EPS* Math.abs(sum) && n < NMAX) {
+ ++n;
+ v = Math.exp(-n*gamma/delta) + Math.exp(n*a3);
+ tem = Math.exp(-n*n*a1) * v / (1 + Math.exp(-2*n*a1));
+ // tem = Math.exp(-n*n*a1) * Math.cosh(n*a2) / Math.cosh(n*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on mean");
+ double A = (0.5 + sum) / (delta);
+
+ a1 = Math.PI * Math.PI * delta * delta;
+ a2 = Math.PI * delta * gamma;
+ int j;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ j = 2*n - 1;
+ tem = Math.exp(-j*j*a1/2.0) * Math.sin(j*a2) / Math.sinh(j*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on mean");
+ double B = 2.0* Math.PI * delta * sum;
+
+ a1 = 2*Math.PI * Math.PI * delta * delta;
+ a2 = 2*Math.PI * delta * gamma;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ tem = Math.exp(-n*n*a1) * Math.cos(n*a2);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on mean");
+ double C = 1 + 2.0 * sum;
+
+ double D = Math.sqrt(2*Math.PI) * Math.exp(gamma* gamma / 2.0);
+ double tmean = (A - B) / (C*D);
+ tpsi[0] = C*D;
+ return tmean;
+ }
+
+
+
+ /**
+ * Constructs a <TT>JohnsonSBDist</TT> object
+ * with shape parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>,
+ * location parameter <SPAN CLASS="MATH"><I>ξ</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public JohnsonSBDist (double gamma, double delta,
+ double xi, double lambda) {
+ super (gamma, delta, xi, lambda);
+ setLastParams(xi, lambda);
+ }
+
+
+ private void setLastParams(double xi, double lambda) {
+ supportA = xi;
+ supportB = xi + lambda;
+ }
+
+ public double density (double x) {
+ return density (gamma, delta, xi, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (gamma, delta, xi, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (gamma, delta, xi, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (gamma, delta, xi, lambda, u);
+ }
+
+ public double getMean() {
+ return JohnsonSBDist.getMean (gamma, delta, xi, lambda);
+ }
+
+ public double getVariance() {
+ return JohnsonSBDist.getVariance (gamma, delta, xi, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return JohnsonSBDist.getStandardDeviation (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Returns the density function.
+ *
+ */
+ public static double density (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (x <= xi || x >= (xi+lambda))
+ return 0.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y/(1.0 - y));
+ return delta/(lambda*y*(1.0 - y)*Math.sqrt (2.0*Math.PI))*
+ Math.exp (-z*z/2.0);
+ }
+
+
+ /**
+ * Returns the distribution function.
+ *
+ */
+ public static double cdf (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (x <= xi)
+ return 0.0;
+ if (x >= xi+lambda)
+ return 1.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y/(1.0 - y));
+ return NormalDist.cdf01 (z);
+ }
+
+
+ /**
+ * Returns the complementary distribution.
+ *
+ */
+ public static double barF (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (x <= xi)
+ return 1.0;
+ if (x >= xi+lambda)
+ return 0.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y/(1.0 - y));
+ return NormalDist.barF01 (z);
+ }
+
+
+ /**
+ * Returns the inverse of the distribution.
+ *
+ */
+ public static double inverseF (double gamma, double delta,
+ double xi, double lambda, double u) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0) // if u == 1, in fact
+ return xi+lambda;
+ if (u <= 0.0) // if u == 0, in fact
+ return xi;
+
+ double z = NormalDist.inverseF01 (u);
+ double v = (z - gamma)/delta;
+
+ if (v >= Num.DBL_MAX_EXP*Num.LN2)
+ return xi + lambda;
+ if (v <= Num.DBL_MIN_EXP*Num.LN2)
+ return xi;
+
+ v = Math.exp (v);
+ return (xi + (xi+lambda)*v)/(1.0 + v);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>γ</I>, <I>δ</I>)</SPAN> of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN> distribution,
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ * Parameters
+ * <SPAN CLASS="MATH"><I>ξ</I> = <texttt>xi</texttt></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> = <texttt>lambda</texttt></SPAN> are known.
+ * The estimated parameters are returned in a two-element
+ * array in the order: [<SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param xi parameter <SPAN CLASS="MATH"><I>ξ</I></SPAN>
+ *
+ * @param lambda parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(γ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(δ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n,
+ double xi, double lambda) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+ double[] ftab = new double[n];
+ double sum = 0.0;
+ double t;
+
+ for (int i = 0; i < n; i++) {
+ t = (x[i] - xi) / lambda;
+ if (t <= 0.)
+ ftab[i] = LN_EPS;
+ else if (t >= 1 - Num.DBL_EPSILON)
+ ftab[i] = Math.log (1. / Num.DBL_EPSILON);
+ else
+ ftab[i] = Math.log (t/(1. - t));
+ sum += ftab[i];
+ }
+ double empiricalMean = sum / n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ t = ftab[i] - empiricalMean;
+ sum += t * t;
+ }
+ double sigmaf = Math.sqrt(sum / n);
+
+ double[] param = new double[2];
+ param[0] = -empiricalMean / sigmaf;
+ param[1] = 1.0 / sigmaf;
+
+ return param;
+ }
+
+
+ /**
+ * Creates a new instance of a <TT>JohnsonSBDist</TT> object
+ * using the maximum likelihood method based
+ * on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ * Given the parameters
+ * <SPAN CLASS="MATH"><I>ξ</I> = <texttt>xi</texttt></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> = <texttt>lambda</texttt></SPAN>,
+ * the parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> are estimated from the observations.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param xi parameter <SPAN CLASS="MATH"><I>ξ</I></SPAN>
+ *
+ * @param lambda parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>
+ *
+ *
+ */
+ public static JohnsonSBDist getInstanceFromMLE (double[] x, int n,
+ double xi, double lambda) {
+ double parameters[] = getMLE (x, n, xi, lambda);
+ return new JohnsonSBDist (parameters[0], parameters[1], xi, lambda);
+ }
+
+
+ /**
+ * Returns the mean
+ * of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN> distribution with parameters
+ * <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public static double getMean (double gamma, double delta,
+ double xi, double lambda) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ double[] tpsi = new double[1];
+ double mu = getMeanPsi (gamma, delta, xi, lambda, tpsi);
+ return xi + lambda * mu;
+ }
+
+
+ /**
+ * Returns the variance
+ * of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN> distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN> distribution.
+ *
+ */
+ public static double getVariance (double gamma, double delta,
+ double xi, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ final int NMAX = 10000;
+ final double EPS = 1.0e-15;
+
+ double a1 = 1.0/(2.0*delta*delta);
+ double a2 = (1.0 - 2.0*delta*gamma)/(2.0*delta*delta);
+ double a3 = (gamma - 1./delta)/delta;
+ double v;
+ int n = 0;
+ double tem = 1;
+ double sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ v = Math.exp(-n*gamma/delta) - Math.exp(n*a3);
+ tem = n*Math.exp(-n*n*a1) * v / (1 + Math.exp(-2*n*a1));
+ // tem = n*Math.exp(-n*n*a1) * Math.sinh(n*a2) / Math.cosh(n*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on variance");
+ double A = -sum / (delta*delta);
+
+ a1 = Math.PI * Math.PI * delta * delta;
+ a2 = Math.PI * delta * gamma;
+ int j;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ j = 2*n - 1;
+ tem = j*Math.exp(-j*j*a1/2.0) * Math.cos(j*a2) / Math.sinh(j*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on variance");
+ double B = 2.0* a1 * sum;
+
+ a1 = 2*Math.PI * Math.PI * delta * delta;
+ a2 = 2*Math.PI * delta * gamma;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ tem = n * Math.exp(-n*n*a1) * Math.sin(n*a2);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on variance");
+ double C = - 4.0 * Math.PI * delta * sum;
+
+ double D = Math.sqrt(2*Math.PI) * Math.exp(0.5 * gamma* gamma);
+ double[] tpsi = new double[1];
+ double mu = getMeanPsi (gamma, delta, xi, lambda, tpsi);
+
+ double tvar = mu *(1 - delta * gamma) - mu *mu +
+ delta / tpsi[0] * (A - B - mu * C * D);
+ return lambda*lambda*tvar;
+
+ }
+
+
+ /**
+ * Returns the standard deviation of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN>
+ * distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN> distribution
+ *
+ */
+ public static double getStandardDeviation (double gamma, double delta,
+ double xi, double lambda) {
+ return Math.sqrt (JohnsonSBDist.getVariance (gamma, delta, xi, lambda));
+ }
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ */
+ public void setParams (double gamma, double delta,
+ double xi, double lambda) {
+ setParams0(gamma, delta, xi, lambda);
+ setLastParams(xi, lambda);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSBDist.tex b/source/umontreal/iro/lecuyer/probdist/JohnsonSBDist.tex
new file mode 100644
index 0000000..59bb203
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSBDist.tex
@@ -0,0 +1,484 @@
+\defmodule {JohnsonSBDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Johnson $S_B$\/} distribution\latex{ \cite{tJOH49a,sLAW00a,tFLY06a}}
+with shape parameters $\gamma$ and $\delta > 0$, location parameter $\xi$,
+and scale parameter $\lambda>0$.
+Denoting $t=(x-\xi)/\lambda$ and $z = \gamma + \delta\ln (t/(1-t))$, the density is
+\eq
+ f(x) = \html{\delta e^{-z^2/2}/(}\latex{\frac {\delta e^{-z^2/2}}}
+ {\lambda t(1 - t)\sqrt{2\pi}}
+ \html{)}, \qquad
+ \mbox { for } \xi < x < \xi+\lambda, \eqlabel{eq:JohnsonSB-density}
+\endeq
+and 0 elsewhere. The distribution function is
+\eq
+ F(x) = \Phi (z),
+ \mbox { for } \xi < x < \xi+\lambda, \eqlabel{eq:JohnsonSB-dist}
+\endeq
+where $\Phi$ is the standard normal distribution function.
+The inverse distribution function is
+\eq
+ F^{-1}(u) = \xi + \lambda \left(1/\left(1+e^{-v(u)}\right)\right) \qquad\mbox{for } 0 \le u \le 1,
+ \eqlabel{eq:JohnsonSB-inverse}
+\endeq
+where
+\eq
+ v(u) = [\Phi^{-1}(u) - \gamma]/\delta.
+\endeq
+
+This class relies on the methods \clsexternalmethod{}{NormalDist}{cdf01}{} and
+ \clsexternalmethod{}{NormalDist}{inverseF01}{}
+of \class{NormalDist} to approximate $\Phi$ and $\Phi^{-1}$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSBDist
+ * Description: Johnson S_B distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class JohnsonSBDist extends JohnsonSystem\begin{hide} {
+ // m_psi is used in computing the mean and the variance
+ private double m_psi = -1.0e100;
+
+
+ private static double getMeanPsi (double gamma, double delta,
+ double xi, double lambda, double[] tpsi) {
+ // Returns the theoretical mean of t = (x - xi)/lambda;
+ // also compute psi and returns it in tpsi[0], since
+ // it is used in computing the mean and the variance
+
+ final int NMAX = 10000;
+ final double EPS = 1.0e-15;
+
+ double a1 = 1.0/(2*delta*delta);
+ double a2 = (1.0 - 2*delta*gamma)/(2*delta*delta);
+ double a3 = (gamma - 1./delta)/delta;
+ int n = 0;
+ double tem = 1;
+ double sum = 0;
+ double v;
+ while (Math.abs(tem) > EPS* Math.abs(sum) && n < NMAX) {
+ ++n;
+ v = Math.exp(-n*gamma/delta) + Math.exp(n*a3);
+ tem = Math.exp(-n*n*a1) * v / (1 + Math.exp(-2*n*a1));
+ // tem = Math.exp(-n*n*a1) * Math.cosh(n*a2) / Math.cosh(n*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on mean");
+ double A = (0.5 + sum) / (delta);
+
+ a1 = Math.PI * Math.PI * delta * delta;
+ a2 = Math.PI * delta * gamma;
+ int j;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ j = 2*n - 1;
+ tem = Math.exp(-j*j*a1/2.0) * Math.sin(j*a2) / Math.sinh(j*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on mean");
+ double B = 2.0* Math.PI * delta * sum;
+
+ a1 = 2*Math.PI * Math.PI * delta * delta;
+ a2 = 2*Math.PI * delta * gamma;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ tem = Math.exp(-n*n*a1) * Math.cos(n*a2);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on mean");
+ double C = 1 + 2.0 * sum;
+
+ double D = Math.sqrt(2*Math.PI) * Math.exp(gamma* gamma / 2.0);
+ double tmean = (A - B) / (C*D);
+ tpsi[0] = C*D;
+ return tmean;
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public JohnsonSBDist (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ super (gamma, delta, xi, lambda);
+ setLastParams(xi, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{JohnsonSBDist} object
+ with shape parameters $\gamma$ and $\delta$,
+ location parameter $\xi$ and scale parameter $\lambda$.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}
+\begin{code}
+
+ private void setLastParams(double xi, double lambda) {
+ supportA = xi;
+ supportB = xi + lambda;
+ }
+
+ public double density (double x) {
+ return density (gamma, delta, xi, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (gamma, delta, xi, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (gamma, delta, xi, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (gamma, delta, xi, lambda, u);
+ }
+
+ public double getMean() {
+ return JohnsonSBDist.getMean (gamma, delta, xi, lambda);
+ }
+
+ public double getVariance() {
+ return JohnsonSBDist.getVariance (gamma, delta, xi, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return JohnsonSBDist.getStandardDeviation (gamma, delta, xi, lambda);
+ }
+\end{code}
+\end{hide}\begin{code}
+
+ public static double density (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (x <= xi || x >= (xi+lambda))
+ return 0.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y/(1.0 - y));
+ return delta/(lambda*y*(1.0 - y)*Math.sqrt (2.0*Math.PI))*
+ Math.exp (-z*z/2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the density function (\ref{eq:JohnsonSB-density}).
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (x <= xi)
+ return 0.0;
+ if (x >= xi+lambda)
+ return 1.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y/(1.0 - y));
+ return NormalDist.cdf01 (z);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the distribution function (\ref{eq:JohnsonSB-dist}).
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (x <= xi)
+ return 1.0;
+ if (x >= xi+lambda)
+ return 0.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y/(1.0 - y));
+ return NormalDist.barF01 (z);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the complementary distribution.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double gamma, double delta,
+ double xi, double lambda, double u)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0) // if u == 1, in fact
+ return xi+lambda;
+ if (u <= 0.0) // if u == 0, in fact
+ return xi;
+
+ double z = NormalDist.inverseF01 (u);
+ double v = (z - gamma)/delta;
+
+ if (v >= Num.DBL_MAX_EXP*Num.LN2)
+ return xi + lambda;
+ if (v <= Num.DBL_MIN_EXP*Num.LN2)
+ return xi;
+
+ v = Math.exp (v);
+ return (xi + (xi+lambda)*v)/(1.0 + v);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the inverse of the distribution (\ref{eq:JohnsonSB-inverse}).
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n,
+ double xi, double lambda) \begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+ double[] ftab = new double[n];
+ double sum = 0.0;
+ double t;
+
+ for (int i = 0; i < n; i++) {
+ t = (x[i] - xi) / lambda;
+ if (t <= 0.)
+ ftab[i] = LN_EPS;
+ else if (t >= 1 - Num.DBL_EPSILON)
+ ftab[i] = Math.log (1. / Num.DBL_EPSILON);
+ else
+ ftab[i] = Math.log (t/(1. - t));
+ sum += ftab[i];
+ }
+ double empiricalMean = sum / n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ t = ftab[i] - empiricalMean;
+ sum += t * t;
+ }
+ double sigmaf = Math.sqrt(sum / n);
+
+ double[] param = new double[2];
+ param[0] = -empiricalMean / sigmaf;
+ param[1] = 1.0 / sigmaf;
+
+ return param;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\gamma,\delta)$ of the Johnson $S_B$ distribution,
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$.
+ Parameters $\xi = \texttt{xi}$ and $\lambda = \texttt{lambda}$ are known.
+ The estimated parameters are returned in a two-element
+ array in the order: [$\gamma$, $\delta$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\gamma , \hat\delta)$
+ that satisfy the equations \cite{tFLY06a}
+ $\hat\gamma = -\bar f / s_f$ and $\hat\delta = 1/s_f$,
+ where $f = \ln (t/(1-t))$, $\bar f$ is the sample mean of the $f_i$, and
+ \[
+ s_f = \sqrt{\frac 1n \sum_{i=0}^{n-1} (f_i - \bar f)^2},
+ \]
+ with $f_i = \ln (t_i/(1-t_i))$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{xi}{parameter $\xi$}
+ \param{lambda}{parameter $\lambda$}
+ \return{returns the parameters [$\hat{\gamma}$, $\hat{\delta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static JohnsonSBDist getInstanceFromMLE (double[] x, int n,
+ double xi, double lambda)\begin{hide} {
+ double parameters[] = getMLE (x, n, xi, lambda);
+ return new JohnsonSBDist (parameters[0], parameters[1], xi, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a \texttt{JohnsonSBDist} object
+ using the maximum likelihood method based
+ on the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+ Given the parameters $\xi = \texttt{xi}$ and $\lambda = \texttt{lambda}$,
+ the parameters $\gamma$ and $\delta$ are estimated from the observations.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{xi}{parameter $\xi$}
+ \param{lambda}{parameter $\lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ double[] tpsi = new double[1];
+ double mu = getMeanPsi (gamma, delta, xi, lambda, tpsi);
+ return xi + lambda * mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the mean \latex{\cite{tJOH49a}}
+ of the Johnson $S_B$ distribution with parameters
+ $\gamma$, $\delta$, $\xi$ and $\lambda$.
+ \end{tabb}
+\begin{code}
+
+ public static double getVariance (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ final int NMAX = 10000;
+ final double EPS = 1.0e-15;
+
+ double a1 = 1.0/(2.0*delta*delta);
+ double a2 = (1.0 - 2.0*delta*gamma)/(2.0*delta*delta);
+ double a3 = (gamma - 1./delta)/delta;
+ double v;
+ int n = 0;
+ double tem = 1;
+ double sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ v = Math.exp(-n*gamma/delta) - Math.exp(n*a3);
+ tem = n*Math.exp(-n*n*a1) * v / (1 + Math.exp(-2*n*a1));
+ // tem = n*Math.exp(-n*n*a1) * Math.sinh(n*a2) / Math.cosh(n*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on variance");
+ double A = -sum / (delta*delta);
+
+ a1 = Math.PI * Math.PI * delta * delta;
+ a2 = Math.PI * delta * gamma;
+ int j;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ j = 2*n - 1;
+ tem = j*Math.exp(-j*j*a1/2.0) * Math.cos(j*a2) / Math.sinh(j*a1);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on variance");
+ double B = 2.0* a1 * sum;
+
+ a1 = 2*Math.PI * Math.PI * delta * delta;
+ a2 = 2*Math.PI * delta * gamma;
+ n = 0;
+ tem = 1;
+ sum = 0;
+ while (Math.abs(tem) > EPS*Math.abs(sum) && n < NMAX) {
+ ++n;
+ tem = n * Math.exp(-n*n*a1) * Math.sin(n*a2);
+ sum += tem;
+ }
+ if (n >= NMAX)
+ System.err.println ("JohnsonSBDist: possible lack of accuracy on variance");
+ double C = - 4.0 * Math.PI * delta * sum;
+
+ double D = Math.sqrt(2*Math.PI) * Math.exp(0.5 * gamma* gamma);
+ double[] tpsi = new double[1];
+ double mu = getMeanPsi (gamma, delta, xi, lambda, tpsi);
+
+ double tvar = mu *(1 - delta * gamma) - mu *mu +
+ delta / tpsi[0] * (A - B - mu * C * D);
+ return lambda*lambda*tvar;
+
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance \latex{\cite{tFLY06a}}
+ of the Johnson $S_B$ distribution with parameters $\gamma$, $\delta$, $\xi$
+ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Johnson $S_B$ distribution.}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ return Math.sqrt (JohnsonSBDist.getVariance (gamma, delta, xi, lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation of the Johnson $S_B$
+ distribution with parameters $\gamma$, $\delta$, $\xi$, $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Johnson $S_B$ distribution}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ setParams0(gamma, delta, xi, lambda);
+ setLastParams(xi, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $\gamma$, $\delta$, $\xi$ and
+ $\lambda$ for this object.
+ \end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSLDist.java b/source/umontreal/iro/lecuyer/probdist/JohnsonSLDist.java
new file mode 100644
index 0000000..c9c2cdb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSLDist.java
@@ -0,0 +1,473 @@
+
+
+/*
+ * Class: JohnsonSLDist
+ * Description: Johnson S_L distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since July 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN></EM> distribution.
+ * It has shape parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> > 0</SPAN>, location parameter
+ * <SPAN CLASS="MATH"><I>ξ</I></SPAN>, and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Denoting
+ * <SPAN CLASS="MATH"><I>t</I> = (<I>x</I> - <I>ξ</I>)/<I>λ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>z</I> = <I>γ</I> + <I>δ</I>ln(<I>t</I>)</SPAN>,
+ * the distribution has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>δe</I><SUP>-z<SUP>2</SUP>/2</SUP>/(<I>λt</I>(2π)<SUP>1/2</SUP>), for <I>ξ</I> < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * and distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>Φ</I>(<I>z</I>), for <I>ξ</I> < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard normal distribution function.
+ * The inverse distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>ξ</I> + <I>λe</I><SUP>v(u)</SUP>, for 0 <= <I>u</I> <= 1,
+ * </DIV><P></P>
+ * where
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>v</I>(<I>u</I>) = [<I>Φ</I><SUP>-1</SUP>(<I>u</I>) - <I>γ</I>]/<I>δ</I>.
+ * </DIV><P></P>
+ *
+ * <P>
+ * Without loss of generality, one may choose
+ * <SPAN CLASS="MATH"><I>γ</I> = 0</SPAN> or <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>.
+ *
+ */
+public class JohnsonSLDist extends JohnsonSystem {
+
+ private static class Function implements MathFunction
+ {
+ // To find value of t > 0 in (Johnson 1949, eq. 16)
+ protected double a;
+
+ public Function (double sb1) {
+ a = sb1;
+ }
+
+ public double evaluate (double t) {
+ return (t*t*t - 3*t - a);
+ }
+ }
+
+
+ private static double[] initPar (double[] x, int n, double xmin)
+ {
+ // Use moments to estimate initial values of params as input to MLE
+ // (Johnson 1949, Biometrika 36, p. 149)
+ int j;
+ double sum = 0.0;
+ for (j = 0; j < n; j++) {
+ sum += x[j];
+ }
+ double mean = sum / n;
+
+ double v;
+ double sum3 = 0.0;
+ sum = 0;
+ for (j = 0; j < n; j++) {
+ v = x[j] - mean;
+ sum += v*v;
+ sum3 += v*v*v;
+ }
+ double m2 = sum / n;
+ double m3 = sum3 / n;
+
+ v = m3 / Math.pow (m2, 1.5);
+ Function f = new Function (v);
+ double t0 = 0;
+ double tlim = Math.cbrt (v);
+ if (tlim <= 0) {
+ t0 = tlim;
+ tlim = 10;
+ }
+ double t = RootFinder.brentDekker (t0, tlim, f, 1e-5);
+ if (t <= 0)
+ throw new UnsupportedOperationException("t <= 0; no MLE");
+ double xi = mean - Math.sqrt(m2 / t);
+ if (xi >= xmin)
+ // throw new UnsupportedOperationException("xi >= xmin; no MLE");
+ xi = xmin - 1.0e-1;
+ v = 1 + m2 / ((mean - xi)*(mean - xi));
+ double delta = 1.0 / Math.sqrt((Math.log(v)));
+ v = Math.sqrt(v);
+ double lambda = (mean - xi) / v;
+ double[] param = new double[3];
+ param[0] = delta;
+ param[1] = xi;
+ param[2] = lambda;
+ return param;
+ }
+
+
+ private static class Optim implements Uncmin_methods
+ {
+ // minimizes the loglikelihood function
+ private int n;
+ private double[] X;
+ private double xmin; // min{X_j}
+ private static final double BARRIER = 1.0e100;
+
+ public Optim (double[] X, int n, double xmin) {
+ this.n = n;
+ this.X = X;
+ this.xmin = xmin;
+ }
+
+ public double f_to_minimize (double[] par) {
+ // par = [0, delta, xi, lambda]
+ // arrays in Uncmin starts at index 1; par[0] is unused
+ double delta = par[1];
+ double xi = par[2];
+ double lambda = par[3];
+ if (delta <= 0.0 || lambda <= 0.0) // barrier at 0
+ return BARRIER;
+ if (xi >= xmin)
+ return BARRIER;
+
+ double loglam = Math.log(lambda);
+ double v, z;
+ double sumv = 0.0;
+ double sumz = 0.0;
+ for (int j = 0; j < n; j++) {
+ v = Math.log (X[j] - xi);
+ z = delta * (v - loglam);
+ sumv += v;
+ sumz += z*z;
+ }
+
+ return sumv + sumz / 2.0 - n *Math.log(delta);
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+
+
+
+ /**
+ * Same as {@link #JohnsonSLDist(double,double,double,double) JohnsonSLDist}
+ * <TT>(gamma, delta, 0, 1)</TT>.
+ *
+ */
+ public JohnsonSLDist (double gamma, double delta) {
+ this (gamma, delta, 0, 1);
+ }
+
+
+ /**
+ * Constructs a <TT>JohnsonSLDist</TT> object
+ * with shape parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>,
+ * location parameter <SPAN CLASS="MATH"><I>ξ</I></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public JohnsonSLDist (double gamma, double delta,
+ double xi, double lambda) {
+ super (gamma, delta, xi, lambda);
+ setLastParams(xi);
+ }
+
+
+ private void setLastParams(double xi) {
+ supportA = xi;
+ }
+
+ public double density (double x) {
+ return density (gamma, delta, xi, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (gamma, delta, xi, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (gamma, delta, xi, lambda, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (gamma, delta, xi, lambda, u);
+ }
+
+ public double getMean() {
+ return JohnsonSLDist.getMean (gamma, delta, xi, lambda);
+ }
+
+ public double getVariance() {
+ return JohnsonSLDist.getVariance (gamma, delta, xi, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return JohnsonSLDist.getStandardDeviation (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Returns the density function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ */
+ public static double density (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ if (x <= xi)
+ return 0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y);
+ if (z >= 1.0e10)
+ return 0;
+ return delta * Math.exp (-z*z/2.0)/(lambda*y*Math.sqrt (2.0*Math.PI));
+ }
+
+
+ /**
+ * Returns the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double cdf (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ if (x <= xi)
+ return 0.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y);
+ return NormalDist.cdf01 (z);
+ }
+
+
+ /**
+ * Returns the complementary distribution function <SPAN CLASS="MATH">1 - <I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double barF (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ if (x <= xi)
+ return 1.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y);
+ return NormalDist.barF01 (z);
+ }
+
+
+ /**
+ * Returns the inverse distribution function <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ *
+ */
+ public static double inverseF (double gamma, double delta,
+ double xi, double lambda, double u) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return xi;
+
+ double z = NormalDist.inverseF01 (u);
+ double t = (z - gamma)/delta;
+ if (t >= Num.DBL_MAX_EXP*Num.LN2)
+ return Double.POSITIVE_INFINITY;
+ if (t <= Num.DBL_MIN_EXP*Num.LN2)
+ return xi;
+
+ return xi + lambda * Math.exp(t);
+ }
+
+
+ /**
+ * Estimates the parameters <SPAN CLASS="MATH">(<I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I>)</SPAN> of the
+ * <SPAN CLASS="textit">Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN></SPAN> distribution using the maximum likelihood method,
+ * from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ * The estimates are returned in a 4-element array in the order
+ * [0, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>] (with <SPAN CLASS="MATH"><I>γ</I></SPAN> always set to 0).
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [0, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ int j;
+ double xmin = Double.MAX_VALUE;
+ for (j = 0; j < n; j++) {
+ if (x[j] < xmin)
+ xmin = x[j];
+ }
+ double[] paramIn = new double[3];
+ paramIn = initPar(x, n, xmin);
+ double[] paramOpt = new double[4];
+ for (int i = 0; i < 3; i++)
+ paramOpt[i+1] = paramIn[i];
+
+ Optim system = new Optim (x, n, xmin);
+
+ double[] xpls = new double[4];
+ double[] fpls = new double[4];
+ double[] gpls = new double[4];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[4][4];
+ double[] udiag = new double[4];
+
+ Uncmin_f77.optif0_f77 (3, paramOpt, system, xpls, fpls, gpls,
+ itrcmd, a, udiag);
+
+ double[] param = new double[4];
+ param[0] = 0;
+ for (int i = 1; i <= 3; i++)
+ param[i] = xpls[i];
+ return param;
+ }
+
+
+ /**
+ * Creates a new instance of a <SPAN CLASS="textit">Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN></SPAN> distribution with
+ * parameters 0, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> over the interval
+ * <SPAN CLASS="MATH">[<I>ξ</I>,∞]</SPAN> estimated using the maximum likelihood
+ * method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static JohnsonSLDist getInstanceFromMLE (double[] x, int n) {
+ double param[] = getMLE (x, n);
+ return new JohnsonSLDist (0, param[0], param[1], param[2]);
+ }
+
+
+ /**
+ * Returns the mean
+ * of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN> distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN> distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>ξ</I> + <I>λe</I><SUP>1/2<I>δ</I><SUP>2</SUP>-<I>γ</I>/<I>δ</I></SUP></SPAN>
+ *
+ */
+ public static double getMean (double gamma, double delta,
+ double xi, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double t = 1.0 / (2.0 * delta * delta) - gamma / delta;
+ return (xi + lambda * Math.exp(t));
+ }
+
+
+ /**
+ * Returns the variance
+ * of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN> distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN> distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>λ</I><SUP>2</SUP>(<I>e</I><SUP>1/<I>δ</I><SUP>2</SUP></SUP> -1)<I>e</I><SUP>1/<I>δ</I><SUP>2</SUP>-2<I>γ</I>/<I>δ</I></SUP></SPAN>
+ *
+ */
+ public static double getVariance (double gamma, double delta,
+ double xi, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double t = 1.0 / (delta * delta) - 2 * gamma / delta;
+ return lambda * lambda * Math.exp(t) * (Math.exp(1.0 / (delta * delta)) - 1);
+ }
+
+
+ /**
+ * Returns the standard deviation of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN>
+ * distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN> distribution
+ *
+ */
+ public static double getStandardDeviation (double gamma, double delta,
+ double xi, double lambda) {
+ return Math.sqrt (JohnsonSLDist.getVariance (gamma, delta, xi, lambda));
+ }
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ */
+ public void setParams (double gamma, double delta,
+ double xi, double lambda) {
+ setParams0(gamma, delta, xi, lambda);
+ setLastParams(xi);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSLDist.tex b/source/umontreal/iro/lecuyer/probdist/JohnsonSLDist.tex
new file mode 100644
index 0000000..1669bda
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSLDist.tex
@@ -0,0 +1,480 @@
+\defmodule {JohnsonSLDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Johnson $S_L$\/} distribution\latex{ (see \cite{tJOH49a,tJOH95a})}.
+It has shape parameters $\gamma$ and $\delta > 0$, location parameter
+$\xi$, and scale parameter $\lambda > 0$.
+Denoting $t=(x-\xi)/\lambda$ and $z = \gamma + \delta\ln(t)$,
+the distribution has density
+%
+\eq
+ f(x) =\html{\delta e^{-z^2/2}/(}\latex{\frac {\delta e^{-z^2/2}}}
+ {\lambda t \sqrt{2\pi}} \html{)},
+ \qquad \mbox{for } \xi < x < \infty,
+\endeq
+%
+and distribution function
+\eq
+ F(x) = \Phi(z), \qquad \mbox{for } \xi < x < \infty,
+\endeq
+where $\Phi$ is the standard normal distribution function.
+The inverse distribution function is
+\eq
+ F^{-1} (u) = \xi + \lambda e^{v(u)}, \qquad\mbox{for } 0 \le u \le 1,
+\endeq
+where
+\eq
+ v(u) = [\Phi^{-1}(u) - \gamma]/\delta.
+\endeq
+
+Without loss of generality, one may choose $\gamma = 0$ or $\lambda=1$.
+% This class relies on the methods \clsexternalmethod{}{NormalDist}{cdf01}{} and
+% \clsexternalmethod{}{NormalDist}{inverseF01}{} of \class{NormalDist} to
+% approximate $\Phi$ and $\Phi^{-1}$.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSLDist
+ * Description: Johnson S_L distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since July 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+\end{hide}
+
+public class JohnsonSLDist extends JohnsonSystem\begin{hide} {
+
+ private static class Function implements MathFunction
+ {
+ // To find value of t > 0 in (Johnson 1949, eq. 16)
+ protected double a;
+
+ public Function (double sb1) {
+ a = sb1;
+ }
+
+ public double evaluate (double t) {
+ return (t*t*t - 3*t - a);
+ }
+ }
+
+
+ private static double[] initPar (double[] x, int n, double xmin)
+ {
+ // Use moments to estimate initial values of params as input to MLE
+ // (Johnson 1949, Biometrika 36, p. 149)
+ int j;
+ double sum = 0.0;
+ for (j = 0; j < n; j++) {
+ sum += x[j];
+ }
+ double mean = sum / n;
+
+ double v;
+ double sum3 = 0.0;
+ sum = 0;
+ for (j = 0; j < n; j++) {
+ v = x[j] - mean;
+ sum += v*v;
+ sum3 += v*v*v;
+ }
+ double m2 = sum / n;
+ double m3 = sum3 / n;
+
+ v = m3 / Math.pow (m2, 1.5);
+ Function f = new Function (v);
+ double t0 = 0;
+ double tlim = Math.cbrt (v);
+ if (tlim <= 0) {
+ t0 = tlim;
+ tlim = 10;
+ }
+ double t = RootFinder.brentDekker (t0, tlim, f, 1e-5);
+ if (t <= 0)
+ throw new UnsupportedOperationException("t <= 0; no MLE");
+ double xi = mean - Math.sqrt(m2 / t);
+ if (xi >= xmin)
+ // throw new UnsupportedOperationException("xi >= xmin; no MLE");
+ xi = xmin - 1.0e-1;
+ v = 1 + m2 / ((mean - xi)*(mean - xi));
+ double delta = 1.0 / Math.sqrt((Math.log(v)));
+ v = Math.sqrt(v);
+ double lambda = (mean - xi) / v;
+ double[] param = new double[3];
+ param[0] = delta;
+ param[1] = xi;
+ param[2] = lambda;
+ return param;
+ }
+
+
+ private static class Optim implements Uncmin_methods
+ {
+ // minimizes the loglikelihood function
+ private int n;
+ private double[] X;
+ private double xmin; // min{X_j}
+ private static final double BARRIER = 1.0e100;
+
+ public Optim (double[] X, int n, double xmin) {
+ this.n = n;
+ this.X = X;
+ this.xmin = xmin;
+ }
+
+ public double f_to_minimize (double[] par) {
+ // par = [0, delta, xi, lambda]
+ // arrays in Uncmin starts at index 1; par[0] is unused
+ double delta = par[1];
+ double xi = par[2];
+ double lambda = par[3];
+ if (delta <= 0.0 || lambda <= 0.0) // barrier at 0
+ return BARRIER;
+ if (xi >= xmin)
+ return BARRIER;
+
+ double loglam = Math.log(lambda);
+ double v, z;
+ double sumv = 0.0;
+ double sumz = 0.0;
+ for (int j = 0; j < n; j++) {
+ v = Math.log (X[j] - xi);
+ z = delta * (v - loglam);
+ sumv += v;
+ sumz += z*z;
+ }
+
+ return sumv + sumz / 2.0 - n *Math.log(delta);
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public JohnsonSLDist (double gamma, double delta)\begin{hide} {
+ this (gamma, delta, 0, 1);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Same as \method{JohnsonSLDist}{double,double,double,double}
+ {\texttt{(gamma, delta, 0, 1)}}.
+ \end{tabb}
+\begin{code}
+
+ public JohnsonSLDist (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ super (gamma, delta, xi, lambda);
+ setLastParams(xi);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{JohnsonSLDist} object
+ with shape parameters $\gamma$ and $\delta$,
+ location parameter $\xi$, and scale parameter $\lambda$.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}
+\begin{code}
+
+ private void setLastParams(double xi) {
+ supportA = xi;
+ }
+
+ public double density (double x) {
+ return density (gamma, delta, xi, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (gamma, delta, xi, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (gamma, delta, xi, lambda, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (gamma, delta, xi, lambda, u);
+ }
+
+ public double getMean() {
+ return JohnsonSLDist.getMean (gamma, delta, xi, lambda);
+ }
+
+ public double getVariance() {
+ return JohnsonSLDist.getVariance (gamma, delta, xi, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return JohnsonSLDist.getStandardDeviation (gamma, delta, xi, lambda);
+ }
+\end{code}
+\end{hide}\begin{code}
+
+ public static double density (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ if (x <= xi)
+ return 0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y);
+ if (z >= 1.0e10)
+ return 0;
+ return delta * Math.exp (-z*z/2.0)/(lambda*y*Math.sqrt (2.0*Math.PI));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the density function $f(x)$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ if (x <= xi)
+ return 0.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y);
+ return NormalDist.cdf01 (z);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the distribution function $F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ if (x <= xi)
+ return 1.0;
+ double y = (x - xi)/lambda;
+ double z = gamma + delta*Math.log (y);
+ return NormalDist.barF01 (z);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the complementary distribution function $1-F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double gamma, double delta,
+ double xi, double lambda, double u)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return xi;
+
+ double z = NormalDist.inverseF01 (u);
+ double t = (z - gamma)/delta;
+ if (t >= Num.DBL_MAX_EXP*Num.LN2)
+ return Double.POSITIVE_INFINITY;
+ if (t <= Num.DBL_MIN_EXP*Num.LN2)
+ return xi;
+
+ return xi + lambda * Math.exp(t);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the inverse distribution function $F^{-1}(u)$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ int j;
+ double xmin = Double.MAX_VALUE;
+ for (j = 0; j < n; j++) {
+ if (x[j] < xmin)
+ xmin = x[j];
+ }
+ double[] paramIn = new double[3];
+ paramIn = initPar(x, n, xmin);
+ double[] paramOpt = new double[4];
+ for (int i = 0; i < 3; i++)
+ paramOpt[i+1] = paramIn[i];
+
+ Optim system = new Optim (x, n, xmin);
+
+ double[] xpls = new double[4];
+ double[] fpls = new double[4];
+ double[] gpls = new double[4];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[4][4];
+ double[] udiag = new double[4];
+
+ Uncmin_f77.optif0_f77 (3, paramOpt, system, xpls, fpls, gpls,
+ itrcmd, a, udiag);
+
+ double[] param = new double[4];
+ param[0] = 0;
+ for (int i = 1; i <= 3; i++)
+ param[i] = xpls[i];
+ return param;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\gamma$, $\delta$, $\xi$, $\lambda)$ of the
+ \emph{Johnson $S_L$} distribution using the maximum likelihood method,
+ from the $n$ observations $x[i]$, $i = 0, 1,\ldots, n-1$.
+ The estimates are returned in a 4-element array in the order
+ [0, $\delta$, $\xi$, $\lambda$] (with $\gamma$ always set to 0).
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [0, $\delta$, $\xi$, $\lambda$]}
+\end{htmlonly}
+\begin{code}
+
+ public static JohnsonSLDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double param[] = getMLE (x, n);
+ return new JohnsonSLDist (0, param[0], param[1], param[2]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a \emph{Johnson $S_L$} distribution with
+ parameters 0, $\delta$, $\xi$ and
+ $\lambda$ over the interval $[\xi,\infty]$ estimated using the maximum likelihood
+ method based on the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double t = 1.0 / (2.0 * delta * delta) - gamma / delta;
+ return (xi + lambda * Math.exp(t));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean
+\begin{latexonly}
+ \[E[X] = \xi + \lambda e^{1/2\delta^2 - \gamma/\delta}\]
+\end{latexonly}%
+ of the Johnson $S_L$ distribution with parameters $\gamma$, $\delta$, $\xi$
+ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Johnson $S_L$ distribution
+ $E[X] = \xi + \lambda e^{1/2\delta^2 - \gamma/\delta}$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double t = 1.0 / (delta * delta) - 2 * gamma / delta;
+ return lambda * lambda * Math.exp(t) * (Math.exp(1.0 / (delta * delta)) - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance
+\begin{latexonly}
+ \[\mbox{Var}[X] = \lambda^2 \left(e^{1/\delta^2} - 1\right)
+ e^{1/\delta^2 - 2\gamma/\delta}\]
+\end{latexonly}%
+ of the Johnson $S_L$ distribution with parameters $\gamma$, $\delta$, $\xi$
+ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Johnson $S_L$ distribution
+ $\mbox{Var}[X] = \lambda^2 \left(e^{1/\delta^2} - 1\right)
+ e^{1/\delta^2 - 2\gamma/\delta}$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ return Math.sqrt (JohnsonSLDist.getVariance (gamma, delta, xi, lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation of the Johnson $S_L$
+ distribution with parameters $\gamma$, $\delta$, $\xi$, $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Johnson $S_L$ distribution}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ setParams0(gamma, delta, xi, lambda);
+ setLastParams(xi);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $\gamma$, $\delta$, $\xi$ and
+ $\lambda$ for this object.
+ \end{tabb}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSUDist.java b/source/umontreal/iro/lecuyer/probdist/JohnsonSUDist.java
new file mode 100644
index 0000000..87e49c3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSUDist.java
@@ -0,0 +1,304 @@
+
+
+/*
+ * Class: JohnsonSUDist
+ * Description: Johnson S_U distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN></EM> distribution.
+ * It has shape parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> > 0</SPAN>, location parameter
+ * <SPAN CLASS="MATH"><I>ξ</I></SPAN>, and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Denoting
+ * <SPAN CLASS="MATH"><I>t</I> = (<I>x</I> - <I>ξ</I>)/<I>λ</I></SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>z</I> = <I>γ</I> + <I>δ</I>ln(<I>t</I> + (t^2 + 1)<SUP>1/2</SUP>)</SPAN>,
+ * the distribution has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>δe</I><SUP>-z<SUP>2</SUP>/2</SUP>/(<I>λ</I>(2π(t^2 + 1))<SUP>1/2</SUP>), for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * and distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>Φ</I>(<I>z</I>), for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard normal distribution function.
+ * The inverse distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>ξ</I> + <I>λ</I>sinh(<I>v</I>(<I>u</I>)), for 0 <= <I>u</I> <= 1,
+ * </DIV><P></P>
+ * where
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>v</I>(<I>u</I>) = [<I>Φ</I><SUP>-1</SUP>(<I>u</I>) - <I>γ</I>]/<I>δ</I>.
+ * </DIV><P></P>
+ *
+ * <P>
+ * This class relies on the methods {@link NormalDist#cdf01 NormalDist.cdf01} and
+ * {@link NormalDist#inverseF01 NormalDist.inverseF01} of {@link NormalDist} to
+ * approximate <SPAN CLASS="MATH"><I>Φ</I></SPAN> and <SPAN CLASS="MATH"><I>Φ</I><SUP>-1</SUP></SPAN>.
+ *
+ */
+public class JohnsonSUDist extends JohnsonSystem {
+
+ private static double calcR (double a, double b, double x) {
+ /* ** cette fonction calcule
+ r = z + sqrt(z*z + 1)
+ en utilisant un algorithme stable
+ ***/
+
+ double z = (x - a)/b;
+
+ double s = Math.abs(z);
+ if (s < 1.0e20)
+ s = Math.sqrt (z * z + 1.0);
+
+ // compute r = z + sqrt (z * z + 1)
+ double r;
+ if (z >= 0.0)
+ r = s + z;
+ else
+ r = 1.0/(s - z);
+
+ return r;
+ }
+
+
+
+ /**
+ * Same as {@link #JohnsonSUDist(double,double,double,double) JohnsonSUDist}
+ * <TT>(gamma, delta, 0, 1)</TT>.
+ *
+ */
+ public JohnsonSUDist (double gamma, double delta) {
+ this (gamma, delta, 0, 1);
+ }
+
+
+ /**
+ * Constructs a <TT>JohnsonSUDist</TT> object
+ * with shape parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>,
+ * location parameter <SPAN CLASS="MATH"><I>ξ</I></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public JohnsonSUDist (double gamma, double delta,
+ double xi, double lambda) {
+ super (gamma, delta, xi, lambda);
+ }
+
+
+ public double density (double x) {
+ return density (gamma, delta, xi, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (gamma, delta, xi, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (gamma, delta, xi, lambda, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (gamma, delta, xi, lambda, u);
+ }
+
+ public double getMean() {
+ return JohnsonSUDist.getMean (gamma, delta, xi, lambda);
+ }
+
+ public double getVariance() {
+ return JohnsonSUDist.getVariance (gamma, delta, xi, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return JohnsonSUDist.getStandardDeviation (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Returns the density function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ */
+ public static double density (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ double r = calcR (xi, lambda, x);
+ if (r <= 0.0)
+ return 0.0;
+ double z = gamma + delta*Math.log (r);
+ double y = (x - xi)/lambda;
+ if (z >= 1.0e10)
+ return 0;
+ return delta/(lambda*Math.sqrt (2.0*Math.PI)*Math.sqrt (y*y + 1.0))*
+ Math.exp (-z*z/2.0);
+ }
+
+
+ /**
+ * Returns the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double cdf (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ double r = calcR (xi, lambda, x);
+ if (r > 0.0)
+ return NormalDist.cdf01 (gamma + delta*Math.log (r));
+ else
+ return 0.0;
+ }
+
+
+ /**
+ * Returns the complementary distribution function <SPAN CLASS="MATH">1 - <I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double barF (double gamma, double delta,
+ double xi, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double r = calcR (xi, lambda, x);
+ if (r > 0.0)
+ return NormalDist.barF01 (gamma + delta * Math.log (r));
+ else
+ return 1.0;
+ }
+
+
+ /**
+ * Returns the inverse distribution function <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ *
+ */
+ public static double inverseF (double gamma, double delta,
+ double xi, double lambda, double u) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+
+ double z = NormalDist.inverseF01 (u);
+ double v = (z - gamma)/delta;
+ if (v >= Num.DBL_MAX_EXP*Num.LN2)
+ return Double.POSITIVE_INFINITY;
+ if (v <= Num.LN2*Num.DBL_MIN_EXP)
+ return Double.NEGATIVE_INFINITY;
+
+ return xi + lambda * Math.sinh(v);
+ }
+
+
+ /**
+ * Returns the mean
+ * of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN> distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN> distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>ξ</I> - <I>λ</I>exp<SUP>1/(2<I>δ</I><SUP>2</SUP>)</SUP><I>sinh</I>(<I>γ</I>/<I>δ</I>)</SPAN>
+ *
+ */
+ public static double getMean (double gamma, double delta,
+ double xi, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ return (xi - (lambda * Math.exp(1.0 / (2.0 * delta * delta)) *
+ Math.sinh(gamma / delta)));
+ }
+
+
+ /**
+ * Returns the variance
+ * of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN> distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN> distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (<I>λ</I><SUP>2</SUP>/2)(exp<SUP>1/<I>δ</I><SUP>2</SUP></SUP> -1)(exp<SUP>1/<I>δ</I><SUP>2</SUP></SUP><I>cosh</I>(2<I>γ</I>/<I>δ</I>) + 1)</SPAN>
+ *
+ */
+ public static double getVariance (double gamma, double delta,
+ double xi, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double omega2 = Math.exp(1 / (delta * delta));
+ return ((omega2 - 1) * (omega2 * Math.cosh(2 * gamma / delta) + 1) / 2 * lambda * lambda);
+ }
+
+
+ /**
+ * Returns the standard deviation of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN>
+ * distribution with parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN> distribution
+ *
+ */
+ public static double getStandardDeviation (double gamma, double delta,
+ double xi, double lambda) {
+ return Math.sqrt (JohnsonSUDist.getVariance (gamma, delta, xi, lambda));
+ }
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ */
+ public void setParams (double gamma, double delta,
+ double xi, double lambda) {
+ setParams0(gamma, delta, xi, lambda);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSUDist.tex b/source/umontreal/iro/lecuyer/probdist/JohnsonSUDist.tex
new file mode 100644
index 0000000..05ebe29
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSUDist.tex
@@ -0,0 +1,314 @@
+\defmodule {JohnsonSUDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Johnson $S_U$\/} distribution\latex{ (see \cite[page 316]{sLAW00a})}.
+It has shape parameters $\gamma$ and $\delta > 0$, location parameter
+$\xi$, and scale parameter $\lambda > 0$.
+Denoting $t=(x-\xi)/\lambda$ and
+$z = \gamma + \delta\ln\left(t + \sqrt{t^2 + 1}\right)$,
+the distribution has density
+%
+\eq
+ f(x) =\html{\delta e^{-z^2/2} /(}\latex{\frac {\delta e^{-z^2/2}}} {\lambda\sqrt{2\pi(t^2 + 1)}}
+ \html{)},
+ \qquad \mbox{for } -\infty < x < \infty,
+\endeq
+%
+and distribution function
+\eq
+ F(x) = \Phi(z), \qquad \mbox{for } -\infty < x < \infty,
+\endeq
+where $\Phi$ is the standard normal distribution function.
+The inverse distribution function is
+\eq
+ F^{-1} (u) = \xi + \lambda \sinh(v(u)), \qquad\mbox{for } 0 \le u \le 1,
+\endeq
+where
+\eq
+ v(u) = [\Phi^{-1}(u) - \gamma]/\delta.
+\endeq
+
+This class relies on the methods \clsexternalmethod{}{NormalDist}{cdf01}{} and
+ \clsexternalmethod{}{NormalDist}{inverseF01}{} of \class{NormalDist} to
+ approximate $\Phi$ and $\Phi^{-1}$.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSUDist
+ * Description: Johnson S_U distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class JohnsonSUDist extends JohnsonSystem\begin{hide} {
+
+ private static double calcR (double a, double b, double x) {
+ /*** cette fonction calcule
+ r = z + sqrt(z*z + 1)
+ en utilisant un algorithme stable
+ ***/
+
+ double z = (x - a)/b;
+
+ double s = Math.abs(z);
+ if (s < 1.0e20)
+ s = Math.sqrt (z * z + 1.0);
+
+ // compute r = z + sqrt (z * z + 1)
+ double r;
+ if (z >= 0.0)
+ r = s + z;
+ else
+ r = 1.0/(s - z);
+
+ return r;
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public JohnsonSUDist (double gamma, double delta)\begin{hide} {
+ this (gamma, delta, 0, 1);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Same as \method{JohnsonSUDist}{double,double,double,double}
+ {\texttt{(gamma, delta, 0, 1)}}.
+ \end{tabb}
+\begin{code}
+
+ public JohnsonSUDist (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ super (gamma, delta, xi, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{JohnsonSUDist} object
+ with shape parameters $\gamma$ and $\delta$,
+ location parameter $\xi$, and scale parameter $\lambda$.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}
+\begin{code}
+
+ public double density (double x) {
+ return density (gamma, delta, xi, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (gamma, delta, xi, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (gamma, delta, xi, lambda, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (gamma, delta, xi, lambda, u);
+ }
+
+ public double getMean() {
+ return JohnsonSUDist.getMean (gamma, delta, xi, lambda);
+ }
+
+ public double getVariance() {
+ return JohnsonSUDist.getVariance (gamma, delta, xi, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return JohnsonSUDist.getStandardDeviation (gamma, delta, xi, lambda);
+ }
+\end{code}
+\end{hide}\begin{code}
+
+ public static double density (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ double r = calcR (xi, lambda, x);
+ if (r <= 0.0)
+ return 0.0;
+ double z = gamma + delta*Math.log (r);
+ double y = (x - xi)/lambda;
+ if (z >= 1.0e10)
+ return 0;
+ return delta/(lambda*Math.sqrt (2.0*Math.PI)*Math.sqrt (y*y + 1.0))*
+ Math.exp (-z*z/2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the density function $f(x)$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ double r = calcR (xi, lambda, x);
+ if (r > 0.0)
+ return NormalDist.cdf01 (gamma + delta*Math.log (r));
+ else
+ return 0.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the distribution function $F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double gamma, double delta,
+ double xi, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double r = calcR (xi, lambda, x);
+ if (r > 0.0)
+ return NormalDist.barF01 (gamma + delta * Math.log (r));
+ else
+ return 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the complementary distribution function $1-F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double gamma, double delta,
+ double xi, double lambda, double u)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+
+ double z = NormalDist.inverseF01 (u);
+ double v = (z - gamma)/delta;
+ if (v >= Num.DBL_MAX_EXP*Num.LN2)
+ return Double.POSITIVE_INFINITY;
+ if (v <= Num.LN2*Num.DBL_MIN_EXP)
+ return Double.NEGATIVE_INFINITY;
+
+ return xi + lambda * Math.sinh(v);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the inverse distribution function $F^{-1}(u)$.
+ \end{tabb}
+\begin{code}
+
+ public static double getMean (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ return (xi - (lambda * Math.exp(1.0 / (2.0 * delta * delta)) *
+ Math.sinh(gamma / delta)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean
+\begin{latexonly}
+ \[E[X] = \xi - \lambda e^{1/(2\delta^2)} \sinh({\gamma}/{\delta})\]
+\end{latexonly}
+ of the Johnson $S_U$ distribution with parameters $\gamma$, $\delta$, $\xi$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Johnson $S_U$ distribution
+ $E[X] = \xi - \lambda\exp^{1 / (2\delta^2)} sinh(\gamma / \delta)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+
+ double omega2 = Math.exp(1 / (delta * delta));
+ return ((omega2 - 1) * (omega2 * Math.cosh(2 * gamma / delta) + 1) / 2 * lambda * lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance
+\begin{latexonly}
+ \[\mbox{Var}[X] = \frac{\lambda^2}{2}
+ \left(e^{1/\delta^2} - 1\right)\left(e^{1/\delta^2}
+ \cosh(2 {\gamma}/{\delta}) + 1\right)\]
+\end{latexonly}
+ of the Johnson $S_U$ distribution with parameters $\gamma$, $\delta$, $\xi$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Johnson $S_U$ distribution
+ $\mbox{Var}[X] = (\lambda^2/2)(\exp^{1/\delta^2} - 1)(\exp^{1/\delta^2} cosh(2 \gamma / \delta) + 1)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ return Math.sqrt (JohnsonSUDist.getVariance (gamma, delta, xi, lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation of the Johnson $S_U$
+ distribution with parameters $\gamma$, $\delta$, $\xi$, $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Johnson $S_U$ distribution}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ setParams0(gamma, delta, xi, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $\gamma$, $\delta$, $\xi$ and
+ $\lambda$ for this object.
+ \end{tabb}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSystem.java b/source/umontreal/iro/lecuyer/probdist/JohnsonSystem.java
new file mode 100644
index 0000000..28df0b1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSystem.java
@@ -0,0 +1,161 @@
+
+
+/*
+ * Class: JohnsonSystem
+ * Description: Johnson system of distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since july 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * This class contains common parameters and methods for
+ * the <EM>Johnson</EM> system of distributions
+ * with shape parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> > 0</SPAN>, location parameter <SPAN CLASS="MATH"><I>ξ</I></SPAN>,
+ * and scale parameter <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Denoting
+ * <SPAN CLASS="MATH"><I>T</I> = (<I>X</I> - <I>ξ</I>)/<I>λ</I></SPAN>, the variable
+ * <SPAN CLASS="MATH"><I>Z</I> = <I>γ</I> + <I>δf</I> (<I>T</I>)</SPAN>
+ * is a standard normal variable, where <SPAN CLASS="MATH"><I>f</I> (<I>t</I>)</SPAN> is one of the following transformations:
+ *
+ * <P>
+ * <DIV ALIGN="CENTER">
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="CENTER">Family</TD>
+ * <TD ALIGN="CENTER"><SPAN CLASS="MATH"><I>f</I> (<I>t</I>)</SPAN></TD>
+ * <TD ALIGN="CENTER"> </TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER"><SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN></TD>
+ * <TD ALIGN="CENTER"><SPAN CLASS="MATH">ln(<I>t</I>)</SPAN></TD>
+ * <TD ALIGN="CENTER"> </TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER"><SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN></TD>
+ * <TD ALIGN="CENTER">
+ * <SPAN CLASS="MATH">ln(<I>t</I>/(1 - <I>t</I>))</SPAN></TD>
+ * <TD ALIGN="CENTER"> </TD>
+ * </TR>
+ * <TR><TD ALIGN="CENTER"><SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN></TD>
+ * <TD ALIGN="CENTER">
+ * <SPAN CLASS="MATH">ln(<I>t</I> + (1 + t^2)<SUP>1/2</SUP>)</SPAN></TD>
+ * <TD ALIGN="CENTER"> </TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ *
+ */
+abstract class JohnsonSystem extends ContinuousDistribution {
+ protected double gamma;
+ protected double delta;
+ protected double xi;
+ protected double lambda;
+
+
+ /**
+ * Constructs a <TT>JohnsonSystem</TT> object
+ * with shape parameters
+ * <SPAN CLASS="MATH"><I>γ</I> = <texttt>gamma</texttt></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> = <texttt>delta</texttt></SPAN>,
+ * location parameter
+ * <SPAN CLASS="MATH"><I>ξ</I> = <texttt>xi</texttt></SPAN>, and scale parameter
+ *
+ * <SPAN CLASS="MATH"><I>λ</I> = <texttt>lambda</texttt></SPAN>.
+ *
+ */
+ protected JohnsonSystem (double gamma, double delta, double xi,
+ double lambda) {
+ setParams0 (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public double getGamma() {
+ return gamma;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>ξ</I></SPAN>.
+ *
+ */
+ public double getXi() {
+ return xi;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ protected void setParams0(double gamma, double delta, double xi,
+ double lambda) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ this.gamma = gamma;
+ this.delta = delta;
+ this.xi = xi;
+ this.lambda = lambda;
+ }
+
+
+ /**
+ * Return an array containing the parameters of the current distribution.
+ * This array is put in regular order: [<SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {gamma, delta, xi, lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : gamma = " + gamma + ", delta = " + delta + ", xi = " + xi + ", lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/JohnsonSystem.tex b/source/umontreal/iro/lecuyer/probdist/JohnsonSystem.tex
new file mode 100644
index 0000000..0c9fd42
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/JohnsonSystem.tex
@@ -0,0 +1,157 @@
+\defmodule {JohnsonSystem}
+
+This class contains common parameters and methods for
+the {\em Johnson} system of distributions \cite{tJOH49a,tJOH95a}
+with shape parameters $\gamma$ and $\delta > 0$, location parameter $\xi$,
+and scale parameter $\lambda>0$.
+Denoting $T=(X-\xi)/\lambda$, the variable $Z = \gamma + \delta f(T)$
+is a standard normal variable, where $f(t)$ is one of the following transformations:
+
+\begin{center}
+\begin{tabular}{|c|c|c|}
+\hline
+ Family & $f(t)$ \\
+\hline
+$S_L$ & $\ln(t)$ \\
+$S_B$ & $\ln(t / (1-t))$ \\
+$S_U$ & $\ln(t + \sqrt{1 + t^2})$ \\
+\hline
+\end{tabular}
+\end{center}
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSystem
+ * Description: Johnson system of distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since july 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+abstract class JohnsonSystem extends ContinuousDistribution\begin{hide} {
+ protected double gamma;
+ protected double delta;
+ protected double xi;
+ protected double lambda;\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ protected JohnsonSystem (double gamma, double delta, double xi,
+ double lambda)\begin{hide} {
+ setParams0 (gamma, delta, xi, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{JohnsonSystem} object
+ with shape parameters $\gamma = \texttt{gamma}$ and $\delta = \texttt{delta}$,
+ location parameter $\xi = \texttt{xi}$, and scale parameter
+ $\lambda = \texttt{lambda}$.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double getGamma()\begin{hide} {
+ return gamma;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\gamma$.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\delta$.
+ \end{tabb}
+\begin{code}
+
+ public double getXi()\begin{hide} {
+ return xi;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\xi$.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\lambda$.
+ \end{tabb}
+\begin{code}
+
+ protected void setParams0(double gamma, double delta, double xi,
+ double lambda)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ this.gamma = gamma;
+ this.delta = delta;
+ this.xi = xi;
+ this.lambda = lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $\gamma$, $\delta$, $\xi$ and
+ $\lambda$.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {gamma, delta, xi, lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameters of the current distribution.
+ This array is put in regular order: [$\gamma$, $\delta$, $\xi$, $\lambda$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : gamma = " + gamma + ", delta = " + delta + ", xi = " + xi + ", lambda = " + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDist.java b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDist.java
new file mode 100644
index 0000000..73d5408
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDist.java
@@ -0,0 +1,462 @@
+
+
+/*
+ * Class: KolmogorovSmirnovDist
+ * Description: Kolmogorov-Smirnov distribution
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * <EM>Kolmogorov-Smirnov</EM> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Given an empirical distribution <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB></SPAN> with <SPAN CLASS="MATH"><I>n</I></SPAN> independent observations and
+ * a continuous distribution <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>, the two-sided statistic is defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>D</I><SUB>n</SUB> = sup<SUB>-∞ <= x <= ∞</SUB>| <I>F</I><SUB>n</SUB>(<I>x</I>) - <I>F</I>(<I>x</I>)| = {<I>D</I><SUB>n</SUB><SUP>+</SUP>, <I>D</I><SUB>n</SUB><SUP>-</SUP>},
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN> and <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN> are the + and <SPAN CLASS="MATH">-</SPAN> statistics as defined in
+ * equations and on page <A HREF="#eq:DNp"><IMG ALIGN="BOTTOM" BORDER="1" ALT="[*]"
+ * SRC="file:/u/simardr/Eric/usr/share/lib/latex2html/icons/crossref.png"></A> of this guide.
+ * This class implements a high precision version of the
+ * distribution
+ * <SPAN CLASS="MATH"><I>P</I>[<I>D</I><SUB>n</SUB> <= <I>x</I>]</SPAN>; it is a Java translation of the <SPAN CLASS="MATH"><I>C</I></SPAN> program
+ * written in. According to its authors, it should give
+ * 13 decimal digits of precision. It is extremely slow
+ * for large values of <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+public class KolmogorovSmirnovDist extends ContinuousDistribution {
+ protected int n;
+ protected static final int NEXACT = 500;
+
+ // For the Durbin matrix algorithm
+ private static final double NORM = 1.0e140;
+ private static final double INORM = 1.0e-140;
+ private static final int LOGNORM = 140;
+
+
+
+ //========================================================================
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+
+
+ /**
+ * Constructs a distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Restriction: <SPAN CLASS="MATH"><I>n</I> >= 1</SPAN>.
+ *
+ */
+ public KolmogorovSmirnovDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ private static double dclem (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+ protected static double densConnue (int n, double x) {
+ if ((x >= 1.0) || (x <= 0.5 / n))
+ return 0.0;
+ if (n == 1)
+ return 2.0;
+
+ if (x <= 1.0 / n) {
+ double w;
+ double t = 2.0 * x * n - 1.0;
+ if (n <= NEXACT) {
+ w = 2.0 * n * n * Num.factoPow (n);
+ w *= Math.pow (t, (double) (n - 1));
+ return w;
+ }
+ w = Num.lnFactorial (n) + (n-1) * Math.log (t/n);
+ return 2*n*Math.exp (w);
+ }
+
+ if (x >= 1.0 - 1.0 / n)
+ return 2.0 * n * Math.pow (1.0 - x, (double) (n - 1));
+
+ return -1.0;
+ }
+
+ /**
+ * Computes the density for the distribution with
+ * parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+ double Res = densConnue(n,x);
+ if (Res != -1.0)
+ return Res;
+
+ double EPS = 1.0 / 200.0;
+ final double D1 = dclem(n, x, EPS);
+ final double D2 = dclem(n, x, 2.0 * EPS);
+ Res = D1 + (D1 - D2) / 3.0;
+ if (Res <= 0.0)
+ return 0.0;
+ return Res;
+ }
+
+
+ /*=========================================================================
+
+ The following implements the Durbin matrix algorithm and was programmed
+ in C by G. Marsaglia, Wai Wan Tsang and Jingbo Wong in C.
+
+ I have translated their program in Java. Only small modifications have
+ been made in their program; the most important is to prevent the return
+ of NAN or infinite values in some regions. (Richard Simard)
+
+ =========================================================================*/
+
+ /*
+ The C program to compute Kolmogorov's distribution
+
+ K(n,d) = Prob(D_n < d), where
+
+ D_n = max(x_1-0/n,x_2-1/n...,x_n-(n-1)/n,1/n-x_1,2/n-x_2,...,n/n-x_n)
+
+ with x_1<x_2,...<x_n a purported set of n independent uniform [0,1)
+ random variables sorted into increasing order.
+ See G. Marsaglia, Wai Wan Tsang and Jingbo Wong,
+ J.Stat.Software, 8, 18, pp 1--4, (2003).
+ */
+
+
+ private static void mMultiply (double[] A, double[] B, double[] C, int m){
+ int i, j, k;
+ double s;
+ for (i = 0; i < m; i++)
+ for (j = 0; j < m; j++) {
+ s = 0.0;
+ for (k = 0; k < m; k++)
+ s += A[i * m + k] * B[k * m + j];
+ C[i * m + j] = s;
+ }
+ }
+
+
+ private static void renormalize (double[] V, int m, int[] p)
+ {
+ int i;
+ for (i = 0; i < m * m; i++)
+ V[i] *= INORM;
+ p[0] += LOGNORM;
+ }
+
+
+ private static void mPower (double[] A, int eA, double[] V, int[] eV,
+ int m, int n)
+ {
+ int i;
+ if (n == 1) {
+ for (i = 0; i < m * m; i++)
+ V[i] = A[i];
+ eV[0] = eA;
+ return;
+ }
+ mPower (A, eA, V, eV, m, n / 2);
+
+ double[] B = new double[m * m];
+ int[] pB = new int[1];
+
+ mMultiply (V, V, B, m);
+ pB[0] = 2 * (eV[0]);
+ if (B[(m / 2) * m + (m / 2)] > NORM)
+ renormalize (B, m, pB);
+
+ if (n % 2 == 0) {
+ for (i = 0; i < m * m; i++)
+ V[i] = B[i];
+ eV[0] = pB[0];
+ } else {
+ mMultiply (A, B, V, m);
+ eV[0] = eA + pB[0];
+ }
+
+ if (V[(m / 2) * m + (m / 2)] > NORM)
+ renormalize (V, m, eV);
+ }
+
+
+ protected static double DurbinMatrix (int n, double d)
+ {
+ int k, m, i, j, g, eH;
+ double h, s;
+ double[] H;
+ double[] Q;
+ int[] pQ;
+
+ //Omit next two lines if you require >7 digit accuracy in the right tail
+ if (false) {
+ s = d * d * n;
+ if (s > 7.24 || (s > 3.76 && n > 99))
+ return 1 - 2 * Math.exp (-(2.000071 + .331 / Math.sqrt (n) +
+ 1.409 / n) * s);
+ }
+ k = (int) (n * d) + 1;
+ m = 2 * k - 1;
+ h = k - n * d;
+ H = new double[m * m];
+ Q = new double[m * m];
+ pQ = new int[1];
+
+ for (i = 0; i < m; i++)
+ for (j = 0; j < m; j++)
+ if (i - j + 1 < 0)
+ H[i * m + j] = 0;
+ else
+ H[i * m + j] = 1;
+ for (i = 0; i < m; i++) {
+ H[i * m] -= Math.pow (h, (double)(i + 1));
+ H[(m - 1) * m + i] -= Math.pow (h, (double)(m - i));
+ }
+ H[(m - 1) * m] += (2 * h - 1 > 0 ? Math.pow (2 * h - 1, (double) m) : 0);
+ for (i = 0; i < m; i++)
+ for (j = 0; j < m; j++)
+ if (i - j + 1 > 0)
+ for (g = 1; g <= i - j + 1; g++)
+ H[i * m + j] /= g;
+ eH = 0;
+ mPower (H, eH, Q, pQ, m, n);
+ s = Q[(k - 1) * m + k - 1];
+
+ for (i = 1; i <= n; i++) {
+ s = s * (double) i / n;
+ if (s < INORM) {
+ s *= NORM;
+ pQ[0] -= LOGNORM;
+ }
+ }
+ s *= Math.pow (10., (double) pQ[0]);
+ return s;
+ }
+
+
+ protected static double cdfConnu (int n, double x) {
+ // For nx^2 > 18, barF(n, x) is smaller than 5e-16
+ if ((n * x * x >= 18.0) || (x >= 1.0))
+ return 1.0;
+
+ if (x <= 0.5 / n)
+ return 0.0;
+
+ if (n == 1)
+ return 2.0 * x - 1.0;
+
+ if (x <= 1.0 / n) {
+ double w;
+ double t = 2.0 * x * n - 1.0;
+ if (n <= NEXACT) {
+ w = Num.factoPow (n);
+ return w * Math.pow (t, (double) n);
+ }
+ w = Num.lnFactorial(n) + n * Math.log (t/n);
+ return Math.exp (w);
+ }
+
+ if (x >= 1.0 - 1.0 / n) {
+ return 1.0 - 2.0 * Math.pow (1.0 - x, (double) n);
+ }
+
+ return -1.0;
+ }
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN> with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * using Durbin's matrix formula. It is a translation of the
+ * <SPAN CLASS="MATH"><I>C</I></SPAN> program in;
+ * according to its authors, it returns 13 decimal digits
+ * of precision. It is extremely slow for large <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double cdf (int n, double x) {
+ double Res = cdfConnu(n,x);
+ if (Res != -1.0)
+ return Res;
+
+ return DurbinMatrix (n, x);
+ }
+
+
+ protected static double barFConnu (int n, double x) {
+ final double w = n * x * x;
+
+ if ((w >= 370.0) || (x >= 1.0))
+ return 0.0;
+ if ((w <= 0.0274) || (x <= 0.5 / n))
+ return 1.0;
+ if (n == 1)
+ return 2.0 - 2.0 * x;
+
+ if (x <= 1.0 / n) {
+ double v;
+ final double t = 2.0 * x*n - 1.0;
+ if (n <= NEXACT) {
+ v = Num.factoPow (n);
+ return 1.0 - v * Math.pow (t, (double) n);
+ }
+ v = Num.lnFactorial(n) + n * Math.log (t/n);
+ return 1.0 - Math.exp (v);
+ }
+
+ if (x >= 1.0 - 1.0 / n) {
+ return 2.0 * Math.pow (1.0 - x, (double) n);
+ }
+
+ return -1.0;
+ }
+
+ /**
+ * Computes the complementary distribution function <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>
+ * with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>. Simply returns <TT>1 - cdf(n,x)</TT>. It is not precise in
+ * the upper tail.
+ *
+ */
+ public static double barF (int n, double x) {
+ double h = barFConnu(n, x);
+ if (h >= 0.0)
+ return h;
+
+ h = 1.0 - cdf(n, x);
+ if (h >= 0.0)
+ return h;
+ return 0.0;
+ }
+
+
+ protected static double inverseConnue (int n, double u) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return 1.0;
+
+ if (u == 0.0)
+ return 0.5/n;
+
+ if (n == 1)
+ return (u + 1.0) / 2.0;
+
+ final double NLNN = n*Math.log (n);
+ final double LNU = Math.log(u) - Num.lnFactorial (n);
+ if (LNU <= -NLNN){
+ double t = 1.0/n*(LNU);
+ return 0.5 * (Math.exp(t) + 1.0/n);
+ }
+
+ if (u >= 1.0 - 2.0 / Math.exp (NLNN))
+ return 1.0 - Math.pow((1.0-u)/2.0, 1.0/n);
+
+ return -1.0;
+ }
+
+ /**
+ * Computes the inverse
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN> of the
+ * distribution <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN> with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ double Res = inverseConnue(n,u);
+ if (Res != -1.0)
+ return Res;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.5/n, 1.0, f, 1e-10);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public void setN (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ supportA = 0.5 / n;
+ supportB = 1.0;
+ }
+
+
+ /**
+ * Returns an array containing the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDist.tex b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDist.tex
new file mode 100644
index 0000000..31e94ab
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDist.tex
@@ -0,0 +1,519 @@
+\defmodule {KolmogorovSmirnovDist}
+
+Extends the class \class{ContinuousDistribution} for the
+ {\em Kolmogorov-Smirnov\/} distribution with parameter $n$ \cite{tDUR73a}.
+Given an empirical distribution $F_n$ with $n$ independent observations and
+a continuous distribution $F(x)$, the two-sided \ks{} statistic is defined as
+\eq
+D_n = \sup_{-\infty\le x \le \infty} |F_n(x) - F(x)| \ = \
+ \max\{D_n^+, D_n^-\},
+\endeq
+where $D_n^+$ and $D_n^-$ are the \ks+ and \ks$-$ statistics as defined in
+equations \ref{eq:DNp} and \ref{eq:DNm} on page \pageref{eq:DNp} of this guide.
+This class implements a high precision version of the \ks{}
+ distribution $P[D_n \le x]$; it is a Java translation of the $C$ program
+written in \cite{tMAR03a}. According to its authors, it should give
+ 13 decimal digits of precision. It is extremely slow
+ for large values of $n$.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: KolmogorovSmirnovDist
+ * Description: Kolmogorov-Smirnov distribution
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class KolmogorovSmirnovDist extends ContinuousDistribution\begin{hide} {
+ protected int n;
+ protected static final int NEXACT = 500;
+
+ // For the Durbin matrix algorithm
+ private static final double NORM = 1.0e140;
+ private static final double INORM = 1.0e-140;
+ private static final int LOGNORM = 140;
+
+
+
+ //========================================================================
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public KolmogorovSmirnovDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \ks{} distribution with parameter $n$.
+ Restriction: $n \ge 1$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ private static double dclem (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+ protected static double densConnue (int n, double x) {
+ if ((x >= 1.0) || (x <= 0.5 / n))
+ return 0.0;
+ if (n == 1)
+ return 2.0;
+
+ if (x <= 1.0 / n) {
+ double w;
+ double t = 2.0 * x * n - 1.0;
+ if (n <= NEXACT) {
+ w = 2.0 * n * n * Num.factoPow (n);
+ w *= Math.pow (t, (double) (n - 1));
+ return w;
+ }
+ w = Num.lnFactorial (n) + (n-1) * Math.log (t/n);
+ return 2*n*Math.exp (w);
+ }
+
+ if (x >= 1.0 - 1.0 / n)
+ return 2.0 * n * Math.pow (1.0 - x, (double) (n - 1));
+
+ return -1.0;
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ double Res = densConnue(n,x);
+ if (Res != -1.0)
+ return Res;
+
+ double EPS = 1.0 / 200.0;
+ final double D1 = dclem(n, x, EPS);
+ final double D2 = dclem(n, x, 2.0 * EPS);
+ Res = D1 + (D1 - D2) / 3.0;
+ if (Res <= 0.0)
+ return 0.0;
+ return Res;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density for the \ks{} distribution with
+ parameter $n$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ /*=========================================================================
+
+ The following implements the Durbin matrix algorithm and was programmed
+ in C by G. Marsaglia, Wai Wan Tsang and Jingbo Wong in C.
+
+ I have translated their program in Java. Only small modifications have
+ been made in their program; the most important is to prevent the return
+ of NAN or infinite values in some regions. (Richard Simard)
+
+ =========================================================================*/
+
+ /*
+ The C program to compute Kolmogorov's distribution
+
+ K(n,d) = Prob(D_n < d), where
+
+ D_n = max(x_1-0/n,x_2-1/n...,x_n-(n-1)/n,1/n-x_1,2/n-x_2,...,n/n-x_n)
+
+ with x_1<x_2,...<x_n a purported set of n independent uniform [0,1)
+ random variables sorted into increasing order.
+ See G. Marsaglia, Wai Wan Tsang and Jingbo Wong,
+ J.Stat.Software, 8, 18, pp 1--4, (2003).
+ */
+
+
+ private static void mMultiply (double[] A, double[] B, double[] C, int m){
+ int i, j, k;
+ double s;
+ for (i = 0; i < m; i++)
+ for (j = 0; j < m; j++) {
+ s = 0.0;
+ for (k = 0; k < m; k++)
+ s += A[i * m + k] * B[k * m + j];
+ C[i * m + j] = s;
+ }
+ }
+
+
+ private static void renormalize (double[] V, int m, int[] p)
+ {
+ int i;
+ for (i = 0; i < m * m; i++)
+ V[i] *= INORM;
+ p[0] += LOGNORM;
+ }
+
+
+ private static void mPower (double[] A, int eA, double[] V, int[] eV,
+ int m, int n)
+ {
+ int i;
+ if (n == 1) {
+ for (i = 0; i < m * m; i++)
+ V[i] = A[i];
+ eV[0] = eA;
+ return;
+ }
+ mPower (A, eA, V, eV, m, n / 2);
+
+ double[] B = new double[m * m];
+ int[] pB = new int[1];
+
+ mMultiply (V, V, B, m);
+ pB[0] = 2 * (eV[0]);
+ if (B[(m / 2) * m + (m / 2)] > NORM)
+ renormalize (B, m, pB);
+
+ if (n % 2 == 0) {
+ for (i = 0; i < m * m; i++)
+ V[i] = B[i];
+ eV[0] = pB[0];
+ } else {
+ mMultiply (A, B, V, m);
+ eV[0] = eA + pB[0];
+ }
+
+ if (V[(m / 2) * m + (m / 2)] > NORM)
+ renormalize (V, m, eV);
+ }
+
+
+ protected static double DurbinMatrix (int n, double d)
+ {
+ int k, m, i, j, g, eH;
+ double h, s;
+ double[] H;
+ double[] Q;
+ int[] pQ;
+
+ //Omit next two lines if you require >7 digit accuracy in the right tail
+ if (false) {
+ s = d * d * n;
+ if (s > 7.24 || (s > 3.76 && n > 99))
+ return 1 - 2 * Math.exp (-(2.000071 + .331 / Math.sqrt (n) +
+ 1.409 / n) * s);
+ }
+ k = (int) (n * d) + 1;
+ m = 2 * k - 1;
+ h = k - n * d;
+ H = new double[m * m];
+ Q = new double[m * m];
+ pQ = new int[1];
+
+ for (i = 0; i < m; i++)
+ for (j = 0; j < m; j++)
+ if (i - j + 1 < 0)
+ H[i * m + j] = 0;
+ else
+ H[i * m + j] = 1;
+ for (i = 0; i < m; i++) {
+ H[i * m] -= Math.pow (h, (double)(i + 1));
+ H[(m - 1) * m + i] -= Math.pow (h, (double)(m - i));
+ }
+ H[(m - 1) * m] += (2 * h - 1 > 0 ? Math.pow (2 * h - 1, (double) m) : 0);
+ for (i = 0; i < m; i++)
+ for (j = 0; j < m; j++)
+ if (i - j + 1 > 0)
+ for (g = 1; g <= i - j + 1; g++)
+ H[i * m + j] /= g;
+ eH = 0;
+ mPower (H, eH, Q, pQ, m, n);
+ s = Q[(k - 1) * m + k - 1];
+
+ for (i = 1; i <= n; i++) {
+ s = s * (double) i / n;
+ if (s < INORM) {
+ s *= NORM;
+ pQ[0] -= LOGNORM;
+ }
+ }
+ s *= Math.pow (10., (double) pQ[0]);
+ return s;
+ }
+
+
+ protected static double cdfConnu (int n, double x) {
+ // For nx^2 > 18, barF(n, x) is smaller than 5e-16
+ if ((n * x * x >= 18.0) || (x >= 1.0))
+ return 1.0;
+
+ if (x <= 0.5 / n)
+ return 0.0;
+
+ if (n == 1)
+ return 2.0 * x - 1.0;
+
+ if (x <= 1.0 / n) {
+ double w;
+ double t = 2.0 * x * n - 1.0;
+ if (n <= NEXACT) {
+ w = Num.factoPow (n);
+ return w * Math.pow (t, (double) n);
+ }
+ w = Num.lnFactorial(n) + n * Math.log (t/n);
+ return Math.exp (w);
+ }
+
+ if (x >= 1.0 - 1.0 / n) {
+ return 1.0 - 2.0 * Math.pow (1.0 - x, (double) n);
+ }
+
+ return -1.0;
+ }\end{hide}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ double Res = cdfConnu(n,x);
+ if (Res != -1.0)
+ return Res;
+
+ return DurbinMatrix (n, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Computes the \ks{} distribution function $F(x)$ with parameter $n$
+using Durbin's matrix formula \cite{tDUR73a}. It is a translation of the
+$C$ program in \cite{tMAR03a};
+according to its authors, it returns 13 decimal digits
+of precision. It is extremely slow for large $n$.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ protected static double barFConnu (int n, double x) {
+ final double w = n * x * x;
+
+ if ((w >= 370.0) || (x >= 1.0))
+ return 0.0;
+ if ((w <= 0.0274) || (x <= 0.5 / n))
+ return 1.0;
+ if (n == 1)
+ return 2.0 - 2.0 * x;
+
+ if (x <= 1.0 / n) {
+ double v;
+ final double t = 2.0 * x*n - 1.0;
+ if (n <= NEXACT) {
+ v = Num.factoPow (n);
+ return 1.0 - v * Math.pow (t, (double) n);
+ }
+ v = Num.lnFactorial(n) + n * Math.log (t/n);
+ return 1.0 - Math.exp (v);
+ }
+
+ if (x >= 1.0 - 1.0 / n) {
+ return 2.0 * Math.pow (1.0 - x, (double) n);
+ }
+
+ return -1.0;
+ }\end{hide}
+
+ public static double barF (int n, double x)\begin{hide} {
+ double h = barFConnu(n, x);
+ if (h >= 0.0)
+ return h;
+
+ h = 1.0 - cdf(n, x);
+ if (h >= 0.0)
+ return h;
+ return 0.0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function $\bar F(x)$
+ with parameter $n$. Simply returns \texttt{1 - cdf(n,x)}. It is not precise in
+ the upper tail.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected static double inverseConnue (int n, double u) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return 1.0;
+
+ if (u == 0.0)
+ return 0.5/n;
+
+ if (n == 1)
+ return (u + 1.0) / 2.0;
+
+ final double NLNN = n*Math.log (n);
+ final double LNU = Math.log(u) - Num.lnFactorial (n);
+ if (LNU <= -NLNN){
+ double t = 1.0/n*(LNU);
+ return 0.5 * (Math.exp(t) + 1.0/n);
+ }
+
+ if (u >= 1.0 - 2.0 / Math.exp (NLNN))
+ return 1.0 - Math.pow((1.0-u)/2.0, 1.0/n);
+
+ return -1.0;
+ }\end{hide}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ double Res = inverseConnue(n,u);
+ if (Res != -1.0)
+ return Res;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.5/n, 1.0, f, 1e-10);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse $x = F^{-1}(u)$ of the
+ \ks{} distribution $F(x)$ with parameter $n$.
+\end{tabb}
+%\begin{code}
+%
+% public static double getMean (int n)\begin{hide} {
+% if (n <= 0)
+% throw new IllegalArgumentException ("n <= 0");
+%
+% return 0.8687311606361591 / Math.sqrt(n);
+% }\end{hide}
+%\end{code}
+%\begin{tabb} Computes and returns the asymptotic (when $n \to\infty$)
+%mean $\mu = E[X] = \mbox{ln}(2)\sqrt{\pi/(2n)}$
+%of the KS distribution with parameter $n$.
+%\end{tabb}
+%\begin{htmlonly}
+% \param{n}{the number of observations}
+% \return{the mean of the KS distribution}
+%\end{htmlonly}
+%\begin{code}
+%
+% public static double getVariance (int n)\begin{hide} {
+% if (n <= 0)
+% throw new IllegalArgumentException ("n <= 0");
+% return 0.067773203963865 / n;
+% }\end{hide}
+%\end{code}
+%\begin{tabb} Computes and returns the asymptotic (when $n \to\infty$)
+%variance $\mbox{Var}[X] = (\pi^2/12 - \mu^2)/n$
+% of the KS distribution with parameter $n$.
+%\end{tabb}
+%\begin{htmlonly}
+% \param{n}{the number of observations}
+% \return{the variance of the KS distribution.}
+%\end{htmlonly}
+%\begin{code}
+%
+% public static double getStandardDeviation (int n)\begin{hide} {
+% if (n <= 0)
+% throw new IllegalArgumentException ("n <= 0");
+% return Math.sqrt(getVariance (n));
+% }\end{hide}
+%\end{code}
+%\begin{tabb} Computes and returns the asymptotic (when $n \to\infty$)
+%standard deviation of the KS distribution with parameter $n$.
+%\end{tabb}
+%\begin{htmlonly}
+% \param{n}{the number of observations}
+% \return{the standard deviation of the KS distribution}
+%\end{htmlonly}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ supportA = 0.5 / n;
+ supportB = 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an array containing the parameter $n$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDistQuick.java b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDistQuick.java
new file mode 100644
index 0000000..eb4dfb8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDistQuick.java
@@ -0,0 +1,470 @@
+
+/*
+ * Class: KolmogorovSmirnovDistQuick
+ * Description: Kolmogorov-Smirnov 2-sided 1-sample distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2010
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link KolmogorovSmirnovDist} for the distribution.
+ * The methods of this class are much faster than those of class
+ * {@link KolmogorovSmirnovDist}.
+ *
+ */
+public class KolmogorovSmirnovDistQuick extends KolmogorovSmirnovDist {
+
+ /*
+ For n <= NEXACT, we use exact algorithms: the Durbin matrix and
+ the Pomeranz algorithms. For n > NEXACT, we use asymptotic methods
+ except for x close to 0 where we still use the method of Durbin
+ for n <= NKOLMO. For n > NKOLMO, we use asymptotic methods only and
+ so the precision is less for x close to 0.
+ We could increase the limit NKOLMO to 10^6 to get better precision
+ for x close to 0, but at the price of a slower speed.
+ */
+ private static final int NKOLMO = 100000;
+
+
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+ /**
+ * Constructs a distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public KolmogorovSmirnovDistQuick (int n) {
+ super (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+
+ /**
+ * Computes the density for the distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+
+ double Res = densConnue(n,x);
+ if (Res != -1.0)
+ return Res;
+
+ final double EPS = 1.0 / Num.TWOEXP[6];
+ Res = (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ if (Res <= 0.0)
+ return 0.0;
+ return Res;
+ }
+
+
+ private static double Pelz (int n, double x) {
+ /* Approximating the Lower Tail-Areas of the Kolmogorov-Smirnov One-Sample
+ Statistic,
+ Wolfgang Pelz and I. J. Good,
+ Journal of the Royal Statistical Society, Series B.
+ Vol. 38, No. 2 (1976), pp. 152-156
+ */
+
+ final int JMAX = 20;
+ final double EPS = 1.0e-10;
+ final double RACN = Math.sqrt ((double) n);
+ final double z = RACN * x;
+ final double z2 = z * z;
+ final double z4 = z2 * z2;
+ final double z6 = z4 * z2;
+ final double C2PI = 2.506628274631001; // sqrt(2*Pi)
+ final double DPI2 = 1.2533141373155001; // sqrt(Pi/2)
+ final double PI2 = Math.PI * Math.PI;
+ final double PI4 = PI2 * PI2;
+ final double w = PI2 / (2.0 * z * z);
+ double ti, term, tom;
+ double sum;
+ int j;
+
+ term = 1;
+ j = 0;
+ sum = 0;
+ while (j <= JMAX && term > EPS * sum) {
+ ti = j + 0.5;
+ term = Math.exp (-ti * ti * w);
+ sum += term;
+ j++;
+ }
+ sum *= C2PI / z;
+
+ term = 1;
+ tom = 0;
+ j = 0;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j + 0.5;
+ term = (PI2 * ti * ti - z2) * Math.exp (-ti * ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (RACN * 3.0 * z4);
+
+ term = 1;
+ tom = 0;
+ j = 0;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j + 0.5;
+ term = 6 * z6 + 2 * z4 + PI2 * (2 * z4 - 5 * z2) * ti * ti +
+ PI4 * (1 - 2 * z2) * ti * ti * ti * ti;
+ term *= Math.exp (-ti * ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (n * 36.0 * z * z6);
+
+ term = 1;
+ tom = 0;
+ j = 1;
+ while (j <= JMAX && term > EPS * tom) {
+ ti = j;
+ term = PI2 * ti * ti * Math.exp (-ti * ti * w);
+ tom += term;
+ j++;
+ }
+ sum -= tom * DPI2 / (n * 18.0 * z * z2);
+
+ term = 1;
+ tom = 0;
+ j = 0;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j + 0.5;
+ ti = ti * ti;
+ term = -30 * z6 - 90 * z6 * z2 + PI2 * (135 * z4 - 96 * z6) * ti +
+ PI4 * (212 * z4 - 60 * z2) * ti * ti +
+ PI2 * PI4 * ti * ti * ti * (5 - 30 * z2);
+ term *= Math.exp (-ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (RACN * n * 3240.0 * z4 * z6);
+
+ term = 1;
+ tom = 0;
+ j = 1;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j * j;
+ term = (3 * PI2 * ti * z2 - PI4 * ti * ti) * Math.exp (-ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (RACN * n * 108.0 * z6);
+
+ return sum;
+ }
+
+
+//========================================================================
+
+ private static void CalcFloorCeil (
+ int n, // sample size
+ double t, // = nx
+ double[] A, // A_i
+ double[] Atflo, // floor (A_i - t)
+ double[] Atcei // ceiling (A_i + t)
+ )
+ {
+ // Precompute A_i, floors, and ceilings for limits of sums in the
+ // Pomeranz algorithm
+ int i;
+ int ell = (int) t; // floor (t)
+ double z = t - ell; // t - floor (t)
+ double w = Math.ceil (t) - t;
+
+ if (z > 0.5) {
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - 2 - ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - 1 - ell;
+
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 + ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 + 1 + ell;
+
+ } else if (z > 0.0) {
+ for (i = 1; i <= 2 * n + 2; i++)
+ Atflo[i] = i / 2 - 1 - ell;
+
+ for (i = 2; i <= 2 * n + 2; i++)
+ Atcei[i] = i / 2 + ell;
+ Atcei[1] = 1 + ell;
+
+ } else { // z == 0
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - 1 - ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - ell;
+
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 - 1 + ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 + ell;
+ }
+
+ if (w < z)
+ z = w;
+ A[0] = A[1] = 0;
+ A[2] = z;
+ A[3] = 1 - A[2];
+ for (i = 4; i <= 2 * n + 1; i++)
+ A[i] = A[i - 2] + 1;
+ A[2 * n + 2] = n;
+ }
+
+
+ //========================================================================
+
+ private static double Pomeranz (int n, double x)
+ {
+ // The Pomeranz algorithm to compute the KS distribution
+ final double EPS = 1.0e-15;
+ final int ENO = 350;
+ final double RENO = Math.scalb (1.0, ENO); // for renormalization of V
+ int coreno; // counter: how many renormalizations
+ final double t = n * x;
+ double w, sum, minsum;
+ int i, j, k, s;
+ int r1, r2; // Indices i and i-1 for V[i][]
+ int jlow, jup, klow, kup, kup0;
+ double[] A = new double[2 * n + 3];
+ double[] Atflo = new double[2 * n + 3];
+ double[] Atcei = new double[2 * n + 3];
+ double[][] V = new double[2][n + 2];
+ double[][] H = new double[4][n + 2]; // = pow(w, j) / Factorial(j)
+
+ CalcFloorCeil (n, t, A, Atflo, Atcei);
+
+ for (j = 1; j <= n + 1; j++)
+ V[0][j] = 0;
+ for (j = 2; j <= n + 1; j++)
+ V[1][j] = 0;
+ V[1][1] = RENO;
+ coreno = 1;
+
+ // Precompute H[][] = (A[j] - A[j-1]^k / k!
+ H[0][0] = 1;
+ w = 2.0 * A[2] / n;
+ for (j = 1; j <= n + 1; j++)
+ H[0][j] = w * H[0][j - 1] / j;
+
+ H[1][0] = 1;
+ w = (1.0 - 2.0 * A[2]) / n;
+ for (j = 1; j <= n + 1; j++)
+ H[1][j] = w * H[1][j - 1] / j;
+
+ H[2][0] = 1;
+ w = A[2] / n;
+ for (j = 1; j <= n + 1; j++)
+ H[2][j] = w * H[2][j - 1] / j;
+
+ H[3][0] = 1;
+ for (j = 1; j <= n + 1; j++)
+ H[3][j] = 0;
+
+ r1 = 0;
+ r2 = 1;
+ for (i = 2; i <= 2 * n + 2; i++) {
+ jlow = (int) (2 + Atflo[i]);
+ if (jlow < 1)
+ jlow = 1;
+ jup = (int) (Atcei[i]);
+ if (jup > n + 1)
+ jup = n + 1;
+
+ klow = (int) (2 + Atflo[i - 1]);
+ if (klow < 1)
+ klow = 1;
+ kup0 = (int) (Atcei[i - 1]);
+
+ // Find to which case it corresponds
+ w = (A[i] - A[i - 1]) / n;
+ s = -1;
+ for (j = 0; j < 4; j++) {
+ if (Math.abs (w - H[j][1]) <= EPS) {
+ s = j;
+ break;
+ }
+ }
+
+ minsum = RENO;
+ r1 = (r1 + 1) & 1; // i - 1
+ r2 = (r2 + 1) & 1; // i
+
+ for (j = jlow; j <= jup; j++) {
+ kup = kup0;
+ if (kup > j)
+ kup = j;
+ sum = 0;
+ for (k = kup; k >= klow; k--)
+ sum += V[r1][k] * H[s][j - k];
+ V[r2][j] = sum;
+ if (sum < minsum)
+ minsum = sum;
+ }
+
+ if (minsum < 1.0e-280) {
+ // V is too small: renormalize to avoid underflow of probabilities
+ for (j = jlow; j <= jup; j++)
+ V[r2][j] *= RENO;
+ coreno++; // keep track of log of RENO
+ }
+ }
+
+ sum = V[r2][n + 1];
+ w = Num.lnFactorial (n) - coreno * ENO * Num.LN2 + Math.log (sum);
+ if (w >= 0.)
+ return 1.;
+ return Math.exp (w);
+ }
+ //========================================================================
+
+
+ /**
+ * Computes the distribution function
+ * <SPAN CLASS="MATH"><I>u</I> = <I>P</I>[<I>D</I><SUB>n</SUB> <= <I>x</I>]</SPAN> with
+ * parameter <SPAN CLASS="MATH"><I>n</I></SPAN>, using the program described in.
+ * This method uses Pomeranz's recursion algorithm and the Durbin matrix algorithm for
+ * <SPAN CLASS="MATH"><I>n</I> <= 500</SPAN>, which returns at least 13 decimal digits of precision. It uses
+ * the Pelz-Good asymptotic expansion in the central part of
+ * the range for <SPAN CLASS="MATH"><I>n</I> > 500</SPAN> and returns at least 7 decimal digits of precision
+ * everywhere for
+ * <SPAN CLASS="MATH">500 < <I>n</I> <= 100000</SPAN>. For
+ * <SPAN CLASS="MATH"><I>n</I> > 100000</SPAN>, it returns
+ * at least 5 decimal digits of precision for all
+ * <SPAN CLASS="MATH"><I>u</I> > 10<SUP>-16</SUP></SPAN>, and a
+ * few correct decimals when
+ * <SPAN CLASS="MATH"><I>u</I> <= 10<SUP>-16</SUP></SPAN>.
+ * This method is much faster than method <TT>cdf</TT> of
+ * {@link KolmogorovSmirnovDist} for moderate or large <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Restriction: <SPAN CLASS="MATH"><I>n</I> >= 1</SPAN>.
+ *
+ */
+ public static double cdf (int n, double x) {
+ double u = cdfConnu (n, x);
+ if (u >= 0.0)
+ return u;
+
+ final double w = n * x * x;
+ if (n <= NEXACT) {
+ if (w < 0.754693)
+ return DurbinMatrix (n, x);
+ if (w < 4.0)
+ return Pomeranz (n, x);
+ return 1.0 - barF (n, x);
+ }
+
+ if ((w * x * n <= 7.0) && (n <= NKOLMO))
+ return DurbinMatrix(n, x);
+
+ return Pelz (n, x);
+ }
+
+
+ /**
+ * Computes the complementary distribution
+ *
+ * <SPAN CLASS="MATH"><I>P</I>[<I>D</I><SUB>n</SUB> >= <I>x</I>]</SPAN> with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>,
+ * in a form that is more precise in the upper tail,
+ * using the program described in.
+ * It returns at least 10 decimal digits of precision everywhere for all
+ * <SPAN CLASS="MATH"><I>n</I> <= 500</SPAN>,
+ * at least 6 decimal digits of precision for
+ * <SPAN CLASS="MATH">500 < <I>n</I> <= 200000</SPAN>,
+ * and a few correct decimal digits (1 to 5) for
+ * <SPAN CLASS="MATH"><I>n</I> > 200000</SPAN>.
+ * This method is much faster and more precise for <SPAN CLASS="MATH"><I>x</I></SPAN> close to 1, than
+ * method <TT>barF</TT> of
+ * {@link KolmogorovSmirnovDist} for moderate or large <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Restriction: <SPAN CLASS="MATH"><I>n</I> >= 1</SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ double v = barFConnu (n, x);
+ if (v >= 0.0)
+ return v;
+
+ final double w = n * x * x;
+ if (n <= NEXACT) {
+ if (w < 4.0)
+ return 1.0 - cdf (n, x);
+ else
+ return 2.0 * KolmogorovSmirnovPlusDist.KSPlusbarUpper(n, x);
+ }
+
+ if (w >= 2.65)
+ return 2.0 * KolmogorovSmirnovPlusDist.KSPlusbarUpper (n, x);
+
+ return 1.0 - cdf (n, x);
+ }
+
+
+ /**
+ * Computes the inverse
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN> of the
+ * distribution <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN> with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ double Res = inverseConnue(n,u);
+ if (Res != -1.0)
+ return Res;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.5/n, 1.0, f, 1e-5);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDistQuick.tex b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDistQuick.tex
new file mode 100644
index 0000000..9151205
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovDistQuick.tex
@@ -0,0 +1,481 @@
+\defmodule {KolmogorovSmirnovDistQuick}
+
+Extends the class \class{KolmogorovSmirnovDist} for the \ks{} distribution.
+The methods of this class %use a local program in our lab, and
+are much faster than those of class
+\class{KolmogorovSmirnovDist}.
+
+\begin{htmlonly}
+\end{htmlonly}%
+\begin{latexonly}%
+\end{latexonly}%
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: KolmogorovSmirnovDistQuick
+ * Description: Kolmogorov-Smirnov 2-sided 1-sample distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2010
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class KolmogorovSmirnovDistQuick extends KolmogorovSmirnovDist \begin{hide} {
+
+ /*
+ For n <= NEXACT, we use exact algorithms: the Durbin matrix and
+ the Pomeranz algorithms. For n > NEXACT, we use asymptotic methods
+ except for x close to 0 where we still use the method of Durbin
+ for n <= NKOLMO. For n > NKOLMO, we use asymptotic methods only and
+ so the precision is less for x close to 0.
+ We could increase the limit NKOLMO to 10^6 to get better precision
+ for x close to 0, but at the price of a slower speed.
+ */
+ private static final int NKOLMO = 100000;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}\begin{hide}
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }\end{hide}
+
+ public KolmogorovSmirnovDistQuick (int n)\begin{hide} {
+ super (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \ks{} distribution with parameter $n$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+
+ double Res = densConnue(n,x);
+ if (Res != -1.0)
+ return Res;
+
+ final double EPS = 1.0 / Num.TWOEXP[6];
+ Res = (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ if (Res <= 0.0)
+ return 0.0;
+ return Res;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density for the \ks{} distribution with parameter $n$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double Pelz (int n, double x) {
+ /* Approximating the Lower Tail-Areas of the Kolmogorov-Smirnov One-Sample
+ Statistic,
+ Wolfgang Pelz and I. J. Good,
+ Journal of the Royal Statistical Society, Series B.
+ Vol. 38, No. 2 (1976), pp. 152-156
+ */
+
+ final int JMAX = 20;
+ final double EPS = 1.0e-10;
+ final double RACN = Math.sqrt ((double) n);
+ final double z = RACN * x;
+ final double z2 = z * z;
+ final double z4 = z2 * z2;
+ final double z6 = z4 * z2;
+ final double C2PI = 2.506628274631001; // sqrt(2*Pi)
+ final double DPI2 = 1.2533141373155001; // sqrt(Pi/2)
+ final double PI2 = Math.PI * Math.PI;
+ final double PI4 = PI2 * PI2;
+ final double w = PI2 / (2.0 * z * z);
+ double ti, term, tom;
+ double sum;
+ int j;
+
+ term = 1;
+ j = 0;
+ sum = 0;
+ while (j <= JMAX && term > EPS * sum) {
+ ti = j + 0.5;
+ term = Math.exp (-ti * ti * w);
+ sum += term;
+ j++;
+ }
+ sum *= C2PI / z;
+
+ term = 1;
+ tom = 0;
+ j = 0;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j + 0.5;
+ term = (PI2 * ti * ti - z2) * Math.exp (-ti * ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (RACN * 3.0 * z4);
+
+ term = 1;
+ tom = 0;
+ j = 0;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j + 0.5;
+ term = 6 * z6 + 2 * z4 + PI2 * (2 * z4 - 5 * z2) * ti * ti +
+ PI4 * (1 - 2 * z2) * ti * ti * ti * ti;
+ term *= Math.exp (-ti * ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (n * 36.0 * z * z6);
+
+ term = 1;
+ tom = 0;
+ j = 1;
+ while (j <= JMAX && term > EPS * tom) {
+ ti = j;
+ term = PI2 * ti * ti * Math.exp (-ti * ti * w);
+ tom += term;
+ j++;
+ }
+ sum -= tom * DPI2 / (n * 18.0 * z * z2);
+
+ term = 1;
+ tom = 0;
+ j = 0;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j + 0.5;
+ ti = ti * ti;
+ term = -30 * z6 - 90 * z6 * z2 + PI2 * (135 * z4 - 96 * z6) * ti +
+ PI4 * (212 * z4 - 60 * z2) * ti * ti +
+ PI2 * PI4 * ti * ti * ti * (5 - 30 * z2);
+ term *= Math.exp (-ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (RACN * n * 3240.0 * z4 * z6);
+
+ term = 1;
+ tom = 0;
+ j = 1;
+ while (j <= JMAX && Math.abs (term) > EPS * Math.abs (tom)) {
+ ti = j * j;
+ term = (3 * PI2 * ti * z2 - PI4 * ti * ti) * Math.exp (-ti * w);
+ tom += term;
+ j++;
+ }
+ sum += tom * DPI2 / (RACN * n * 108.0 * z6);
+
+ return sum;
+ }
+
+
+//========================================================================
+
+ private static void CalcFloorCeil (
+ int n, // sample size
+ double t, // = nx
+ double[] A, // A_i
+ double[] Atflo, // floor (A_i - t)
+ double[] Atcei // ceiling (A_i + t)
+ )
+ {
+ // Precompute A_i, floors, and ceilings for limits of sums in the
+ // Pomeranz algorithm
+ int i;
+ int ell = (int) t; // floor (t)
+ double z = t - ell; // t - floor (t)
+ double w = Math.ceil (t) - t;
+
+ if (z > 0.5) {
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - 2 - ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - 1 - ell;
+
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 + ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 + 1 + ell;
+
+ } else if (z > 0.0) {
+ for (i = 1; i <= 2 * n + 2; i++)
+ Atflo[i] = i / 2 - 1 - ell;
+
+ for (i = 2; i <= 2 * n + 2; i++)
+ Atcei[i] = i / 2 + ell;
+ Atcei[1] = 1 + ell;
+
+ } else { // z == 0
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - 1 - ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atflo[i] = i / 2 - ell;
+
+ for (i = 2; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 - 1 + ell;
+ for (i = 1; i <= 2 * n + 2; i += 2)
+ Atcei[i] = i / 2 + ell;
+ }
+
+ if (w < z)
+ z = w;
+ A[0] = A[1] = 0;
+ A[2] = z;
+ A[3] = 1 - A[2];
+ for (i = 4; i <= 2 * n + 1; i++)
+ A[i] = A[i - 2] + 1;
+ A[2 * n + 2] = n;
+ }
+
+
+ //========================================================================
+
+ private static double Pomeranz (int n, double x)
+ {
+ // The Pomeranz algorithm to compute the KS distribution
+ final double EPS = 1.0e-15;
+ final int ENO = 350;
+ final double RENO = Math.scalb (1.0, ENO); // for renormalization of V
+ int coreno; // counter: how many renormalizations
+ final double t = n * x;
+ double w, sum, minsum;
+ int i, j, k, s;
+ int r1, r2; // Indices i and i-1 for V[i][]
+ int jlow, jup, klow, kup, kup0;
+ double[] A = new double[2 * n + 3];
+ double[] Atflo = new double[2 * n + 3];
+ double[] Atcei = new double[2 * n + 3];
+ double[][] V = new double[2][n + 2];
+ double[][] H = new double[4][n + 2]; // = pow(w, j) / Factorial(j)
+
+ CalcFloorCeil (n, t, A, Atflo, Atcei);
+
+ for (j = 1; j <= n + 1; j++)
+ V[0][j] = 0;
+ for (j = 2; j <= n + 1; j++)
+ V[1][j] = 0;
+ V[1][1] = RENO;
+ coreno = 1;
+
+ // Precompute H[][] = (A[j] - A[j-1]^k / k!
+ H[0][0] = 1;
+ w = 2.0 * A[2] / n;
+ for (j = 1; j <= n + 1; j++)
+ H[0][j] = w * H[0][j - 1] / j;
+
+ H[1][0] = 1;
+ w = (1.0 - 2.0 * A[2]) / n;
+ for (j = 1; j <= n + 1; j++)
+ H[1][j] = w * H[1][j - 1] / j;
+
+ H[2][0] = 1;
+ w = A[2] / n;
+ for (j = 1; j <= n + 1; j++)
+ H[2][j] = w * H[2][j - 1] / j;
+
+ H[3][0] = 1;
+ for (j = 1; j <= n + 1; j++)
+ H[3][j] = 0;
+
+ r1 = 0;
+ r2 = 1;
+ for (i = 2; i <= 2 * n + 2; i++) {
+ jlow = (int) (2 + Atflo[i]);
+ if (jlow < 1)
+ jlow = 1;
+ jup = (int) (Atcei[i]);
+ if (jup > n + 1)
+ jup = n + 1;
+
+ klow = (int) (2 + Atflo[i - 1]);
+ if (klow < 1)
+ klow = 1;
+ kup0 = (int) (Atcei[i - 1]);
+
+ // Find to which case it corresponds
+ w = (A[i] - A[i - 1]) / n;
+ s = -1;
+ for (j = 0; j < 4; j++) {
+ if (Math.abs (w - H[j][1]) <= EPS) {
+ s = j;
+ break;
+ }
+ }
+
+ minsum = RENO;
+ r1 = (r1 + 1) & 1; // i - 1
+ r2 = (r2 + 1) & 1; // i
+
+ for (j = jlow; j <= jup; j++) {
+ kup = kup0;
+ if (kup > j)
+ kup = j;
+ sum = 0;
+ for (k = kup; k >= klow; k--)
+ sum += V[r1][k] * H[s][j - k];
+ V[r2][j] = sum;
+ if (sum < minsum)
+ minsum = sum;
+ }
+
+ if (minsum < 1.0e-280) {
+ // V is too small: renormalize to avoid underflow of probabilities
+ for (j = jlow; j <= jup; j++)
+ V[r2][j] *= RENO;
+ coreno++; // keep track of log of RENO
+ }
+ }
+
+ sum = V[r2][n + 1];
+ w = Num.lnFactorial (n) - coreno * ENO * Num.LN2 + Math.log (sum);
+ if (w >= 0.)
+ return 1.;
+ return Math.exp (w);
+ }
+ //========================================================================
+\end{hide}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ double u = cdfConnu (n, x);
+ if (u >= 0.0)
+ return u;
+
+ final double w = n * x * x;
+ if (n <= NEXACT) {
+ if (w < 0.754693)
+ return DurbinMatrix (n, x);
+ if (w < 4.0)
+ return Pomeranz (n, x);
+ return 1.0 - barF (n, x);
+ }
+
+ if ((w * x * n <= 7.0) && (n <= NKOLMO))
+ return DurbinMatrix(n, x);
+
+ return Pelz (n, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the \ks{} distribution function $u = P[D_n \le x]$ with
+ parameter $n$, using the program described in \cite{tSIM11a}.
+ This method uses Pomeranz's recursion algorithm and the Durbin matrix algorithm
+ \cite{tBRO08a,tPOM74a,tMAR03a} for
+$n \le 500$, which returns at least 13 decimal digits of precision. It uses
+the Pelz-Good asymptotic expansion \cite{tPEL76a} in the central part of
+ the range for $n > 500$ and returns at least 7 decimal digits of precision
+ everywhere for $500 < n \le 100000$. For $n > 100000$, it returns
+ at least 5 decimal digits of precision for all $u > 10^{-16}$, and a
+ few correct decimals when $u \le 10^{-16}$.
+% For a given $n> 500$, the precision increases as $x$ increases.
+ This method is much faster than method \texttt{cdf} of
+\class{KolmogorovSmirnovDist} for moderate or large $n$.
+Restriction: $n\ge 1$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double x)\begin{hide} {
+ double v = barFConnu (n, x);
+ if (v >= 0.0)
+ return v;
+
+ final double w = n * x * x;
+ if (n <= NEXACT) {
+ if (w < 4.0)
+ return 1.0 - cdf (n, x);
+ else
+ return 2.0 * KolmogorovSmirnovPlusDist.KSPlusbarUpper(n, x);
+ }
+
+ if (w >= 2.65)
+ return 2.0 * KolmogorovSmirnovPlusDist.KSPlusbarUpper (n, x);
+
+ return 1.0 - cdf (n, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary \ks{} distribution
+$P[D_n \ge x]$ with parameter $n$,
+ in a form that is more precise in the upper tail,
+using the program described in \cite{tSIM11a}.
+It returns at least 10 decimal digits of precision everywhere for all
+ $n \le 500$,
+ at least 6 decimal digits of precision for $500 < n \le 200000$,
+and a few correct decimal digits (1 to 5) for $n > 200000$.
+This method is much faster and more precise for $x$ close to 1, than
+ method \texttt{barF} of
+\class{KolmogorovSmirnovDist} for moderate or large $n$.
+ Restriction: $n\ge 1$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ double Res = inverseConnue(n,u);
+ if (Res != -1.0)
+ return Res;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.5/n, 1.0, f, 1e-5);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse $x = F^{-1}(u)$ of the
+ distribution $F(x)$ with parameter $n$.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovPlusDist.java b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovPlusDist.java
new file mode 100644
index 0000000..73eee87
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovPlusDist.java
@@ -0,0 +1,392 @@
+
+
+/*
+ * Class: KolmogorovSmirnovPlusDist
+ * Description: Kolmogorov-Smirnov+ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * <EM>Kolmogorov-Smirnov</EM>+ distribution (see).
+ * Given a sample of <SPAN CLASS="MATH"><I>n</I></SPAN> independent uniforms <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> over <SPAN CLASS="MATH">[0, 1]</SPAN>,
+ * the <EM>Kolmogorov-Smirnov</EM>+ statistic <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN> and the <EM>Kolmogorov-Smirnov</EM><SPAN CLASS="MATH">-</SPAN> statistic <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>-</SUP></SPAN>,
+ * are defined by
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>D</I><SUB>n</SUB><SUP>+</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>max<SUB>1 <= j <= n</SUB>(<I>j</I>/<I>n</I> - <I>U</I><SUB>(j)</SUB>),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>D</I><SUB>n</SUB><SUP>-</SUP></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>max<SUB>1 <= j <= n</SUB>(<I>U</I><SUB>(j)</SUB> - (<I>j</I> - 1)/<I>n</I>),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * where the <SPAN CLASS="MATH"><I>U</I><SUB>(j)</SUB></SPAN> are the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> sorted in increasing order. Both statistics
+ * follows the same distribution function, i.e.
+ *
+ * <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>) = <I>P</I>[<I>D</I><SUB>n</SUB><SUP>+</SUP> <= <I>x</I>] = <I>P</I>[<I>D</I><SUB>n</SUB><SUP>-</SUP> <= <I>x</I>]</SPAN>.
+ *
+ */
+public class KolmogorovSmirnovPlusDist extends ContinuousDistribution {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+
+
+ /**
+ * Constructs an <EM>Kolmogorov-Smirnov</EM>+ distribution for a sample of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public KolmogorovSmirnovPlusDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ private static double dclem (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+ /**
+ * Computes the density of the <EM>Kolmogorov-Smirnov</EM>+ distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlus with n < 1");
+ if (x <= 0.0 || x >= 1.0)
+ return 0.0;
+ if (n == 1)
+ return 1.0;
+ final double EPS = 1.0 / 100.0;
+ final double D1 = dclem(n, x, EPS);
+ final double D2 = dclem(n, x, 2.0 * EPS);
+ final double RES = D1 + (D1 - D2) / 3.0;
+ if (RES < 0.0)
+ return 0.0;
+ return RES;
+ }
+
+
+ /**
+ * Computes the <EM>Kolmogorov-Smirnov</EM>+ distribution function <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>)</SPAN> with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * The relative error on
+ * <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>) = <I>P</I>[<I>D</I><SUB>n</SUB><SUP>+</SUP> <= <I>x</I>]</SPAN> is always less than
+ * <SPAN CLASS="MATH">10<SUP>-5</SUP></SPAN>.
+ */
+ public static double cdf (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlus with n < 1");
+ if (x <= 0.0)
+ return 0.0;
+ if ((x >= 1.0) || (n*x*x >= 25.0))
+ return 1.0;
+ if (n == 1)
+ return x;
+
+ final double NXPARAM = 6.5; // frontier: alternating series
+ final int NPARAM = 4000; // frontier: non-alternating series
+ double q;
+ double Sum = 0.0;
+ double term;
+ double Njreal;
+ double jreal;
+ double LogCom = Math.log ((double)n);
+ int j;
+ int jmax;
+
+ //--------------------------------------------------------------
+ // the alternating series is stable and fast for n*x very small
+ //--------------------------------------------------------------
+
+ if (n*x <= NXPARAM) {
+ final double EPSILON = Num.DBL_MIN;
+ jmax = (int)(n*x);
+ int Sign = -1;
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = n - j;
+ q = jreal/n - x;
+ // we must avoid log (0.0) for j = jmax and n*x near an integer
+ if (-q > EPSILON) {
+ term = LogCom + jreal*Math.log(-q) + (Njreal - 1.0)
+ *Math.log1p (-q);
+ Sum += Sign*Math.exp (term);
+ }
+ Sign = -Sign;
+ LogCom += Math.log (Njreal/(j + 1));
+ }
+ // add the term j = 0
+ Sum += Math.exp ((n - 1)*Math.log1p (x));
+ return Sum*x;
+ }
+
+ //-----------------------------------------------------------
+ // For nx > NxParam, we use the other exact series for small
+ // n, and the asymptotic form for n larger than NPARAM
+ //-----------------------------------------------------------
+
+ if (n <= NPARAM) {
+ jmax = (int)(n*(1.0 - x));
+ if (1.0 - x - (double) jmax/n <= 0.0)
+ --jmax;
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = n - j;
+ q = jreal/n + x;
+ term = LogCom+(jreal - 1.0)*Math.log(q) + Njreal*Math.log1p (-q);
+ Sum += Math.exp (term);
+ LogCom += Math.log (Njreal/(jreal + 1.0));
+ }
+ Sum *= x;
+
+ // add the term j = 0; avoid log (0.0)
+ if (1.0 > x)
+ Sum += Math.exp (n*Math.log1p (-x));
+ return 1.0 - Sum;
+ }
+
+ //--------------------------
+ // Use an asymptotic formula
+ //--------------------------
+
+ term = 2.0/3.0;
+ q = x*x*n;
+ Sum = 1.0 - Math.exp (-2.0*q)*(1.0 - term*x*(1.0 - x*(1.0 - term*q)
+ - term/n*(0.2 - 19.0/15.0*q + term*q*q)));
+ return Sum;
+ }
+
+
+ private static double KSPlusbarAsymp (int n, double x) {
+ /* Compute the probability of the complementary KSPlus distribution
+ using an asymptotic formula */
+ double t = (6.0*n*x + 1);
+ double z = t*t/(18.0*n);
+ double v = 1.0 - (2.0*z*z - 4.0*z - 1.0)/(18.0*n);
+ if (v <= 0.0)
+ return 0.0;
+ v = v*Math.exp(-z);
+ if (v >= 1.0)
+ return 1.0;
+ return v;
+ }
+
+
+//-------------------------------------------------------------------------
+
+ static double KSPlusbarUpper (int n, double x) {
+ /* Compute the probability of the complementary KS+ distribution in
+ the upper tail using Smirnov's stable formula */
+
+ if (n > 200000)
+ return KSPlusbarAsymp (n, x);
+
+ int jmax = (int) (n* (1.0 - x));
+ // Avoid log(0) for j = jmax and q ~ 1.0
+ if ((1.0 - x - (double)jmax/n) <= 0.0)
+ jmax--;
+
+ int jdiv;
+ if (n > 3000)
+ jdiv = 2;
+ else
+ jdiv = 3;
+ int j = jmax / jdiv + 1;
+
+ double LogCom = Num.lnFactorial(n) - Num.lnFactorial(j)
+ - Num.lnFactorial(n-j);
+ final double LOGJM = LogCom;
+
+ final double EPSILON = 1.0E-12;
+ double q;
+ double term;
+ double t;
+ double Sum = 0.0;
+
+ while (j <= jmax) {
+ q = (double)j / n + x;
+ term = LogCom + (j - 1)*Math.log(q) + (n-j)*Math.log1p(-q);
+ t = Math.exp (term);
+ Sum += t;
+ LogCom += Math.log ((double)(n - j)/(j + 1));
+ if (t <= Sum*EPSILON)
+ break;
+ j++;
+ }
+
+ j = jmax / jdiv;
+ LogCom = LOGJM + Math.log ((double)(j+1)/(n - j));
+
+ while (j > 0) {
+ q = (double)j / n + x;
+ term = LogCom+(j - 1)*Math.log (q)+ (n - j)*Math.log1p (-q);
+ t = Math.exp (term);
+ Sum += t;
+ LogCom += Math.log ((double)j/(n - j + 1));
+ if (t <= Sum*EPSILON)
+ break;
+ j--;
+ }
+
+ Sum *= x;
+ // add the term j = 0
+ Sum += Math.exp (n*Math.log1p (-x));
+ return Sum;
+ }
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)<SUB>n</SUB>(<I>x</I>)</SPAN>
+ * with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ if (n <= 0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlus with n < 1");
+ if (x <= 0.0)
+ return 1.0;
+ if ((x >= 1.0) || (n*x*x >= 365.0))
+ return 0.0;
+ if (n == 1)
+ return 1.0 - x;
+
+ final double NXPARAM = 6.5; // frontier: alternating series
+ final int NPARAM = 4000; // frontier: non-alternating series
+ final int NASYMP = 200000; // frontier: asymptotic
+
+ // the alternating series is stable and fast for n*x very small
+ if (n*x <= NXPARAM)
+ return 1.0 - cdf(n, x);
+
+ if (n >= NASYMP)
+ return KSPlusbarAsymp (n, x);
+
+ if ((n <= NPARAM) || (n*x*x > 1.0))
+ return KSPlusbarUpper(n,x);
+
+ return KSPlusbarAsymp (n, x);
+ // return (1.0 - 2.0*x/3.0)*Math.exp(-2.0*n*x*x);
+ }
+
+
+ /**
+ * Computes the inverse
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN> of the distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return 1.0;
+ if (u == 0.0)
+ return 0.0;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 1.0, f, 1e-8);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public void setN (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ supportA = 0.0;
+ supportB = 1.0;
+ }
+
+
+ /**
+ * Returns an array containing the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovPlusDist.tex b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovPlusDist.tex
new file mode 100644
index 0000000..c48c69f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/KolmogorovSmirnovPlusDist.tex
@@ -0,0 +1,424 @@
+\defmodule {KolmogorovSmirnovPlusDist}
+
+\newcommand{\ks}{{\em Kolmogorov-Smirnov\/}}
+
+Extends the class \class{ContinuousDistribution} for the
+\ks+{} distribution (see \cite{tDAR60a,tDUR73a,tBRO07a}).
+Given a sample of $n$ independent uniforms $U_i$ over $[0,1]$,
+the \ks+{} statistic $D_n^+$ and the \ks$-${} statistic $D_n^-$,
+ are defined by
+\begin {eqnarray}
+ D_n^+ &=& \max_{1\le j\le n} \left(j/n - U_{(j)}\right),
+ \eqlabel{eq:DNp} \\
+ D_n^- &=& \max_{1\le j\le n} \left(U_{(j)} - (j-1)/n\right),
+ \eqlabel{eq:DNm}
+\end {eqnarray}
+where the $U_{(j)}$ are the $U_i$ sorted in increasing order. Both statistics
+follows the same distribution function, i.e.
+$F_n(x) = P[D_n^+ \le x] = P[D_n^- \le x]$.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: KolmogorovSmirnovPlusDist
+ * Description: Kolmogorov-Smirnov+ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class KolmogorovSmirnovPlusDist extends ContinuousDistribution\begin{hide} {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public KolmogorovSmirnovPlusDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs an \ks+{} distribution for a sample of size $n$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ private static double dclem (int n, double x, double EPS) {
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlus with n < 1");
+ if (x <= 0.0 || x >= 1.0)
+ return 0.0;
+ if (n == 1)
+ return 1.0;
+ final double EPS = 1.0 / 100.0;
+ final double D1 = dclem(n, x, EPS);
+ final double D2 = dclem(n, x, 2.0 * EPS);
+ final double RES = D1 + (D1 - D2) / 3.0;
+ if (RES < 0.0)
+ return 0.0;
+ return RES;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density of the \ks+ distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlus with n < 1");
+ if (x <= 0.0)
+ return 0.0;
+ if ((x >= 1.0) || (n*x*x >= 25.0))
+ return 1.0;
+ if (n == 1)
+ return x;
+
+ final double NXPARAM = 6.5; // frontier: alternating series
+ final int NPARAM = 4000; // frontier: non-alternating series
+ double q;
+ double Sum = 0.0;
+ double term;
+ double Njreal;
+ double jreal;
+ double LogCom = Math.log ((double)n);
+ int j;
+ int jmax;
+
+ //--------------------------------------------------------------
+ // the alternating series is stable and fast for n*x very small
+ //--------------------------------------------------------------
+
+ if (n*x <= NXPARAM) {
+ final double EPSILON = Num.DBL_MIN;
+ jmax = (int)(n*x);
+ int Sign = -1;
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = n - j;
+ q = jreal/n - x;
+ // we must avoid log (0.0) for j = jmax and n*x near an integer
+ if (-q > EPSILON) {
+ term = LogCom + jreal*Math.log(-q) + (Njreal - 1.0)
+ *Math.log1p (-q);
+ Sum += Sign*Math.exp (term);
+ }
+ Sign = -Sign;
+ LogCom += Math.log (Njreal/(j + 1));
+ }
+ // add the term j = 0
+ Sum += Math.exp ((n - 1)*Math.log1p (x));
+ return Sum*x;
+ }
+
+ //-----------------------------------------------------------
+ // For nx > NxParam, we use the other exact series for small
+ // n, and the asymptotic form for n larger than NPARAM
+ //-----------------------------------------------------------
+
+ if (n <= NPARAM) {
+ jmax = (int)(n*(1.0 - x));
+ if (1.0 - x - (double) jmax/n <= 0.0)
+ --jmax;
+ for (j = 1; j <= jmax; j++) {
+ jreal = j;
+ Njreal = n - j;
+ q = jreal/n + x;
+ term = LogCom+(jreal - 1.0)*Math.log(q) + Njreal*Math.log1p (-q);
+ Sum += Math.exp (term);
+ LogCom += Math.log (Njreal/(jreal + 1.0));
+ }
+ Sum *= x;
+
+ // add the term j = 0; avoid log (0.0)
+ if (1.0 > x)
+ Sum += Math.exp (n*Math.log1p (-x));
+ return 1.0 - Sum;
+ }
+
+ //--------------------------
+ // Use an asymptotic formula
+ //--------------------------
+
+ term = 2.0/3.0;
+ q = x*x*n;
+ Sum = 1.0 - Math.exp (-2.0*q)*(1.0 - term*x*(1.0 - x*(1.0 - term*q)
+ - term/n*(0.2 - 19.0/15.0*q + term*q*q)));
+ return Sum;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the \ks+ distribution function $F_n(x)$ with parameter $n$.
+ \begin{latexonly}%
+ The distribution function can be approximated via the
+ following expressions:
+ \begin {eqnarray}
+ P[D_n^+ \le x]
+ &=& 1 - x \sum_{i=0}^{\lfloor n(1-x)\rfloor} \binom{n}{i}
+ \left(\frac{i}{n} + x \right)^{i-1}
+ \left(1 - \frac{i}{n} - x \right)^{n-i} \label {DistKS1} \\[4pt]
+ &=& x \sum_{j=0}^{\lfloor nx \rfloor}
+ \binom{n}{j} \left(\frac{j}{n} - x \right)^j
+ \left(1 - \frac{j}{n} + x \right)^{n-j-1} \label {DistKS2} \\[4pt]
+ &\approx& 1 - e^{-2 n x^2} \left[1 - \frac{2x}{3} \left(
+ 1 - x\left(1 - \frac{2 n x^2}{3}\right) \right.\right.
+ \nonumber\\[4pt]
+ && \left.\left. - \frac{2}{3n} \left(\frac{1}{5} - \frac{19 n x^2}{15}
+ + \frac{2n^2 x^4}{3}\right) \right) + O(n^{-2}) \right].
+ \label {DistKS3}
+ \end {eqnarray}
+ Formula (\ref{DistKS1}) and (\ref{DistKS2}) can be found in
+ \cite{tDUR73a}, equations (2.1.12) and (2.1.16), while (\ref{DistKS3})
+ can be found in \cite{tDAR60a}.
+ Formula (\ref{DistKS2}) becomes numerically unstable as $nx$ increases.
+ The approximation (\ref{DistKS3}) is simpler to compute and excellent
+ when $nx$ is large.
+\end{latexonly}%
+ The relative error on $F_n(x) = P[D_n^+ \le x]$ is always less than
+ $10^{-5}$. %, and the relative error on $\bar F_n(x)$ is less than
+ % $10^{-1}$ when $\bar F_n(x) > 10^{-10}$.
+% The {\em absolute\/} error on $\bar F_n(x)$ is less than $10^{-11}$
+% when $\bar F_n(x) < 10^{-10}$.
+% It must be noted that Korolyuk \cite{tKOR59a} and many others
+% gave an erroneous formula for the asymptotic form (\ref{DistKS3}).
+% Darling \cite{tDAR60a} gives the correct formula with references.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double KSPlusbarAsymp (int n, double x) {
+ /* Compute the probability of the complementary KSPlus distribution
+ using an asymptotic formula */
+ double t = (6.0*n*x + 1);
+ double z = t*t/(18.0*n);
+ double v = 1.0 - (2.0*z*z - 4.0*z - 1.0)/(18.0*n);
+ if (v <= 0.0)
+ return 0.0;
+ v = v*Math.exp(-z);
+ if (v >= 1.0)
+ return 1.0;
+ return v;
+ }
+
+
+//-------------------------------------------------------------------------
+
+ static double KSPlusbarUpper (int n, double x) {
+ /* Compute the probability of the complementary KS+ distribution in
+ the upper tail using Smirnov's stable formula */
+
+ if (n > 200000)
+ return KSPlusbarAsymp (n, x);
+
+ int jmax = (int) (n* (1.0 - x));
+ // Avoid log(0) for j = jmax and q ~ 1.0
+ if ((1.0 - x - (double)jmax/n) <= 0.0)
+ jmax--;
+
+ int jdiv;
+ if (n > 3000)
+ jdiv = 2;
+ else
+ jdiv = 3;
+ int j = jmax / jdiv + 1;
+
+ double LogCom = Num.lnFactorial(n) - Num.lnFactorial(j)
+ - Num.lnFactorial(n-j);
+ final double LOGJM = LogCom;
+
+ final double EPSILON = 1.0E-12;
+ double q;
+ double term;
+ double t;
+ double Sum = 0.0;
+
+ while (j <= jmax) {
+ q = (double)j / n + x;
+ term = LogCom + (j - 1)*Math.log(q) + (n-j)*Math.log1p(-q);
+ t = Math.exp (term);
+ Sum += t;
+ LogCom += Math.log ((double)(n - j)/(j + 1));
+ if (t <= Sum*EPSILON)
+ break;
+ j++;
+ }
+
+ j = jmax / jdiv;
+ LogCom = LOGJM + Math.log ((double)(j+1)/(n - j));
+
+ while (j > 0) {
+ q = (double)j / n + x;
+ term = LogCom+(j - 1)*Math.log (q)+ (n - j)*Math.log1p (-q);
+ t = Math.exp (term);
+ Sum += t;
+ LogCom += Math.log ((double)j/(n - j + 1));
+ if (t <= Sum*EPSILON)
+ break;
+ j--;
+ }
+
+ Sum *= x;
+ // add the term j = 0
+ Sum += Math.exp (n*Math.log1p (-x));
+ return Sum;
+ }\end{hide}
+
+ public static double barF (int n, double x)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException (
+ "Calling kolmogorovSmirnovPlus with n < 1");
+ if (x <= 0.0)
+ return 1.0;
+ if ((x >= 1.0) || (n*x*x >= 365.0))
+ return 0.0;
+ if (n == 1)
+ return 1.0 - x;
+
+ final double NXPARAM = 6.5; // frontier: alternating series
+ final int NPARAM = 4000; // frontier: non-alternating series
+ final int NASYMP = 200000; // frontier: asymptotic
+
+ // the alternating series is stable and fast for n*x very small
+ if (n*x <= NXPARAM)
+ return 1.0 - cdf(n, x);
+
+ if (n >= NASYMP)
+ return KSPlusbarAsymp (n, x);
+
+ if ((n <= NPARAM) || (n*x*x > 1.0))
+ return KSPlusbarUpper(n,x);
+
+ return KSPlusbarAsymp (n, x);
+ // return (1.0 - 2.0*x/3.0)*Math.exp(-2.0*n*x*x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function $\bar F_n(x)$
+ with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return 1.0;
+ if (u == 0.0)
+ return 0.0;
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 1.0, f, 1e-8);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse $x = F^{-1}(u)$ of the distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ supportA = 0.0;
+ supportB = 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an array containing the parameter $n$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/LaplaceDist.java b/source/umontreal/iro/lecuyer/probdist/LaplaceDist.java
new file mode 100644
index 0000000..6b7a6e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LaplaceDist.java
@@ -0,0 +1,324 @@
+
+
+/*
+ * Class: LaplaceDist
+ * Description: Laplace distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Laplace</EM> distribution.
+ * It has location parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>e</I><SUP>-| x-<I>μ</I>|/<I>β</I></SUP>/(2<I>β</I>) for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">(1/2)<I>e</I><SUP>(x-<I>μ</I>)/<I>β</I></SUP></TD>
+ * <TD ALIGN="LEFT"> if <I>x</I> <= <I>μ</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">1 - (1/2)<I>e</I><SUP>(<I>μ</I>-x)/<I>β</I></SUP></TD>
+ * <TD ALIGN="LEFT"> otherwise, </TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ * and its inverse is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I><SUP>-1</SUP>(<I>u</I>) =</TD>
+ * <TD ALIGN="LEFT"><I>β</I>log(2<I>u</I>) + <I>μ</I></TD>
+ * <TD ALIGN="LEFT"> if 0 <= <I>u</I> <= 1/2,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I><SUP>-1</SUP>(<I>u</I>) =</TD>
+ * <TD ALIGN="LEFT"><I>μ</I> - <I>β</I>log(2(1 - <I>u</I>))</TD>
+ * <TD ALIGN="LEFT"> otherwise. </TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ *
+ */
+public class LaplaceDist extends ContinuousDistribution {
+ private double mu;
+ private double beta;
+
+
+
+ /**
+ * Constructs a <TT>LaplaceDist</TT> object with default
+ * parameters <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>.
+ *
+ */
+ public LaplaceDist() {
+ mu = 0;
+ beta = 1;
+ }
+
+
+ /**
+ * Constructs a <TT>LaplaceDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT> and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>.
+ *
+ */
+ public LaplaceDist (double mu, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ this.mu = mu;
+ this.beta = beta;
+ }
+
+
+ public double density (double x) {
+ return density (mu, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, beta, u);
+ }
+
+ public double getMean() {
+ return LaplaceDist.getMean (mu, beta);
+ }
+
+ public double getVariance() {
+ return LaplaceDist.getVariance (mu, beta);
+ }
+
+ public double getStandardDeviation() {
+ return LaplaceDist.getStandardDeviation (mu, beta);
+ }
+
+ /**
+ * Computes the Laplace density function.
+ *
+ */
+ public static double density (double mu, double beta, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ return Math.exp (-Math.abs (x - mu)/beta)/(2.0*beta);
+ }
+
+
+ /**
+ * Computes the Laplace distribution function.
+ *
+ */
+ public static double cdf (double mu, double beta, double x) {
+ if (x <= mu)
+ return Math.exp ((x - mu)/beta)/2.0;
+ else
+ return 1.0 - Math.exp ((mu - x)/beta)/2.0;
+ }
+
+
+ /**
+ * Computes the Laplace complementary distribution function.
+ *
+ */
+ public static double barF (double mu, double beta, double x) {
+ if (x <= mu)
+ return 1.0 - Math.exp ((x - mu)/beta)/2.0;
+ else
+ return Math.exp ((mu - x)/beta)/2.0;
+ }
+
+ //====================================================
+ // code taken and adapted from unuran
+ // file /distribution/c_laplca_gen.c
+ //====================================================
+
+ /**
+ * Computes the inverse Laplace distribution function.
+ *
+ */
+ public static double inverseF (double mu, double beta, double u) {
+ // transform to random variate
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u should be in [0,1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ double x = (u>0.5) ? -Math.log(2.-2*u) : Math.log(2*u);
+ return mu + beta*x;
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>μ</I>, <I>β</I>)</SPAN> of the Laplace distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[2];
+
+ parameters[0] = EmpiricalDist.getMedian (x, n);
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += Math.abs (x[i] - parameters[0]);
+ parameters[1] = sum / (double) n;
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a Laplace distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>β</I></SPAN> estimated using the maximum likelihood method based on the
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static LaplaceDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new LaplaceDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN> of the Laplace
+ * distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the mean of the Laplace distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN>
+ *
+ */
+ public static double getMean (double mu, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return mu;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 2<I>β</I><SUP>2</SUP></SPAN>
+ * of the Laplace distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the variance of the Laplace distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 2<I>β</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double mu, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return (2.0 * beta * beta);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the Laplace
+ * distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the standard deviation of the Laplace distribution
+ *
+ */
+ public static double getStandardDeviation (double mu, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return (Num.RAC2 * beta);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu+ ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/LaplaceDist.tex b/source/umontreal/iro/lecuyer/probdist/LaplaceDist.tex
new file mode 100644
index 0000000..f9be0bb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LaplaceDist.tex
@@ -0,0 +1,341 @@
+\defmodule {LaplaceDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Laplace\/} distribution\latex{ (see, e.g., \cite[page 165]{tJOH95b})}.
+It has location parameter $\mu$ and scale parameter $\beta > 0$.
+The density function is given by
+\begin{htmlonly}
+\eq
+ f(x) = e^{-|x - \mu|/\beta}/(2\beta)
+ \qquad\mbox{ for }-\infty < x < \infty.
+ \eqlabel{eq:flaplace}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac{e^{-|x - \mu|/\beta}}{2\beta}
+ \qquad\mbox{ for }-\infty < x < \infty.
+ \eqlabel{eq:flaplace}
+\endeq
+\end{latexonly}
+The distribution function is
+\begin{htmlonly}
+\[\begin{array}{rll}
+ F (x) =& (1/2) e^{(x - \mu)/\beta} &\qquad\mbox { if } x\le\mu, \\[5pt]
+ F (x) =& 1 - (1/2) e^{(\mu - x)/\beta} &\qquad\mbox { otherwise, }
+\end{array}\]
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ F (x) = \left\{\begin{array}{ll}
+ \frac12 e^{(x - \mu)/\beta} & \mbox { if } x\le\mu, \\[5pt]
+ 1 - \frac12 e^{(\mu - x)/\beta} & \mbox { otherwise, }
+\end{array}\right.
+\endeq
+\end{latexonly}
+and its inverse is
+\begin{htmlonly}
+\[\begin{array}{rll}
+ F^{-1} (u) =& \beta\log (2u) + \mu &\qquad\mbox { if } 0\le u\le 1/2, \\[5pt]
+ F^{-1} (u) =& \mu - \beta\log (2(1-u)) &\qquad\mbox { otherwise. }
+\end{array}\]
+\end{htmlonly}
+\begin{latexonly}
+\eq
+F^{-1} (u) = \left\{\begin{array}{ll}
+ \beta\log (2u) + \mu & \mbox { if } 0\le u\le \frac12, \\[5pt]
+ \mu - \beta\log (2(1-u)) & \mbox { otherwise. }
+\end{array}\right.
+\endeq
+\end{latexonly}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: LaplaceDist
+ * Description: Laplace distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+
+import umontreal.iro.lecuyer.util.Num;\end{hide}
+
+public class LaplaceDist extends ContinuousDistribution\begin{hide} {
+ private double mu;
+ private double beta;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public LaplaceDist()\begin{hide} {
+ mu = 0;
+ beta = 1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{LaplaceDist} object with default
+ parameters $\mu = 0$ and $\beta = 1$.
+\end{tabb}
+\begin{code}
+
+ public LaplaceDist (double mu, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ this.mu = mu;
+ this.beta = beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{LaplaceDist} object with parameters
+ $\mu$ = \texttt{mu} and $\beta$ = \texttt{beta}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (mu, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, beta, u);
+ }
+
+ public double getMean() {
+ return LaplaceDist.getMean (mu, beta);
+ }
+
+ public double getVariance() {
+ return LaplaceDist.getVariance (mu, beta);
+ }
+
+ public double getStandardDeviation() {
+ return LaplaceDist.getStandardDeviation (mu, beta);
+ }\end{hide}
+
+ public static double density (double mu, double beta, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ return Math.exp (-Math.abs (x - mu)/beta)/(2.0*beta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the Laplace density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu, double beta, double x)\begin{hide} {
+ if (x <= mu)
+ return Math.exp ((x - mu)/beta)/2.0;
+ else
+ return 1.0 - Math.exp ((mu - x)/beta)/2.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the Laplace distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double mu, double beta, double x)\begin{hide} {
+ if (x <= mu)
+ return 1.0 - Math.exp ((x - mu)/beta)/2.0;
+ else
+ return Math.exp ((mu - x)/beta)/2.0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the Laplace complementary distribution function.
+\end{tabb}
+\begin{code}\begin{hide}
+ //====================================================
+ // code taken and adapted from unuran
+ // file /distribution/c_laplca_gen.c
+ //====================================================\end{hide}
+
+ public static double inverseF (double mu, double beta, double u)\begin{hide} {
+ // transform to random variate
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u should be in [0,1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ double x = (u>0.5) ? -Math.log(2.-2*u) : Math.log(2*u);
+ return mu + beta*x;
+ }\end{hide}
+\end{code}
+\begin{tabb}Computes the inverse Laplace distribution function.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[2];
+
+ parameters[0] = EmpiricalDist.getMedian (x, n);
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += Math.abs (x[i] - parameters[0]);
+ parameters[1] = sum / (double) n;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\mu, \beta)$ of the Laplace distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\mu$, $\beta$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\mu , \hat\beta)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat{\mu} & = & \mbox{the median of } \{x_1,\ldots,x_n\}\\
+ \hat{\beta} & = & \frac{1}{n} \sum_{i=1}^{n} |x_i - \hat{\mu}|.
+ \end{eqnarray*}
+ See \cite[page 172]{tJOH95b}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\mu}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static LaplaceDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new LaplaceDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Laplace distribution with parameters $\mu$
+ and $\beta$ estimated using the maximum likelihood method based on the
+ $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \mu$ of the Laplace
+ distribution with parameters $\mu$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Laplace distribution $E[X] = \mu$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double mu, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return (2.0 * beta * beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = 2 \beta^2$
+ of the Laplace distribution with parameters $\mu$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Laplace distribution $\mbox{Var}[X] = 2 \beta^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return (Num.RAC2 * beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the Laplace
+ distribution with parameters $\mu$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Laplace distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\mu$.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\mu$, $\beta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu+ ", beta = " + beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/LogarithmicDist.java b/source/umontreal/iro/lecuyer/probdist/LogarithmicDist.java
new file mode 100644
index 0000000..4891469
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LogarithmicDist.java
@@ -0,0 +1,326 @@
+
+
+/*
+ * Class: LogarithmicDist
+ * Description: logarithmic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for
+ * the <EM>logarithmic</EM> distribution. It has shape parameter
+ * <SPAN CLASS="MATH"><I>θ</I></SPAN>, where
+ * <SPAN CLASS="MATH">0 < <I>θ</I> < 1</SPAN>.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = - <I>θ</I><SUP>x</SUP>/(<I>x</I> log(1 - <I>θ</I>) for <I>x</I> = 1, 2, 3,...
+ * </DIV><P></P>
+ * Its distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = -1/log(1-<I>θ</I>)∑<SUB>i=1</SUB><SUP>x</SUP><I>θ</I><SUP>i</SUP>/<I>i</I>, & for <I>x</I> > 0.
+ * </DIV><P></P>
+ *
+ */
+public class LogarithmicDist extends DiscreteDistributionInt {
+
+ private double theta;
+ private double t;
+
+ private static class Function implements MathFunction {
+ protected double mean;
+
+ public Function (double mean) {
+ this.mean = mean;
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0 || x >= 1.0) return 1.0e200;
+ return (x + mean * (1.0 - x) * Math.log1p (-x));
+ }
+ }
+
+
+ /**
+ * Constructs a logarithmic distribution with parameter <SPAN CLASS="MATH"><I>θ</I> =</SPAN>
+ * <TT>theta</TT>.
+ *
+ */
+ public LogarithmicDist (double theta) {
+ setTheta (theta);
+ }
+
+
+ public double prob (int x) {
+ if (x < 1)
+ return 0;
+ return t*Math.pow (theta, x)/x;
+ }
+
+ public double cdf (int x) {
+ if (x < 1)
+ return 0;
+ double res = prob (1);
+ double term = res;
+ for (int i = 2; i <= x; i++) {
+ term *= theta;
+ res += term/i;
+ }
+ return res;
+ }
+
+ public double barF (int x) {
+ if (x <= 1)
+ return 1.0;
+ double res = prob (x);
+ double term = res;
+ int i = x + 1;
+ while (term > EPSILON) {
+ term *= theta*(i-1)/i;
+ res += term;
+ }
+ return res;
+ }
+
+ public int inverseFInt (double u) {
+ return inverseF (theta, u);
+ }
+
+ public double getMean() {
+ return LogarithmicDist.getMean (theta);
+ }
+
+ public double getVariance() {
+ return LogarithmicDist.getVariance (theta);
+ }
+
+ public double getStandardDeviation() {
+ return LogarithmicDist.getStandardDeviation (theta);
+ }
+
+ /**
+ * Computes the logarithmic probability <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double prob (double theta, int x) {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ if (x < 1)
+ return 0;
+ return -1.0/Math.log1p(-theta) * Math.pow (theta, x)/x;
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double cdf (double theta, int x) {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ if (x < 1)
+ return 0;
+ double res = prob (theta, 1);
+ double term = res;
+ for (int i = 2; i <= x; i++) {
+ term *= theta;
+ res += term/i;
+ }
+ return res;
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ * <SPAN CLASS="textit">WARNING:</SPAN> The complementary distribution function is defined as
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>.
+ *
+ */
+ public static double barF (double theta, int x) {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ if (x <= 1)
+ return 1.0;
+ double res = prob (theta, x);
+ double term = res;
+ int i = x + 1;
+ while (term > EPSILON) {
+ term *= theta*(i-1)/i;
+ res += term;
+ }
+ return res;
+ }
+
+
+ public static int inverseF (double theta, double u) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>θ</I></SPAN> of the logarithmic distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimate is returned in element 0
+ * of the returned array.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(&thetas;)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[1];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+
+ double mean = (double) sum / (double) n;
+
+ Function f = new Function (mean);
+ parameters[0] = RootFinder.brentDekker (1e-15, 1.0-1e-15, f, 1e-7);
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a logarithmic distribution with parameter
+ * <SPAN CLASS="MATH"><I>θ</I></SPAN> estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static LogarithmicDist getInstanceFromMLE (int[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new LogarithmicDist (parameters[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * of the logarithmic distribution with parameter <SPAN CLASS="MATH"><I>θ</I> =</SPAN> <TT>theta</TT>.
+ *
+ * @return the mean of the logarithmic distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = - <I>θ</I>/((1 - <I>θ</I>)<I>ln</I>(1 - <I>θ</I>))</SPAN>
+ *
+ */
+ public static double getMean (double theta) {
+ if (theta <= 0.0 || theta >= 1.0)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+
+ return ((-1 / Math.log1p(-theta)) * (theta / (1 - theta)));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * of the logarithmic distribution with parameter <SPAN CLASS="MATH"><I>θ</I> =</SPAN> <TT>theta</TT>.
+ *
+ * @return the variance of the logarithmic distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = - <I>θ</I>(<I>θ</I> + <I>ln</I>(1 - <I>θ</I>))/((1 - <I>θ</I>)<SUP>2</SUP>(<I>ln</I>(1 - <I>θ</I>))<SUP>2</SUP>)</SPAN>
+ *
+ */
+ public static double getVariance (double theta) {
+ if (theta <= 0.0 || theta >= 1.0)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+
+ double v = Math.log1p(-theta);
+ return ((-theta * (theta + v)) / ((1 - theta) * (1 - theta) * v * v));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the
+ * logarithmic distribution with parameter <SPAN CLASS="MATH"><I>θ</I> =</SPAN> <TT>theta</TT>.
+ *
+ * @return the standard deviation of the logarithmic distribution
+ *
+ */
+ public static double getStandardDeviation (double theta) {
+ return Math.sqrt (LogarithmicDist.getVariance (theta));
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>θ</I></SPAN> associated with this object.
+ *
+ */
+ public double getTheta() {
+ return theta;
+ }
+
+
+
+ /**
+ * Sets the <SPAN CLASS="MATH"><I>θ</I></SPAN> associated with this object.
+ *
+ */
+ public void setTheta (double theta) {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ this.theta = theta;
+ t = -1.0/Math.log1p (-theta);
+ supportA = 1;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {theta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : theta = " + theta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/LogarithmicDist.tex b/source/umontreal/iro/lecuyer/probdist/LogarithmicDist.tex
new file mode 100644
index 0000000..48b455a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LogarithmicDist.tex
@@ -0,0 +1,358 @@
+\defmodule {LogarithmicDist}
+
+Extends the class \class{DiscreteDistributionInt} for
+the {\em logarithmic\/} distribution. It has shape parameter
+$\theta$, where $0 < \theta <1$.
+Its mass function is
+\eq
+ p(x) = \latex{\frac{-\theta^x} {x\log(1- \theta)}}
+ \html{-\theta^x/(x\log(1- \theta)}
+ \qquad \mbox{for } x = 1,2,3,\dots \eqlabel{eq:flogar}
+\endeq
+Its distribution function is
+\begin{htmlonly}
+\eq
+ F(x) = {-1/\log(1- \theta)}\sum_{i=1}^x {\theta^i}/{i}, &
+ \mbox { for } x > 0.
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ F(x) =
+ \frac{-1}{\log (1 - \theta)}\sum_{i=1}^x \frac{\theta^i}{i}, \qquad
+ \mbox { for } x = 1, 2, 3, \ldots
+\endeq
+and is 0 for $ x\le 0$.
+\end{latexonly}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: LogarithmicDist
+ * Description: logarithmic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class LogarithmicDist extends DiscreteDistributionInt\begin{hide} {
+
+ private double theta;
+ private double t;
+
+ private static class Function implements MathFunction {
+ protected double mean;
+
+ public Function (double mean) {
+ this.mean = mean;
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0 || x >= 1.0) return 1.0e200;
+ return (x + mean * (1.0 - x) * Math.log1p (-x));
+ }
+ }
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public LogarithmicDist (double theta)\begin{hide} {
+ setTheta (theta);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a logarithmic distribution with parameter $\theta = $
+ \texttt{theta}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double prob (int x) {
+ if (x < 1)
+ return 0;
+ return t*Math.pow (theta, x)/x;
+ }
+
+ public double cdf (int x) {
+ if (x < 1)
+ return 0;
+ double res = prob (1);
+ double term = res;
+ for (int i = 2; i <= x; i++) {
+ term *= theta;
+ res += term/i;
+ }
+ return res;
+ }
+
+ public double barF (int x) {
+ if (x <= 1)
+ return 1.0;
+ double res = prob (x);
+ double term = res;
+ int i = x + 1;
+ while (term > EPSILON) {
+ term *= theta*(i-1)/i;
+ res += term;
+ }
+ return res;
+ }
+
+ public int inverseFInt (double u) {
+ return inverseF (theta, u);
+ }
+
+ public double getMean() {
+ return LogarithmicDist.getMean (theta);
+ }
+
+ public double getVariance() {
+ return LogarithmicDist.getVariance (theta);
+ }
+
+ public double getStandardDeviation() {
+ return LogarithmicDist.getStandardDeviation (theta);
+ }\end{hide}
+
+ public static double prob (double theta, int x)\begin{hide} {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ if (x < 1)
+ return 0;
+ return -1.0/Math.log1p(-theta) * Math.pow (theta, x)/x;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the logarithmic probability $p(x)$%
+\latex{ given in (\ref{eq:flogar}) }.
+ \end{tabb}
+\begin{code}
+
+ public static double cdf (double theta, int x)\begin{hide} {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ if (x < 1)
+ return 0;
+ double res = prob (theta, 1);
+ double term = res;
+ for (int i = 2; i <= x; i++) {
+ term *= theta;
+ res += term/i;
+ }
+ return res;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the distribution function $F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double theta, int x)\begin{hide} {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ if (x <= 1)
+ return 1.0;
+ double res = prob (theta, x);
+ double term = res;
+ int i = x + 1;
+ while (term > EPSILON) {
+ term *= theta*(i-1)/i;
+ res += term;
+ }
+ return res;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the complementary distribution function.
+\emph{WARNING:} The complementary distribution function is defined as
+$\bar F(x) = P[X \ge x]$.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ public static int inverseF (double theta, double u) {
+ throw new UnsupportedOperationException();
+ }\end{hide}
+
+ public static double[] getMLE (int[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[1];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+
+ double mean = (double) sum / (double) n;
+
+ Function f = new Function (mean);
+ parameters[0] = RootFinder.brentDekker (1e-15, 1.0-1e-15, f, 1e-7);
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $\theta$ of the logarithmic distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$. The estimate is returned in element 0
+ of the returned array.
+ \begin{detailed}
+ The maximum likelihood estimator $\hat{\theta}$ satisfies the equation
+ (see \cite[page 122]{mEVA00a})
+ \begin{eqnarray*}
+ \bar{x}_n = \frac{-\hat{\theta}}{(1 - \hat{\theta}) \ln(1 - \hat{\theta})}
+ \end{eqnarray*}
+ where $\bar{x}_n$ is the average of $x[0], \ldots, x[n-1]$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameter [$\hat{\theta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static LogarithmicDist getInstanceFromMLE (int[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new LogarithmicDist (parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a logarithmic distribution with parameter
+ $\theta$ estimated using the maximum likelihood method based on the $n$
+ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double theta)\begin{hide} {
+ if (theta <= 0.0 || theta >= 1.0)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+
+ return ((-1 / Math.log1p(-theta)) * (theta / (1 - theta)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+\begin{latexonly}
+ $$E[X] = \frac{-\theta}{(1 - \theta)\ln(1 - \theta)}$$
+\end{latexonly}
+ of the logarithmic distribution with parameter $\theta = $ \texttt{theta}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the logarithmic distribution
+ $E[X] = -\theta / ((1 - \theta) ln(1 - \theta))$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double theta)\begin{hide} {
+ if (theta <= 0.0 || theta >= 1.0)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+
+ double v = Math.log1p(-theta);
+ return ((-theta * (theta + v)) / ((1 - theta) * (1 - theta) * v * v));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+\begin{latexonly}
+ $$\mbox{Var}[X] = \frac{-\theta (\theta + \ln(1 - \theta))}{[(1 - \theta)
+ \ln(1 - \theta)]^2}$$
+\end{latexonly}
+ of the logarithmic distribution with parameter $\theta =$ \texttt{theta}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the logarithmic distribution
+ $\mbox{Var}[X] = -\theta (\theta + ln(1 - \theta)) / ((1 - \theta)^2
+(ln(1 - \theta))^2)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double theta)\begin{hide} {
+ return Math.sqrt (LogarithmicDist.getVariance (theta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the
+ logarithmic distribution with parameter $\theta = $ \texttt{theta}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the logarithmic distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getTheta()\begin{hide} {
+ return theta;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\theta$ associated with this object.
+\end{tabb}
+%% \pierre{Add \texttt{setTheta} method here, and similar
+%% \texttt{setParams} methods
+%% for all classes that follow. In most cases, the code in the constructor
+%% should be moved to \texttt{setParams} and the constructor should call it. }
+\begin{code}
+
+ public void setTheta (double theta)\begin{hide} {
+ if (theta <= 0 || theta >= 1)
+ throw new IllegalArgumentException ("theta not in range (0,1)");
+ this.theta = theta;
+ t = -1.0/Math.log1p (-theta);
+ supportA = 1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the $\theta$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {theta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : theta = " + theta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/LogisticDist.java b/source/umontreal/iro/lecuyer/probdist/LogisticDist.java
new file mode 100644
index 0000000..52bb148
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LogisticDist.java
@@ -0,0 +1,427 @@
+
+
+/*
+ * Class: LogisticDist
+ * Description: logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * <EM>logistic</EM> distribution.
+ * It has location parameter <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>λe</I><SUP>-<I>λ</I>(x-<I>α</I>)</SUP>)/((1 + <I>e</I><SUP>-<I>λ</I>(x-<I>α</I>)</SUP>)<SUP>2</SUP>) for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * and the distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1/[1 + <I>e</I><SUP>-<I>λ</I>(x-<I>α</I>)</SUP>] for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * For <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>α</I> = 0</SPAN>, one can write
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = (1+tanh(<I>x</I>/2))/2.
+ * </DIV><P></P>
+ * The inverse distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = ln(<I>u</I>/(1 - <I>u</I>))/<I>λ</I> + <I>α</I> for 0 <= <I>u</I> < 1.
+ * </DIV><P></P>
+ *
+ */
+public class LogisticDist extends ContinuousDistribution {
+ private double alpha;
+ private double lambda;
+
+ private static class Optim implements Lmder_fcn
+ {
+ protected double[] xi;
+ protected int n;
+
+ public Optim (double[] x, int n) {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public void fcn (int m, int n, double[] x, double[] fvec, double[][] fjac, int iflag[])
+ {
+ if (x[2] <= 0.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fjac[1][1] = BIG;
+ fjac[1][2] = 0.0;
+ fjac[2][1] = 0.0;
+ fjac[2][2] = BIG;
+ return;
+ }
+
+ double sum;
+ double prod;
+
+ if (iflag[1] == 1)
+ {
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (1.0 / (1.0 + Math.exp (x[2] * (xi[i] - x[1]))));
+ fvec[1] = sum - n / 2.0;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = x[2] * (xi[i] - x[1]);
+ sum -= prod * Math.tanh(prod/2.0);
+ }
+ fvec[2] = sum - n;
+ }
+ else if (iflag[1] == 2)
+ {
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= x[2] * prod / ((1 + prod) * (1 + prod));
+ }
+ fjac[1][1] = sum;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= (xi[i] - x[1]) * prod / ((1 + prod) * (1 + prod));
+ }
+ fjac[1][2] = sum;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= (x[2] * ((-1.0 + prod) * (1.0 + prod) - (2.0 * (x[2] * (xi[i] - x[1])) * prod))) / ((1.0 + prod) * (1.0 + prod));
+ }
+ fjac[2][1] = sum;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= ((x[1] - xi[1]) * ((-1.0 + prod) * (1.0 + prod) - (2.0 * (x[2] * (xi[i] - x[1])) * prod))) / ((1.0 + prod) * (1.0 + prod));
+ }
+ fjac[2][2] = sum;
+ }
+ }
+ }
+
+
+
+ /**
+ * Constructs a <TT>LogisticDist</TT> object with default parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>.
+ *
+ */
+ public LogisticDist() {
+ setParams (0.0, 1.0);
+ }
+
+
+ /**
+ * Constructs a <TT>LogisticDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>.
+ *
+ */
+ public LogisticDist (double alpha, double lambda) {
+ setParams (alpha, lambda);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, lambda, u);
+ }
+
+ public double getMean() {
+ return LogisticDist.getMean (alpha, lambda);
+ }
+
+ public double getVariance() {
+ return LogisticDist.getVariance (alpha, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return LogisticDist.getStandardDeviation (alpha, lambda);
+ }
+
+ /**
+ * Computes the density function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ */
+ public static double density (double alpha, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ double z = lambda * (x - alpha);
+ if (z >= -100.0) {
+ double v = Math.exp(-z);
+ return lambda * v / ((1.0 + v)*(1.0 + v));
+ }
+ return lambda * Math.exp(z);
+ }
+
+
+ /**
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double cdf (double alpha, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ double z = lambda * (x - alpha);
+ if (z >= -100.0)
+ return 1.0 / (1.0 + Math.exp(-z));
+ return Math.exp(z);
+ }
+
+
+ /**
+ * Computes the complementary distribution function <SPAN CLASS="MATH">1 - <I>F</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double barF (double alpha, double lambda, double x) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ double z = lambda * (x - alpha);
+ if (z <= 100.0)
+ return 1.0 / (1.0 + Math.exp(z));
+ return Math.exp(-z);
+ }
+
+
+ /**
+ * Computes the inverse distribution function <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ *
+ */
+ public static double inverseF (double alpha, double lambda, double u) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+
+ return Math.log (u/(1.0 - u))/lambda + alpha;
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>λ</I>)</SPAN> of the logistic distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+
+ double[] param = new double[3];
+ param[1] = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += ((x[i] - param[1]) * (x[i] - param[1]));
+
+ param[2] = Math.sqrt (Math.PI * Math.PI * n / (3.0 * sum));
+
+ double[] fvec = new double [3];
+ double[][] fjac = new double[3][3];
+ int[] iflag = new int[2];
+ int[] info = new int[2];
+ int[] ipvt = new int[3];
+ Optim system = new Optim (x, n);
+
+ Minpack_f77.lmder1_f77 (system, 2, 2, param, fvec, fjac, 1e-5, info, ipvt);
+
+ double parameters[] = new double[2];
+ parameters[0] = param[1];
+ parameters[1] = param[2];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a logistic distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>λ</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static LogisticDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new LogisticDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I></SPAN> of the logistic distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the logistic distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I></SPAN>
+ *
+ */
+ public static double getMean (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return alpha;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>π</I><SUP>2</SUP>/(3<I>λ</I><SUP>2</SUP>)</SPAN> of the logistic distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the logistic distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1/3<I>π</I><SUP>2</SUP>*(1/<I>λ</I><SUP>2</SUP>)</SPAN>
+ *
+ */
+ public static double getVariance (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return ((Math.PI * Math.PI / 3) * (1 / (lambda * lambda)));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the logistic distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the logistic distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt(1.0 / 3.0) * Math.PI / lambda);
+ }
+
+
+ /**
+ * Return the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double alpha, double lambda) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.alpha = alpha;
+ this.lambda = lambda;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/LogisticDist.tex b/source/umontreal/iro/lecuyer/probdist/LogisticDist.tex
new file mode 100644
index 0000000..4890363
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LogisticDist.tex
@@ -0,0 +1,445 @@
+\defmodule{LogisticDist}
+
+Extends the class \class{ContinuousDistribution} for the
+{\em logistic\/} distribution\latex{ (e.g., \cite[page 115]{tJOH95b})}.
+It has location parameter $\alpha$
+and scale parameter $\lambda > 0$.
+The density is
+\begin{htmlonly}
+\eq f (x) = (\lambda e^{-\lambda (x - \alpha )})/
+ ((1 + e^{-\lambda (x - \alpha )})^2)
+ \qquad \qquad \mbox{for } -\infty < x < \infty.
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq f (x) = \frac{\lambda e^{-\lambda (x - \alpha )}}
+ {(1 + e^{-\lambda (x - \alpha )})^2}
+ \qquad \qquad \mbox{for } -\infty < x < \infty, \eqlabel{eq:flogistic}
+\endeq
+\end{latexonly}
+and the distribution function is
+\eq
+ F(x) = \latex{\frac 1{1 + e^{-\lambda (x - \alpha)}}}
+ \html{1/[1 + e^{-\lambda (x - \alpha)}]}
+ \qquad \qquad \mbox{for } -\infty < x < \infty. \eqlabel{eq:Flogistic}
+\endeq
+For $\lambda=1$ and $\alpha=0$, one can write
+\begin{latexonly}
+\eq
+ F(x) = \frac{1 + \tanh({x/2})}{2}.
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ F(x) = {(1 + \tanh({x/2}))} / {2}.
+\endeq
+\end{htmlonly}
+The inverse distribution function is given by
+$$
+ F^{-1}(u) = \ln (u/(1-u))/\lambda + \alpha
+ \qquad \mbox{for } 0 \le u < 1.
+$$
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: LogisticDist
+ * Description: logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import optimization.*;
+\end{hide}
+
+public class LogisticDist extends ContinuousDistribution\begin{hide} {
+ private double alpha;
+ private double lambda;
+
+ private static class Optim implements Lmder_fcn
+ {
+ protected double[] xi;
+ protected int n;
+
+ public Optim (double[] x, int n) {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public void fcn (int m, int n, double[] x, double[] fvec, double[][] fjac, int iflag[])
+ {
+ if (x[2] <= 0.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fjac[1][1] = BIG;
+ fjac[1][2] = 0.0;
+ fjac[2][1] = 0.0;
+ fjac[2][2] = BIG;
+ return;
+ }
+
+ double sum;
+ double prod;
+
+ if (iflag[1] == 1)
+ {
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += (1.0 / (1.0 + Math.exp (x[2] * (xi[i] - x[1]))));
+ fvec[1] = sum - n / 2.0;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = x[2] * (xi[i] - x[1]);
+ sum -= prod * Math.tanh(prod/2.0);
+ }
+ fvec[2] = sum - n;
+ }
+ else if (iflag[1] == 2)
+ {
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= x[2] * prod / ((1 + prod) * (1 + prod));
+ }
+ fjac[1][1] = sum;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= (xi[i] - x[1]) * prod / ((1 + prod) * (1 + prod));
+ }
+ fjac[1][2] = sum;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= (x[2] * ((-1.0 + prod) * (1.0 + prod) - (2.0 * (x[2] * (xi[i] - x[1])) * prod))) / ((1.0 + prod) * (1.0 + prod));
+ }
+ fjac[2][1] = sum;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ prod = Math.exp (x[2] * (xi[i] - x[1]));
+ sum -= ((x[1] - xi[1]) * ((-1.0 + prod) * (1.0 + prod) - (2.0 * (x[2] * (xi[i] - x[1])) * prod))) / ((1.0 + prod) * (1.0 + prod));
+ }
+ fjac[2][2] = sum;
+ }
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public LogisticDist()\begin{hide} {
+ setParams (0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{LogisticDist} object with default parameters
+ $\alpha = 0$ and $\lambda =1$.
+ \end{tabb}
+\begin{code}
+
+ public LogisticDist (double alpha, double lambda)\begin{hide} {
+ setParams (alpha, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{LogisticDist} object with parameters
+ $\alpha$ = \texttt{alpha} and $\lambda$ = \texttt{lambda}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (alpha, lambda, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, lambda, u);
+ }
+
+ public double getMean() {
+ return LogisticDist.getMean (alpha, lambda);
+ }
+
+ public double getVariance() {
+ return LogisticDist.getVariance (alpha, lambda);
+ }
+
+ public double getStandardDeviation() {
+ return LogisticDist.getStandardDeviation (alpha, lambda);
+ }\end{hide}
+
+ public static double density (double alpha, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ double z = lambda * (x - alpha);
+ if (z >= -100.0) {
+ double v = Math.exp(-z);
+ return lambda * v / ((1.0 + v)*(1.0 + v));
+ }
+ return lambda * Math.exp(z);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function $f(x)$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ double z = lambda * (x - alpha);
+ if (z >= -100.0)
+ return 1.0 / (1.0 + Math.exp(-z));
+ return Math.exp(z);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function $F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double lambda, double x)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ double z = lambda * (x - alpha);
+ if (z <= 100.0)
+ return 1.0 / (1.0 + Math.exp(z));
+ return Math.exp(-z);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function $1-F(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double lambda, double u)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+
+ return Math.log (u/(1.0 - u))/lambda + alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse distribution function $F^{-1}(u)$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+
+ double[] param = new double[3];
+ param[1] = sum / (double) n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += ((x[i] - param[1]) * (x[i] - param[1]));
+
+ param[2] = Math.sqrt (Math.PI * Math.PI * n / (3.0 * sum));
+
+ double[] fvec = new double [3];
+ double[][] fjac = new double[3][3];
+ int[] iflag = new int[2];
+ int[] info = new int[2];
+ int[] ipvt = new int[3];
+ Optim system = new Optim (x, n);
+
+ Minpack_f77.lmder1_f77 (system, 2, 2, param, fvec, fjac, 1e-5, info, ipvt);
+
+ double parameters[] = new double[2];
+ parameters[0] = param[1];
+ parameters[1] = param[2];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha, \lambda)$ of the logistic distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\lambda$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\alpha, \hat\lambda)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \sum_{i=1}^{n} \frac1{1 + e^{\hat{\lambda} (x_i - \hat{\alpha})}} & = &
+ \frac{n}{2}\\[6pt]
+ \sum_{i=1}^{n} \hat{\lambda} (x_i - \hat{\alpha}) \frac{1 - e^{\hat{\lambda}
+ (x_i - \hat{\alpha})}}{1 + e^{\hat{\lambda} (x_i - \hat{\alpha})}} & = & n.
+ \end{eqnarray*}
+ See \cite[page 128]{mEVA00a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameter [$\hat{\alpha}$, $\hat{\lambda}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static LogisticDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new LogisticDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a logistic distribution with parameters $\alpha$
+ and $\lambda$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \alpha$ of the logistic distribution
+ with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the logistic distribution $E[X] = \alpha$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return ((Math.PI * Math.PI / 3) * (1 / (lambda * lambda)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] =
+ \pi^2 /(3\lambda^2)$ of the logistic distribution
+ with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the logistic distribution
+ $\mbox{Var}[X] = 1 / 3 \pi^2 * (1 / \lambda^2)$
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (Math.sqrt(1.0 / 3.0) * Math.PI / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the logistic distribution
+ with parameters $\alpha$ and $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the logistic distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Return the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\lambda$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double lambda)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.alpha = alpha;
+ this.lambda = lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\alpha$ and $\lambda$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\lambda$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/LoglogisticDist.java b/source/umontreal/iro/lecuyer/probdist/LoglogisticDist.java
new file mode 100644
index 0000000..7f9ff39
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LoglogisticDist.java
@@ -0,0 +1,425 @@
+
+
+/*
+ * Class: LoglogisticDist
+ * Description: log-logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.Misc;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * <EM>Log-Logistic</EM> distribution with shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>
+ * and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>α</I>(<I>x</I>/<I>β</I>)<SUP><I>α</I>-1</SUP>)/(<I>β</I>[1 + (<I>x</I>/<I>β</I>)<SUP><I>α</I></SUP>]<SUP>2</SUP>) for <I>x</I> > 0
+ * </DIV><P></P>
+ * and its distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1/(1 + (<I>x</I>/<I>β</I>)<SUP>-<I>α</I></SUP>) for <I>x</I> > 0.
+ * </DIV><P></P>
+ * The complementary distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * bar(F)(<I>x</I>) = 1/(1 + (<I>x</I>/<I>β</I>)<SUP><I>α</I></SUP>) for <I>x</I> > 0.
+ * </DIV><P></P>
+ *
+ */
+public class LoglogisticDist extends ContinuousDistribution {
+ private double alpha;
+ private double beta;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+
+ public Optim (double[] x, int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ if ((p[1] <= 0.0) || (p[2] <= 0.0))
+ return 1e200;
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ double tmp = density (p[1], p[2], xi[i]);
+ if (tmp > 0.0)
+ sum -= Math.log (tmp);
+ else
+ sum += 709.0; // log (Double.MIN_VALUE)
+ }
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+
+
+
+ /**
+ * Constructs a log-logistic distribution with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public LoglogisticDist (double alpha, double beta) {
+ setParams (alpha, beta);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean() {
+ return getMean (alpha, beta);
+ }
+
+ public double getVariance() {
+ return getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (alpha, beta);
+ }
+
+ /**
+ * Computes the density function
+ * for a log-logisitic distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double density (double alpha, double beta, double x) {
+ double denominateur;
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= 0.0 || x >= Double.MAX_VALUE / 2.0)
+ return 0.0;
+
+ if (x <= beta) {
+ double v = Math.pow (x / beta, alpha);
+ denominateur = 1.0 + v;
+ denominateur *= denominateur * beta;
+ return alpha * v * beta / (x * denominateur);
+ } else {
+ double v = Math.pow (beta / x, alpha);
+ denominateur = 1.0 + v;
+ denominateur *= denominateur * beta;
+ return alpha * v * beta / (x * denominateur);
+ }
+ }
+
+
+ /**
+ * Computes the distribution function of the
+ * log-logistic distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double cdf (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= Double.MAX_VALUE / 2.0)
+ return 1.0;
+ double z = x/beta;
+ if (z >= 1.0)
+ return 1.0 / (1.0 + Math.pow (1.0/z, alpha));
+ double v = Math.pow (z, alpha);
+ return v/(v + 1.0);
+ }
+
+
+ /**
+ * Computes the complementary distribution function
+ * of the log-logistic distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double barF (double alpha, double beta, double x) {
+ double power;
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= Double.MAX_VALUE / 2.0)
+ return 0.0;
+
+ double z = x/beta;
+ if (z <= 1.0)
+ return 1.0 / (1.0 + Math.pow (z, alpha));
+ double v = Math.pow (1.0/z, alpha);
+ return v/(v + 1.0);
+ }
+
+
+ /**
+ * Computes the inverse of the log-logistic distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double u) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in (0, 1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ if (u <= 0.5)
+ return (beta * Math.pow (u / (1.0 - u), 1.0 / alpha));
+ else
+ return (beta / Math.pow ((1.0 - u)/ u, 1.0 / alpha));
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>β</I>)</SPAN> of the log-logistic distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ Optim system = new Optim (x, n);
+
+ double[] parameters = new double[2];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[3][3];
+ double[] udiag = new double[3];
+
+ param[2] = EmpiricalDist.getMedian (x, n);
+
+ if (param[2] < 0) throw new IllegalArgumentException ("median < 0");
+ if (param[2] <= 0) param[2] = 1.0;
+
+ int m = Math.round ((float) n / 4.0f);
+ double q1 = Misc.quickSelect (x, n, m);
+
+ if (q1 < 0) throw new IllegalArgumentException ("x[i] < 0");
+ if (q1 > 0)
+ param[1] = Math.log (3) / (Math.log(param[2]) - Math.log(q1));
+ else
+ param[1] = 1.0;
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (int i = 0; i < 2; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a log-logistic distribution with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> estimated using the maximum likelihood method based on
+ * the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static LoglogisticDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new LoglogisticDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * of the log-logistic distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the mean of the log-logistic distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>βθ</I> cosec(<I>θ</I>), where <I>θ</I> = <I>π</I>/<I>α</I></SPAN>
+ *
+ */
+ public static double getMean (double alpha, double beta) {
+ double theta;
+
+ if (alpha <= 1.0)
+ throw new IllegalArgumentException ("alpha <= 1");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ theta = Math.PI / alpha;
+
+ return (beta * theta / Math.sin (theta));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * of the log-logistic distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the variance of the log-logistic distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>β</I><SUP>2</SUP><I>θ</I>(2cosec(2<I>θ</I>) - <I>θ</I>[cosec(<I>θ</I>)]<SUP>2</SUP>), where <I>θ</I> = <I>π</I>/<I>α</I></SPAN>
+ *
+ */
+ public static double getVariance (double alpha, double beta) {
+ double theta;
+
+ if (alpha <= 2.0)
+ throw new IllegalArgumentException ("alpha <= 2");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ theta = Math.PI / alpha;
+
+ return (beta * beta * theta * ((2.0 / Math.sin (2.0 * theta)) -
+ (theta / (Math.sin (theta) * Math.sin (theta)))));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the log-logistic
+ * distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the standard deviation of the log-logistic distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta) {
+ return Math.sqrt (getVariance (alpha, beta));
+ }
+
+
+ /**
+ * Return the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ this.alpha = alpha;
+ this.beta = beta;
+ supportA = 0.0;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/LoglogisticDist.tex b/source/umontreal/iro/lecuyer/probdist/LoglogisticDist.tex
new file mode 100644
index 0000000..cf255ba
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LoglogisticDist.tex
@@ -0,0 +1,464 @@
+\defmodule{LoglogisticDist}
+
+Extends the class \class{ContinuousDistribution} for the
+{\em Log-Logistic\/} distribution with shape parameter $\alpha > 0$
+and scale parameter $\beta > 0$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = (\alpha (x / \beta)^{\alpha - 1}) / (\beta [1 + (x / \beta)^\alpha]^2)
+\qquad \qquad \mbox{for } x > 0
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac{\alpha (x / \beta)^{\alpha - 1}}{\beta [1 + (x / \beta)^\alpha]^2}
+\qquad \qquad \mbox{for } x > 0
+\eqlabel{eq:floglogistic}
+\endeq
+\end{latexonly}
+and its distribution function is
+\begin{htmlonly}
+\eq
+ F(x) = 1 / (1 + (x / \beta)^{-\alpha})
+\qquad \qquad \mbox{for } x > 0.
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ F(x) = \frac{1}{1 + (\frac{x}{\beta})^{-\alpha}}
+\qquad \qquad \mbox{for } x > 0.
+\eqlabel{eq:Floglogistic}
+\endeq
+\end{latexonly}
+The complementary distribution is
+\begin{htmlonly}
+\eq
+ \bar F(x) = 1 / (1 + (x / \beta)^{\alpha})
+\qquad \qquad \mbox{for } x > 0.
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ \bar F(x) = \frac{1}{1 + (\frac{x}{\beta})^{\alpha}}
+\qquad \qquad \mbox{for } x > 0.
+\eqlabel{eq:Fbarloglogistic}
+\endeq
+\end{latexonly}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: LoglogisticDist
+ * Description: log-logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.Misc;
+import optimization.*;
+\end{hide}
+
+public class LoglogisticDist extends ContinuousDistribution\begin{hide} {
+ private double alpha;
+ private double beta;
+
+ private static class Optim implements Uncmin_methods
+ {
+ private int n;
+ private double[] xi;
+
+ public Optim (double[] x, int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double f_to_minimize (double[] p)
+ {
+ if ((p[1] <= 0.0) || (p[2] <= 0.0))
+ return 1e200;
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ double tmp = density (p[1], p[2], xi[i]);
+ if (tmp > 0.0)
+ sum -= Math.log (tmp);
+ else
+ sum += 709.0; // log (Double.MIN_VALUE)
+ }
+ return sum;
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public LoglogisticDist (double alpha, double beta)\begin{hide} {
+ setParams (alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a log-logistic distribution with parameters
+ $\alpha$ and $\beta$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}
+\begin{code}
+
+ public double density (double x) {
+ return density (alpha, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean() {
+ return getMean (alpha, beta);
+ }
+
+ public double getVariance() {
+ return getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (alpha, beta);
+ }
+\end{code}
+\end{hide}\begin{code}
+ public static double density (double alpha, double beta, double x)\begin{hide} {
+ double denominateur;
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= 0.0 || x >= Double.MAX_VALUE / 2.0)
+ return 0.0;
+
+ if (x <= beta) {
+ double v = Math.pow (x / beta, alpha);
+ denominateur = 1.0 + v;
+ denominateur *= denominateur * beta;
+ return alpha * v * beta / (x * denominateur);
+ } else {
+ double v = Math.pow (beta / x, alpha);
+ denominateur = 1.0 + v;
+ denominateur *= denominateur * beta;
+ return alpha * v * beta / (x * denominateur);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:floglogistic})
+ for a log-logisitic distribution with parameters $\alpha$
+ and $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ if (x >= Double.MAX_VALUE / 2.0)
+ return 1.0;
+ double z = x/beta;
+ if (z >= 1.0)
+ return 1.0 / (1.0 + Math.pow (1.0/z, alpha));
+ double v = Math.pow (z, alpha);
+ return v/(v + 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function (\ref{eq:Floglogistic}) of the
+ log-logistic distribution with parameters $\alpha$ and $\beta$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double x)\begin{hide} {
+ double power;
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ if (x >= Double.MAX_VALUE / 2.0)
+ return 0.0;
+
+ double z = x/beta;
+ if (z <= 1.0)
+ return 1.0 / (1.0 + Math.pow (z, alpha));
+ double v = Math.pow (1.0/z, alpha);
+ return v/(v + 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function (\ref{eq:Fbarloglogistic})
+ of the log-logistic distribution with parameters $\alpha$ and $\beta$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double beta, double u)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in (0, 1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u <= 0.0)
+ return 0.0;
+
+ if (u <= 0.5)
+ return (beta * Math.pow (u / (1.0 - u), 1.0 / alpha));
+ else
+ return (beta / Math.pow ((1.0 - u)/ u, 1.0 / alpha));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse of the log-logistic distribution
+ with parameters $\alpha$ and $\beta$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ Optim system = new Optim (x, n);
+
+ double[] parameters = new double[2];
+ double[] xpls = new double[3];
+ double[] param = new double[3];
+ double[] fpls = new double[3];
+ double[] gpls = new double[3];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[3][3];
+ double[] udiag = new double[3];
+
+ param[2] = EmpiricalDist.getMedian (x, n);
+
+ if (param[2] < 0) throw new IllegalArgumentException ("median < 0");
+ if (param[2] <= 0) param[2] = 1.0;
+
+ int m = Math.round ((float) n / 4.0f);
+ double q1 = Misc.quickSelect (x, n, m);
+
+ if (q1 < 0) throw new IllegalArgumentException ("x[i] < 0");
+ if (q1 > 0)
+ param[1] = Math.log (3) / (Math.log(param[2]) - Math.log(q1));
+ else
+ param[1] = 1.0;
+
+ Uncmin_f77.optif0_f77 (2, param, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (int i = 0; i < 2; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\beta)$ of the log-logistic distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\beta$].
+ \begin{detailed}
+ The estimate of the parameters is given by maximizing numerically the
+ log-likelihood function, using the Uncmin package \cite{iSCHa,iVERa}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static LoglogisticDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new LoglogisticDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a log-logistic distribution with parameters
+ $\alpha$ and $\beta$ estimated using the maximum likelihood method based on
+ the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta)\begin{hide} {
+ double theta;
+
+ if (alpha <= 1.0)
+ throw new IllegalArgumentException ("alpha <= 1");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ theta = Math.PI / alpha;
+
+ return (beta * theta / Math.sin (theta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+\begin{latexonly}
+ $E[X] = \beta \theta \,\mbox{cosec}(\theta), \mbox{ where } \theta = \pi/\alpha,$
+\end{latexonly}
+ of the log-logistic distribution with parameters $\alpha$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the log-logistic distribution
+ $E[X] = \beta \theta \,\mbox{cosec}(\theta),
+ \mbox{ where } \theta = \pi / \alpha$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta)\begin{hide} {
+ double theta;
+
+ if (alpha <= 2.0)
+ throw new IllegalArgumentException ("alpha <= 2");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ theta = Math.PI / alpha;
+
+ return (beta * beta * theta * ((2.0 / Math.sin (2.0 * theta)) -
+ (theta / (Math.sin (theta) * Math.sin (theta)))));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+\begin{latexonly}
+ $\mbox{Var}[X] = \beta^2 \theta (2 \mbox{cosec}(2 \theta) -
+ \theta[\mbox{cosec}(\theta)]^2), \mbox{ where } \theta = {\pi/\alpha},$
+\end{latexonly}
+ of the log-logistic distribution with parameters $\alpha$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the log-logistic distribution
+ $\mbox{Var}[X] = \beta^2 \theta (2 \mbox{cosec}(2 \theta) -
+ \theta[\mbox{cosec}(\theta)]^2), \mbox{ where } \theta = \pi / \alpha$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta)\begin{hide} {
+ return Math.sqrt (getVariance (alpha, beta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the log-logistic
+ distribution with parameters $\alpha$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the log-logistic distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Return the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ this.alpha = alpha;
+ this.beta = beta;
+ supportA = 0.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\alpha$ and $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\beta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/LognormalDist.java b/source/umontreal/iro/lecuyer/probdist/LognormalDist.java
new file mode 100644
index 0000000..2e30c3b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LognormalDist.java
@@ -0,0 +1,355 @@
+
+
+/*
+ * Class: LognormalDist
+ * Description: lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * <EM>lognormal</EM> distribution.
+ * (See also the <EM>Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN></EM> distribution <TT>JohnsonSLDist</TT>
+ * in this package.) It has scale
+ * parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> and shape parameter
+ * <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = ((2π)<SUP>1/2</SUP><I>σx</I>)<SUP>-1</SUP><I>e</I><SUP>-(ln(x)-<I>μ</I>)<SUP>2</SUP>/(2<I>σ</I><SUP>2</SUP>)</SUP> for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and 0 elsewhere. The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>Φ</I>((ln(<I>x</I>)-<I>μ</I>)/<I>σ</I>) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard normal distribution function.
+ * Its inverse is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>e</I><SUP><I>μ</I>+<I>σΦ</I><SUP>-1</SUP>(u)</SUP> for 0 <= <I>u</I> < 1.
+ * </DIV><P></P>
+ * If <SPAN CLASS="MATH">ln(<I>Y</I>)</SPAN> has a <EM>normal</EM> distribution, then <SPAN CLASS="MATH"><I>Y</I></SPAN> has a
+ * <EM>lognormal</EM> distribution with the same parameters.
+ *
+ * <P>
+ * This class relies on the methods
+ * {@link NormalDist#cdf01 NormalDist.cdf01} and
+ * {@link NormalDist#inverseF01 NormalDist.inverseF01}
+ * of {@link NormalDist} to approximate <SPAN CLASS="MATH"><I>Φ</I></SPAN> and <SPAN CLASS="MATH"><I>Φ</I><SUP>-1</SUP></SPAN>.
+ *
+ */
+public class LognormalDist extends ContinuousDistribution {
+ private double mu;
+ private double sigma;
+
+
+ /**
+ * Constructs a <TT>LognormalDist</TT> object with default
+ * parameters <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I> = 1</SPAN>.
+ *
+ */
+ public LognormalDist() {
+ setParams (0.0, 1.0);
+ }
+
+
+ /**
+ * Constructs a <TT>LognormalDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I></SPAN> = <TT>sigma</TT>.
+ *
+ */
+ public LognormalDist (double mu, double sigma) {
+ setParams (mu, sigma);
+ }
+
+
+ public double density (double x) {
+ return density (mu, sigma, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return LognormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return LognormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return LognormalDist.getStandardDeviation (mu, sigma);
+ }
+
+ /**
+ * Computes the lognormal density function
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ */
+ public static double density (double mu, double sigma, double x) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (x <= 0)
+ return 0;
+ double diff = Math.log (x) - mu;
+ return Math.exp (-diff*diff/(2*sigma*sigma))/
+ (Math.sqrt (2*Math.PI)*sigma*x);
+ }
+
+
+ /**
+ * Computes the lognormal distribution function, using
+ * {@link NormalDist#cdf01 NormalDist.cdf01}.
+ *
+ */
+ public static double cdf (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ return NormalDist.cdf01 ((Math.log (x) - mu)/sigma);
+ }
+
+
+ /**
+ * Computes the lognormal complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>,
+ * using {@link NormalDist#barF01 NormalDist.barF01}.
+ *
+ */
+ public static double barF (double mu, double sigma, double x) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ return NormalDist.barF01 ((Math.log (x) - mu)/sigma);
+ }
+
+
+ /**
+ * Computes the inverse of the lognormal distribution function,
+ * using {@link NormalDist#inverseF01 NormalDist.inverseF01}.
+ *
+ */
+ public static double inverseF (double mu, double sigma, double u) {
+ double t, v;
+
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (Num.DBL_EPSILON >= 1.0 - u)
+ return Double.POSITIVE_INFINITY;
+
+ if (u <= 0.0)
+ return 0.0;
+
+ t = NormalDist.inverseF01 (u);
+ v = mu + sigma * t;
+
+ if ((t >= XBIG) || (v >= Num.DBL_MAX_EXP * Num.LN2))
+ return Double.POSITIVE_INFINITY;
+ if ((t <= -XBIG) || (v <= -Num.DBL_MAX_EXP*Num.LN2))
+ return 0.0;
+
+ return Math.exp (v);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>μ</I>, <I>σ</I>)</SPAN> of the lognormal distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(σ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+ double parameters[];
+ parameters = new double[2];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ sum += Math.log (x[i]);
+ else
+ sum += LN_EPS; // log(DBL_MIN / 2)
+ }
+ parameters[0] = sum / n;
+
+ double temp;
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ temp = Math.log (x[i]) - parameters[0];
+ else
+ temp = LN_EPS - parameters[0];
+ sum += temp * temp;
+ }
+ parameters[1] = Math.sqrt (sum / n);
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a lognormal distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static LognormalDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new LognormalDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>e</I><SUP><I>μ</I>+<I>σ</I><SUP>2</SUP>/2</SUP></SPAN>
+ * of the lognormal distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the mean of the lognormal distribution
+ *
+ */
+ public static double getMean (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (Math.exp(mu + (sigma * sigma) / 2.0));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>e</I><SUP>2<I>μ</I>+<I>σ</I><SUP>2</SUP></SUP>(<I>e</I><SUP><I>σ</I><SUP>2</SUP></SUP> - 1)</SPAN>
+ * of the lognormal distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the variance of the lognormal distribution
+ *
+ */
+ public static double getVariance (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (Math.exp(2.0 * mu + sigma * sigma) * (Math.exp(sigma * sigma) - 1.0));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the lognormal distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the standard deviation of the lognormal distribution
+ *
+ */
+ public static double getStandardDeviation (double mu, double sigma) {
+ return Math.sqrt (LognormalDist.getVariance (mu, sigma));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double mu, double sigma) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ supportA = 0.0;
+ }
+
+
+ /**
+ * Returns a table containing the parameters of the current distribution,
+ * in the order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, sigma};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/LognormalDist.tex b/source/umontreal/iro/lecuyer/probdist/LognormalDist.tex
new file mode 100644
index 0000000..68a3f07
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LognormalDist.tex
@@ -0,0 +1,358 @@
+\defmodule {LognormalDist}
+
+Extends the class \class{ContinuousDistribution} for the
+{\em lognormal\/} distribution \cite{tJOH95a}.
+(See also the {\em Johnson $S_L$\/} distribution \texttt{JohnsonSLDist}
+in this package.) It has scale
+parameter $\mu$ and shape parameter $\sigma > 0$.
+The density is
+\eq
+ f(x) = \latex{\frac{1}{\sqrt{2\pi}\sigma x}}\html{(\sqrt{2\pi}\sigma x)^{-1}}
+ e^{-(\ln (x) - \mu)^2/(2\sigma^2)}
+ \qquad\mbox{for } x>0, \eqlabel{eq:flognormal}
+\endeq
+and 0 elsewhere. The distribution function is
+\eq
+ F(x) = \Phi \left({(\ln(x) - \mu)/\sigma}\right) \qquad\mbox{for } x>0,
+\endeq
+where $\Phi$ is the standard normal distribution function.
+Its inverse is given by
+\eq
+ F^{-1}(u) = e^{\mu + \sigma\Phi^{-1} (u)}
+ \qquad \mbox{for } 0 \le u < 1.
+\endeq
+If $\ln(Y)$ has a {\em normal\/} distribution, then $Y$ has a
+{\em lognormal\/} distribution with the same parameters.
+
+This class relies on the methods
+ \clsexternalmethod{}{NormalDist}{cdf01}{} and
+ \clsexternalmethod{}{NormalDist}{inverseF01}{}
+of \class{NormalDist} to approximate $\Phi$ and $\Phi^{-1}$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: LognormalDist
+ * Description: lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;\end{hide}
+
+public class LognormalDist extends ContinuousDistribution\begin{hide} {
+ private double mu;
+ private double sigma; \end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public LognormalDist()\begin{hide} {
+ setParams (0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{LognormalDist} object with default
+ parameters $\mu = 0$ and $\sigma = 1$.
+ \end{tabb}
+\begin{code}
+
+ public LognormalDist (double mu, double sigma)\begin{hide} {
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{LognormalDist} object with parameters
+ $\mu$ = \texttt{mu} and $\sigma$ = \texttt{sigma}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (mu, sigma, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (mu, sigma, x);
+ }
+
+ public double barF (double x) {
+ return barF (mu, sigma, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (mu, sigma, u);
+ }
+
+ public double getMean() {
+ return LognormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return LognormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return LognormalDist.getStandardDeviation (mu, sigma);
+ }\end{hide}
+
+ public static double density (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (x <= 0)
+ return 0;
+ double diff = Math.log (x) - mu;
+ return Math.exp (-diff*diff/(2*sigma*sigma))/
+ (Math.sqrt (2*Math.PI)*sigma*x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the lognormal density function
+ $f(x)$\latex{ in (\ref{eq:flognormal})}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (x <= 0.0)
+ return 0.0;
+ return NormalDist.cdf01 ((Math.log (x) - mu)/sigma);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the lognormal distribution function, using
+ \clsexternalmethod{}{NormalDist}{cdf01}{}.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (x <= 0.0)
+ return 1.0;
+ return NormalDist.barF01 ((Math.log (x) - mu)/sigma);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the lognormal complementary distribution function $\bar{F}(x)$,
+ using \clsexternalmethod{}{NormalDist}{barF01}{}.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double mu, double sigma, double u)\begin{hide} {
+ double t, v;
+
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (Num.DBL_EPSILON >= 1.0 - u)
+ return Double.POSITIVE_INFINITY;
+
+ if (u <= 0.0)
+ return 0.0;
+
+ t = NormalDist.inverseF01 (u);
+ v = mu + sigma * t;
+
+ if ((t >= XBIG) || (v >= Num.DBL_MAX_EXP * Num.LN2))
+ return Double.POSITIVE_INFINITY;
+ if ((t <= -XBIG) || (v <= -Num.DBL_MAX_EXP*Num.LN2))
+ return 0.0;
+
+ return Math.exp (v);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the lognormal distribution function,
+ using \clsexternalmethod{}{NormalDist}{inverseF01}{}.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+ double parameters[];
+ parameters = new double[2];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ sum += Math.log (x[i]);
+ else
+ sum += LN_EPS; // log(DBL_MIN / 2)
+ }
+ parameters[0] = sum / n;
+
+ double temp;
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ temp = Math.log (x[i]) - parameters[0];
+ else
+ temp = LN_EPS - parameters[0];
+ sum += temp * temp;
+ }
+ parameters[1] = Math.sqrt (sum / n);
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\mu, \sigma)$ of the lognormal distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\mu$, $\sigma$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\mu , \hat\sigma)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat{\mu} & = & \frac{1}{n} \sum_{i=1}^{n} \ln(x_i)\\[6pt]
+ \hat{\sigma} & = & \sqrt{\frac{1}{n} \sum_{i=1}^{n} (\ln(x_i) - \hat{\mu})^2}.
+ \end{eqnarray*}
+ See \cite[page 220]{tJOH95a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\mu}$, $\hat{\sigma}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static LognormalDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new LognormalDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a lognormal distribution with parameters $\mu$ and $\sigma$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (Math.exp(mu + (sigma * sigma) / 2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = e^{\mu + \sigma^2/2}$
+ of the lognormal distribution with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the lognormal distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (Math.exp(2.0 * mu + sigma * sigma) * (Math.exp(sigma * sigma) - 1.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+ $\mbox{Var}[X] = e^{2\mu + \sigma^2}(e^{\sigma^2} - 1)$
+ of the lognormal distribution with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the lognormal distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double sigma)\begin{hide} {
+ return Math.sqrt (LognormalDist.getVariance (mu, sigma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the lognormal distribution with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the lognormal distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ supportA = 0.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\mu$ and $\sigma$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, sigma};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a table containing the parameters of the current distribution,
+ in the order: [$\mu$, $\sigma$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/LognormalDistFromMoments.java b/source/umontreal/iro/lecuyer/probdist/LognormalDistFromMoments.java
new file mode 100644
index 0000000..c0a83cc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LognormalDistFromMoments.java
@@ -0,0 +1,65 @@
+
+
+/*
+ * Class: LognormalDistFromMoments
+ * Description: lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Extends the {@link LognormalDist} class with a constructor accepting the
+ * mean <SPAN CLASS="MATH"><I>m</I></SPAN> and the variance <SPAN CLASS="MATH"><I>v</I></SPAN> of the distribution as arguments.
+ * The mean and variance of a lognormal random variable with
+ * parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are
+ *
+ * <SPAN CLASS="MATH"><I>e</I><SUP><I>μ</I>+<I>σ</I><SUP>2</SUP>/2</SUP></SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>e</I><SUP>2<I>μ</I>+<I>σ</I><SUP>2</SUP></SUP>(<I>e</I><SUP><I>σ</I><SUP>2</SUP></SUP> - 1)</SPAN> respectively, so
+ * the parameters are given by
+ * <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP> = ln(<I>v</I>/<I>m</I><SUP>2</SUP>+1)</SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = ln(<I>m</I>) - <I>σ</I><SUP>2</SUP>/2</SPAN>.
+ *
+ */
+public class LognormalDistFromMoments extends LognormalDist {
+
+
+
+ public LognormalDistFromMoments (double mean, double var) {
+ super (getMu (mean, var), Math.sqrt (getSigma2 (mean, var)));
+ }
+
+ private static double getMu (double mean, double var) {
+ final double sigma2 = getSigma2 (mean, var);
+ return Math.log (mean) - sigma2 / 2.0;
+ }
+
+ private static double getSigma2 (double mean, double var) {
+ if (mean <= 0)
+ throw new IllegalArgumentException ("Mean must be positive");
+ if (var <= 0)
+ throw new IllegalArgumentException ("Variance must be positive");
+ return Math.log (var / (mean * mean) + 1);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/LognormalDistFromMoments.tex b/source/umontreal/iro/lecuyer/probdist/LognormalDistFromMoments.tex
new file mode 100644
index 0000000..fd65312
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/LognormalDistFromMoments.tex
@@ -0,0 +1,73 @@
+\defmodule{LognormalDistFromMoments}
+
+Extends the \class{LognormalDist} class with a constructor accepting the
+mean $m$ and the variance $v$ of the distribution as arguments.
+The mean and variance of a lognormal random variable with
+parameters $\mu$ and $\sigma$ are
+$e^{\mu+\sigma^2/2}$ and
+$e^{2\mu + \sigma^2}(e^{\sigma^2} - 1)$ respectively, so
+the parameters are given by
+\begin{latexonly} $\sigma=\sqrt{\ln(v/m^2+1)}$ \end{latexonly}
+\begin{htmlonly} $\sigma^2={\ln(v/m^2+1)}$ \end{htmlonly}
+and
+$\mu=\ln(m) - \sigma^2/2$.
+
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LognormalDistFromMoments
+ * Description: lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class LognormalDistFromMoments extends LognormalDist\begin{hide} {
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public LognormalDistFromMoments (double mean, double var)\begin{hide} {
+ super (getMu (mean, var), Math.sqrt (getSigma2 (mean, var)));
+ }
+
+ private static double getMu (double mean, double var) {
+ final double sigma2 = getSigma2 (mean, var);
+ return Math.log (mean) - sigma2 / 2.0;
+ }
+
+ private static double getSigma2 (double mean, double var) {
+ if (mean <= 0)
+ throw new IllegalArgumentException ("Mean must be positive");
+ if (var <= 0)
+ throw new IllegalArgumentException ("Variance must be positive");
+ return Math.log (var / (mean * mean) + 1);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/NakagamiDist.java b/source/umontreal/iro/lecuyer/probdist/NakagamiDist.java
new file mode 100644
index 0000000..953e4de
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NakagamiDist.java
@@ -0,0 +1,369 @@
+
+
+/*
+ * Class: NakagamiDist
+ * Description: Nakagami distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Nakagami</EM> distribution with location parameter <SPAN CLASS="MATH"><I>a</I></SPAN>,
+ * scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN> and shape parameter <SPAN CLASS="MATH"><I>c</I> > 0</SPAN>.
+ * The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 2<I>λ</I><SUP>c</SUP>/<I>Γ</I>(<I>c</I>) (<I>x</I> - <I>a</I>)<SUP>2c-1</SUP> <I>e</I><SUP>-<I>λ</I>(x-a)<SUP>2</SUP></SUP> for <I>x</I> > <I>a</I>,
+ * </DIV><P></P>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 0 for <I>x</I> <= <I>a</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function.
+ *
+ */
+public class NakagamiDist extends ContinuousDistribution {
+ protected double a; // Location parameter
+ protected double lambda; // Scale parameter
+ protected double c; // Shape parameter
+ private double factor;
+ private double ratio; // Gamma(c + 1/2)/Gamma(c)
+
+
+
+ /**
+ * Constructs a <TT>NakagamiDist</TT> object with parameters <SPAN CLASS="MATH"><I>a</I> =</SPAN>
+ * <TT>a</TT>, <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>.
+ *
+ */
+ public NakagamiDist (double a, double lambda, double c) {
+ setParams (a, lambda, c);
+ }
+
+
+ public double density (double x) {
+ if (x <= a) return 0.0;
+ return 2.0 * Math.exp( factor
+ + Math.log(x-a)*(2.0*c-1.0)
+ - lambda*(x-a)*(x-a) );
+ }
+
+ public double cdf (double x) {
+ return cdf (a, lambda, c, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, lambda, c, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, lambda, c, u);
+ }
+
+ public double getMean() {
+ return a + ratio/Math.sqrt(lambda);
+ }
+
+ public double getVariance() {
+ return (c - ratio*ratio)/lambda;
+ }
+
+ public double getStandardDeviation() {
+ return Math.sqrt(getVariance ());
+ }
+
+ /**
+ * Computes the density function of the <EM>Nakagami</EM> distribution.
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @param x the value at which the density is evaluated
+ *
+ * @return returns the density function
+ *
+ */
+ public static double density (double a, double lambda, double c,
+ double x) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+
+ return 2.0 * Math.exp( Math.log(lambda)*c - Num.lnGamma(c)
+ + Math.log(x-a)*(2.0*c-1.0)
+ - lambda*(x-a)*(x-a) );
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @param x the value at which the distribution is evaluated
+ *
+ * @return returns the cdf function
+ *
+ */
+ public static double cdf (double a, double lambda, double c, double x) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+
+ return GammaDist.cdf(c, 12, lambda*(x-a)*(x-a));
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @param x the value at which the complementary distribution is evaluated
+ *
+ * @return returns the complementary distribution function
+ *
+ */
+ public static double barF (double a, double lambda, double c, double x) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 1.0;
+ return GammaDist.barF(c, 12, lambda*(x-a)*(x-a));
+ }
+
+
+ /**
+ * Computes the inverse of the distribution function.
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @param u the value at which the inverse distribution is evaluated
+ *
+ * @return returns the inverse distribution function
+ *
+ */
+ public static double inverseF (double a, double lambda, double c,
+ double u) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) return a;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ double res = GammaDist.inverseF(c, 12, u);
+ return a + Math.sqrt(res/lambda);
+ }
+
+
+ /**
+ * .
+ *
+ * Computes and returns the mean
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>E</I>[<I>X</I>] = <I>a</I> + 1#1 2#2.
+ * </DIV><P></P>
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @return returns the mean
+ *
+ */
+ public static double getMean (double a, double lambda, double c) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ return a + Num.gammaRatioHalf(c) / Math.sqrt(lambda);
+ }
+
+
+ /**
+ * .
+ *
+ * Computes and returns the variance
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * Var[<I>X</I>] = 3#3[<I>c</I> - ([tex2html_wrap_indisplay259])<SUP>2</SUP>].
+ * </DIV><P></P>
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @return returns the variance
+ *
+ */
+ public static double getVariance (double a, double lambda, double c) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ double rat = Num.gammaRatioHalf(c);
+ return (c - rat*rat) / lambda;
+ }
+
+
+ /**
+ * Computes the standard deviation of the Nakagami distribution with
+ * parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN> and <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @return returns the standard deviation
+ *
+ */
+ public static double getStandardDeviation (double a, double lambda,
+ double c) {
+ return Math.sqrt (NakagamiDist.getVariance (a, lambda, c));
+ }
+
+
+ /**
+ * Returns the location parameter <SPAN CLASS="MATH"><I>a</I></SPAN> of this object.
+ *
+ * @return returns the location parameter
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the scale parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ * @return returns the scale parameter
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Returns the shape parameter <SPAN CLASS="MATH"><I>c</I></SPAN> of this object.
+ *
+ * @return returns the shape parameter
+ *
+ */
+ public double getC() {
+ return c;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN> and <SPAN CLASS="MATH"><I>c</I></SPAN> of this object.
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ *
+ */
+ public void setParams (double a, double lambda, double c) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ this.a = a;
+ this.lambda = lambda;
+ this.c = c;
+ factor = (Math.log(lambda)*c - Num.lnGamma(c));
+ ratio = Num.gammaRatioHalf(c);
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>, <SPAN CLASS="MATH"><I>c</I></SPAN>].
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>, <SPAN CLASS="MATH"><I>c</I></SPAN>]
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {a, lambda, c};
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ * @return returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + ", lambda = " + lambda
+ + ", c = " + c;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/NakagamiDist.tex b/source/umontreal/iro/lecuyer/probdist/NakagamiDist.tex
new file mode 100644
index 0000000..11d32c1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NakagamiDist.tex
@@ -0,0 +1,354 @@
+\defmodule {NakagamiDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Nakagami\/} distribution with location parameter $a$,
+scale parameter $\lambda > 0$ and shape parameter $c > 0$.
+The density is
+\begin{htmlonly}
+\eq
+ f(x) = 2\lambda^c / \Gamma(c) \; (x-a)^{2c-1}\; e^{-\lambda (x-a)^{2}}
+ \qquad \mbox {for } x > a,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac{2\lambda^c}{\Gamma(c)}
+ (x-a)^{2c-1}
+ e^{-{\lambda}(x-a)^{2}}
+ \qquad \mbox {for } x > a,\eqlabel{eq:fnakagami}
+\endeq
+\end{latexonly}
+$$
+f(x) = 0 \qquad \mbox{ for } x \le a,
+$$
+where $\Gamma$ is the gamma function.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NakagamiDist
+ * Description: Nakagami distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class NakagamiDist extends ContinuousDistribution\begin{hide} {
+ protected double a; // Location parameter
+ protected double lambda; // Scale parameter
+ protected double c; // Shape parameter
+ private double factor;
+ private double ratio; // Gamma(c + 1/2)/Gamma(c)
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NakagamiDist (double a, double lambda, double c)\begin{hide} {
+ setParams (a, lambda, c);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{NakagamiDist} object with parameters $a =$
+ \texttt{a}, $\lambda =$ \texttt{lambda} and $c =$ \texttt{c}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ if (x <= a) return 0.0;
+ return 2.0 * Math.exp( factor
+ + Math.log(x-a)*(2.0*c-1.0)
+ - lambda*(x-a)*(x-a) );
+ }
+
+ public double cdf (double x) {
+ return cdf (a, lambda, c, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, lambda, c, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, lambda, c, u);
+ }
+
+ public double getMean() {
+ return a + ratio/Math.sqrt(lambda);
+ }
+
+ public double getVariance() {
+ return (c - ratio*ratio)/lambda;
+ }
+
+ public double getStandardDeviation() {
+ return Math.sqrt(getVariance ());
+ }\end{hide}
+
+ public static double density (double a, double lambda, double c,
+ double x)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+
+ return 2.0 * Math.exp( Math.log(lambda)*c - Num.lnGamma(c)
+ + Math.log(x-a)*(2.0*c-1.0)
+ - lambda*(x-a)*(x-a) );
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function of the {\em Nakagami\/} distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \param{x}{the value at which the density is evaluated}
+ \return{returns the density function}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double a, double lambda, double c, double x)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+
+ return GammaDist.cdf(c, 12, lambda*(x-a)*(x-a));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \param{x}{the value at which the distribution is evaluated}
+ \return{returns the cdf function}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double a, double lambda, double c, double x)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 1.0;
+ return GammaDist.barF(c, 12, lambda*(x-a)*(x-a));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \param{x}{the value at which the complementary distribution is evaluated}
+ \return{returns the complementary distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double a, double lambda, double c,
+ double u)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+ if (u <= 0.0) return a;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+ double res = GammaDist.inverseF(c, 12, u);
+ return a + Math.sqrt(res/lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse of the distribution function.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \param{u}{the value at which the inverse distribution is evaluated}
+ \return{returns the inverse distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double a, double lambda, double c)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ return a + Num.gammaRatioHalf(c) / Math.sqrt(lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+ $$
+E[X] = a + \frac{1}{\sqrt{\lambda}}\; \frac{\Gamma(c+1/2)}{\Gamma(c)}.
+$$
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \return{returns the mean}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double a, double lambda, double c)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ double rat = Num.gammaRatioHalf(c);
+ return (c - rat*rat) / lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+ $$
+\mbox{Var}[X] = \frac{1}{\lambda} \left[c -
+\left(\frac{\Gamma(c+1/2)}{\Gamma(c)}\right)^2\right].
+$$
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \return{returns the variance}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double a, double lambda,
+ double c) \begin{hide} {
+ return Math.sqrt (NakagamiDist.getVariance (a, lambda, c));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard deviation of the Nakagami distribution with
+ parameters $a$, $\lambda$ and $c$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \return{returns the standard deviation}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the location parameter $a$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{returns the location parameter}
+\end{htmlonly}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the scale parameter $\lambda$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{returns the scale parameter}
+\end{htmlonly}
+\begin{code}
+
+ public double getC()\begin{hide} {
+ return c;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the shape parameter $c$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{returns the shape parameter}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double a, double lambda, double c)\begin{hide} {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ this.a = a;
+ this.lambda = lambda;
+ this.c = c;
+ factor = (Math.log(lambda)*c - Num.lnGamma(c));
+ ratio = Num.gammaRatioHalf(c);
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the parameters $a$, $\lambda$ and $c$ of this object.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {a, lambda, c};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$a$, $\lambda$, $c$].
+\end{tabb}
+\begin{htmlonly}
+ \return{returns the parameters [$a$, $\lambda$, $c$]}
+\end{htmlonly}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : a = " + a + ", lambda = " + lambda
+ + ", c = " + c;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{returns a \texttt{String} containing information about the current distribution.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/NegativeBinomialDist.java b/source/umontreal/iro/lecuyer/probdist/NegativeBinomialDist.java
new file mode 100644
index 0000000..d252734
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NegativeBinomialDist.java
@@ -0,0 +1,933 @@
+
+
+/*
+ * Class: NegativeBinomialDist
+ * Description: negative binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for
+ * the <SPAN CLASS="textit">negative binomial</SPAN> distribution with real
+ * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>, where <SPAN CLASS="MATH"><I>n</I> > 0</SPAN> and
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN>.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:fmass-negbin"></A>
+ * <I>p</I>(<I>x</I>) = <I>Γ</I>(<I>n</I> + <I>x</I>)/(<I>Γ</I>(<I>n</I>) <I>x</I>!)<I>p</I><SUP>n</SUP>(1 - <I>p</I>)<SUP>x</SUP>, for <I>x</I> = 0, 1, 2,…
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function.
+ *
+ * <P>
+ * If <SPAN CLASS="MATH"><I>n</I></SPAN> is an integer, <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN> can be interpreted as the probability
+ * of having <SPAN CLASS="MATH"><I>x</I></SPAN> failures before the <SPAN CLASS="MATH"><I>n</I></SPAN>-th success in a sequence of
+ * independent Bernoulli trials with probability of success <SPAN CLASS="MATH"><I>p</I></SPAN>. This special
+ * case is implemented as the Pascal distribution (see {@link PascalDist}).
+ *
+ */
+public class NegativeBinomialDist extends DiscreteDistributionInt {
+ protected double n;
+ protected double p;
+ private static final double EPS2 = 1000.0*EPSILON;
+
+ private static class Func1 implements MathFunction {
+ protected int m;
+ protected int[] x;
+ protected double p;
+
+ public Func1 (double p, int[] x, int m) {
+ this.p = p;
+ this.m = m;
+ this.x = x;
+ }
+
+ public double evaluate (double gam) {
+ if (gam <= 0 ) return 1.0e100;
+
+ double sum = 0.0;
+ for (int j = 0; j < m; j++)
+ sum += Num.digamma (gam + x[j]);
+ return sum/m + Math.log (p) - Num.digamma (gam);
+ }
+ }
+
+
+ private static class Function implements MathFunction {
+ protected int m;
+ protected int max;
+ protected double mean;
+ protected int[] Fj;
+
+ public Function (int m, int max, double mean, int[] Fj) {
+ this.m = m;
+ this.max = max;
+ this.mean = mean;
+ this.Fj = new int[Fj.length];
+ System.arraycopy(Fj, 0, this.Fj, 0, Fj.length);
+ }
+
+ public double evaluate (double s) {
+ // if (s <= 0 ) return 1.0e100;
+ double sum = 0.0;
+ double p = s / (s + mean);
+
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (s + (double) j);
+
+ return sum + m * Math.log (p);
+ }
+ }
+
+
+ private static class FuncInv extends Function implements MathFunction {
+
+ public FuncInv (int m, int max, double mean, int[] Fj) {
+ super (m, max, mean, Fj);
+ }
+
+ public double evaluate (double nu) {
+ double r = nu*mean;
+ double sum = 0.;
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (1.0 + nu* j);
+
+ return (nu*sum - m * Math.log1p (r));
+ }
+ }
+
+
+/* ***********************
+ // Class Optim seems to be useless
+ private static class Optim implements Lmder_fcn
+ {
+ private double mean;
+ private int N;
+ private int max;
+ private int [] Fj;
+
+ public Optim (int N, int max, double mean, int[] Fj)
+ {
+ this.N = N;
+ this.max = max;
+ this.mean = mean;
+ this.Fj = new int[max];
+ System.arraycopy (Fj, 0, this.Fj, 0, max);
+ }
+
+ public void fcn (int m, int n, double[] x, double[] fvec, double[][] fjac,
+ int iflag[])
+ {
+ if (x[1] <= 0.0 || x[2] <= 0.0 || x[2] >= 1.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fjac[1][1] = BIG;
+ fjac[1][2] = 0.0;
+ fjac[2][1] = 0.0;
+ fjac[2][2] = BIG;
+ return;
+ }
+
+ double trig;
+ if (iflag[1] == 1)
+ {
+ double sum = 0.0;
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (x[1] + j);
+ fvec[1] = x[2] * mean - x[1] * (1.0 - x[2]);
+ fvec[2] = N * Math.log (x[2]) + sum;
+
+ } else if (iflag[1] == 2) {
+
+ fjac[1][1] = x[2] - 1.0;
+ fjac[1][2] = mean + x[1];
+ double sum = 0.0;
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / ((x[1] + j)*(x[1] + j));
+ fjac[2][1] = -sum;
+ fjac[2][2] = N / x[2];
+ }
+ }
+ }
+****************************/
+
+
+ public static double MAXN = 100000;
+
+
+ protected NegativeBinomialDist() {}
+
+
+
+ /**
+ * Creates an object that contains the probability
+ * terms and the distribution function for
+ * the negative binomial distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ */
+ public NegativeBinomialDist (double n, double p) {
+ setParams (n, p);
+ }
+
+ public double prob (int x) {
+ if (x < 0)
+ return 0.0;
+
+ if (p == 0.0)
+ return 0.0;
+
+ if (p == 1.0) {
+ if (x > 0)
+ return 0.0;
+ else
+ return 1.0;
+ }
+
+ if (pdf == null)
+ return prob (n, p, x);
+
+ if (x > xmax || x < xmin)
+ return prob (n, p, x);
+
+ return pdf[x - xmin];
+ }
+
+ public double cdf (int x) {
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 1.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+
+ if (cdf != null) {
+ if (x >= xmax)
+ return 1.0;
+ if (x < xmin)
+ return cdf (n, p, x);
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+
+ }
+ else
+ return cdf (n, p, x);
+ }
+
+ public double barF (int x) {
+ if (x < 1)
+ return 1.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 0.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 1.0;
+
+ if (cdf == null)
+ //return BinomialDist.cdf (x - 1 + n, p, n - 1);
+ return BetaDist.barF (n, x, p);
+
+ if (x > xmax)
+ //return BinomialDist.cdf (x - 1 + n, p, n - 1);
+ return BetaDist.barF (n, x, p);
+
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ }
+
+ public int inverseFInt (double u) {
+ if ((cdf == null) || (u <= EPS2))
+ return inverseF (n, p, u);
+ else
+ return super.inverseFInt (u);
+ }
+
+ public double getMean() {
+ return NegativeBinomialDist.getMean (n, p);
+ }
+
+ public double getVariance() {
+ return NegativeBinomialDist.getVariance (n, p);
+ }
+
+ public double getStandardDeviation() {
+ return NegativeBinomialDist.getStandardDeviation (n, p);
+ }
+
+ /**
+ * Computes the probability <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double prob (double n, double p, int x) {
+ final int SLIM = 15; // To avoid overflow
+ final double MAXEXP = (Num.DBL_MAX_EXP - 1)*Num.LN2;// To avoid overflow
+ final double MINEXP = (Num.DBL_MIN_EXP - 1)*Num.LN2;// To avoid underflow
+ double y;
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0.0");
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) { // In fact, p == 1
+ if (x == 0)
+ return 1.0;
+ else
+ return 0.0;
+ }
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+
+ y = Num.lnGamma (n + x) - (Num.lnFactorial (x) + Num.lnGamma (n))
+ + n * Math.log (p) + x * Math.log1p (-p) ;
+
+ if (y >= MAXEXP)
+ throw new IllegalArgumentException ("term overflow");
+ return Math.exp (y);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double n, double p, int x) {
+ final double EPSILON = DiscreteDistributionInt.EPSILON;
+ final int LIM1 = 100000;
+ double sum, term, termmode;
+ int i, mode;
+ final double q = 1.0 - p;
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0.0");
+
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 1.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+
+ // Compute the maximum term
+ mode = 1 + (int) Math.floor ((n*q - 1.0)/p);
+ if (mode < 0)
+ mode = 0;
+ else if (mode > x)
+ mode = x;
+
+ if (mode <= LIM1) {
+ sum = term = termmode = prob (n, p, mode);
+ for (i = mode; i > 0; i--) {
+ term *= i/(q*(n + i - 1.0));
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+
+ term = termmode;
+ for (i = mode; i < x; i++) {
+ term *= q*(n + i)/(i + 1);
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+ if (sum <= 1.0)
+ return sum;
+ else
+ return 1.0;
+ }
+ else
+ //return 1.0 - BinomialDist.cdf (x + n, p, n - 1);
+ return BetaDist.cdf (n, x + 1.0, p);
+ }
+
+
+ /**
+ * Returns
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>, the complementary
+ * distribution function.
+ *
+ */
+ public static double barF (double n, double p, int x) {
+ return 1.0 - cdf (n, p, x - 1);
+ }
+
+
+ /**
+ * Computes the inverse function without precomputing tables.
+ *
+ */
+ public static int inverseF (double n, double p, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p >= 1.0) // In fact, p == 1
+ return 0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0;
+ if (u <= prob (n, p, 0))
+ return 0;
+ if (u >= 1.0)
+ return Integer.MAX_VALUE;
+
+ double sum, term, termmode;
+ final double q = 1.0 - p;
+
+ // Compute the maximum term
+ int mode = 1 + (int) Math.floor ((n * q - 1.0) / p);
+ if (mode < 0)
+ mode = 0;
+ int i = mode;
+ term = prob (n, p, i);
+ while ((term >= u) && (term > Double.MIN_NORMAL)) {
+ i /= 2;
+ term = prob (n, p, i);
+ }
+
+ if (term <= Double.MIN_NORMAL) {
+ i *= 2;
+ term = prob (n, p, i);
+ while (term >= u && (term > Double.MIN_NORMAL)) {
+ term *= i / (q * (n + i - 1.0));
+ i--;
+ }
+ }
+
+ mode = i;
+ sum = termmode = prob (n, p, i);
+
+ for (i = mode; i > 0; i--) {
+ term *= i / (q * (n + i - 1.0));
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+
+ term = termmode;
+ i = mode;
+ double prev = -1;
+ if (sum < u) {
+ // The CDF at the mode is less than u, so we add term to get >= u.
+ while ((sum < u) && (sum > prev)){
+ term *= q * (n + i) / (i + 1);
+ prev = sum;
+ sum += term;
+ i++;
+ }
+ } else {
+ // The computed CDF is too big so we substract from it.
+ sum -= term;
+ while (sum >= u) {
+ term *= i / (q * (n + i - 1.0));
+ i--;
+ sum -= term;
+ }
+ }
+ return i;
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of the negative binomial distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The parameter
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> is assumed known.
+ * The estimate <SPAN CLASS="MATH">hat(p)</SPAN> is returned in element 0
+ * of the returned array.
+ * The maximum likelihood estimator <SPAN CLASS="MATH">hat(p)</SPAN> satisfies the equation
+ *
+ * <SPAN CLASS="MATH">hat(p) = <I>n</I>/(<I>n</I> + bar(x)<SUB>m</SUB>)</SPAN>,
+ * where <SPAN CLASS="MATH">bar(x)<SUB>m</SUB></SPAN> is the average of
+ * <SPAN CLASS="MATH"><I>x</I>[0],…, <I>x</I>[<I>m</I> - 1]</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @param n the first parameter of the negative binomial
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(p)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int m, double n) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ double mean = 0.0;
+ for (int i = 0; i < m; i++) {
+ mean += x[i];
+ }
+ mean /= (double) m;
+ double[] param = new double[1];
+ param[0] = n / (n + mean);
+ return param;
+ }
+
+
+ /**
+ * Creates a new instance of a negative binomial distribution with parameters
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> given and <SPAN CLASS="MATH">hat(p)</SPAN> estimated using the maximum
+ * likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ * @param n the first parameter of the negative binomial
+ *
+ *
+ */
+ public static NegativeBinomialDist getInstanceFromMLE (int[] x, int m,
+ double n) {
+ double parameters[] = getMLE (x, m, n);
+ return new NegativeBinomialDist (n, parameters[0]);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of the negative binomial distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The parameter <SPAN CLASS="MATH"><I>p</I></SPAN> is assumed known.
+ * The estimate <SPAN CLASS="MATH">hat(n)</SPAN> is returned in element 0 of the returned array.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @param p the second parameter of the negative binomial
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(n)</SPAN>]
+ *
+ */
+ public static double[] getMLE1 (int[] x, int m, double p) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ double mean = 0.0;
+ for (int i = 0; i < m; i++)
+ mean += x[i];
+ mean /= m;
+
+ double gam0 = mean*p/(1.0 - p);
+ double[] param = new double[1];
+ Func1 f = new Func1 (p, x, m);
+ param[0] = RootFinder.brentDekker (gam0/100.0, 100.0*gam0, f, 1e-5);
+ return param;
+ }
+
+
+ /**
+ * Creates a new instance of a negative binomial distribution with parameters
+ * <SPAN CLASS="MATH"><I>p</I></SPAN> given and <SPAN CLASS="MATH">hat(n)</SPAN> estimated using the maximum
+ * likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ * @param p the second parameter of the negative binomial
+ *
+ *
+ */
+ public static NegativeBinomialDist getInstanceFromMLE1 (int[] x, int m,
+ double p) {
+ double param[] = getMLE1 (x, m, p);
+ return new NegativeBinomialDist (param[0], p);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH">(<I>n</I>, <I>p</I>)</SPAN> of the negative binomial distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>n</I></SPAN>, <SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(n)</SPAN>, <SPAN CLASS="MATH">hat(p)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int m) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m<= 0");
+
+ int i, j;
+ int max = Integer.MIN_VALUE;
+ double sum = 0.0;
+ for (i = 0; i < m; i++) {
+ sum += x[i];
+ if (x[i] > max)
+ max = x[i];
+ }
+ double mean = sum / (double) m;
+
+ double var = 0.0;
+ for (i = 0; i < m; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) m;
+
+ if (mean >= var) {
+ throw new UnsupportedOperationException("mean >= variance");
+ }
+ double estimGamma = (mean * mean) / ( var - mean );
+
+ int[] Fj = new int[max];
+ for (j = 0; j < max; j++) {
+ int prop = 0;
+ for (i = 0; i < m; i++)
+ if (x[i] > j)
+ prop++;
+
+ Fj[j] = prop;
+ }
+
+ double[] param = new double[3];
+ Function f = new Function (m, max, mean, Fj);
+ param[1] = RootFinder.brentDekker (estimGamma/100, estimGamma*100, f, 1.0e-5);
+ param[2] = param[1] / (param[1] + mean);
+
+/* // Seems to be useless
+ Optim system = new Optim (m, max, mean, Fj);
+ double[] fvec = new double [3];
+ double[][] fjac = new double[3][3];
+ int[] iflag = new int[2];
+ int[] info = new int[2];
+ int[] ipvt = new int[3];
+
+ Minpack_f77.lmder1_f77 (system, 2, 2, param, fvec, fjac, 1e-5, info, ipvt);
+*/
+ double parameters[] = new double[2];
+ parameters[0] = param[1];
+ parameters[1] = param[2];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a negative binomial distribution with
+ * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> estimated using the maximum likelihood method
+ * based on the <SPAN CLASS="MATH"><I>m</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ *
+ */
+ public static NegativeBinomialDist getInstanceFromMLE (int[] x, int m) {
+ double parameters[] = getMLE (x, m);
+ return new NegativeBinomialDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Estimates and returns the parameter
+ * <SPAN CLASS="MATH"><I>ν</I> = 1/hat(n)</SPAN>
+ * of the negative binomial distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameter
+ *
+ * @param m the number of observations used to evaluate parameter
+ *
+ * @return returns the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>
+ *
+ */
+ public static double getMLEninv (int[] x, int m) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ int i, j;
+ int max = Integer.MIN_VALUE;
+ double mean = 0.0;
+ for (i = 0; i < m; i++) {
+ mean += x[i];
+ if (x[i] > max)
+ max = x[i];
+ }
+ mean /= (double) m;
+
+ double var = 0.0;
+ for (i = 0; i < m; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) m;
+
+ if (mean >= var) {
+ throw new UnsupportedOperationException("mean >= variance");
+ }
+
+ int[] Fj = new int[max];
+ for (j = 0; j < max; j++) {
+ int prop = 0;
+ for (i = 0; i < m; i++)
+ if (x[i] > j)
+ prop++;
+
+ Fj[j] = prop;
+ }
+
+ FuncInv f = new FuncInv (m, max, mean, Fj);
+ double nu = RootFinder.brentDekker (1.0e-8, 1.0e8, f, 1.0e-5);
+ return nu;
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>n</I>(1 - <I>p</I>)/<I>p</I></SPAN>
+ * of the negative binomial distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the mean of the negative binomial distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>n</I>(1 - <I>p</I>)/<I>p</I></SPAN>
+ *
+ */
+ public static double getMean (double n, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ return (n * (1 - p) / p);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>n</I>(1 - <I>p</I>)/<I>p</I><SUP>2</SUP></SPAN>
+ * of the negative binomial distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the variance of the negative binomial distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>n</I>(1 - <I>p</I>)/<I>p</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double n, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ return (n * (1 - p) / (p * p));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the negative
+ * binomial distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ * @return the standard deviation of the negative binomial distribution
+ *
+ *
+ */
+ public static double getStandardDeviation (double n, double p) {
+ return Math.sqrt (NegativeBinomialDist.getVariance (n, p));
+ }
+
+
+ @Deprecated
+ public double getGamma() {
+ return n;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public double getN() {
+ return n;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double n, double p) {
+ /* *
+ * Compute all probability terms of the negative binomial distribution;
+ * start at the mode, and calculate probabilities on each side until they
+ * become smaller than EPSILON. Set all others to 0.
+ */
+ supportA = 0;
+ int i, mode, Nmax;
+ int imin, imax;
+ double sum;
+ double[] P; // Negative Binomial mass probabilities
+ double[] F; // Negative Binomial cumulative
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ this.n = n;
+ this.p = p;
+
+ // Compute the mode (at the maximum term)
+ mode = 1 + (int) Math.floor((n*(1.0 - p) - 1.0)/p);
+
+ /* *
+ For mode > MAXN, we shall not use pre-computed arrays.
+ mode < 0 should be impossible, unless overflow of long occur, in
+ which case mode will be = LONG_MIN.
+ */
+
+ if (mode < 0.0 || mode > MAXN) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ /* *
+ In theory, the negative binomial distribution has an infinite range.
+ But for i > Nmax, probabilities should be extremely small.
+ Nmax = Mean + 16 * Standard deviation.
+ */
+
+ Nmax = (int)(n*(1.0 - p)/p + 16*Math.sqrt (n*(1.0 - p)/(p*p)));
+ if (Nmax < 32)
+ Nmax = 32;
+ P = new double[1 + Nmax];
+
+ double epsilon = EPSILON/prob (n, p, mode);
+
+ // We shall normalize by explicitly summing all terms >= epsilon
+ sum = P[mode] = 1.0;
+
+ // Start from the maximum and compute terms > epsilon on each side.
+ i = mode;
+ while (i > 0 && P[i] >= epsilon) {
+ P[i - 1] = P[i]*i/((1.0 - p)*(n + i - 1));
+ i--;
+ sum += P[i];
+ }
+ imin = i;
+
+ i = mode;
+ while (P[i] >= epsilon) {
+ P[i + 1] = P[i]*(1.0 - p)*(n + i)/(i + 1);
+ i++;
+ sum += P[i];
+ if (i == Nmax - 1) {
+ Nmax *= 2;
+ double[] nT = new double[1 + Nmax];
+ System.arraycopy (P, 0, nT, 0, P.length);
+ P = nT;
+ }
+ }
+ imax = i;
+
+ // Renormalize the sum of probabilities to 1
+ for (i = imin; i <= imax; i++)
+ P[i] /= sum;
+
+ // Compute the cumulative probabilities for F and keep them in the
+ // lower part of CDF.
+ F = new double[1 + Nmax];
+ F[imin] = P[imin];
+ i = imin;
+ while (i < imax && F[i] < 0.5) {
+ i++;
+ F[i] = F[i - 1] + P[i];
+ }
+
+ // This is the boundary between F (i <= xmed) and 1 - F (i > xmed) in
+ // the array CDF
+ xmed = i;
+
+ // Compute the cumulative probabilities of the complementary
+ // distribution 1 - F and keep them in the upper part of the array
+ F[imax] = P[imax];
+ i = imax - 1;
+ do {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ } while (i > xmed);
+
+ xmin = imin;
+ xmax = imax;
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax + 1 - imin);
+ System.arraycopy (F, imin, cdf, 0, imax + 1 - imin);
+
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>n</I></SPAN>, <SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n, p};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n + ", p = " + p;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/NegativeBinomialDist.tex b/source/umontreal/iro/lecuyer/probdist/NegativeBinomialDist.tex
new file mode 100644
index 0000000..96d557f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NegativeBinomialDist.tex
@@ -0,0 +1,971 @@
+\defmodule {NegativeBinomialDist}
+
+Extends the class \class{DiscreteDistributionInt} for
+the \emph{negative binomial\/} distribution
+\cite[page 324]{sLAW00a} with real
+parameters $n$ and $p$, where $n > 0$ and $0\le p\le 1$.
+Its mass function is
+\begin{htmlonly}
+\eq
+ p(x) = \Gamma (n + x) / (\Gamma (n)\: x!) p^n (1 - p)^x,
+ \qquad\mbox{for } x = 0, 1, 2, \ldots\label{eq:fmass-negbin}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ p(x) = \frac{\Gamma(n + x)}{\Gamma (n)\; x!} p^n (1 - p)^x,
+ \qquad\mbox{for } x = 0, 1, 2, \ldots \label{eq:fmass-negbin}
+\endeq
+\end{latexonly}
+where $\Gamma(x)$ is the gamma function.
+
+If $n$ is an integer, $p(x)$ can be interpreted as the probability
+of having $x$ failures before the $n$-th success in a sequence of
+independent Bernoulli trials with probability of success $p$. This special
+case is implemented as the Pascal distribution (see \class{PascalDist}).
+% For $n=1$, this gives the \emph{geometric} distribution.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NegativeBinomialDist
+ * Description: negative binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+import optimization.*;
+\end{hide}
+
+public class NegativeBinomialDist extends DiscreteDistributionInt\begin{hide} {
+ protected double n;
+ protected double p;
+ private static final double EPS2 = 1000.0*EPSILON;
+
+ private static class Func1 implements MathFunction {
+ protected int m;
+ protected int[] x;
+ protected double p;
+
+ public Func1 (double p, int[] x, int m) {
+ this.p = p;
+ this.m = m;
+ this.x = x;
+ }
+
+ public double evaluate (double gam) {
+ if (gam <= 0 ) return 1.0e100;
+
+ double sum = 0.0;
+ for (int j = 0; j < m; j++)
+ sum += Num.digamma (gam + x[j]);
+ return sum/m + Math.log (p) - Num.digamma (gam);
+ }
+ }
+
+
+ private static class Function implements MathFunction {
+ protected int m;
+ protected int max;
+ protected double mean;
+ protected int[] Fj;
+
+ public Function (int m, int max, double mean, int[] Fj) {
+ this.m = m;
+ this.max = max;
+ this.mean = mean;
+ this.Fj = new int[Fj.length];
+ System.arraycopy(Fj, 0, this.Fj, 0, Fj.length);
+ }
+
+ public double evaluate (double s) {
+ // if (s <= 0 ) return 1.0e100;
+ double sum = 0.0;
+ double p = s / (s + mean);
+
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (s + (double) j);
+
+ return sum + m * Math.log (p);
+ }
+ }
+
+
+ private static class FuncInv extends Function implements MathFunction {
+
+ public FuncInv (int m, int max, double mean, int[] Fj) {
+ super (m, max, mean, Fj);
+ }
+
+ public double evaluate (double nu) {
+ double r = nu*mean;
+ double sum = 0.;
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (1.0 + nu* j);
+
+ return (nu*sum - m * Math.log1p (r));
+ }
+ }
+
+
+/************************
+ // Class Optim seems to be useless
+ private static class Optim implements Lmder_fcn
+ {
+ private double mean;
+ private int N;
+ private int max;
+ private int [] Fj;
+
+ public Optim (int N, int max, double mean, int[] Fj)
+ {
+ this.N = N;
+ this.max = max;
+ this.mean = mean;
+ this.Fj = new int[max];
+ System.arraycopy (Fj, 0, this.Fj, 0, max);
+ }
+
+ public void fcn (int m, int n, double[] x, double[] fvec, double[][] fjac,
+ int iflag[])
+ {
+ if (x[1] <= 0.0 || x[2] <= 0.0 || x[2] >= 1.0) {
+ final double BIG = 1.0e100;
+ fvec[1] = BIG;
+ fvec[2] = BIG;
+ fjac[1][1] = BIG;
+ fjac[1][2] = 0.0;
+ fjac[2][1] = 0.0;
+ fjac[2][2] = BIG;
+ return;
+ }
+
+ double trig;
+ if (iflag[1] == 1)
+ {
+ double sum = 0.0;
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (x[1] + j);
+ fvec[1] = x[2] * mean - x[1] * (1.0 - x[2]);
+ fvec[2] = N * Math.log (x[2]) + sum;
+
+ } else if (iflag[1] == 2) {
+
+ fjac[1][1] = x[2] - 1.0;
+ fjac[1][2] = mean + x[1];
+ double sum = 0.0;
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / ((x[1] + j)*(x[1] + j));
+ fjac[2][1] = -sum;
+ fjac[2][2] = N / x[2];
+ }
+ }
+ }
+****************************/
+\end{hide}
+\end{code}
+\begin{detailed}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\unmoved\subsubsection* {Constant}
+
+\begin{code}
+ public static double MAXN = 100000;
+\end{code}
+ \begin{tabb} If the maximum term is greater than this constant,
+ then the tables will {\em not\/} be precomputed.
+\end{tabb}
+\end{detailed}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+\begin{hide}
+ protected NegativeBinomialDist() {}
+
+\end{hide}
+
+ public NegativeBinomialDist (double n, double p)\begin{hide} {
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates an object that contains the probability
+ terms (\ref{eq:fmass-negbin}) and the distribution function for
+ the negative binomial distribution with parameters $n$ and $p$.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+ public double prob (int x) {
+ if (x < 0)
+ return 0.0;
+
+ if (p == 0.0)
+ return 0.0;
+
+ if (p == 1.0) {
+ if (x > 0)
+ return 0.0;
+ else
+ return 1.0;
+ }
+
+ if (pdf == null)
+ return prob (n, p, x);
+
+ if (x > xmax || x < xmin)
+ return prob (n, p, x);
+
+ return pdf[x - xmin];
+ }
+
+ public double cdf (int x) {
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 1.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+
+ if (cdf != null) {
+ if (x >= xmax)
+ return 1.0;
+ if (x < xmin)
+ return cdf (n, p, x);
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+
+ }
+ else
+ return cdf (n, p, x);
+ }
+
+ public double barF (int x) {
+ if (x < 1)
+ return 1.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 0.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 1.0;
+
+ if (cdf == null)
+ //return BinomialDist.cdf (x - 1 + n, p, n - 1);
+ return BetaDist.barF (n, x, p);
+
+ if (x > xmax)
+ //return BinomialDist.cdf (x - 1 + n, p, n - 1);
+ return BetaDist.barF (n, x, p);
+
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ }
+
+ public int inverseFInt (double u) {
+ if ((cdf == null) || (u <= EPS2))
+ return inverseF (n, p, u);
+ else
+ return super.inverseFInt (u);
+ }
+
+ public double getMean() {
+ return NegativeBinomialDist.getMean (n, p);
+ }
+
+ public double getVariance() {
+ return NegativeBinomialDist.getVariance (n, p);
+ }
+
+ public double getStandardDeviation() {
+ return NegativeBinomialDist.getStandardDeviation (n, p);
+ }\end{hide}
+
+ public static double prob (double n, double p, int x)\begin{hide} {
+ final int SLIM = 15; // To avoid overflow
+ final double MAXEXP = (Num.DBL_MAX_EXP - 1)*Num.LN2;// To avoid overflow
+ final double MINEXP = (Num.DBL_MIN_EXP - 1)*Num.LN2;// To avoid underflow
+ double y;
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0.0");
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) { // In fact, p == 1
+ if (x == 0)
+ return 1.0;
+ else
+ return 0.0;
+ }
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+
+ y = Num.lnGamma (n + x) - (Num.lnFactorial (x) + Num.lnGamma (n))
+ + n * Math.log (p) + x * Math.log1p (-p) ;
+
+ if (y >= MAXEXP)
+ throw new IllegalArgumentException ("term overflow");
+ return Math.exp (y);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the probability $p(x)$%
+\latex{ defined in (\ref{eq:fmass-negbin})}.
+ \end{tabb}
+\begin{code}
+
+ public static double cdf (double n, double p, int x)\begin{hide} {
+ final double EPSILON = DiscreteDistributionInt.EPSILON;
+ final int LIM1 = 100000;
+ double sum, term, termmode;
+ int i, mode;
+ final double q = 1.0 - p;
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0.0");
+
+ if (x < 0)
+ return 0.0;
+ if (p >= 1.0) // In fact, p == 1
+ return 1.0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0.0;
+
+ // Compute the maximum term
+ mode = 1 + (int) Math.floor ((n*q - 1.0)/p);
+ if (mode < 0)
+ mode = 0;
+ else if (mode > x)
+ mode = x;
+
+ if (mode <= LIM1) {
+ sum = term = termmode = prob (n, p, mode);
+ for (i = mode; i > 0; i--) {
+ term *= i/(q*(n + i - 1.0));
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+
+ term = termmode;
+ for (i = mode; i < x; i++) {
+ term *= q*(n + i)/(i + 1);
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+ if (sum <= 1.0)
+ return sum;
+ else
+ return 1.0;
+ }
+ else
+ //return 1.0 - BinomialDist.cdf (x + n, p, n - 1);
+ return BetaDist.cdf (n, x + 1.0, p);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double n, double p, int x)\begin{hide} {
+ return 1.0 - cdf (n, p, x - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\bar F(x) = P[X \ge x]$, the complementary
+ distribution function.
+\end{tabb}
+\begin{code}
+
+ public static int inverseF (double n, double p, double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p >= 1.0) // In fact, p == 1
+ return 0;
+ if (p <= 0.0) // In fact, p == 0
+ return 0;
+ if (u <= prob (n, p, 0))
+ return 0;
+ if (u >= 1.0)
+ return Integer.MAX_VALUE;
+
+ double sum, term, termmode;
+ final double q = 1.0 - p;
+
+ // Compute the maximum term
+ int mode = 1 + (int) Math.floor ((n * q - 1.0) / p);
+ if (mode < 0)
+ mode = 0;
+ int i = mode;
+ term = prob (n, p, i);
+ while ((term >= u) && (term > Double.MIN_NORMAL)) {
+ i /= 2;
+ term = prob (n, p, i);
+ }
+
+ if (term <= Double.MIN_NORMAL) {
+ i *= 2;
+ term = prob (n, p, i);
+ while (term >= u && (term > Double.MIN_NORMAL)) {
+ term *= i / (q * (n + i - 1.0));
+ i--;
+ }
+ }
+
+ mode = i;
+ sum = termmode = prob (n, p, i);
+
+ for (i = mode; i > 0; i--) {
+ term *= i / (q * (n + i - 1.0));
+ if (term < EPSILON)
+ break;
+ sum += term;
+ }
+
+ term = termmode;
+ i = mode;
+ double prev = -1;
+ if (sum < u) {
+ // The CDF at the mode is less than u, so we add term to get >= u.
+ while ((sum < u) && (sum > prev)){
+ term *= q * (n + i) / (i + 1);
+ prev = sum;
+ sum += term;
+ i++;
+ }
+ } else {
+ // The computed CDF is too big so we substract from it.
+ sum -= term;
+ while (sum >= u) {
+ term *= i / (q * (n + i - 1.0));
+ i--;
+ sum -= term;
+ }
+ }
+ return i;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse function without precomputing tables.
+%% If the mode is too large, it will
+%% use the fact that $F_{n,p}(x)=B_{x+n,p}(n-1)$, where $B_{x+n,p}$ is the
+%% distribution function of a binomial variable with parameters $(x+n, p)$,
+%% to perform linear search.
+%% The latter case is not efficient and therefore should be avoided.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int m, double n)\begin{hide} {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ double mean = 0.0;
+ for (int i = 0; i < m; i++) {
+ mean += x[i];
+ }
+ mean /= (double) m;
+ double[] param = new double[1];
+ param[0] = n / (n + mean);
+ return param;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $p$ of the negative binomial distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1, \ldots, m-1$. The parameter
+ $n$ is assumed known.
+ The estimate $\hat{p}$ is returned in element 0
+ of the returned array.
+ The maximum likelihood estimator $\hat{p}$ satisfies the equation
+ $\hat{p} = n /(n + \bar{x}_m)$,
+ where $\bar{x}_m$ is the average of $x[0], \ldots, x[m-1]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \param{n}{the first parameter of the negative binomial}
+ \return{returns the parameters [$\hat{p}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static NegativeBinomialDist getInstanceFromMLE (int[] x, int m,
+ double n)\begin{hide} {
+ double parameters[] = getMLE (x, m, n);
+ return new NegativeBinomialDist (n, parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a negative binomial distribution with parameters
+ $n$ given and $\hat{p}$ estimated using the maximum
+ likelihood method, from the $m$ observations $x[i]$,
+ $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+ \param{n}{the first parameter of the negative binomial}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE1 (int[] x, int m, double p)\begin{hide} {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ double mean = 0.0;
+ for (int i = 0; i < m; i++)
+ mean += x[i];
+ mean /= m;
+
+ double gam0 = mean*p/(1.0 - p);
+ double[] param = new double[1];
+ Func1 f = new Func1 (p, x, m);
+ param[0] = RootFinder.brentDekker (gam0/100.0, 100.0*gam0, f, 1e-5);
+ return param;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $n$ of the negative binomial distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1, \ldots, m-1$. The parameter $p$ is assumed known.
+ The estimate $\hat{n}$ is returned in element 0 of the returned array.
+\begin{detailed}
+ The maximum likelihood estimator $\hat{p}$ satisfies the equation
+ \[
+ \frac1m\sum_{j=0}^{m-1} \psi(n +x_j) = \psi(n) - \ln(p)
+ \]
+ where $\psi(x)$ is the digamma function, i.e. the logarithmic derivative
+ of the Gamma function $\psi(x) = \Gamma^\prime(x)/\Gamma(x)$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \param{p}{the second parameter of the negative binomial}
+ \return{returns the parameters [$\hat{n}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static NegativeBinomialDist getInstanceFromMLE1 (int[] x, int m,
+ double p)\begin{hide} {
+ double param[] = getMLE1 (x, m, p);
+ return new NegativeBinomialDist (param[0], p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a negative binomial distribution with parameters
+ $p$ given and $\hat{n}$ estimated using the maximum
+ likelihood method, from the $m$ observations $x[i]$,
+ $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+ \param{p}{the second parameter of the negative binomial}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int m)\begin{hide} {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m<= 0");
+
+ int i, j;
+ int max = Integer.MIN_VALUE;
+ double sum = 0.0;
+ for (i = 0; i < m; i++) {
+ sum += x[i];
+ if (x[i] > max)
+ max = x[i];
+ }
+ double mean = sum / (double) m;
+
+ double var = 0.0;
+ for (i = 0; i < m; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) m;
+
+ if (mean >= var) {
+ throw new UnsupportedOperationException("mean >= variance");
+ }
+ double estimGamma = (mean * mean) / ( var - mean );
+
+ int[] Fj = new int[max];
+ for (j = 0; j < max; j++) {
+ int prop = 0;
+ for (i = 0; i < m; i++)
+ if (x[i] > j)
+ prop++;
+
+ Fj[j] = prop;
+ }
+
+ double[] param = new double[3];
+ Function f = new Function (m, max, mean, Fj);
+ param[1] = RootFinder.brentDekker (estimGamma/100, estimGamma*100, f, 1.0e-5);
+ param[2] = param[1] / (param[1] + mean);
+
+/* // Seems to be useless
+ Optim system = new Optim (m, max, mean, Fj);
+ double[] fvec = new double [3];
+ double[][] fjac = new double[3][3];
+ int[] iflag = new int[2];
+ int[] info = new int[2];
+ int[] ipvt = new int[3];
+
+ Minpack_f77.lmder1_f77 (system, 2, 2, param, fvec, fjac, 1e-5, info, ipvt);
+*/
+ double parameters[] = new double[2];
+ parameters[0] = param[1];
+ parameters[1] = param[2];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $(n, p)$ of the negative binomial distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1, \ldots, m-1$. The estimates are returned in a two-element
+ array, in regular order: [$n$, $p$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat{n}$, $\hat{p})$
+ satisfying the equations
+ \begin{eqnarray*}
+ \frac{\hat{n}(1 - \hat{p})}{\hat{p}} & = & \bar{x}_m\\
+ \sum_{j=1}^{\infty} \frac{F_j}{(\hat{n} + j - 1)} & = & -m\ln (\hat{p})
+ \end{eqnarray*}
+ where $\bar x_m$ is the average of $x[0],\dots,x[m-1]$, and
+ $F_j = \sum_{i=j}^{\infty} f_i$ = number of $x_i \ge j$ (see
+ \cite[page 132]{tJOH69a}).
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{n}$, $\hat{p}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static NegativeBinomialDist getInstanceFromMLE (int[] x, int m)\begin{hide} {
+ double parameters[] = getMLE (x, m);
+ return new NegativeBinomialDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a negative binomial distribution with
+ parameters $n$ and $p$ estimated using the maximum likelihood method
+ based on the $m$ observations $x[i]$, $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMLEninv (int[] x, int m)\begin{hide} {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ int i, j;
+ int max = Integer.MIN_VALUE;
+ double mean = 0.0;
+ for (i = 0; i < m; i++) {
+ mean += x[i];
+ if (x[i] > max)
+ max = x[i];
+ }
+ mean /= (double) m;
+
+ double var = 0.0;
+ for (i = 0; i < m; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) m;
+
+ if (mean >= var) {
+ throw new UnsupportedOperationException("mean >= variance");
+ }
+
+ int[] Fj = new int[max];
+ for (j = 0; j < max; j++) {
+ int prop = 0;
+ for (i = 0; i < m; i++)
+ if (x[i] > j)
+ prop++;
+
+ Fj[j] = prop;
+ }
+
+ FuncInv f = new FuncInv (m, max, mean, Fj);
+ double nu = RootFinder.brentDekker (1.0e-8, 1.0e8, f, 1.0e-5);
+ return nu;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates and returns the parameter $\nu = 1/\hat{n}$
+ of the negative binomial distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1, \ldots, m-1$.
+ \begin{detailed}
+ The maximum likelihood estimator is the value $\nu$
+ satisfying the equation
+ \[
+ \sum_{j=1}^{\infty} \frac{\nu F_j}{1 + \nu (j - 1)} = m\ln (1 + \nu\bar x_m)
+ \]
+ where $\bar x_m$ is the average of $x[0],\dots,x[m-1]$, and
+ $F_j = \sum_{i=j}^{\infty} f_i$ = number of $x_i \ge j$ (see
+ \cite[page 132]{tJOH69a}).
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameter}
+ \param{m}{the number of observations used to evaluate parameter}
+ \return{returns the parameter $\nu$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double n, double p)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ return (n * (1 - p) / p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = n(1 - p)/p$
+ of the negative binomial distribution with parameters $n$ and $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the negative binomial distribution
+ $E[X] = n(1 - p) / p$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double n, double p)\begin{hide} {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ return (n * (1 - p) / (p * p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = n(1
+- p)/p^2$
+ of the negative binomial distribution with parameters $n$ and $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the negative binomial distribution
+$\mbox{Var}[X] = n(1 - p) / p^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double n, double p)\begin{hide} {
+ return Math.sqrt (NegativeBinomialDist.getVariance (n, p));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the negative
+ binomial distribution with parameters $n$ and $p$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the negative binomial distribution}
+\end{htmlonly}
+\begin{hide}\begin{code}
+
+ @Deprecated
+ public double getGamma() {
+ return n;
+ }
+\end{code}
+\begin{tabb} Returns the parameter $n$ of this object.
+\end{tabb}\end{hide}
+\begin{code}
+
+ public double getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double n, double p)\begin{hide} {
+ /**
+ * Compute all probability terms of the negative binomial distribution;
+ * start at the mode, and calculate probabilities on each side until they
+ * become smaller than EPSILON. Set all others to 0.
+ */
+ supportA = 0;
+ int i, mode, Nmax;
+ int imin, imax;
+ double sum;
+ double[] P; // Negative Binomial mass probabilities
+ double[] F; // Negative Binomial cumulative
+
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0.0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ this.n = n;
+ this.p = p;
+
+ // Compute the mode (at the maximum term)
+ mode = 1 + (int) Math.floor((n*(1.0 - p) - 1.0)/p);
+
+ /**
+ For mode > MAXN, we shall not use pre-computed arrays.
+ mode < 0 should be impossible, unless overflow of long occur, in
+ which case mode will be = LONG_MIN.
+ */
+
+ if (mode < 0.0 || mode > MAXN) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ /**
+ In theory, the negative binomial distribution has an infinite range.
+ But for i > Nmax, probabilities should be extremely small.
+ Nmax = Mean + 16 * Standard deviation.
+ */
+
+ Nmax = (int)(n*(1.0 - p)/p + 16*Math.sqrt (n*(1.0 - p)/(p*p)));
+ if (Nmax < 32)
+ Nmax = 32;
+ P = new double[1 + Nmax];
+
+ double epsilon = EPSILON/prob (n, p, mode);
+
+ // We shall normalize by explicitly summing all terms >= epsilon
+ sum = P[mode] = 1.0;
+
+ // Start from the maximum and compute terms > epsilon on each side.
+ i = mode;
+ while (i > 0 && P[i] >= epsilon) {
+ P[i - 1] = P[i]*i/((1.0 - p)*(n + i - 1));
+ i--;
+ sum += P[i];
+ }
+ imin = i;
+
+ i = mode;
+ while (P[i] >= epsilon) {
+ P[i + 1] = P[i]*(1.0 - p)*(n + i)/(i + 1);
+ i++;
+ sum += P[i];
+ if (i == Nmax - 1) {
+ Nmax *= 2;
+ double[] nT = new double[1 + Nmax];
+ System.arraycopy (P, 0, nT, 0, P.length);
+ P = nT;
+ }
+ }
+ imax = i;
+
+ // Renormalize the sum of probabilities to 1
+ for (i = imin; i <= imax; i++)
+ P[i] /= sum;
+
+ // Compute the cumulative probabilities for F and keep them in the
+ // lower part of CDF.
+ F = new double[1 + Nmax];
+ F[imin] = P[imin];
+ i = imin;
+ while (i < imax && F[i] < 0.5) {
+ i++;
+ F[i] = F[i - 1] + P[i];
+ }
+
+ // This is the boundary between F (i <= xmed) and 1 - F (i > xmed) in
+ // the array CDF
+ xmed = i;
+
+ // Compute the cumulative probabilities of the complementary
+ // distribution 1 - F and keep them in the upper part of the array
+ F[imax] = P[imax];
+ i = imax - 1;
+ do {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ } while (i > xmed);
+
+ xmin = imin;
+ xmax = imax;
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax + 1 - imin);
+ System.arraycopy (F, imin, cdf, 0, imax + 1 - imin);
+
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameter $n$ and $p$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n, p};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$n$, $p$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n + ", p = " + p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/NormalDist.java b/source/umontreal/iro/lecuyer/probdist/NormalDist.java
new file mode 100644
index 0000000..7b94fe9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NormalDist.java
@@ -0,0 +1,633 @@
+
+
+/*
+ * Class: NormalDist
+ * Description: normal or Gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the <EM>normal</EM>
+ * distribution (e.g.,). It has mean <SPAN CLASS="MATH"><I>μ</I></SPAN>
+ * and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN>. Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>e</I><SUP>-(x-<I>μ</I>)<SUP>2</SUP>/(2<I>σ</I><SUP>2</SUP>)</SUP>/((2π)<SUP>1/2</SUP><I>σ</I>) for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * When <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>σ</I> = 1</SPAN>, we have the <EM>standard normal</EM>
+ * distribution, with corresponding distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>Φ</I>(<I>x</I>) = ∫<SUB>-∞</SUB><SUP>x</SUP><I>e</I><SUP>-t<SUP>2</SUP>/2</SUP> <I>dt</I>/(2π)<SUP>1/2</SUP> for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * The non-static methods <TT>cdf</TT>, <TT>barF</TT>, and <TT>inverseF</TT> are
+ * implemented via {@link #cdf01 cdf01}, {@link #barF01 barF01},
+ * and {@link #inverseF01 inverseF01}, respectively.
+ *
+ */
+public class NormalDist extends ContinuousDistribution {
+ protected double mu;
+ protected double sigma;
+ protected static final double RAC2PI = 2.50662827463100050; // Sqrt(2*Pi)
+
+ private static final double[] AbarF = {
+ 6.10143081923200418E-1,
+ -4.34841272712577472E-1,
+ 1.76351193643605501E-1,
+ -6.07107956092494149E-2,
+ 1.77120689956941145E-2,
+ -4.32111938556729382E-3,
+ 8.54216676887098679E-4,
+ -1.27155090609162743E-4,
+ 1.12481672436711895E-5,
+ 3.13063885421820973E-7,
+ -2.70988068537762022E-7,
+ 3.07376227014076884E-8,
+ 2.51562038481762294E-9,
+ -1.02892992132031913E-9,
+ 2.99440521199499394E-11,
+ 2.60517896872669363E-11,
+ -2.63483992417196939E-12,
+ -6.43404509890636443E-13,
+ 1.12457401801663447E-13,
+ 1.7281533389986098E-14,
+ -4.2641016949424E-15,
+ -5.4537197788E-16,
+ 1.5869760776E-16,
+ 2.08998378E-17,
+ -0.5900E-17
+ };
+
+
+
+
+ /**
+ * Constructs a <TT>NormalDist</TT> object with default parameters <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>σ</I> = 1</SPAN>.
+ *
+ */
+ public NormalDist() {
+ setParams (0.0, 1.0);
+ }
+
+
+ /**
+ * Constructs a <TT>NormalDist</TT> object with mean <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT>
+ * and standard deviation <SPAN CLASS="MATH"><I>σ</I></SPAN> = <TT>sigma</TT>.
+ *
+ */
+ public NormalDist (double mu, double sigma) {
+ setParams (mu, sigma);
+ }
+
+
+ public double density (double x) {
+ double z = (x - mu)/sigma;
+ return Math.exp (-0.5*z*z)/ (RAC2PI*sigma);
+ }
+
+ public double cdf (double x) {
+ return cdf01 ((x-mu)/sigma);
+ }
+
+ public double barF (double x) {
+ return barF01 ((x-mu)/sigma);
+ }
+
+ public double inverseF (double u) {
+ return mu + sigma * inverseF01 (u);
+ }
+
+ public double getMean() {
+ return NormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return NormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return NormalDist.getStandardDeviation (mu, sigma);
+ }
+
+ /**
+ * Same as {@link #density(double,double,double) density} <TT>(0, 1, x)</TT>.
+ *
+ */
+ public static double density01 (double x) {
+ return Math.exp(-0.5*x*x)/RAC2PI;
+ }
+
+
+ /**
+ * Computes the normal density function.
+ *
+ */
+ public static double density (double mu, double sigma, double x) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ double z = (x - mu)/sigma;
+ return Math.exp (-0.5*z*z)/ (RAC2PI*sigma);
+ }
+
+ /*
+ * The precision of double is 16 decimals; we shall thus use coeffmax = 24
+ * coefficients. But the approximation is good to 30 decimals of precision
+ * with 44 coefficients.
+ */
+ private static final int COEFFMAX = 24;
+
+ private static final double NORMAL2_A[] = {
+ 6.10143081923200417926465815756e-1,
+ -4.34841272712577471828182820888e-1,
+ 1.76351193643605501125840298123e-1,
+ -6.0710795609249414860051215825e-2,
+ 1.7712068995694114486147141191e-2,
+ -4.321119385567293818599864968e-3,
+ 8.54216676887098678819832055e-4,
+ -1.27155090609162742628893940e-4,
+ 1.1248167243671189468847072e-5,
+ 3.13063885421820972630152e-7,
+ -2.70988068537762022009086e-7,
+ 3.0737622701407688440959e-8,
+ 2.515620384817622937314e-9,
+ -1.028929921320319127590e-9,
+ 2.9944052119949939363e-11,
+ 2.6051789687266936290e-11,
+ -2.634839924171969386e-12,
+ -6.43404509890636443e-13,
+ 1.12457401801663447e-13,
+ 1.7281533389986098e-14,
+ -4.264101694942375e-15,
+ -5.45371977880191e-16,
+ 1.58697607761671e-16,
+ 2.0899837844334e-17,
+ -5.900526869409e-18,
+ -9.41893387554e-19
+/*, 2.14977356470e-19,
+ 4.6660985008e-20,
+ -7.243011862e-21,
+ -2.387966824e-21,
+ 1.91177535e-22,
+ 1.20482568e-22,
+ -6.72377e-25,
+ -5.747997e-24,
+ -4.28493e-25,
+ 2.44856e-25,
+ 4.3793e-26,
+ -8.151e-27,
+ -3.089e-27,
+ 9.3e-29,
+ 1.74e-28,
+ 1.6e-29,
+ -8.0e-30,
+ -2.0e-30
+*/
+ };
+
+
+ /**
+ * Same as {@link #cdf(double,double,double) cdf} <TT>(0, 1, x)</TT>.
+ *
+ */
+ public static double cdf01 (double x) {
+ /*
+ * Returns P[X < x] for the normal distribution.
+ * As in J. L. Schonfelder, Math. of Computation, Vol. 32,
+ * pp 1232--1240, (1978).
+ */
+
+ double t, r;
+
+ if (x <= -XBIG)
+ return 0.0;
+ if (x >= XBIG)
+ return 1.0;
+
+ x = -x/Num.RAC2;
+ if (x < 0) {
+ x = -x;
+ t = (x - 3.75) / (x + 3.75);
+ r = 1.0 - 0.5 * Math.exp ( -x * x) * Num.evalCheby (NORMAL2_A, COEFFMAX, t);
+ } else {
+ t = (x - 3.75) / (x + 3.75);
+ r = 0.5 * Math.exp ( -x * x) * Num.evalCheby (NORMAL2_A, COEFFMAX, t);
+ }
+ return r;
+ }
+
+
+ /**
+ * Computes the normal distribution function with mean
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN>.
+ * Uses the Chebyshev approximation ,
+ * which gives 16 decimals of precision.
+ *
+ */
+ public static double cdf (double mu, double sigma, double x) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return cdf01 ((x-mu)/sigma);
+ }
+
+
+ /**
+ * Same as
+ * {@link #barF(double,double,double) barF} <TT>(0, 1, x)</TT>.
+ *
+ */
+ public static double barF01 (double x) {
+ /*
+ * Returns P[X >= x] = 1 - F (x) where F is the normal distribution by
+ * computing the complementary distribution directly; it is thus more
+ * precise in the tail.
+ */
+
+ final double KK = 5.30330085889910643300; // 3.75 Sqrt (2)
+ double y, t;
+ int neg;
+
+ if (x >= XBIG)
+ return 0.0;
+ if (x <= -XBIG)
+ return 1.0;
+
+ if (x >= 0.0)
+ neg = 0;
+ else {
+ neg = 1;
+ x = -x;
+ }
+
+ t = (x - KK)/(x + KK);
+ y = Num.evalCheby (AbarF, 24, t);
+ y = y * Math.exp ( -x * x / 2.0) / 2.0;
+
+ if (neg == 1)
+ return 1.0 - y;
+ else
+ return y;
+ }
+
+
+ /**
+ * Computes the complementary normal distribution function
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = 1 - <I>Φ</I>((<I>x</I> - <I>μ</I>)/<I>σ</I>)</SPAN>,
+ * with mean <SPAN CLASS="MATH"><I>μ</I></SPAN> and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN>.
+ * Uses a Chebyshev series giving 16 decimal digits of
+ * precision.
+ *
+ */
+ public static double barF (double mu, double sigma, double x) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return barF01 ((x-mu)/sigma);
+ }
+
+ private static final double[] InvP1 = {
+ 0.160304955844066229311E2,
+ -0.90784959262960326650E2,
+ 0.18644914861620987391E3,
+ -0.16900142734642382420E3,
+ 0.6545466284794487048E2,
+ -0.864213011587247794E1,
+ 0.1760587821390590
+ };
+
+ private static final double[] InvQ1 = {
+ 0.147806470715138316110E2,
+ -0.91374167024260313396E2,
+ 0.21015790486205317714E3,
+ -0.22210254121855132366E3,
+ 0.10760453916055123830E3,
+ -0.206010730328265443E2,
+ 0.1E1
+ };
+
+ private static final double[] InvP2 = {
+ -0.152389263440726128E-1,
+ 0.3444556924136125216,
+ -0.29344398672542478687E1,
+ 0.11763505705217827302E2,
+ -0.22655292823101104193E2,
+ 0.19121334396580330163E2,
+ -0.5478927619598318769E1,
+ 0.237516689024448000
+ };
+
+ private static final double[] InvQ2 = {
+ -0.108465169602059954E-1,
+ 0.2610628885843078511,
+ -0.24068318104393757995E1,
+ 0.10695129973387014469E2,
+ -0.23716715521596581025E2,
+ 0.24640158943917284883E2,
+ -0.10014376349783070835E2,
+ 0.1E1
+ };
+
+ private static final double[] InvP3 = {
+ 0.56451977709864482298E-4,
+ 0.53504147487893013765E-2,
+ 0.12969550099727352403,
+ 0.10426158549298266122E1,
+ 0.28302677901754489974E1,
+ 0.26255672879448072726E1,
+ 0.20789742630174917228E1,
+ 0.72718806231556811306,
+ 0.66816807711804989575E-1,
+ -0.17791004575111759979E-1,
+ 0.22419563223346345828E-2
+ };
+
+ private static final double[] InvQ3 = {
+ 0.56451699862760651514E-4,
+ 0.53505587067930653953E-2,
+ 0.12986615416911646934,
+ 0.10542932232626491195E1,
+ 0.30379331173522206237E1,
+ 0.37631168536405028901E1,
+ 0.38782858277042011263E1,
+ 0.20372431817412177929E1,
+ 0.1E1
+ };
+
+ /**
+ * Same as
+ * {@link #inverseF(double,double,double) inverseF} <TT>(0, 1, u)</TT>.
+ *
+ */
+ public static double inverseF01 (double u) {
+ /*
+ * Returns the inverse of the cdf of the normal distribution.
+ * Rational approximations giving 16 decimals of precision.
+ * J.M. Blair, C.A. Edwards, J.H. Johnson, "Rational Chebyshev
+ * approximations for the Inverse of the Error Function", in
+ * Mathematics of Computation, Vol. 30, 136, pp 827, (1976)
+ */
+
+ int i;
+ boolean negatif;
+ double y, z, v, w;
+ double x = u;
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0, 1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ // Transform x as argument of InvErf
+ x = 2.0 * x - 1.0;
+ if (x < 0.0) {
+ x = -x;
+ negatif = true;
+ } else {
+ negatif = false;
+ }
+
+ if (x <= 0.75) {
+ y = x * x - 0.5625;
+ v = w = 0.0;
+ for (i = 6; i >= 0; i--) {
+ v = v * y + InvP1[i];
+ w = w * y + InvQ1[i];
+ }
+ z = (v / w) * x;
+
+ } else if (x <= 0.9375) {
+ y = x * x - 0.87890625;
+ v = w = 0.0;
+ for (i = 7; i >= 0; i--) {
+ v = v * y + InvP2[i];
+ w = w * y + InvQ2[i];
+ }
+ z = (v / w) * x;
+
+ } else {
+ if (u > 0.5)
+ y = 1.0 / Math.sqrt (-Math.log (1.0 - x));
+ else
+ y = 1.0 / Math.sqrt (-Math.log (2.0 * u));
+ v = 0.0;
+ for (i = 10; i >= 0; i--)
+ v = v * y + InvP3[i];
+ w = 0.0;
+ for (i = 8; i >= 0; i--)
+ w = w * y + InvQ3[i];
+ z = (v / w) / y;
+ }
+
+ if (negatif) {
+ if (u < 1.0e-105) {
+ final double RACPI = 1.77245385090551602729;
+ w = Math.exp (-z * z) / RACPI; // pdf
+ y = 2.0 * z * z;
+ v = 1.0;
+ double term = 1.0;
+ // Asymptotic series for erfc(z) (apart from exp factor)
+ for (i = 0; i < 6; ++i) {
+ term *= -(2 * i + 1) / y;
+ v += term;
+ }
+ // Apply 1 iteration of Newton solver to get last few decimals
+ z -= u / w - 0.5 * v / z;
+
+ }
+ return -(z * Num.RAC2);
+
+ } else
+ return z * Num.RAC2;
+ }
+
+
+ /**
+ * Computes the inverse normal distribution function
+ * with mean <SPAN CLASS="MATH"><I>μ</I></SPAN> and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN>. Uses different
+ * rational Chebyshev approximations.
+ * Returns 16 decimal digits of precision for
+ * <SPAN CLASS="MATH">2.2×10<SUP>-308</SUP> < <I>u</I> < 1</SPAN>.
+ *
+ */
+ public static double inverseF (double mu, double sigma, double u) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu + sigma*inverseF01 (u);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>μ</I>, <I>σ</I>)</SPAN> of the normal distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(σ)</SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(σ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double[] parameters = new double[2];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+ parameters[0] = sum / n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum = sum + (x[i] - parameters[0]) * (x[i] - parameters[0]);
+ parameters[1] = Math.sqrt (sum / n);
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a normal distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static NormalDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new NormalDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN> of the normal distribution
+ * with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the mean of the normal distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I></SPAN>
+ *
+ */
+ public static double getMean (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return mu;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>σ</I><SUP>2</SUP></SPAN> of the
+ * normal distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the variance of the normal distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>σ</I><SUP>2</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (sigma * sigma);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation <SPAN CLASS="MATH"><I>σ</I></SPAN> of the
+ * normal distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ * @return the standard deviation of the normal distribution
+ *
+ */
+ public static double getStandardDeviation (double mu, double sigma) {
+ return sigma;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {mu, sigma};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/NormalDist.tex b/source/umontreal/iro/lecuyer/probdist/NormalDist.tex
new file mode 100644
index 0000000..24843d5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NormalDist.tex
@@ -0,0 +1,650 @@
+\defmodule {NormalDist}
+
+Extends the class \class{ContinuousDistribution} for the {\em normal\/}
+distribution (e.g., \cite[page 80]{tJOH95a}). It has mean $\mu$
+and variance $\sigma^2$. Its density function is
+\eq
+ f (x) = \latex{\frac{1}{\sigma\sqrt{2\pi}}}e^{-(x-\mu)^2/(2\sigma^2)}
+ \html{/(\sqrt{2\pi}\sigma)}
+ \qquad \mbox{for } -\infty < x < \infty, \eqlabel{eq:fnormal}
+\endeq
+where $\sigma > 0$.
+When $\mu=0$ and $\sigma=1$, we have the {\em standard normal\/}
+distribution, with corresponding distribution function
+\begin{htmlonly}
+\eq
+ F(x) = \Phi(x) = \int_{-\infty}^x e^{-t^2/2}\ dt/\sqrt{2\pi}
+ \qquad \mbox{for } -\infty < x < \infty.
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ F(x) = \Phi(x) = \frac{1}{\sqrt{2\pi}} \int_{-\infty}^x e^{-t^2/2}\ dt
+ \qquad \mbox{for } -\infty < x < \infty. \eqlabel{eq:cdfnormal}
+\endeq
+\end{latexonly}
+
+The non-static methods \texttt{cdf}, \texttt{barF}, and \texttt{inverseF} are
+implemented via \method{cdf01}{}, \method{barF01}{},
+and \method{inverseF01}{}, respectively.
+
+%% \pierre{Should give an idea of the relative performance of the two
+%% types of approximations. See also \cite{rMAR94b} and compare.
+%% Do we really want to keep the sloppy \texttt{inverseF1, cdf1, ...},
+%% as the defaults? }
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalDist
+ * Description: normal or Gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;\end{hide}
+
+public class NormalDist extends ContinuousDistribution\begin{hide} {
+ protected double mu;
+ protected double sigma;
+ protected static final double RAC2PI = 2.50662827463100050; // Sqrt(2*Pi)
+
+ private static final double[] AbarF = {
+ 6.10143081923200418E-1,
+ -4.34841272712577472E-1,
+ 1.76351193643605501E-1,
+ -6.07107956092494149E-2,
+ 1.77120689956941145E-2,
+ -4.32111938556729382E-3,
+ 8.54216676887098679E-4,
+ -1.27155090609162743E-4,
+ 1.12481672436711895E-5,
+ 3.13063885421820973E-7,
+ -2.70988068537762022E-7,
+ 3.07376227014076884E-8,
+ 2.51562038481762294E-9,
+ -1.02892992132031913E-9,
+ 2.99440521199499394E-11,
+ 2.60517896872669363E-11,
+ -2.63483992417196939E-12,
+ -6.43404509890636443E-13,
+ 1.12457401801663447E-13,
+ 1.7281533389986098E-14,
+ -4.2641016949424E-15,
+ -5.4537197788E-16,
+ 1.5869760776E-16,
+ 2.08998378E-17,
+ -0.5900E-17
+ };
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NormalDist()\begin{hide} {
+ setParams (0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{NormalDist} object with default parameters $\mu = 0$
+ and $\sigma = 1$.
+ \end{tabb}
+\begin{code}
+
+ public NormalDist (double mu, double sigma)\begin{hide} {
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{NormalDist} object with mean $\mu$ = \texttt{mu}
+ and standard deviation $\sigma$ = \texttt{sigma}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ double z = (x - mu)/sigma;
+ return Math.exp (-0.5*z*z)/ (RAC2PI*sigma);
+ }
+
+ public double cdf (double x) {
+ return cdf01 ((x-mu)/sigma);
+ }
+
+ public double barF (double x) {
+ return barF01 ((x-mu)/sigma);
+ }
+
+ public double inverseF (double u) {
+ return mu + sigma * inverseF01 (u);
+ }
+
+ public double getMean() {
+ return NormalDist.getMean (mu, sigma);
+ }
+
+ public double getVariance() {
+ return NormalDist.getVariance (mu, sigma);
+ }
+
+ public double getStandardDeviation() {
+ return NormalDist.getStandardDeviation (mu, sigma);
+ }\end{hide}
+
+ public static double density01 (double x) \begin{hide} {
+ return Math.exp(-0.5*x*x)/RAC2PI;
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{density}{double,double,double}~\texttt{(0, 1, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double density (double mu, double sigma, double x) \begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ double z = (x - mu)/sigma;
+ return Math.exp (-0.5*z*z)/ (RAC2PI*sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the normal density function (\ref{eq:fnormal}).
+\end{tabb}
+\begin{code}\begin{hide}
+ /*
+ * The precision of double is 16 decimals; we shall thus use coeffmax = 24
+ * coefficients. But the approximation is good to 30 decimals of precision
+ * with 44 coefficients.
+ */
+ private static final int COEFFMAX = 24;
+
+ private static final double NORMAL2_A[] = {
+ 6.10143081923200417926465815756e-1,
+ -4.34841272712577471828182820888e-1,
+ 1.76351193643605501125840298123e-1,
+ -6.0710795609249414860051215825e-2,
+ 1.7712068995694114486147141191e-2,
+ -4.321119385567293818599864968e-3,
+ 8.54216676887098678819832055e-4,
+ -1.27155090609162742628893940e-4,
+ 1.1248167243671189468847072e-5,
+ 3.13063885421820972630152e-7,
+ -2.70988068537762022009086e-7,
+ 3.0737622701407688440959e-8,
+ 2.515620384817622937314e-9,
+ -1.028929921320319127590e-9,
+ 2.9944052119949939363e-11,
+ 2.6051789687266936290e-11,
+ -2.634839924171969386e-12,
+ -6.43404509890636443e-13,
+ 1.12457401801663447e-13,
+ 1.7281533389986098e-14,
+ -4.264101694942375e-15,
+ -5.45371977880191e-16,
+ 1.58697607761671e-16,
+ 2.0899837844334e-17,
+ -5.900526869409e-18,
+ -9.41893387554e-19
+/*, 2.14977356470e-19,
+ 4.6660985008e-20,
+ -7.243011862e-21,
+ -2.387966824e-21,
+ 1.91177535e-22,
+ 1.20482568e-22,
+ -6.72377e-25,
+ -5.747997e-24,
+ -4.28493e-25,
+ 2.44856e-25,
+ 4.3793e-26,
+ -8.151e-27,
+ -3.089e-27,
+ 9.3e-29,
+ 1.74e-28,
+ 1.6e-29,
+ -8.0e-30,
+ -2.0e-30
+*/
+ };
+\end{hide}
+
+ public static double cdf01 (double x)\begin{hide} {
+ /*
+ * Returns P[X < x] for the normal distribution.
+ * As in J. L. Schonfelder, Math. of Computation, Vol. 32,
+ * pp 1232--1240, (1978).
+ */
+
+ double t, r;
+
+ if (x <= -XBIG)
+ return 0.0;
+ if (x >= XBIG)
+ return 1.0;
+
+ x = -x/Num.RAC2;
+ if (x < 0) {
+ x = -x;
+ t = (x - 3.75) / (x + 3.75);
+ r = 1.0 - 0.5 * Math.exp ( -x * x) * Num.evalCheby (NORMAL2_A, COEFFMAX, t);
+ } else {
+ t = (x - 3.75) / (x + 3.75);
+ r = 0.5 * Math.exp ( -x * x) * Num.evalCheby (NORMAL2_A, COEFFMAX, t);
+ }
+ return r;
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{cdf}{double,double,double}~\texttt{(0, 1, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return cdf01 ((x-mu)/sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the normal distribution function with mean
+ $\mu$ and variance $\sigma^2$.
+ Uses the Chebyshev approximation \latex {proposed in \cite{tSCH78a}},
+% J. L. Schonfelder, Math. of Computation, Vol. 32, pp 1232--1240, (1978).
+ which gives 16 decimals of precision.
+% In the paper, the author gives the coefficients with 30 decimals of
+% precision.
+\end{tabb}
+\begin{code}
+
+ public static double barF01 (double x)\begin{hide} {
+ /*
+ * Returns P[X >= x] = 1 - F (x) where F is the normal distribution by
+ * computing the complementary distribution directly; it is thus more
+ * precise in the tail.
+ */
+
+ final double KK = 5.30330085889910643300; // 3.75 Sqrt (2)
+ double y, t;
+ int neg;
+
+ if (x >= XBIG)
+ return 0.0;
+ if (x <= -XBIG)
+ return 1.0;
+
+ if (x >= 0.0)
+ neg = 0;
+ else {
+ neg = 1;
+ x = -x;
+ }
+
+ t = (x - KK)/(x + KK);
+ y = Num.evalCheby (AbarF, 24, t);
+ y = y * Math.exp ( -x * x / 2.0) / 2.0;
+
+ if (neg == 1)
+ return 1.0 - y;
+ else
+ return y;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Same as
+ \method{barF}{double,double,double}~\texttt{(0, 1, x)}.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double mu, double sigma, double x)\begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return barF01 ((x-mu)/sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary normal distribution function
+ $\bar{F}(x) = 1 - \Phi ((x-\mu)/\sigma)$,
+ with mean $\mu$ and variance $\sigma^2$.
+ Uses a Chebyshev series giving 16 decimal digits of
+ precision \cite{tSCH78a}.
+\end{tabb}
+% Cette fonction est en fait (Erfc (x/Sqrt (2))) / 2, ou Erfc est la
+% fonction d'erreur complementaire.
+\begin{code}\begin{hide}
+ private static final double[] InvP1 = {
+ 0.160304955844066229311E2,
+ -0.90784959262960326650E2,
+ 0.18644914861620987391E3,
+ -0.16900142734642382420E3,
+ 0.6545466284794487048E2,
+ -0.864213011587247794E1,
+ 0.1760587821390590
+ };
+
+ private static final double[] InvQ1 = {
+ 0.147806470715138316110E2,
+ -0.91374167024260313396E2,
+ 0.21015790486205317714E3,
+ -0.22210254121855132366E3,
+ 0.10760453916055123830E3,
+ -0.206010730328265443E2,
+ 0.1E1
+ };
+
+ private static final double[] InvP2 = {
+ -0.152389263440726128E-1,
+ 0.3444556924136125216,
+ -0.29344398672542478687E1,
+ 0.11763505705217827302E2,
+ -0.22655292823101104193E2,
+ 0.19121334396580330163E2,
+ -0.5478927619598318769E1,
+ 0.237516689024448000
+ };
+
+ private static final double[] InvQ2 = {
+ -0.108465169602059954E-1,
+ 0.2610628885843078511,
+ -0.24068318104393757995E1,
+ 0.10695129973387014469E2,
+ -0.23716715521596581025E2,
+ 0.24640158943917284883E2,
+ -0.10014376349783070835E2,
+ 0.1E1
+ };
+
+ private static final double[] InvP3 = {
+ 0.56451977709864482298E-4,
+ 0.53504147487893013765E-2,
+ 0.12969550099727352403,
+ 0.10426158549298266122E1,
+ 0.28302677901754489974E1,
+ 0.26255672879448072726E1,
+ 0.20789742630174917228E1,
+ 0.72718806231556811306,
+ 0.66816807711804989575E-1,
+ -0.17791004575111759979E-1,
+ 0.22419563223346345828E-2
+ };
+
+ private static final double[] InvQ3 = {
+ 0.56451699862760651514E-4,
+ 0.53505587067930653953E-2,
+ 0.12986615416911646934,
+ 0.10542932232626491195E1,
+ 0.30379331173522206237E1,
+ 0.37631168536405028901E1,
+ 0.38782858277042011263E1,
+ 0.20372431817412177929E1,
+ 0.1E1
+ };\end{hide}
+
+ public static double inverseF01 (double u)\begin{hide} {
+ /*
+ * Returns the inverse of the cdf of the normal distribution.
+ * Rational approximations giving 16 decimals of precision.
+ * J.M. Blair, C.A. Edwards, J.H. Johnson, "Rational Chebyshev
+ * approximations for the Inverse of the Error Function", in
+ * Mathematics of Computation, Vol. 30, 136, pp 827, (1976)
+ */
+
+ int i;
+ boolean negatif;
+ double y, z, v, w;
+ double x = u;
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0, 1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ // Transform x as argument of InvErf
+ x = 2.0 * x - 1.0;
+ if (x < 0.0) {
+ x = -x;
+ negatif = true;
+ } else {
+ negatif = false;
+ }
+
+ if (x <= 0.75) {
+ y = x * x - 0.5625;
+ v = w = 0.0;
+ for (i = 6; i >= 0; i--) {
+ v = v * y + InvP1[i];
+ w = w * y + InvQ1[i];
+ }
+ z = (v / w) * x;
+
+ } else if (x <= 0.9375) {
+ y = x * x - 0.87890625;
+ v = w = 0.0;
+ for (i = 7; i >= 0; i--) {
+ v = v * y + InvP2[i];
+ w = w * y + InvQ2[i];
+ }
+ z = (v / w) * x;
+
+ } else {
+ if (u > 0.5)
+ y = 1.0 / Math.sqrt (-Math.log (1.0 - x));
+ else
+ y = 1.0 / Math.sqrt (-Math.log (2.0 * u));
+ v = 0.0;
+ for (i = 10; i >= 0; i--)
+ v = v * y + InvP3[i];
+ w = 0.0;
+ for (i = 8; i >= 0; i--)
+ w = w * y + InvQ3[i];
+ z = (v / w) / y;
+ }
+
+ if (negatif) {
+ if (u < 1.0e-105) {
+ final double RACPI = 1.77245385090551602729;
+ w = Math.exp (-z * z) / RACPI; // pdf
+ y = 2.0 * z * z;
+ v = 1.0;
+ double term = 1.0;
+ // Asymptotic series for erfc(z) (apart from exp factor)
+ for (i = 0; i < 6; ++i) {
+ term *= -(2 * i + 1) / y;
+ v += term;
+ }
+ // Apply 1 iteration of Newton solver to get last few decimals
+ z -= u / w - 0.5 * v / z;
+
+ }
+ return -(z * Num.RAC2);
+
+ } else
+ return z * Num.RAC2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as
+\method{inverseF}{double,double,double}~\texttt{(0, 1, u)}.
+\end{tabb}
+ \begin{code}
+
+ public static double inverseF (double mu, double sigma, double u) \begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu + sigma*inverseF01 (u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the inverse normal distribution function
+ with mean $\mu$ and variance $\sigma^2$. Uses different
+ rational Chebyshev approximations \cite{tBLA76a}.
+ Returns 16 decimal digits of precision for $2.2\times10^{-308} < u < 1$.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double[] parameters = new double[2];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += x[i];
+ parameters[0] = sum / n;
+
+ sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum = sum + (x[i] - parameters[0]) * (x[i] - parameters[0]);
+ parameters[1] = Math.sqrt (sum / n);
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\mu, \sigma)$ of the normal distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\hat\mu$, $\hat\sigma$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\mu , \hat\sigma)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat{\mu} & = & \bar{x}_n \ = \ \frac{1}{n} \sum_{i=1}^{n} x_i\\
+ \hat{\sigma} & = & \sqrt{\frac{1}{n} \sum_{i=1}^{n} (x_i - \bar{x}_n)^2}.
+ \end{eqnarray*}
+ See \cite[page 123]{tJOH95a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\mu}$, $\hat{\sigma}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static NormalDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new NormalDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a normal distribution with parameters $\mu$ and $\sigma$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \mu$ of the normal distribution
+ with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the normal distribution $E[X] = \mu$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+
+ return (sigma * sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = \sigma^2$ of the
+ normal distribution with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the normal distribution $\mbox{Var}[X] = \sigma^2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double mu, double sigma)\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation $\sigma$ of the
+ normal distribution with parameters $\mu$ and $\sigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the normal distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$.
+ \end{tabb}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\mu$ and $\sigma$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {mu, sigma};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\mu$, $\sigma$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : mu = " + mu + ", sigma = " + sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/NormalDistQuick.java b/source/umontreal/iro/lecuyer/probdist/NormalDistQuick.java
new file mode 100644
index 0000000..e4ac639
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NormalDistQuick.java
@@ -0,0 +1,966 @@
+
+
+/*
+ * Class: NormalDistQuick
+ * Description: normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * A variant of the class {@link NormalDist} (for the <EM>normal</EM>
+ * distribution with mean <SPAN CLASS="MATH"><I>μ</I></SPAN> and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN>).
+ * The difference is in the implementation of the methods {@link #cdf01 cdf01},
+ * {@link #barF01 barF01} and {@link #inverseF01 inverseF01}, which are faster
+ * but less accurate than those of the class {@link NormalDist}.
+ *
+ */
+public class NormalDistQuick extends NormalDist {
+
+
+
+ /**
+ * Constructs a <TT>NormalDistQuick</TT> object with default parameters <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>σ</I> = 1</SPAN>.
+ *
+ */
+ public NormalDistQuick() {
+ super ();
+ }
+
+
+ /**
+ * Constructs a <TT>NormalDistQuick</TT> object with mean <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT>
+ * and standard deviation <SPAN CLASS="MATH"><I>σ</I></SPAN> = <TT>sigma</TT>.
+ *
+ */
+ public NormalDistQuick (double mu, double sigma) {
+ setParams (mu, sigma);
+ }
+
+
+
+ /* *************************************************************/
+ /* These public methods are necessary so that the methods cdf01,
+ * barF01 and inverseF01 that are used are those of the present
+ * class and not those of 'NormalDist'
+ */
+
+ public double cdf (double x) {
+ return cdf01 ((x-mu)/sigma);
+ }
+
+ public double barF (double x) {
+ return barF01 ((x-mu)/sigma);
+ }
+
+ public double inverseF (double u) {
+ return mu + sigma * inverseF01 (u);
+ }
+ /* ************************************************************/
+
+ private static final double V[] = {
+ 1.2533141373155, 1.137490921203605, 1.037824575853727,
+ 0.951527192071207, 0.8763644564536924, 0.8105337152790306,
+ 0.7525711790634081, 0.7012808218544303, 0.6556795424187987,
+ 0.61495459615093, 0.5784303460476312, 0.5455421356582171,
+ 0.5158156382179634, 0.4888504415275737, 0.4643069280394423,
+ 0.4418957328326002, 0.4213692292880546, 0.4025146181296722,
+ 0.3851482907984348, 0.3691112106902635, 0.3542651113297938,
+ 0.3404893532870847, 0.3276783146905521, 0.31573921586941,
+ 0.3045902987101033, 0.2941592970402893, 0.284382146748493,
+ 0.2752018941576065, 0.2665677689682238, 0.2584343943120386,
+ 0.2507611114439651, 0.243511400615456, 0.2366523829135607,
+ 0.230154390478801, 0.2239905946538289, 0.2181366833614714,
+ 0.2125705804420318, 0.2072722008565011, 0.2022232366330547,
+ 0.1974069692375194, 0.1928081047153158, 0.1884126285076003,
+ 0.1842076773079702, 0.1801814257143918, 0.1763229857571027,
+ 0.1726223176578506, 0.1690701504076941, 0.1656579109468773,
+ 0.1623776608968675, 0.1592220399363674, 0.1561842150339759,
+ 0.153257834853479, 0.1504369887362691, 0.1477161697413935,
+ 0.145090241289131, 0.1425544070104023, 0.1401041834530503,
+ 0.1377353753382303, 0.1354440530967635, 0.1332265324471292,
+ 0.1310793558044918, 0.1289992753343376, 0.126983237485437,
+ 0.1250283688553504, 0.1231319632579323, 0.1212914698765462,
+ 0.119504482399253, 0.1177687290432979, 0.1160820633859823,
+ 0.1144424559276431, 0.112847986320103, 0.1112968362007359,
+ 0.1097872825783083, 0.1083176917221132, 0.1068865135106745,
+ 0.1054922762005562, 0.1041335815795983, 0.1028091004723001,
+ 0.1015175685681028, 0.1002577825460485, 0.09902859647173194,
+ 0.09782891844465691, 0.09665770747608191, 0.09551397057921558,
+ 0.09439676005522439, 0.09330517095996169, 0.09223833873763035,
+ 0.09119543700877471, 0.09017567550106469, 0.08917829811230435,
+ 0.08820258109597616, 0.08724783136042988, 0.08631338487354936,
+ 0.08539860516539227, 0.08450288192189578, 0.08362562966329139,
+ 0.08276628650136918, 0.08192431297018954, 0.08109919092525536,
+ 0.08029042250654048, 0.07949752916111721, 0.07872005072144664,
+ 0.07795754453568722, 0.07720958464664668, 0.07647576101624852,
+ 0.07575567879261112, 0.07504895761704659, 0.07435523096847724,
+ 0.07367414554294564, 0.07300536066605566, 0.07234854773633338,
+ 0.07170338969763433, 0.07106958053885212, 0.07044682481930167,
+ 0.06983483721825942, 0.06923334210724434, 0.06864207314371742,
+ 0.06806077288496332, 0.0674891924209997, 0.06692709102543307,
+ 0.06637423582325017
+};
+
+
+ /**
+ * Same as {@link #cdf(double,double,double) cdf} <TT>(0.0, 1.0, x)</TT>.
+ *
+ */
+ public static double cdf01 (double x) {
+ if (x >= XBIG)
+ return 1.0;
+ if (x <= -XBIG)
+ return 0.0;
+
+ /* The relative precision of NormalDistQuick decreases gradually:
+ at x = -8, there are 15 decimals; at x = -26, there are about
+ 2 decimals. NormalDist is more precise. */
+ if (x < -8.0)
+ return NormalDist.cdf01 (x);
+
+ boolean negatif;
+ if (x < 0.0) {
+ negatif = true;
+ x = -x;
+ } else {
+ negatif = false;
+ }
+ int j = (int) (8.0 * x + 0.5);
+ if (j > 120)
+ j = 120;
+ double r = V[j];
+ double z = 0.125 * j;
+ double h = x - z;
+ double r1 = r * z - 1.0;
+ double r2 = 0.5 * (r + z * r1);
+ double r3 = (r1 + z * r2) / 3.0;
+ double r4 = 0.25 * (r2 + z * r3);
+ double r5 = 0.2 * (r3 + z * r4);
+ double r6 = (r4 + z * r5) / 6.0;
+ double r7 = (r5 + z * r6) / 7.0;
+ double r8 = 0.125 * (r6 + z * r7);
+ double t = r + h * (r1 + h * (r2 + h * (r3 +
+ h * (r4 + h * (r5 + h * (r6 + h * (r7 + h * r8)))))));
+ double u = t * Math.exp (-0.5 * x * x - 0.9189385332046727);
+ if (negatif)
+ return u;
+ else
+ return 1.0 - u;
+ }
+
+
+ /**
+ * Returns an approximation of <SPAN CLASS="MATH"><I>Φ</I>(<I>x</I>)</SPAN>, where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard
+ * normal distribution function, with mean 0 and variance 1.
+ * Uses Marsaglia et al's fast method
+ * with table lookups. Returns 15 decimal digits of precision.
+ * This method is approximately 60% faster than <TT>NormalDist.cdf</TT>.
+ *
+ */
+ public static double cdf (double mu, double sigma, double x) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return cdf01 ((x-mu)/sigma);
+ }
+
+
+ /**
+ * Same as {@link #barF(double,double,double) barF} <TT>(0.0, 1.0, x)</TT>.
+ *
+ */
+ public static double barF01 (double x) {
+ return cdf01(-x);
+ }
+
+
+ /**
+ * Returns an approximation of
+ * <SPAN CLASS="MATH">1 - <I>Φ</I>(<I>x</I>)</SPAN>, where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard
+ * normal distribution function, with mean 0 and variance 1.
+ * Uses Marsaglia et al's fast method
+ * with table lookups. Returns 15 decimal digits of precision.
+ * This method is approximately twice faster than <TT>NormalDist.barF</TT>.
+ *
+ */
+ public static double barF (double mu, double sigma, double x) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return barF01 ((x-mu)/sigma);
+ }
+
+ private static final double[] A = {
+
+ 6.33795775455, 6.33321372178, 6.32860807635,
+ 6.32413288056, 6.31978086301, 6.31554534594,
+ 6.31142018224, 6.30739970055, 6.30347865735,
+ 6.29965219486, 6.29591580408, 6.29226529211,
+ 6.28869675323, 6.28520654322, 6.28179125646,
+ 6.27844770553, 6.27517290294, 6.27196404468,
+ 6.2688184955, 6.26573377562, 6.26270754867,
+ 6.25973761088, 6.25682188116, 6.25395839218,
+ 6.25114528219, 6.24838078761, 6.2456632362,
+ 6.24299104087, 6.240362694, 6.23777676216,
+ 6.23523188139, 6.23272675268, 6.23026013799,
+ 6.22543778081, 6.22075598811, 6.2162067,
+ 6.21178253304, 6.20747670647, 6.20328297819,
+ 6.19919558917, 6.19520921468, 6.19131892165,
+ 6.18752013112, 6.18380858505, 6.180180317,
+ 6.17663162601, 6.1731590534, 6.16975936206,
+ 6.16642951782, 6.16316667288, 6.15996815078,
+ 6.1568314329, 6.15375414629, 6.15073405259,
+ 6.14776903807, 6.14485710448, 6.14199636082,
+ 6.13918501574, 6.13642137069, 6.13370381356,
+ 6.13103081295, 6.12840091282, 6.12581272765,
+ 6.12326493791, 6.12075628597, 6.11585165173,
+ 6.11108986339, 6.10646273321, 6.10196276044,
+ 6.09758305644, 6.09331727974, 6.08915957943,
+ 6.08510454578, 6.08114716689, 6.07728279052,
+ 6.07350709041, 6.06981603653, 6.06620586849,
+ 6.06267307204, 6.05921435799, 6.05582664334,
+ 6.05250703436, 6.04925281142, 6.04606141524,
+ 6.0429304345, 6.03985759465, 6.03684074773,
+ 6.03387786311, 6.03096701912, 6.02810639533,
+ 6.02529426559, 6.0225289916, 6.01980901702,
+ 6.01713286212, 6.01449911878, 6.01190644597,
+ 6.00935356553, 6.00436236049, 5.99951639563,
+ 5.99480734912, 5.99022759726, 5.98577013833,
+ 5.98142852661, 5.9771968149, 5.97306950434,
+ 5.96904150032, 5.96510807378, 5.96126482692,
+ 5.95750766299, 5.95383275929, 5.95023654327,
+ 5.94671567111, 5.94326700856, 5.93988761376,
+ 5.93657472175, 5.9333257306, 5.93013818875,
+ 5.92700978364, 5.92393833142, 5.92092176747,
+ 5.91795813793, 5.91504559188, 5.91218237419,
+ 5.90936681905, 5.90659734398, 5.90387244436,
+ 5.90119068838, 5.89855071241, 5.89595121674,
+ 5.89086876372, 5.88593406746, 5.88113866541,
+ 5.87647480476, 5.87193536504, 5.86751379108,
+ 5.86320403456, 5.85900050299, 5.85489801495,
+ 5.85089176064, 5.84697726716, 5.84315036764,
+ 5.83940717394, 5.83574405227, 5.83215760142,
+ 5.82864463328, 5.82520215541, 5.82182735527,
+ 5.81851758606, 5.81527035394, 5.81208330646,
+ 5.80895422198, 5.80588100021, 5.80286165344,
+ 5.79989429868, 5.79697715037, 5.79410851379,
+ 5.79128677896, 5.78851041509, 5.78577796547,
+ 5.78308804272, 5.78043932448, 5.7752605134,
+ 5.77023210866, 5.76534549972, 5.76059279799,
+ 5.75596675813, 5.75146070984, 5.74706849845,
+ 5.74278443301, 5.73860324073, 5.73452002692,
+ 5.73053023962, 5.72662963823, 5.72281426575,
+ 5.71908042393, 5.71542465116, 5.7118437027,
+ 5.70833453283, 5.70489427893, 5.70152024702,
+ 5.69820989876, 5.69496083963, 5.69177080831,
+ 5.68863766688, 5.68555939205, 5.68253406704,
+ 5.67955987426, 5.67663508853, 5.67375807094,
+ 5.67092726314, 5.66814118219, 5.6653984157,
+ 5.66269761746, 5.65741684752, 5.65228927845,
+ 5.64730614385, 5.64245941209, 5.6377417063,
+ 5.63314623489, 5.62866673113, 5.62429740032,
+ 5.6200328734, 5.61586816622, 5.61179864349,
+ 5.60781998693, 5.60392816693, 5.6001194173,
+ 5.5963902128, 5.59273724893, 5.58915742386,
+ 5.58564782214, 5.58220570003, 5.57882847227,
+ 5.57551370004, 5.57225908009, 5.56906243489,
+ 5.56592170358, 5.56283493377, 5.55980027406,
+ 5.55681596716, 5.55388034364, 5.55099181609,
+ 5.54814887388, 5.54535007824, 5.5425940578,
+ 5.53720516928, 5.53197243466, 5.52688692307,
+ 5.52194045196, 5.5171255056, 5.51243516442,
+ 5.50786304341, 5.50340323832, 5.49905027849,
+ 5.4947990853, 5.49064493552, 5.4865834288,
+ 5.48261045887, 5.47872218783, 5.4749150232,
+ 5.47118559738, 5.46753074927, 5.46394750763,
+ 5.46043307616, 5.45698482001, 5.45360025356,
+ 5.45027702931, 5.44701292782, 5.44380584855,
+ 5.44065380149, 5.43755489952, 5.43450735142,
+ 5.43150945548, 5.42855959358, 5.42565622584,
+ 5.42279788561, 5.41998317492, 5.41447936842,
+ 5.40913484272, 5.40394049304, 5.39888797725,
+ 5.39396963282, 5.38917840481, 5.38450778312,
+ 5.37995174765, 5.37550472027, 5.37116152244,
+ 5.36691733786, 5.36276767939, 5.35870835965,
+ 5.35473546482, 5.3508453313, 5.34703452473,
+ 5.34329982125, 5.33963819058, 5.3360467808,
+ 5.33252290459, 5.32906402674, 5.32566775291,
+ 5.32233181932, 5.31905408341, 5.31583251534,
+ 5.31266519015, 5.30955028069, 5.30648605108,
+ 5.30347085068, 5.30050310862, 5.29758132874,
+ 5.29470408485, 5.28907782499, 5.28361416415,
+ 5.27830381332, 5.2731382613, 5.26810969,
+ 5.26321090103, 5.25843525172, 5.2537765992,
+ 5.24922925139, 5.24478792384, 5.24044770161,
+ 5.23620400551, 5.23205256217, 5.22798937729,
+ 5.22401071182, 5.2201130606, 5.21629313323,
+ 5.21254783681, 5.20887426039, 5.205269661,
+ 5.20173145085, 5.19825718588, 5.19484455528,
+ 5.19149137189, 5.1881955636, 5.18495516531,
+ 5.18176831175, 5.17863323075, 5.17554823716,
+ 5.17251172724, 5.16952217346, 5.16657811973,
+ 5.16082101937, 5.15523004763, 5.14979571985,
+ 5.14450934531, 5.13936294072, 5.13434915534,
+ 5.12946120568, 5.12469281843, 5.1200381805,
+ 5.11549189488, 5.11104894181, 5.10670464433,
+ 5.10245463765, 5.09829484194, 5.09422143797,
+ 5.09023084532, 5.08631970285, 5.08248485106,
+ 5.0787233163, 5.07503229637, 5.0714091476,
+ 5.06785137307, 5.06435661191, 5.06092262957,
+ 5.05754730896, 5.05422864229, 5.05096472372,
+ 5.0477537425, 5.04459397676, 5.04148378777,
+ 5.03842161464, 5.03540596946, 5.02950864968,
+ 5.02378122248, 5.01821399503, 5.01279808548,
+ 5.00752533468, 5.0023882297, 4.99737983708,
+ 4.99249374467, 4.98772401045, 4.98306511755,
+ 4.97851193459, 4.97405968039, 4.96970389282,
+ 4.96544040091, 4.96126529999, 4.95717492946,
+ 4.95316585275, 4.94923483945, 4.94537884901,
+ 4.94159501614, 4.93788063759, 4.93423316006,
+ 4.93065016935, 4.92712938035, 4.92366862801,
+ 4.92026585904, 4.91691912431, 4.91362657193,
+ 4.91038644087, 4.90719705507, 4.90405681805,
+ 4.90096420796, 4.89491612703, 4.8890419637,
+ 4.88333180298, 4.87777655906, 4.87236788511,
+ 4.86709809501, 4.86196009527, 4.85694732544,
+ 4.85205370587, 4.8472735917, 4.84260173228,
+ 4.8380332352, 4.8335635343, 4.82918836126,
+ 4.82490372013, 4.8207058646, 4.81659127758,
+ 4.81255665279, 4.80859887824, 4.8047150212,
+ 4.80090231471, 4.79715814524, 4.79348004153,
+ 4.78986566443, 4.78631279763, 4.78281933914,
+ 4.77938329358, 4.77600276506, 4.77267595063,
+ 4.76940113431, 4.76617668159, 4.76300103427,
+ 4.75679027709, 4.7507577546, 4.7448933141,
+ 4.73918765149, 4.73363221901, 4.72821914523,
+ 4.72294116527, 4.71779155991, 4.71276410212,
+ 4.70785300996, 4.70305290507, 4.69835877584,
+ 4.6937659447, 4.689270039, 4.68486696501,
+ 4.68055288459, 4.67632419431, 4.67217750662,
+ 4.66810963292, 4.6641175682, 4.66019847719,
+ 4.65634968177, 4.65256864953, 4.64885298336,
+ 4.64520041195, 4.64160878112, 4.63807604585,
+ 4.63460026304, 4.63117958481, 4.62781225236,
+ 4.6244965903, 4.6212310015, 4.61484401763,
+ 4.60863991397, 4.60260828265, 4.59673958525,
+ 4.59102505831, 4.58545663137, 4.58002685553,
+ 4.57472884104, 4.56955620253, 4.56450301082,
+ 4.55956375032, 4.55473328135, 4.55000680661,
+ 4.54537984133, 4.54084818664, 4.5364079057,
+ 4.53205530226, 4.52778690138, 4.52359943209,
+ 4.51948981163, 4.51545513127, 4.51149264343,
+ 4.5075997499, 4.50377399122, 4.50001303691,
+ 4.49631467662, 4.49267681191, 4.4890974489,
+ 4.48557469132, 4.48210673427, 4.47869185842,
+ 4.47532842465, 4.46874969883, 4.46235887005,
+ 4.45614525563, 4.45009906476, 4.44421130165,
+ 4.43847368137, 4.43287855675, 4.42741885424,
+ 4.42208801786, 4.41687995966, 4.41178901614,
+ 4.4068099095, 4.40193771332, 4.39716782193,
+ 4.39249592307, 4.3879179734, 4.38343017655,
+ 4.37902896328, 4.37471097372, 4.37047304122,
+ 4.36631217778, 4.3622255608, 4.35821052107,
+ 4.35426453179, 4.35038519863, 4.34657025053,
+ 4.34281753142, 4.33912499252, 4.33549068532,
+ 4.33191275513, 4.32838943511, 4.32491904083,
+ 4.31813067359, 4.31153564248, 4.3051229678,
+ 4.29888258592, 4.2928052498, 4.28688244266,
+ 4.28110630283, 4.27546955801, 4.26996546758,
+ 4.26458777186, 4.25933064724, 4.25418866648,
+ 4.24915676339, 4.2442302014, 4.23940454548,
+ 4.234675637, 4.23003957111, 4.22549267647,
+ 4.22103149691, 4.2166527749, 4.21235343653,
+ 4.20813057793, 4.20398145293, 4.19990346176,
+ 4.19589414084, 4.19195115337, 4.18807228072,
+ 4.18425541464, 4.18049854994, 4.17679977794,
+ 4.17315728028, 4.16956932335, 4.16255048996,
+ 4.15573091512, 4.14909929681, 4.14264527528,
+ 4.13635933075, 4.1302326947, 4.12425727258,
+ 4.11842557618, 4.11273066434, 4.1071660908,
+ 4.10172585798, 4.09640437621, 4.0911964274,
+ 4.08609713269, 4.08110192358, 4.07620651603,
+ 4.07140688728, 4.0666992549, 4.06208005798,
+ 4.05754594009, 4.05309373387, 4.04872044701,
+ 4.04442324957, 4.04019946237, 4.03604654644,
+ 4.03196209338, 4.02794381649, 4.0239895427,
+ 4.02009720515, 4.0162648363, 4.01249056171,
+ 4.00877259417, 4.00149883606, 3.99443081604,
+ 3.98755688078, 3.98086634771, 3.97434939969,
+ 3.96799699364, 3.96180078097, 3.95575303793,
+ 3.94984660457, 3.94407483096, 3.93843152965,
+ 3.93291093367, 3.92750765903, 3.9222166715,
+ 3.91703325678, 3.91195299388, 3.90697173117,
+ 3.90208556486, 3.89729081955, 3.89258403075,
+ 3.88796192889, 3.88342142501, 3.87895959754,
+ 3.87457368047, 3.87026105237, 3.86601922652,
+ 3.86184584173, 3.85773865405, 3.85369552908,
+ 3.84971443491, 3.84579343567, 3.8419306855,
+ 3.83437296653, 3.82702811235, 3.81988408487,
+ 3.81292984787, 3.80615525838, 3.79955097242,
+ 3.79310836293, 3.78681944792, 3.7806768275,
+ 3.77467362835, 3.76880345477, 3.76306034518,
+ 3.75743873361, 3.75193341525, 3.74653951576,
+ 3.74125246377, 3.73606796613, 3.73098198575,
+ 3.72599072151, 3.72109059012, 3.71627820978,
+ 3.71155038522, 3.70690409424, 3.70233647531,
+ 3.69784481637, 3.69342654456, 3.68907921677,
+ 3.68480051108, 3.68058821879, 3.67644023716,
+ 3.67235456274, 3.66832928512, 3.66045271041,
+ 3.65279688532, 3.64534934809, 3.63809867295,
+ 3.63103435798, 3.62414672762, 3.61742684789,
+ 3.6108664521, 3.60445787576, 3.59819399923,
+ 3.592068197, 3.58607429286, 3.58020651993,
+ 3.57445948514, 3.56882813748, 3.5633077395,
+ 3.5578938418, 3.55258226001, 3.54736905409,
+ 3.54225050954, 3.53722312046, 3.53228357414,
+ 3.52742873704, 3.52265564207, 3.51796147693,
+ 3.51334357346, 3.50879939795, 3.50432654219,
+ 3.49992271527, 3.49558573607, 3.4913135263,
+ 3.48710410411, 3.47886614237, 3.47085771144,
+ 3.46306588016, 3.45547879117, 3.44808554464,
+ 3.44087609744, 3.4338411752, 3.42697219544,
+ 3.42026120015, 3.41370079626, 3.40728410324,
+ 3.40100470653, 3.39485661617, 3.38883422999,
+ 3.38293230069, 3.37714590628, 3.37147042368,
+ 3.36590150482, 3.36043505519, 3.35506721435,
+ 3.34979433839, 3.34461298392, 3.33951989355,
+ 3.33451198267, 3.3295863274, 3.32474015352,
+ 3.3199708264, 3.31527584174, 3.31065281706,
+ 3.3060994839, 3.3016136806, 3.29719334569,
+ 3.28854129969, 3.28012863599, 3.27194189999,
+ 3.26396875282, 3.25619785067, 3.24861874006,
+ 3.24122176661, 3.23399799517, 3.22693913976,
+ 3.22003750184, 3.21328591581, 3.20667770063,
+ 3.2002066169, 3.19386682855, 3.18765286865,
+ 3.18155960874, 3.17558223129, 3.16971620498,
+ 3.1639572623, 3.15830137938, 3.15274475767,
+ 3.14728380734, 3.14191513221, 3.13663551594,
+ 3.13144190952, 3.12633141979, 3.12130129892,
+ 3.11634893481, 3.11147184224, 3.10666765471,
+ 3.10193411699, 3.0972690782, 3.08813637782,
+ 3.07925420286, 3.07060851058, 3.0621864209,
+ 3.0539760908, 3.04596660532, 3.03814788252,
+ 3.03051059033, 3.02304607359, 3.01574628977,
+ 3.00860375216, 3.00161147949, 2.9947629512,
+ 2.98805206756, 2.98147311398, 2.97502072908,
+ 2.96868987601, 2.96247581659, 2.95637408809,
+ 2.95038048206, 2.94449102535, 2.93870196271,
+ 2.93300974113, 2.92741099539, 2.92190253507,
+ 2.9164813325, 2.91114451188, 2.90588933915,
+ 2.90071321284, 2.89561365554, 2.8905883061,
+ 2.88563491243, 2.8759354898, 2.86649931119,
+ 2.85731166746, 2.84835906508, 2.83962909503,
+ 2.83111031887, 2.82279216952, 2.8146648644,
+ 2.80671932916, 2.79894713055, 2.79134041701,
+ 2.78389186607, 2.77659463754, 2.76944233182,
+ 2.76242895259, 2.75554887345, 2.74879680789,
+ 2.74216778225, 2.73565711142, 2.72926037674,
+ 2.72297340607, 2.71679225565, 2.71071319364,
+ 2.70473268509, 2.69884737824, 2.693054092,
+ 2.68734980447, 2.68173164244, 2.6761968717,
+ 2.67074288817, 2.66536720972, 2.66006746862,
+ 2.64968685818, 2.63958415075, 2.6297438751,
+ 2.62015183589, 2.61079497623, 2.60166125833,
+ 2.59273955939, 2.5840195806, 2.5754917671,
+ 2.5671472376, 2.55897772201, 2.55097550632,
+ 2.54313338341, 2.53544460937, 2.52790286427,
+ 2.52050221719, 2.51323709464, 2.50610225225,
+ 2.49909274914, 2.4922039248, 2.4854313781,
+ 2.4787709483, 2.47221869765, 2.4657708957,
+ 2.45942400483, 2.45317466714, 2.44701969236,
+ 2.44095604678, 2.43498084314, 2.42909133123,
+ 2.42328488933, 2.41755901624, 2.40633953091,
+ 2.39541501107, 2.38476910497, 2.37438680605,
+ 2.36425430827, 2.35435888041, 2.34468875653,
+ 2.33523304007, 2.3259816196, 2.31692509462,
+ 2.30805470992, 2.2993622974, 2.29084022432,
+ 2.28248134715, 2.27427897031, 2.26622680921,
+ 2.25831895701, 2.25054985471, 2.24291426415,
+ 2.23540724356, 2.22802412548, 2.22076049665,
+ 2.2136121797, 2.20657521654, 2.19964585312,
+ 2.19282052555, 2.18609584736, 2.17946859779,
+ 2.1729357111, 2.1664942667, 2.16014147999,
+ 2.15387469406, 2.14158908915, 2.12961846912,
+ 2.11794545368, 2.10655408816, 2.09542969053,
+ 2.08455871841, 2.07392865314, 2.06352789832,
+ 2.05334569055, 2.04337202094, 2.03359756554,
+ 2.02401362372, 2.01461206336, 2.00538527192,
+ 1.99632611278, 1.98742788593, 1.97868429286,
+ 1.97008940476, 1.9616376339, 1.95332370775,
+ 1.94514264545, 1.93708973655, 1.92916052156,
+ 1.92135077429, 1.91365648573, 1.90607384923,
+ 1.89859924703, 1.89122923782, 1.88396054536,
+ 1.87679004798, 1.86971476892, 1.86273186742,
+ 1.84903246517, 1.83567153691, 1.82263048664,
+ 1.80989223848, 1.79744107395, 1.78526249044,
+ 1.77334307781, 1.76167041036, 1.75023295184,
+ 1.73901997173, 1.72802147122, 1.71722811751,
+ 1.70663118536, 1.69622250501, 1.68599441547,
+ 1.67593972277, 1.66605166239, 1.65632386534,
+ 1.64675032767, 1.63732538277, 1.62804367632,
+ 1.61890014354, 1.60988998842, 1.60100866489,
+ 1.59225185952, 1.5836154758, 1.57509561963,
+ 1.56668858607, 1.55839084718, 1.55019904079,
+ 1.54210996014, 1.53412054435, 1.51842914115,
+ 1.50310294313, 1.48812189602, 1.47346757795,
+ 1.45912302502, 1.44507257982, 1.4313017591,
+ 1.417797138, 1.40454624816, 1.391537488,
+ 1.37876004322, 1.36620381637, 1.35385936408,
+ 1.34171784108, 1.32977095018, 1.3180108973,
+ 1.30643035113, 1.29502240671, 1.28378055261,
+ 1.27269864119, 1.26177086164, 1.25099171546,
+ 1.24035599423, 1.22985875922, 1.21949532285,
+ 1.20926123171, 1.19915225099, 1.1891643502,
+ 1.17929369001, 1.16953661021, 1.15988961853,
+ 1.15034938038, 1.13157655839, 1.11319427716,
+ 1.09518065276, 1.07751556704, 1.06018047944,
+ 1.04315826332, 1.02643306314, 1.00999016925,
+ 0.993815907861, 0.977897543941, 0.962223195295,
+ 0.946781756301, 0.931562830007, 0.916556667533,
+ 0.90175411383, 0.887146559019, 0.872725894627,
+ 0.858484474142, 0.844415077375, 0.830510878205,
+ 0.816765415315, 0.803172565598, 0.789726519943,
+ 0.776421761148, 0.763253043733, 0.750215375468,
+ 0.737304000439, 0.724514383492, 0.711842195939,
+ 0.699283302383, 0.686833748575, 0.674489750196,
+ 0.650104070648, 0.626099012346, 0.602449453164,
+ 0.579132162256, 0.556125593619, 0.533409706241,
+ 0.510965806738, 0.488776411115, 0.466825122853,
+ 0.445096524986, 0.423576084201, 0.402250065322,
+ 0.381105454764, 0.36012989179, 0.339311606539,
+ 0.318639363964, 0.29810241293, 0.277690439822,
+ 0.257393526101, 0.237202109329, 0.21710694721,
+ 0.197099084294, 0.177169820992, 0.15731068461,
+ 0.137513402144, 0.117769874579, 0.0980721524887,
+ 0.0784124127331, 0.0587829360689, 0.0391760855031,
+ 0.0195842852301, 0
+ };
+
+ private static final double[] B = {
+
+ 468043598.745, 454185281.982, 441133386.786,
+ 428819269.757, 417181876.023, 406166717.813,
+ 395725013.783, 385812960.329, 376391111.851,
+ 367423851.404, 358878936.738, 350727109.464,
+ 342941757.343, 335498621.458, 328375541.45,
+ 321552233.174, 315010094.053, 308732032.185,
+ 302702315.899, 296906440.935, 291331012.915,
+ 285963643.058, 280792855.461, 275808004.446,
+ 270999200.737, 266357245.389, 261873570.517,
+ 257540186.041, 253349631.735, 249294933.976,
+ 245369566.664, 241567415.856, 237882747.698,
+ 230844652.301, 224215968.814, 217961855.044,
+ 212051320.023, 206456705.678, 201153250.091,
+ 196118717.706, 191333084.832, 186778271.013,
+ 182437908.646, 178297144.629, 174342468.981,
+ 170561566.22, 166943186.067, 163477030.605,
+ 160153655.481, 156964383.183, 153901226.667,
+ 150956821.959, 148124368.489, 145397576.172,
+ 142770618.331, 140238089.759, 137794969.238,
+ 135436586.005, 133158589.665, 130956923.161,
+ 128827798.432, 126767674.455, 124773237.414,
+ 122841382.741, 120969198.842, 117393074.498,
+ 114024901.787, 110846987.361, 107843593.262,
+ 105000673.785, 102305653.75, 99747240.7669,
+ 97315265.5629, 95000545.5991, 92794768.101,
+ 90690389.3539, 88680547.6828, 86758987.9953,
+ 84919996.1313, 83158341.5649, 81469227.2427,
+ 79848245.5433, 78291339.5027, 76794768.5853,
+ 75355078.3902, 73969073.775, 72633794.9552,
+ 71346496.2016, 70104626.8117, 68905814.0771,
+ 67747848.0069, 66628667.5985, 65546348.4768,
+ 64499091.7445, 63485213.9074, 62503137.7568,
+ 61551384.1017, 59733373.2481, 58021039.3817,
+ 56405393.2293, 54878438.8023, 53433039.7483,
+ 52062806.7313, 50762002.0764, 49525458.6675,
+ 48348510.6706, 47226934.1194, 46156895.7613,
+ 45134908.8531, 44157794.8309, 43222649.9599,
+ 42326816.2279, 41467855.862, 40643528.9565,
+ 39851773.7738, 39090689.3553, 38358520.1316,
+ 37653642.2677, 36974551.5205, 36319852.4159,
+ 35688248.581, 35078534.0907, 34489585.7054,
+ 33920355.8956, 33369866.562, 32837203.3696,
+ 32321510.6295, 31821986.6654, 31337879.6134,
+ 30413135.3113, 29542122.4706, 28720271.6567,
+ 27943518.2501, 27208234.5323, 26511172.4564,
+ 25849415.1891, 25220335.8956, 24621562.5335,
+ 24050947.6575, 23506542.4223, 22986574.1168,
+ 22489426.6833, 22013623.7674, 21557813.9244,
+ 21120757.6673, 20701316.0956, 20298440.8828,
+ 19911165.4376, 19538597.0812, 19179910.1071,
+ 18834339.6081, 18501175.9757, 18179759.9851,
+ 17869478.3966, 17569760.0097, 17280072.1174,
+ 16999917.3132, 16728830.6117, 16466376.8456,
+ 16212148.3111, 15965762.6321, 15495105.5155,
+ 15051783.6023, 14633472.8806, 14238106.0377,
+ 13863837.9303, 13509016.4872, 13172158.0713,
+ 12851926.5229, 12547115.2589, 12256631.9188,
+ 11979485.1457, 11714773.1625, 11461673.8654,
+ 11219436.2047, 10987372.6619, 10764852.663,
+ 10551296.7957, 10346171.7177, 10148985.6613,
+ 9959284.45525, 9776647.99501, 9600687.10337,
+ 9431040.73252, 9267373.46466, 9109373.27455,
+ 8956749.52272, 8809231.15191, 8666565.06313,
+ 8528514.65085, 8394858.47935, 8265389.08465,
+ 8139911.88833, 7900214.37602, 7674431.7041,
+ 7461381.19, 7260010.7574, 7069381.37083,
+ 6888652.23304, 6717068.2507, 6553949.37303,
+ 6398681.4844, 6250708.59292, 6109526.10467,
+ 5974675.01146, 5845736.85046, 5722329.31864,
+ 5604102.4449, 5490735.23866, 5381932.74725,
+ 5277423.465, 5176957.04587, 5080302.27894,
+ 4987245.29223, 4897587.95517, 4811146.45477,
+ 4727750.02357, 4647239.80112, 4569467.81256,
+ 4494296.05084, 4421595.65018, 4351246.14058,
+ 4283134.77418, 4217155.91547, 4153210.48844,
+ 4031053.45636, 3915984.28239, 3807400.7263,
+ 3704767.03978, 3607605.02544, 3515486.5018,
+ 3428026.92286, 3344879.95083, 3265732.81994,
+ 3190302.35977, 3118331.57125, 3049586.66763,
+ 2983854.50841, 2920940.3665, 2860665.97936,
+ 2802867.84264, 2747395.71194, 2694111.28361,
+ 2642887.03006, 2593605.16898, 2546156.74866,
+ 2500440.83456, 2456363.78429, 2413838.59984,
+ 2372784.34781, 2333125.63928, 2294792.16242,
+ 2257718.26156, 2221842.55759, 2187107.60483,
+ 2153459.58052, 2120848.00323, 2058547.45994,
+ 1999859.79463, 1944478.13147, 1892129.468,
+ 1842570.12107, 1795581.88886, 1750968.80087,
+ 1708554.35336, 1668179.14769, 1629698.86458,
+ 1592982.5198, 1557910.95684, 1524375.53953,
+ 1492277.01457, 1461524.51863, 1432034.70898,
+ 1403731.00015, 1376542.89185, 1350405.37546,
+ 1325258.40889, 1301046.45049, 1277718.04459,
+ 1255225.45204, 1233524.32016, 1212573.38728,
+ 1192334.21771, 1172770.96356, 1153850.15023,
+ 1135540.48296, 1117812.67195, 1100639.27416,
+ 1083994.54978, 1052195.90814, 1022240.22693,
+ 993971.022577, 967249.079973, 941950.131258,
+ 917962.899577, 895187.442482, 873533.742731,
+ 852920.504418, 833274.120331, 814527.782755,
+ 796620.715006, 779497.504963, 763107.525152,
+ 747404.426526, 732345.695244, 717892.263476,
+ 704008.166703, 690660.241153, 677817.855988,
+ 665452.675682, 653538.448664, 642050.818932,
+ 630967.157747, 620266.41297, 609928.973904,
+ 599936.549831, 590272.060629, 580919.538101,
+ 571864.036815, 563091.553384, 554588.953291,
+ 538344.816665, 523041.548206, 508599.29108,
+ 494946.998456, 482021.249542, 469765.251724,
+ 458127.995525, 447063.535752, 436530.377359,
+ 426490.948641, 416911.147609, 407759.949924,
+ 399009.068882, 390632.659531, 382607.060391,
+ 374910.567309, 367523.234875, 360426.701557,
+ 353604.035312, 347039.596927, 340718.91876,
+ 334628.596889, 328756.19497, 323090.158348,
+ 317619.737168, 312334.917401, 307226.358847,
+ 302285.339317, 297503.704266, 292873.821293,
+ 288388.538937, 284041.149331, 275735.234396,
+ 267910.070584, 260524.871062, 253543.347354,
+ 246933.104947, 240665.13389, 234713.377391,
+ 229054.364815, 223666.898142, 218531.783,
+ 213631.597053, 208950.489813, 204474.009027,
+ 200188.949594, 196083.221672, 192145.735205,
+ 188366.298503, 184735.528953, 181244.774164,
+ 177886.04218, 174651.939551, 171535.616247,
+ 168530.716557, 165631.335218, 162831.978148,
+ 160127.527208, 157513.208541, 154984.564055,
+ 152537.425701, 150167.892222, 147872.308115,
+ 145647.244544, 141395.994603, 137390.624505,
+ 133610.283442, 130036.419482, 126652.470773,
+ 123443.605264, 120396.500282, 117499.155002,
+ 114740.730226, 112111.41094, 109602.287952,
+ 107205.255591, 104912.922975, 102718.536798,
+ 100615.913924, 98599.3823607, 96663.7294285,
+ 94804.156116, 93016.2367777, 91295.8834589,
+ 89639.3142378, 88043.0250685, 86503.7646782,
+ 85018.5121417, 83584.4568033, 82198.980265,
+ 80859.6401976, 79564.1557618, 78310.3944557,
+ 77096.3602287, 75920.1827217, 74780.1075103,
+ 72601.7735874, 70549.3244291, 68612.0902638,
+ 66780.5772822, 65046.3097201, 63401.6967477,
+ 61839.9197339, 60354.8363331, 58940.8985361,
+ 57593.0823673, 56306.8273413, 55077.9841309,
+ 53902.7691783, 52777.7251933, 51699.6866696,
+ 50665.7496879, 49673.2453971, 48719.716661,
+ 47802.8974376, 46920.6945257, 46071.1713659,
+ 45252.5336325, 44463.1163889, 43701.3726106,
+ 42965.8629118, 42255.2463268, 41568.2720255,
+ 40903.7718523, 40260.6535941, 39637.8948977,
+ 39034.5377622, 38449.6835453, 37332.1592893,
+ 36279.1577368, 35285.2130631, 34345.4616221,
+ 33455.5611057, 32611.6223983, 31810.1518596,
+ 31048.0022185, 30322.3306142, 29630.5625994,
+ 28970.3611385, 28339.5998107, 27736.3395662,
+ 27158.8084976, 26605.3841795, 26074.5782033,
+ 25565.0225957, 25075.4578572, 24604.7224005,
+ 24151.7432007, 23715.5274976, 23295.1554155,
+ 22889.7733832, 22498.5882546, 22120.8620455,
+ 21755.9072116, 21403.082403, 21061.7886423,
+ 20731.4658758, 20411.5898558, 20101.6693198,
+ 19801.2434311, 19227.1706446, 18686.2100916,
+ 18175.5584758, 17692.7212135, 17235.4710025,
+ 16801.8128973, 16389.9547259, 15998.2819199,
+ 15625.3360073, 15269.7961595, 14930.463299,
+ 14606.2463622, 14296.1503821, 13999.2661181,
+ 13714.7610004, 13441.871201, 13179.8946694,
+ 12928.184999, 12686.1460114, 12453.2269616,
+ 12228.9182827, 12012.7478009, 11804.2773608,
+ 11603.0998103, 11408.8363005, 11221.1338643,
+ 11039.6632386, 10864.1169034, 10694.2073132,
+ 10529.6652976, 10370.2386141, 10215.690635,
+ 9920.35530902, 9642.03589933, 9379.29279296,
+ 9130.84483975, 8895.54809344, 8672.3778898,
+ 8460.41366569, 8258.82604172, 8066.86578413,
+ 7883.85433402, 7709.17565017, 7542.26915762,
+ 7382.6236306, 7229.77186868, 7083.28604825,
+ 6942.7736517, 6807.8738919, 6678.25456313,
+ 6553.60926028, 6433.65491683, 6318.12961989,
+ 6206.79066653, 6099.41283073, 5995.78681498,
+ 5895.71786375, 5799.02451956, 5705.53750473,
+ 5615.09871424, 5527.56030707, 5442.78388486,
+ 5360.63974837, 5281.00622313, 5128.82081305,
+ 4985.39281878, 4849.98187786, 4721.92907957,
+ 4600.64604199, 4485.60570297, 4376.33451913,
+ 4272.40582769, 4173.43417369, 4079.07044241,
+ 3988.99766684, 3902.92740311, 3820.59658617,
+ 3741.76479301, 3666.21185297, 3593.73575486,
+ 3524.1508087, 3457.28602662, 3392.98369304,
+ 3331.09809869, 3271.49441705, 3214.04770487,
+ 3158.64201089, 3105.16957952, 3053.53013775,
+ 3003.63025539, 2955.38276983, 2908.70626808,
+ 2863.52461931, 2819.76655239, 2777.36527337,
+ 2736.25811858, 2657.69431628, 2583.6451563,
+ 2513.72928085, 2447.60726343, 2384.97598811,
+ 2325.56391052, 2269.1270432, 2215.44553905,
+ 2164.32077155, 2115.57282931, 2069.03835784,
+ 2024.56869368, 1982.02824566, 1941.29308582,
+ 1902.24971907, 1864.79400552, 1828.83021394,
+ 1794.27018797, 1761.03260977, 1729.04234809,
+ 1698.22987957, 1668.53077394, 1639.88523494,
+ 1612.23769017, 1585.53642374, 1559.73324667,
+ 1534.78320059, 1510.64429085, 1487.27724571,
+ 1464.64529866, 1442.7139913, 1421.45099465,
+ 1380.81030481, 1342.50136932, 1306.32740443,
+ 1272.1132493, 1239.70246899, 1208.95491167,
+ 1179.74463906, 1151.9581651, 1125.49295061,
+ 1100.25611146, 1076.16330573, 1053.13777154,
+ 1031.10949225, 1010.01446977, 989.794089986,
+ 970.394566925, 951.766454485, 933.864216342,
+ 916.645846088, 900.072530873, 884.108352851,
+ 868.720023553, 853.876647032, 839.549508182,
+ 825.711883191, 812.33886945, 799.407232626,
+ 786.895268923, 774.782680783, 763.050464528,
+ 751.680808624, 740.657001406, 719.585090388,
+ 699.720033366, 680.960086838, 663.214678935,
+ 646.402913465, 630.452308433, 615.297727189,
+ 600.880468722, 587.147490089, 574.050739088,
+ 561.546579359, 549.59529327, 538.160650604,
+ 527.209533055, 516.711606313, 506.639032813,
+ 496.96621939, 487.669594983, 478.727414292,
+ 470.119583902, 461.827507957, 453.833950828,
+ 446.122914658, 438.679529921, 431.489957406,
+ 424.541300267, 417.821524944, 411.319389924,
+ 405.024381463, 398.926655468, 393.01698488,
+ 387.286711944, 376.332318284, 366.004045462,
+ 356.249171211, 347.020757519, 338.276876563,
+ 329.979957895, 322.096235284, 314.59527586,
+ 307.449577629, 300.634224017, 294.126586238,
+ 287.906065913, 281.95387174, 276.252825046,
+ 270.787189968, 265.542524683, 260.50555071,
+ 255.664037759, 251.006702025, 246.523116109,
+ 242.203629065, 238.039295241, 234.021810832,
+ 230.143457166, 226.397049911, 222.775893491,
+ 219.273740104, 215.884752796, 212.60347214,
+ 209.424786113, 206.343902815, 203.356325724,
+ 197.644448118, 192.25828345, 187.17044241,
+ 182.356537811, 177.794783117, 173.465653828,
+ 169.351600521, 165.436804568, 161.706969302,
+ 158.149140755, 154.751553204, 151.503495587,
+ 148.395195583, 145.417718676, 142.562879997,
+ 139.823167083, 137.191672013, 134.662031621,
+ 132.228374673, 129.885275086, 127.627710402,
+ 125.451024827, 123.350896269, 121.323306878,
+ 119.364516656, 117.471039772, 115.639623265,
+ 113.867227861, 112.151010651, 110.488309437,
+ 108.876628554, 107.313626007, 104.324987222,
+ 101.506311917, 98.8433301246, 96.3233343285,
+ 93.9349706793, 91.6680628859, 89.5134629529,
+ 87.4629241036, 85.5089921298, 83.6449121192,
+ 81.8645480786, 80.1623134152, 78.5331106002,
+ 76.9722786296, 75.4755471284, 74.0389961376,
+ 72.6590207791, 71.3323001202, 70.0557696658,
+ 68.8265969947, 67.6421601273, 66.5000282736,
+ 65.3979446612, 64.3338111858, 63.3056746605,
+ 62.3117144747, 61.3502314957, 60.4196380687,
+ 59.5184489908, 58.6452733506, 57.7988071362,
+ 56.9778265301, 55.4077918373, 53.9267643608,
+ 52.5272846644, 51.2027090519, 49.9471006653,
+ 48.7551376125, 47.622035089, 46.5434790675,
+ 45.5155695954, 44.5347721114, 43.5978754878,
+ 42.7019557373, 41.8443445089, 41.0226016526,
+ 40.2344912502, 39.4779606118, 38.751121818,
+ 38.0522354538, 37.3796962368, 36.7320202863,
+ 36.1078338186, 35.5058630854, 34.9249253981,
+ 34.3639211041, 33.8218263982, 33.2976868701,
+ 32.7906117014, 32.2997684362, 31.8243782605,
+ 31.3637117346, 30.917084926, 30.4838559023,
+ 29.6552146411, 28.8733782988, 28.134432563,
+ 27.4348905633, 26.7716358691, 26.1418743927,
+ 25.5430936131, 24.9730278502, 24.4296285677,
+ 23.9110388709, 23.4155715259, 22.9416899422,
+ 22.4879916639, 22.0531939906, 21.6361214127,
+ 21.2356946011, 20.8509207292, 20.4808849434,
+ 20.1247428261, 19.7817137183, 19.4510747894,
+ 19.132155759, 18.824334187, 18.5270312637,
+ 18.2397080369, 17.9618620255, 17.6930241733,
+ 17.4327561035, 17.1806476409, 16.9363145704,
+ 16.6993966065, 16.4695555516, 16.0298519219,
+ 15.6148798725, 15.2225761964, 14.8511026109,
+ 14.4988157995, 14.1642421305, 13.8460562183,
+ 13.5430626642, 13.2541804371, 12.97842946,
+ 12.7149190454, 12.4628378888, 12.2214453808,
+ 11.9900640388, 11.7680728923, 11.5549016866,
+ 11.3500257858, 11.1529616813, 10.9632630215,
+ 10.7805170944, 10.6043417028, 10.4343823832,
+ 10.2703099236, 10.1118181445, 9.95862191002,
+ 9.81045534239, 9.6670702159, 9.52823450908,
+ 9.3937310977, 9.26335657253, 9.13692016828,
+ 9.01424279167, 8.77950188724, 8.5579029016,
+ 8.34835265336, 8.14987690272, 7.96160453251,
+ 7.78275419548, 7.61262298955, 7.45057681007,
+ 7.29604209573, 7.14849873828, 7.00747396878,
+ 6.87253706654, 6.74329476426, 6.6193872446,
+ 6.50048464099, 6.38628396998, 6.27650643422,
+ 6.17089504471, 6.06921251895, 5.97123941835,
+ 5.87677249363, 5.78562321153, 5.69761644002,
+ 5.61258927241, 5.53038997362, 5.45087703381,
+ 5.37391831699, 5.29939029358, 5.22717734731,
+ 5.15717114836, 5.08927008528, 5.02337874941,
+ 4.89727186893, 4.77819450111, 4.66556389779,
+ 4.5588605707, 4.45761989234, 4.36142500459,
+ 4.26990080294, 4.18270881023, 4.09954278998,
+ 4.02012497734, 3.9442028284, 3.87154620636,
+ 3.8019449374, 3.73520668068, 3.67115506623,
+ 3.60962806223, 3.5504765392, 3.49356300388,
+ 3.43876047989, 3.38595151559, 3.33502730255,
+ 3.28588689046, 3.2384364864, 3.19258882806,
+ 3.14826262191, 3.10538203853, 3.06387625858,
+ 3.0236790634, 2.98472846526, 2.9469663728,
+ 2.91033828793, 2.8747930306, 2.8067613898,
+ 2.74251940505, 2.68175424311, 2.62418694186,
+ 2.56956792209, 2.51767319702, 2.46830115574,
+ 2.42126982168, 2.37641450585, 2.33358579,
+ 2.29264778675, 2.2534766331, 2.21595918157,
+ 2.17999185932, 2.14547967046, 2.11233532111,
+ 2.08047844974, 2.0498349484, 2.02033636247,
+ 1.99191935846, 1.9645252511, 1.93809958206,
+ 1.91259174386, 1.8879546434, 1.86414440028,
+ 1.84112007579, 1.81884342903, 1.79727869694,
+ 1.77639239563, 1.75615314058, 1.73653148369,
+ 1.71749976531, 1.68110365251, 1.64677447223,
+ 1.61434304574, 1.58365847132, 1.55458570812,
+ 1.5270035349, 1.50080281743, 1.47588503118,
+ 1.45216099646, 1.42954979098, 1.40797781157,
+ 1.38737796141, 1.36768894386, 1.34885464657,
+ 1.33082360294, 1.31354851955, 1.2969858605,
+ 1.28109548066, 1.2658403013, 1.25118602242,
+ 1.23710086713, 1.22355535382, 1.21052209274,
+ 1.19797560406, 1.18589215458, 1.17424961117,
+ 1.16302730872, 1.15220593111, 1.14176740369,
+ 1.1316947959, 1.12197223308, 1.11258481634,
+ 1.09476027388, 1.07811888208, 1.06256992542,
+ 1.04803270312, 1.03443522136, 1.02171308882,
+ 1.00980857969, 0.998669835485, 0.988250182301,
+ 0.978507544678, 0.96940394074, 0.960905045915,
+ 0.952979814867, 0.945600153008, 0.938740630449,
+ 0.932378232401, 0.926492141047, 0.921063544663,
+ 0.916075470469, 0.911512638216, 0.907361331987,
+ 0.903609288065, 0.900245597058, 0.897260618748,
+ 0.894645908353, 0.892394153106, 0.890499118227,
+ 0.888955601518, 0.887759395947, 0.886907259694,
+ 0.886396893265, 0
+
+ };
+
+ /**
+ * Same as {@link #inverseF(double,double,double) inverseF} <TT>(0.0, 1.0, u)</TT>.
+ *
+ */
+ public static double inverseF01 (double u) {
+
+ /* Method of Marsaglia. REF : ? */
+
+ int n, k, j;
+ double v, x, w;
+ boolean negatif;
+
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+/* if (u >= 1.0)
+ return XBIG;
+ if (u <= 0.0)
+ return -XBIG; */
+ if (u < 0.5) {
+ negatif = true;
+ u = 2.0 * u;
+ } else {
+ negatif = false;
+ u = 2.0 * (1.0 - u);
+ }
+
+ k = 0;
+ w = u;
+ while (w < 0.5) {
+ ++k;
+ w *= 2.0;
+ }
+ j = (int) ((w - 0.5) * 64.0);
+ n = 992 - 32 * k + j;
+ if (n < 0) {
+ if (negatif)
+ return -XBIG;
+ else
+ return XBIG;
+ }
+ v = ((int) (Num.TWOEXP[k + 6] * u)) / Num.TWOEXP[k + 6];
+ v = (u - v) * B[n];
+
+ x = A[n] - v * (1.414214 - v * (A[n] - 0.4714045 * (1.0 +
+ 2.0 * A[n] * A[n]) * v));
+ if (negatif)
+ return -x;
+ else
+ return x;
+
+ }
+
+
+ /**
+ * Returns an approximation of
+ * <SPAN CLASS="MATH"><I>Φ</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, where <SPAN CLASS="MATH"><I>Φ</I></SPAN> is the standard
+ * normal distribution function, with mean 0 and variance 1.
+ * Uses the method of Marsaglia, Zaman, and Marsaglia,
+ * with table lookups.
+ *
+ * Returns 6 decimal digits of precision. This method is approximately
+ * 20% faster than <TT>NormalDist.inverseF</TT>.
+ *
+ */
+ public static double inverseF (double mu, double sigma, double u)
+ {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu + sigma*inverseF01 (u);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/NormalDistQuick.tex b/source/umontreal/iro/lecuyer/probdist/NormalDistQuick.tex
new file mode 100644
index 0000000..545a2a3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NormalDistQuick.tex
@@ -0,0 +1,1004 @@
+\defmodule {NormalDistQuick}
+
+A variant of the class \class{NormalDist} (for the {\em normal\/}
+distribution with mean $\mu$ and variance $\sigma^2$).
+The difference is in the implementation of the methods \method{cdf01}{},
+\method{barF01}{} and \method{inverseF01}{}, which are faster
+but less accurate than those of the class \class{NormalDist}.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalDistQuick
+ * Description: normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;\end{hide}
+
+public class NormalDistQuick extends NormalDist \begin{hide} {
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NormalDistQuick()\begin{hide} {
+ super ();
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Constructs a \texttt{NormalDistQuick} object with default parameters $\mu = 0$
+ and $\sigma = 1$.
+ \end{tabb}
+\begin{code}
+
+ public NormalDistQuick (double mu, double sigma)\begin{hide} {
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{NormalDistQuick} object with mean $\mu$ = \texttt{mu}
+ and standard deviation $\sigma$ = \texttt{sigma}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+\begin{hide}
+
+ /**************************************************************/
+ /* These public methods are necessary so that the methods cdf01,
+ * barF01 and inverseF01 that are used are those of the present
+ * class and not those of 'NormalDist'
+ */
+
+ public double cdf (double x) {
+ return cdf01 ((x-mu)/sigma);
+ }
+
+ public double barF (double x) {
+ return barF01 ((x-mu)/sigma);
+ }
+
+ public double inverseF (double u) {
+ return mu + sigma * inverseF01 (u);
+ }
+ /*************************************************************/
+
+ private static final double V[] = {
+ 1.2533141373155, 1.137490921203605, 1.037824575853727,
+ 0.951527192071207, 0.8763644564536924, 0.8105337152790306,
+ 0.7525711790634081, 0.7012808218544303, 0.6556795424187987,
+ 0.61495459615093, 0.5784303460476312, 0.5455421356582171,
+ 0.5158156382179634, 0.4888504415275737, 0.4643069280394423,
+ 0.4418957328326002, 0.4213692292880546, 0.4025146181296722,
+ 0.3851482907984348, 0.3691112106902635, 0.3542651113297938,
+ 0.3404893532870847, 0.3276783146905521, 0.31573921586941,
+ 0.3045902987101033, 0.2941592970402893, 0.284382146748493,
+ 0.2752018941576065, 0.2665677689682238, 0.2584343943120386,
+ 0.2507611114439651, 0.243511400615456, 0.2366523829135607,
+ 0.230154390478801, 0.2239905946538289, 0.2181366833614714,
+ 0.2125705804420318, 0.2072722008565011, 0.2022232366330547,
+ 0.1974069692375194, 0.1928081047153158, 0.1884126285076003,
+ 0.1842076773079702, 0.1801814257143918, 0.1763229857571027,
+ 0.1726223176578506, 0.1690701504076941, 0.1656579109468773,
+ 0.1623776608968675, 0.1592220399363674, 0.1561842150339759,
+ 0.153257834853479, 0.1504369887362691, 0.1477161697413935,
+ 0.145090241289131, 0.1425544070104023, 0.1401041834530503,
+ 0.1377353753382303, 0.1354440530967635, 0.1332265324471292,
+ 0.1310793558044918, 0.1289992753343376, 0.126983237485437,
+ 0.1250283688553504, 0.1231319632579323, 0.1212914698765462,
+ 0.119504482399253, 0.1177687290432979, 0.1160820633859823,
+ 0.1144424559276431, 0.112847986320103, 0.1112968362007359,
+ 0.1097872825783083, 0.1083176917221132, 0.1068865135106745,
+ 0.1054922762005562, 0.1041335815795983, 0.1028091004723001,
+ 0.1015175685681028, 0.1002577825460485, 0.09902859647173194,
+ 0.09782891844465691, 0.09665770747608191, 0.09551397057921558,
+ 0.09439676005522439, 0.09330517095996169, 0.09223833873763035,
+ 0.09119543700877471, 0.09017567550106469, 0.08917829811230435,
+ 0.08820258109597616, 0.08724783136042988, 0.08631338487354936,
+ 0.08539860516539227, 0.08450288192189578, 0.08362562966329139,
+ 0.08276628650136918, 0.08192431297018954, 0.08109919092525536,
+ 0.08029042250654048, 0.07949752916111721, 0.07872005072144664,
+ 0.07795754453568722, 0.07720958464664668, 0.07647576101624852,
+ 0.07575567879261112, 0.07504895761704659, 0.07435523096847724,
+ 0.07367414554294564, 0.07300536066605566, 0.07234854773633338,
+ 0.07170338969763433, 0.07106958053885212, 0.07044682481930167,
+ 0.06983483721825942, 0.06923334210724434, 0.06864207314371742,
+ 0.06806077288496332, 0.0674891924209997, 0.06692709102543307,
+ 0.06637423582325017
+};
+\end{hide}
+
+ public static double cdf01 (double x) \begin{hide} {
+ if (x >= XBIG)
+ return 1.0;
+ if (x <= -XBIG)
+ return 0.0;
+
+ /* The relative precision of NormalDistQuick decreases gradually:
+ at x = -8, there are 15 decimals; at x = -26, there are about
+ 2 decimals. NormalDist is more precise. */
+ if (x < -8.0)
+ return NormalDist.cdf01 (x);
+
+ boolean negatif;
+ if (x < 0.0) {
+ negatif = true;
+ x = -x;
+ } else {
+ negatif = false;
+ }
+ int j = (int) (8.0 * x + 0.5);
+ if (j > 120)
+ j = 120;
+ double r = V[j];
+ double z = 0.125 * j;
+ double h = x - z;
+ double r1 = r * z - 1.0;
+ double r2 = 0.5 * (r + z * r1);
+ double r3 = (r1 + z * r2) / 3.0;
+ double r4 = 0.25 * (r2 + z * r3);
+ double r5 = 0.2 * (r3 + z * r4);
+ double r6 = (r4 + z * r5) / 6.0;
+ double r7 = (r5 + z * r6) / 7.0;
+ double r8 = 0.125 * (r6 + z * r7);
+ double t = r + h * (r1 + h * (r2 + h * (r3 +
+ h * (r4 + h * (r5 + h * (r6 + h * (r7 + h * r8)))))));
+ double u = t * Math.exp (-0.5 * x * x - 0.9189385332046727);
+ if (negatif)
+ return u;
+ else
+ return 1.0 - u;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Same as \method{cdf}{double,double,double}~\texttt{(0.0, 1.0, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu, double sigma, double x) \begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return cdf01 ((x-mu)/sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an approximation of $\Phi(x)$, where $\Phi$ is the standard
+ normal distribution function, with mean 0 and variance 1.
+ Uses Marsaglia et al's \cite{rMAR94b} fast method
+ with table lookups. Returns 15 decimal digits of precision.
+ This method is approximately 60\% faster than \texttt{NormalDist.cdf}.
+\end{tabb}
+\begin{code}
+
+ public static double barF01 (double x) \begin{hide} {
+ return cdf01(-x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Same as \method{barF}{double,double,double}~\texttt{(0.0, 1.0, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double mu, double sigma, double x) \begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return barF01 ((x-mu)/sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an approximation of $1 - \Phi(x)$, where $\Phi$ is the standard
+ normal distribution function, with mean 0 and variance 1.
+ Uses Marsaglia et al's \cite{rMAR94b} fast method
+ with table lookups. Returns 15 decimal digits of precision.
+ This method is approximately twice faster than \texttt{NormalDist.barF}.
+\end{tabb}
+\begin{code}\begin{hide}
+ private static final double[] A = {
+
+ 6.33795775455, 6.33321372178, 6.32860807635,
+ 6.32413288056, 6.31978086301, 6.31554534594,
+ 6.31142018224, 6.30739970055, 6.30347865735,
+ 6.29965219486, 6.29591580408, 6.29226529211,
+ 6.28869675323, 6.28520654322, 6.28179125646,
+ 6.27844770553, 6.27517290294, 6.27196404468,
+ 6.2688184955, 6.26573377562, 6.26270754867,
+ 6.25973761088, 6.25682188116, 6.25395839218,
+ 6.25114528219, 6.24838078761, 6.2456632362,
+ 6.24299104087, 6.240362694, 6.23777676216,
+ 6.23523188139, 6.23272675268, 6.23026013799,
+ 6.22543778081, 6.22075598811, 6.2162067,
+ 6.21178253304, 6.20747670647, 6.20328297819,
+ 6.19919558917, 6.19520921468, 6.19131892165,
+ 6.18752013112, 6.18380858505, 6.180180317,
+ 6.17663162601, 6.1731590534, 6.16975936206,
+ 6.16642951782, 6.16316667288, 6.15996815078,
+ 6.1568314329, 6.15375414629, 6.15073405259,
+ 6.14776903807, 6.14485710448, 6.14199636082,
+ 6.13918501574, 6.13642137069, 6.13370381356,
+ 6.13103081295, 6.12840091282, 6.12581272765,
+ 6.12326493791, 6.12075628597, 6.11585165173,
+ 6.11108986339, 6.10646273321, 6.10196276044,
+ 6.09758305644, 6.09331727974, 6.08915957943,
+ 6.08510454578, 6.08114716689, 6.07728279052,
+ 6.07350709041, 6.06981603653, 6.06620586849,
+ 6.06267307204, 6.05921435799, 6.05582664334,
+ 6.05250703436, 6.04925281142, 6.04606141524,
+ 6.0429304345, 6.03985759465, 6.03684074773,
+ 6.03387786311, 6.03096701912, 6.02810639533,
+ 6.02529426559, 6.0225289916, 6.01980901702,
+ 6.01713286212, 6.01449911878, 6.01190644597,
+ 6.00935356553, 6.00436236049, 5.99951639563,
+ 5.99480734912, 5.99022759726, 5.98577013833,
+ 5.98142852661, 5.9771968149, 5.97306950434,
+ 5.96904150032, 5.96510807378, 5.96126482692,
+ 5.95750766299, 5.95383275929, 5.95023654327,
+ 5.94671567111, 5.94326700856, 5.93988761376,
+ 5.93657472175, 5.9333257306, 5.93013818875,
+ 5.92700978364, 5.92393833142, 5.92092176747,
+ 5.91795813793, 5.91504559188, 5.91218237419,
+ 5.90936681905, 5.90659734398, 5.90387244436,
+ 5.90119068838, 5.89855071241, 5.89595121674,
+ 5.89086876372, 5.88593406746, 5.88113866541,
+ 5.87647480476, 5.87193536504, 5.86751379108,
+ 5.86320403456, 5.85900050299, 5.85489801495,
+ 5.85089176064, 5.84697726716, 5.84315036764,
+ 5.83940717394, 5.83574405227, 5.83215760142,
+ 5.82864463328, 5.82520215541, 5.82182735527,
+ 5.81851758606, 5.81527035394, 5.81208330646,
+ 5.80895422198, 5.80588100021, 5.80286165344,
+ 5.79989429868, 5.79697715037, 5.79410851379,
+ 5.79128677896, 5.78851041509, 5.78577796547,
+ 5.78308804272, 5.78043932448, 5.7752605134,
+ 5.77023210866, 5.76534549972, 5.76059279799,
+ 5.75596675813, 5.75146070984, 5.74706849845,
+ 5.74278443301, 5.73860324073, 5.73452002692,
+ 5.73053023962, 5.72662963823, 5.72281426575,
+ 5.71908042393, 5.71542465116, 5.7118437027,
+ 5.70833453283, 5.70489427893, 5.70152024702,
+ 5.69820989876, 5.69496083963, 5.69177080831,
+ 5.68863766688, 5.68555939205, 5.68253406704,
+ 5.67955987426, 5.67663508853, 5.67375807094,
+ 5.67092726314, 5.66814118219, 5.6653984157,
+ 5.66269761746, 5.65741684752, 5.65228927845,
+ 5.64730614385, 5.64245941209, 5.6377417063,
+ 5.63314623489, 5.62866673113, 5.62429740032,
+ 5.6200328734, 5.61586816622, 5.61179864349,
+ 5.60781998693, 5.60392816693, 5.6001194173,
+ 5.5963902128, 5.59273724893, 5.58915742386,
+ 5.58564782214, 5.58220570003, 5.57882847227,
+ 5.57551370004, 5.57225908009, 5.56906243489,
+ 5.56592170358, 5.56283493377, 5.55980027406,
+ 5.55681596716, 5.55388034364, 5.55099181609,
+ 5.54814887388, 5.54535007824, 5.5425940578,
+ 5.53720516928, 5.53197243466, 5.52688692307,
+ 5.52194045196, 5.5171255056, 5.51243516442,
+ 5.50786304341, 5.50340323832, 5.49905027849,
+ 5.4947990853, 5.49064493552, 5.4865834288,
+ 5.48261045887, 5.47872218783, 5.4749150232,
+ 5.47118559738, 5.46753074927, 5.46394750763,
+ 5.46043307616, 5.45698482001, 5.45360025356,
+ 5.45027702931, 5.44701292782, 5.44380584855,
+ 5.44065380149, 5.43755489952, 5.43450735142,
+ 5.43150945548, 5.42855959358, 5.42565622584,
+ 5.42279788561, 5.41998317492, 5.41447936842,
+ 5.40913484272, 5.40394049304, 5.39888797725,
+ 5.39396963282, 5.38917840481, 5.38450778312,
+ 5.37995174765, 5.37550472027, 5.37116152244,
+ 5.36691733786, 5.36276767939, 5.35870835965,
+ 5.35473546482, 5.3508453313, 5.34703452473,
+ 5.34329982125, 5.33963819058, 5.3360467808,
+ 5.33252290459, 5.32906402674, 5.32566775291,
+ 5.32233181932, 5.31905408341, 5.31583251534,
+ 5.31266519015, 5.30955028069, 5.30648605108,
+ 5.30347085068, 5.30050310862, 5.29758132874,
+ 5.29470408485, 5.28907782499, 5.28361416415,
+ 5.27830381332, 5.2731382613, 5.26810969,
+ 5.26321090103, 5.25843525172, 5.2537765992,
+ 5.24922925139, 5.24478792384, 5.24044770161,
+ 5.23620400551, 5.23205256217, 5.22798937729,
+ 5.22401071182, 5.2201130606, 5.21629313323,
+ 5.21254783681, 5.20887426039, 5.205269661,
+ 5.20173145085, 5.19825718588, 5.19484455528,
+ 5.19149137189, 5.1881955636, 5.18495516531,
+ 5.18176831175, 5.17863323075, 5.17554823716,
+ 5.17251172724, 5.16952217346, 5.16657811973,
+ 5.16082101937, 5.15523004763, 5.14979571985,
+ 5.14450934531, 5.13936294072, 5.13434915534,
+ 5.12946120568, 5.12469281843, 5.1200381805,
+ 5.11549189488, 5.11104894181, 5.10670464433,
+ 5.10245463765, 5.09829484194, 5.09422143797,
+ 5.09023084532, 5.08631970285, 5.08248485106,
+ 5.0787233163, 5.07503229637, 5.0714091476,
+ 5.06785137307, 5.06435661191, 5.06092262957,
+ 5.05754730896, 5.05422864229, 5.05096472372,
+ 5.0477537425, 5.04459397676, 5.04148378777,
+ 5.03842161464, 5.03540596946, 5.02950864968,
+ 5.02378122248, 5.01821399503, 5.01279808548,
+ 5.00752533468, 5.0023882297, 4.99737983708,
+ 4.99249374467, 4.98772401045, 4.98306511755,
+ 4.97851193459, 4.97405968039, 4.96970389282,
+ 4.96544040091, 4.96126529999, 4.95717492946,
+ 4.95316585275, 4.94923483945, 4.94537884901,
+ 4.94159501614, 4.93788063759, 4.93423316006,
+ 4.93065016935, 4.92712938035, 4.92366862801,
+ 4.92026585904, 4.91691912431, 4.91362657193,
+ 4.91038644087, 4.90719705507, 4.90405681805,
+ 4.90096420796, 4.89491612703, 4.8890419637,
+ 4.88333180298, 4.87777655906, 4.87236788511,
+ 4.86709809501, 4.86196009527, 4.85694732544,
+ 4.85205370587, 4.8472735917, 4.84260173228,
+ 4.8380332352, 4.8335635343, 4.82918836126,
+ 4.82490372013, 4.8207058646, 4.81659127758,
+ 4.81255665279, 4.80859887824, 4.8047150212,
+ 4.80090231471, 4.79715814524, 4.79348004153,
+ 4.78986566443, 4.78631279763, 4.78281933914,
+ 4.77938329358, 4.77600276506, 4.77267595063,
+ 4.76940113431, 4.76617668159, 4.76300103427,
+ 4.75679027709, 4.7507577546, 4.7448933141,
+ 4.73918765149, 4.73363221901, 4.72821914523,
+ 4.72294116527, 4.71779155991, 4.71276410212,
+ 4.70785300996, 4.70305290507, 4.69835877584,
+ 4.6937659447, 4.689270039, 4.68486696501,
+ 4.68055288459, 4.67632419431, 4.67217750662,
+ 4.66810963292, 4.6641175682, 4.66019847719,
+ 4.65634968177, 4.65256864953, 4.64885298336,
+ 4.64520041195, 4.64160878112, 4.63807604585,
+ 4.63460026304, 4.63117958481, 4.62781225236,
+ 4.6244965903, 4.6212310015, 4.61484401763,
+ 4.60863991397, 4.60260828265, 4.59673958525,
+ 4.59102505831, 4.58545663137, 4.58002685553,
+ 4.57472884104, 4.56955620253, 4.56450301082,
+ 4.55956375032, 4.55473328135, 4.55000680661,
+ 4.54537984133, 4.54084818664, 4.5364079057,
+ 4.53205530226, 4.52778690138, 4.52359943209,
+ 4.51948981163, 4.51545513127, 4.51149264343,
+ 4.5075997499, 4.50377399122, 4.50001303691,
+ 4.49631467662, 4.49267681191, 4.4890974489,
+ 4.48557469132, 4.48210673427, 4.47869185842,
+ 4.47532842465, 4.46874969883, 4.46235887005,
+ 4.45614525563, 4.45009906476, 4.44421130165,
+ 4.43847368137, 4.43287855675, 4.42741885424,
+ 4.42208801786, 4.41687995966, 4.41178901614,
+ 4.4068099095, 4.40193771332, 4.39716782193,
+ 4.39249592307, 4.3879179734, 4.38343017655,
+ 4.37902896328, 4.37471097372, 4.37047304122,
+ 4.36631217778, 4.3622255608, 4.35821052107,
+ 4.35426453179, 4.35038519863, 4.34657025053,
+ 4.34281753142, 4.33912499252, 4.33549068532,
+ 4.33191275513, 4.32838943511, 4.32491904083,
+ 4.31813067359, 4.31153564248, 4.3051229678,
+ 4.29888258592, 4.2928052498, 4.28688244266,
+ 4.28110630283, 4.27546955801, 4.26996546758,
+ 4.26458777186, 4.25933064724, 4.25418866648,
+ 4.24915676339, 4.2442302014, 4.23940454548,
+ 4.234675637, 4.23003957111, 4.22549267647,
+ 4.22103149691, 4.2166527749, 4.21235343653,
+ 4.20813057793, 4.20398145293, 4.19990346176,
+ 4.19589414084, 4.19195115337, 4.18807228072,
+ 4.18425541464, 4.18049854994, 4.17679977794,
+ 4.17315728028, 4.16956932335, 4.16255048996,
+ 4.15573091512, 4.14909929681, 4.14264527528,
+ 4.13635933075, 4.1302326947, 4.12425727258,
+ 4.11842557618, 4.11273066434, 4.1071660908,
+ 4.10172585798, 4.09640437621, 4.0911964274,
+ 4.08609713269, 4.08110192358, 4.07620651603,
+ 4.07140688728, 4.0666992549, 4.06208005798,
+ 4.05754594009, 4.05309373387, 4.04872044701,
+ 4.04442324957, 4.04019946237, 4.03604654644,
+ 4.03196209338, 4.02794381649, 4.0239895427,
+ 4.02009720515, 4.0162648363, 4.01249056171,
+ 4.00877259417, 4.00149883606, 3.99443081604,
+ 3.98755688078, 3.98086634771, 3.97434939969,
+ 3.96799699364, 3.96180078097, 3.95575303793,
+ 3.94984660457, 3.94407483096, 3.93843152965,
+ 3.93291093367, 3.92750765903, 3.9222166715,
+ 3.91703325678, 3.91195299388, 3.90697173117,
+ 3.90208556486, 3.89729081955, 3.89258403075,
+ 3.88796192889, 3.88342142501, 3.87895959754,
+ 3.87457368047, 3.87026105237, 3.86601922652,
+ 3.86184584173, 3.85773865405, 3.85369552908,
+ 3.84971443491, 3.84579343567, 3.8419306855,
+ 3.83437296653, 3.82702811235, 3.81988408487,
+ 3.81292984787, 3.80615525838, 3.79955097242,
+ 3.79310836293, 3.78681944792, 3.7806768275,
+ 3.77467362835, 3.76880345477, 3.76306034518,
+ 3.75743873361, 3.75193341525, 3.74653951576,
+ 3.74125246377, 3.73606796613, 3.73098198575,
+ 3.72599072151, 3.72109059012, 3.71627820978,
+ 3.71155038522, 3.70690409424, 3.70233647531,
+ 3.69784481637, 3.69342654456, 3.68907921677,
+ 3.68480051108, 3.68058821879, 3.67644023716,
+ 3.67235456274, 3.66832928512, 3.66045271041,
+ 3.65279688532, 3.64534934809, 3.63809867295,
+ 3.63103435798, 3.62414672762, 3.61742684789,
+ 3.6108664521, 3.60445787576, 3.59819399923,
+ 3.592068197, 3.58607429286, 3.58020651993,
+ 3.57445948514, 3.56882813748, 3.5633077395,
+ 3.5578938418, 3.55258226001, 3.54736905409,
+ 3.54225050954, 3.53722312046, 3.53228357414,
+ 3.52742873704, 3.52265564207, 3.51796147693,
+ 3.51334357346, 3.50879939795, 3.50432654219,
+ 3.49992271527, 3.49558573607, 3.4913135263,
+ 3.48710410411, 3.47886614237, 3.47085771144,
+ 3.46306588016, 3.45547879117, 3.44808554464,
+ 3.44087609744, 3.4338411752, 3.42697219544,
+ 3.42026120015, 3.41370079626, 3.40728410324,
+ 3.40100470653, 3.39485661617, 3.38883422999,
+ 3.38293230069, 3.37714590628, 3.37147042368,
+ 3.36590150482, 3.36043505519, 3.35506721435,
+ 3.34979433839, 3.34461298392, 3.33951989355,
+ 3.33451198267, 3.3295863274, 3.32474015352,
+ 3.3199708264, 3.31527584174, 3.31065281706,
+ 3.3060994839, 3.3016136806, 3.29719334569,
+ 3.28854129969, 3.28012863599, 3.27194189999,
+ 3.26396875282, 3.25619785067, 3.24861874006,
+ 3.24122176661, 3.23399799517, 3.22693913976,
+ 3.22003750184, 3.21328591581, 3.20667770063,
+ 3.2002066169, 3.19386682855, 3.18765286865,
+ 3.18155960874, 3.17558223129, 3.16971620498,
+ 3.1639572623, 3.15830137938, 3.15274475767,
+ 3.14728380734, 3.14191513221, 3.13663551594,
+ 3.13144190952, 3.12633141979, 3.12130129892,
+ 3.11634893481, 3.11147184224, 3.10666765471,
+ 3.10193411699, 3.0972690782, 3.08813637782,
+ 3.07925420286, 3.07060851058, 3.0621864209,
+ 3.0539760908, 3.04596660532, 3.03814788252,
+ 3.03051059033, 3.02304607359, 3.01574628977,
+ 3.00860375216, 3.00161147949, 2.9947629512,
+ 2.98805206756, 2.98147311398, 2.97502072908,
+ 2.96868987601, 2.96247581659, 2.95637408809,
+ 2.95038048206, 2.94449102535, 2.93870196271,
+ 2.93300974113, 2.92741099539, 2.92190253507,
+ 2.9164813325, 2.91114451188, 2.90588933915,
+ 2.90071321284, 2.89561365554, 2.8905883061,
+ 2.88563491243, 2.8759354898, 2.86649931119,
+ 2.85731166746, 2.84835906508, 2.83962909503,
+ 2.83111031887, 2.82279216952, 2.8146648644,
+ 2.80671932916, 2.79894713055, 2.79134041701,
+ 2.78389186607, 2.77659463754, 2.76944233182,
+ 2.76242895259, 2.75554887345, 2.74879680789,
+ 2.74216778225, 2.73565711142, 2.72926037674,
+ 2.72297340607, 2.71679225565, 2.71071319364,
+ 2.70473268509, 2.69884737824, 2.693054092,
+ 2.68734980447, 2.68173164244, 2.6761968717,
+ 2.67074288817, 2.66536720972, 2.66006746862,
+ 2.64968685818, 2.63958415075, 2.6297438751,
+ 2.62015183589, 2.61079497623, 2.60166125833,
+ 2.59273955939, 2.5840195806, 2.5754917671,
+ 2.5671472376, 2.55897772201, 2.55097550632,
+ 2.54313338341, 2.53544460937, 2.52790286427,
+ 2.52050221719, 2.51323709464, 2.50610225225,
+ 2.49909274914, 2.4922039248, 2.4854313781,
+ 2.4787709483, 2.47221869765, 2.4657708957,
+ 2.45942400483, 2.45317466714, 2.44701969236,
+ 2.44095604678, 2.43498084314, 2.42909133123,
+ 2.42328488933, 2.41755901624, 2.40633953091,
+ 2.39541501107, 2.38476910497, 2.37438680605,
+ 2.36425430827, 2.35435888041, 2.34468875653,
+ 2.33523304007, 2.3259816196, 2.31692509462,
+ 2.30805470992, 2.2993622974, 2.29084022432,
+ 2.28248134715, 2.27427897031, 2.26622680921,
+ 2.25831895701, 2.25054985471, 2.24291426415,
+ 2.23540724356, 2.22802412548, 2.22076049665,
+ 2.2136121797, 2.20657521654, 2.19964585312,
+ 2.19282052555, 2.18609584736, 2.17946859779,
+ 2.1729357111, 2.1664942667, 2.16014147999,
+ 2.15387469406, 2.14158908915, 2.12961846912,
+ 2.11794545368, 2.10655408816, 2.09542969053,
+ 2.08455871841, 2.07392865314, 2.06352789832,
+ 2.05334569055, 2.04337202094, 2.03359756554,
+ 2.02401362372, 2.01461206336, 2.00538527192,
+ 1.99632611278, 1.98742788593, 1.97868429286,
+ 1.97008940476, 1.9616376339, 1.95332370775,
+ 1.94514264545, 1.93708973655, 1.92916052156,
+ 1.92135077429, 1.91365648573, 1.90607384923,
+ 1.89859924703, 1.89122923782, 1.88396054536,
+ 1.87679004798, 1.86971476892, 1.86273186742,
+ 1.84903246517, 1.83567153691, 1.82263048664,
+ 1.80989223848, 1.79744107395, 1.78526249044,
+ 1.77334307781, 1.76167041036, 1.75023295184,
+ 1.73901997173, 1.72802147122, 1.71722811751,
+ 1.70663118536, 1.69622250501, 1.68599441547,
+ 1.67593972277, 1.66605166239, 1.65632386534,
+ 1.64675032767, 1.63732538277, 1.62804367632,
+ 1.61890014354, 1.60988998842, 1.60100866489,
+ 1.59225185952, 1.5836154758, 1.57509561963,
+ 1.56668858607, 1.55839084718, 1.55019904079,
+ 1.54210996014, 1.53412054435, 1.51842914115,
+ 1.50310294313, 1.48812189602, 1.47346757795,
+ 1.45912302502, 1.44507257982, 1.4313017591,
+ 1.417797138, 1.40454624816, 1.391537488,
+ 1.37876004322, 1.36620381637, 1.35385936408,
+ 1.34171784108, 1.32977095018, 1.3180108973,
+ 1.30643035113, 1.29502240671, 1.28378055261,
+ 1.27269864119, 1.26177086164, 1.25099171546,
+ 1.24035599423, 1.22985875922, 1.21949532285,
+ 1.20926123171, 1.19915225099, 1.1891643502,
+ 1.17929369001, 1.16953661021, 1.15988961853,
+ 1.15034938038, 1.13157655839, 1.11319427716,
+ 1.09518065276, 1.07751556704, 1.06018047944,
+ 1.04315826332, 1.02643306314, 1.00999016925,
+ 0.993815907861, 0.977897543941, 0.962223195295,
+ 0.946781756301, 0.931562830007, 0.916556667533,
+ 0.90175411383, 0.887146559019, 0.872725894627,
+ 0.858484474142, 0.844415077375, 0.830510878205,
+ 0.816765415315, 0.803172565598, 0.789726519943,
+ 0.776421761148, 0.763253043733, 0.750215375468,
+ 0.737304000439, 0.724514383492, 0.711842195939,
+ 0.699283302383, 0.686833748575, 0.674489750196,
+ 0.650104070648, 0.626099012346, 0.602449453164,
+ 0.579132162256, 0.556125593619, 0.533409706241,
+ 0.510965806738, 0.488776411115, 0.466825122853,
+ 0.445096524986, 0.423576084201, 0.402250065322,
+ 0.381105454764, 0.36012989179, 0.339311606539,
+ 0.318639363964, 0.29810241293, 0.277690439822,
+ 0.257393526101, 0.237202109329, 0.21710694721,
+ 0.197099084294, 0.177169820992, 0.15731068461,
+ 0.137513402144, 0.117769874579, 0.0980721524887,
+ 0.0784124127331, 0.0587829360689, 0.0391760855031,
+ 0.0195842852301, 0
+ };
+
+ private static final double[] B = {
+
+ 468043598.745, 454185281.982, 441133386.786,
+ 428819269.757, 417181876.023, 406166717.813,
+ 395725013.783, 385812960.329, 376391111.851,
+ 367423851.404, 358878936.738, 350727109.464,
+ 342941757.343, 335498621.458, 328375541.45,
+ 321552233.174, 315010094.053, 308732032.185,
+ 302702315.899, 296906440.935, 291331012.915,
+ 285963643.058, 280792855.461, 275808004.446,
+ 270999200.737, 266357245.389, 261873570.517,
+ 257540186.041, 253349631.735, 249294933.976,
+ 245369566.664, 241567415.856, 237882747.698,
+ 230844652.301, 224215968.814, 217961855.044,
+ 212051320.023, 206456705.678, 201153250.091,
+ 196118717.706, 191333084.832, 186778271.013,
+ 182437908.646, 178297144.629, 174342468.981,
+ 170561566.22, 166943186.067, 163477030.605,
+ 160153655.481, 156964383.183, 153901226.667,
+ 150956821.959, 148124368.489, 145397576.172,
+ 142770618.331, 140238089.759, 137794969.238,
+ 135436586.005, 133158589.665, 130956923.161,
+ 128827798.432, 126767674.455, 124773237.414,
+ 122841382.741, 120969198.842, 117393074.498,
+ 114024901.787, 110846987.361, 107843593.262,
+ 105000673.785, 102305653.75, 99747240.7669,
+ 97315265.5629, 95000545.5991, 92794768.101,
+ 90690389.3539, 88680547.6828, 86758987.9953,
+ 84919996.1313, 83158341.5649, 81469227.2427,
+ 79848245.5433, 78291339.5027, 76794768.5853,
+ 75355078.3902, 73969073.775, 72633794.9552,
+ 71346496.2016, 70104626.8117, 68905814.0771,
+ 67747848.0069, 66628667.5985, 65546348.4768,
+ 64499091.7445, 63485213.9074, 62503137.7568,
+ 61551384.1017, 59733373.2481, 58021039.3817,
+ 56405393.2293, 54878438.8023, 53433039.7483,
+ 52062806.7313, 50762002.0764, 49525458.6675,
+ 48348510.6706, 47226934.1194, 46156895.7613,
+ 45134908.8531, 44157794.8309, 43222649.9599,
+ 42326816.2279, 41467855.862, 40643528.9565,
+ 39851773.7738, 39090689.3553, 38358520.1316,
+ 37653642.2677, 36974551.5205, 36319852.4159,
+ 35688248.581, 35078534.0907, 34489585.7054,
+ 33920355.8956, 33369866.562, 32837203.3696,
+ 32321510.6295, 31821986.6654, 31337879.6134,
+ 30413135.3113, 29542122.4706, 28720271.6567,
+ 27943518.2501, 27208234.5323, 26511172.4564,
+ 25849415.1891, 25220335.8956, 24621562.5335,
+ 24050947.6575, 23506542.4223, 22986574.1168,
+ 22489426.6833, 22013623.7674, 21557813.9244,
+ 21120757.6673, 20701316.0956, 20298440.8828,
+ 19911165.4376, 19538597.0812, 19179910.1071,
+ 18834339.6081, 18501175.9757, 18179759.9851,
+ 17869478.3966, 17569760.0097, 17280072.1174,
+ 16999917.3132, 16728830.6117, 16466376.8456,
+ 16212148.3111, 15965762.6321, 15495105.5155,
+ 15051783.6023, 14633472.8806, 14238106.0377,
+ 13863837.9303, 13509016.4872, 13172158.0713,
+ 12851926.5229, 12547115.2589, 12256631.9188,
+ 11979485.1457, 11714773.1625, 11461673.8654,
+ 11219436.2047, 10987372.6619, 10764852.663,
+ 10551296.7957, 10346171.7177, 10148985.6613,
+ 9959284.45525, 9776647.99501, 9600687.10337,
+ 9431040.73252, 9267373.46466, 9109373.27455,
+ 8956749.52272, 8809231.15191, 8666565.06313,
+ 8528514.65085, 8394858.47935, 8265389.08465,
+ 8139911.88833, 7900214.37602, 7674431.7041,
+ 7461381.19, 7260010.7574, 7069381.37083,
+ 6888652.23304, 6717068.2507, 6553949.37303,
+ 6398681.4844, 6250708.59292, 6109526.10467,
+ 5974675.01146, 5845736.85046, 5722329.31864,
+ 5604102.4449, 5490735.23866, 5381932.74725,
+ 5277423.465, 5176957.04587, 5080302.27894,
+ 4987245.29223, 4897587.95517, 4811146.45477,
+ 4727750.02357, 4647239.80112, 4569467.81256,
+ 4494296.05084, 4421595.65018, 4351246.14058,
+ 4283134.77418, 4217155.91547, 4153210.48844,
+ 4031053.45636, 3915984.28239, 3807400.7263,
+ 3704767.03978, 3607605.02544, 3515486.5018,
+ 3428026.92286, 3344879.95083, 3265732.81994,
+ 3190302.35977, 3118331.57125, 3049586.66763,
+ 2983854.50841, 2920940.3665, 2860665.97936,
+ 2802867.84264, 2747395.71194, 2694111.28361,
+ 2642887.03006, 2593605.16898, 2546156.74866,
+ 2500440.83456, 2456363.78429, 2413838.59984,
+ 2372784.34781, 2333125.63928, 2294792.16242,
+ 2257718.26156, 2221842.55759, 2187107.60483,
+ 2153459.58052, 2120848.00323, 2058547.45994,
+ 1999859.79463, 1944478.13147, 1892129.468,
+ 1842570.12107, 1795581.88886, 1750968.80087,
+ 1708554.35336, 1668179.14769, 1629698.86458,
+ 1592982.5198, 1557910.95684, 1524375.53953,
+ 1492277.01457, 1461524.51863, 1432034.70898,
+ 1403731.00015, 1376542.89185, 1350405.37546,
+ 1325258.40889, 1301046.45049, 1277718.04459,
+ 1255225.45204, 1233524.32016, 1212573.38728,
+ 1192334.21771, 1172770.96356, 1153850.15023,
+ 1135540.48296, 1117812.67195, 1100639.27416,
+ 1083994.54978, 1052195.90814, 1022240.22693,
+ 993971.022577, 967249.079973, 941950.131258,
+ 917962.899577, 895187.442482, 873533.742731,
+ 852920.504418, 833274.120331, 814527.782755,
+ 796620.715006, 779497.504963, 763107.525152,
+ 747404.426526, 732345.695244, 717892.263476,
+ 704008.166703, 690660.241153, 677817.855988,
+ 665452.675682, 653538.448664, 642050.818932,
+ 630967.157747, 620266.41297, 609928.973904,
+ 599936.549831, 590272.060629, 580919.538101,
+ 571864.036815, 563091.553384, 554588.953291,
+ 538344.816665, 523041.548206, 508599.29108,
+ 494946.998456, 482021.249542, 469765.251724,
+ 458127.995525, 447063.535752, 436530.377359,
+ 426490.948641, 416911.147609, 407759.949924,
+ 399009.068882, 390632.659531, 382607.060391,
+ 374910.567309, 367523.234875, 360426.701557,
+ 353604.035312, 347039.596927, 340718.91876,
+ 334628.596889, 328756.19497, 323090.158348,
+ 317619.737168, 312334.917401, 307226.358847,
+ 302285.339317, 297503.704266, 292873.821293,
+ 288388.538937, 284041.149331, 275735.234396,
+ 267910.070584, 260524.871062, 253543.347354,
+ 246933.104947, 240665.13389, 234713.377391,
+ 229054.364815, 223666.898142, 218531.783,
+ 213631.597053, 208950.489813, 204474.009027,
+ 200188.949594, 196083.221672, 192145.735205,
+ 188366.298503, 184735.528953, 181244.774164,
+ 177886.04218, 174651.939551, 171535.616247,
+ 168530.716557, 165631.335218, 162831.978148,
+ 160127.527208, 157513.208541, 154984.564055,
+ 152537.425701, 150167.892222, 147872.308115,
+ 145647.244544, 141395.994603, 137390.624505,
+ 133610.283442, 130036.419482, 126652.470773,
+ 123443.605264, 120396.500282, 117499.155002,
+ 114740.730226, 112111.41094, 109602.287952,
+ 107205.255591, 104912.922975, 102718.536798,
+ 100615.913924, 98599.3823607, 96663.7294285,
+ 94804.156116, 93016.2367777, 91295.8834589,
+ 89639.3142378, 88043.0250685, 86503.7646782,
+ 85018.5121417, 83584.4568033, 82198.980265,
+ 80859.6401976, 79564.1557618, 78310.3944557,
+ 77096.3602287, 75920.1827217, 74780.1075103,
+ 72601.7735874, 70549.3244291, 68612.0902638,
+ 66780.5772822, 65046.3097201, 63401.6967477,
+ 61839.9197339, 60354.8363331, 58940.8985361,
+ 57593.0823673, 56306.8273413, 55077.9841309,
+ 53902.7691783, 52777.7251933, 51699.6866696,
+ 50665.7496879, 49673.2453971, 48719.716661,
+ 47802.8974376, 46920.6945257, 46071.1713659,
+ 45252.5336325, 44463.1163889, 43701.3726106,
+ 42965.8629118, 42255.2463268, 41568.2720255,
+ 40903.7718523, 40260.6535941, 39637.8948977,
+ 39034.5377622, 38449.6835453, 37332.1592893,
+ 36279.1577368, 35285.2130631, 34345.4616221,
+ 33455.5611057, 32611.6223983, 31810.1518596,
+ 31048.0022185, 30322.3306142, 29630.5625994,
+ 28970.3611385, 28339.5998107, 27736.3395662,
+ 27158.8084976, 26605.3841795, 26074.5782033,
+ 25565.0225957, 25075.4578572, 24604.7224005,
+ 24151.7432007, 23715.5274976, 23295.1554155,
+ 22889.7733832, 22498.5882546, 22120.8620455,
+ 21755.9072116, 21403.082403, 21061.7886423,
+ 20731.4658758, 20411.5898558, 20101.6693198,
+ 19801.2434311, 19227.1706446, 18686.2100916,
+ 18175.5584758, 17692.7212135, 17235.4710025,
+ 16801.8128973, 16389.9547259, 15998.2819199,
+ 15625.3360073, 15269.7961595, 14930.463299,
+ 14606.2463622, 14296.1503821, 13999.2661181,
+ 13714.7610004, 13441.871201, 13179.8946694,
+ 12928.184999, 12686.1460114, 12453.2269616,
+ 12228.9182827, 12012.7478009, 11804.2773608,
+ 11603.0998103, 11408.8363005, 11221.1338643,
+ 11039.6632386, 10864.1169034, 10694.2073132,
+ 10529.6652976, 10370.2386141, 10215.690635,
+ 9920.35530902, 9642.03589933, 9379.29279296,
+ 9130.84483975, 8895.54809344, 8672.3778898,
+ 8460.41366569, 8258.82604172, 8066.86578413,
+ 7883.85433402, 7709.17565017, 7542.26915762,
+ 7382.6236306, 7229.77186868, 7083.28604825,
+ 6942.7736517, 6807.8738919, 6678.25456313,
+ 6553.60926028, 6433.65491683, 6318.12961989,
+ 6206.79066653, 6099.41283073, 5995.78681498,
+ 5895.71786375, 5799.02451956, 5705.53750473,
+ 5615.09871424, 5527.56030707, 5442.78388486,
+ 5360.63974837, 5281.00622313, 5128.82081305,
+ 4985.39281878, 4849.98187786, 4721.92907957,
+ 4600.64604199, 4485.60570297, 4376.33451913,
+ 4272.40582769, 4173.43417369, 4079.07044241,
+ 3988.99766684, 3902.92740311, 3820.59658617,
+ 3741.76479301, 3666.21185297, 3593.73575486,
+ 3524.1508087, 3457.28602662, 3392.98369304,
+ 3331.09809869, 3271.49441705, 3214.04770487,
+ 3158.64201089, 3105.16957952, 3053.53013775,
+ 3003.63025539, 2955.38276983, 2908.70626808,
+ 2863.52461931, 2819.76655239, 2777.36527337,
+ 2736.25811858, 2657.69431628, 2583.6451563,
+ 2513.72928085, 2447.60726343, 2384.97598811,
+ 2325.56391052, 2269.1270432, 2215.44553905,
+ 2164.32077155, 2115.57282931, 2069.03835784,
+ 2024.56869368, 1982.02824566, 1941.29308582,
+ 1902.24971907, 1864.79400552, 1828.83021394,
+ 1794.27018797, 1761.03260977, 1729.04234809,
+ 1698.22987957, 1668.53077394, 1639.88523494,
+ 1612.23769017, 1585.53642374, 1559.73324667,
+ 1534.78320059, 1510.64429085, 1487.27724571,
+ 1464.64529866, 1442.7139913, 1421.45099465,
+ 1380.81030481, 1342.50136932, 1306.32740443,
+ 1272.1132493, 1239.70246899, 1208.95491167,
+ 1179.74463906, 1151.9581651, 1125.49295061,
+ 1100.25611146, 1076.16330573, 1053.13777154,
+ 1031.10949225, 1010.01446977, 989.794089986,
+ 970.394566925, 951.766454485, 933.864216342,
+ 916.645846088, 900.072530873, 884.108352851,
+ 868.720023553, 853.876647032, 839.549508182,
+ 825.711883191, 812.33886945, 799.407232626,
+ 786.895268923, 774.782680783, 763.050464528,
+ 751.680808624, 740.657001406, 719.585090388,
+ 699.720033366, 680.960086838, 663.214678935,
+ 646.402913465, 630.452308433, 615.297727189,
+ 600.880468722, 587.147490089, 574.050739088,
+ 561.546579359, 549.59529327, 538.160650604,
+ 527.209533055, 516.711606313, 506.639032813,
+ 496.96621939, 487.669594983, 478.727414292,
+ 470.119583902, 461.827507957, 453.833950828,
+ 446.122914658, 438.679529921, 431.489957406,
+ 424.541300267, 417.821524944, 411.319389924,
+ 405.024381463, 398.926655468, 393.01698488,
+ 387.286711944, 376.332318284, 366.004045462,
+ 356.249171211, 347.020757519, 338.276876563,
+ 329.979957895, 322.096235284, 314.59527586,
+ 307.449577629, 300.634224017, 294.126586238,
+ 287.906065913, 281.95387174, 276.252825046,
+ 270.787189968, 265.542524683, 260.50555071,
+ 255.664037759, 251.006702025, 246.523116109,
+ 242.203629065, 238.039295241, 234.021810832,
+ 230.143457166, 226.397049911, 222.775893491,
+ 219.273740104, 215.884752796, 212.60347214,
+ 209.424786113, 206.343902815, 203.356325724,
+ 197.644448118, 192.25828345, 187.17044241,
+ 182.356537811, 177.794783117, 173.465653828,
+ 169.351600521, 165.436804568, 161.706969302,
+ 158.149140755, 154.751553204, 151.503495587,
+ 148.395195583, 145.417718676, 142.562879997,
+ 139.823167083, 137.191672013, 134.662031621,
+ 132.228374673, 129.885275086, 127.627710402,
+ 125.451024827, 123.350896269, 121.323306878,
+ 119.364516656, 117.471039772, 115.639623265,
+ 113.867227861, 112.151010651, 110.488309437,
+ 108.876628554, 107.313626007, 104.324987222,
+ 101.506311917, 98.8433301246, 96.3233343285,
+ 93.9349706793, 91.6680628859, 89.5134629529,
+ 87.4629241036, 85.5089921298, 83.6449121192,
+ 81.8645480786, 80.1623134152, 78.5331106002,
+ 76.9722786296, 75.4755471284, 74.0389961376,
+ 72.6590207791, 71.3323001202, 70.0557696658,
+ 68.8265969947, 67.6421601273, 66.5000282736,
+ 65.3979446612, 64.3338111858, 63.3056746605,
+ 62.3117144747, 61.3502314957, 60.4196380687,
+ 59.5184489908, 58.6452733506, 57.7988071362,
+ 56.9778265301, 55.4077918373, 53.9267643608,
+ 52.5272846644, 51.2027090519, 49.9471006653,
+ 48.7551376125, 47.622035089, 46.5434790675,
+ 45.5155695954, 44.5347721114, 43.5978754878,
+ 42.7019557373, 41.8443445089, 41.0226016526,
+ 40.2344912502, 39.4779606118, 38.751121818,
+ 38.0522354538, 37.3796962368, 36.7320202863,
+ 36.1078338186, 35.5058630854, 34.9249253981,
+ 34.3639211041, 33.8218263982, 33.2976868701,
+ 32.7906117014, 32.2997684362, 31.8243782605,
+ 31.3637117346, 30.917084926, 30.4838559023,
+ 29.6552146411, 28.8733782988, 28.134432563,
+ 27.4348905633, 26.7716358691, 26.1418743927,
+ 25.5430936131, 24.9730278502, 24.4296285677,
+ 23.9110388709, 23.4155715259, 22.9416899422,
+ 22.4879916639, 22.0531939906, 21.6361214127,
+ 21.2356946011, 20.8509207292, 20.4808849434,
+ 20.1247428261, 19.7817137183, 19.4510747894,
+ 19.132155759, 18.824334187, 18.5270312637,
+ 18.2397080369, 17.9618620255, 17.6930241733,
+ 17.4327561035, 17.1806476409, 16.9363145704,
+ 16.6993966065, 16.4695555516, 16.0298519219,
+ 15.6148798725, 15.2225761964, 14.8511026109,
+ 14.4988157995, 14.1642421305, 13.8460562183,
+ 13.5430626642, 13.2541804371, 12.97842946,
+ 12.7149190454, 12.4628378888, 12.2214453808,
+ 11.9900640388, 11.7680728923, 11.5549016866,
+ 11.3500257858, 11.1529616813, 10.9632630215,
+ 10.7805170944, 10.6043417028, 10.4343823832,
+ 10.2703099236, 10.1118181445, 9.95862191002,
+ 9.81045534239, 9.6670702159, 9.52823450908,
+ 9.3937310977, 9.26335657253, 9.13692016828,
+ 9.01424279167, 8.77950188724, 8.5579029016,
+ 8.34835265336, 8.14987690272, 7.96160453251,
+ 7.78275419548, 7.61262298955, 7.45057681007,
+ 7.29604209573, 7.14849873828, 7.00747396878,
+ 6.87253706654, 6.74329476426, 6.6193872446,
+ 6.50048464099, 6.38628396998, 6.27650643422,
+ 6.17089504471, 6.06921251895, 5.97123941835,
+ 5.87677249363, 5.78562321153, 5.69761644002,
+ 5.61258927241, 5.53038997362, 5.45087703381,
+ 5.37391831699, 5.29939029358, 5.22717734731,
+ 5.15717114836, 5.08927008528, 5.02337874941,
+ 4.89727186893, 4.77819450111, 4.66556389779,
+ 4.5588605707, 4.45761989234, 4.36142500459,
+ 4.26990080294, 4.18270881023, 4.09954278998,
+ 4.02012497734, 3.9442028284, 3.87154620636,
+ 3.8019449374, 3.73520668068, 3.67115506623,
+ 3.60962806223, 3.5504765392, 3.49356300388,
+ 3.43876047989, 3.38595151559, 3.33502730255,
+ 3.28588689046, 3.2384364864, 3.19258882806,
+ 3.14826262191, 3.10538203853, 3.06387625858,
+ 3.0236790634, 2.98472846526, 2.9469663728,
+ 2.91033828793, 2.8747930306, 2.8067613898,
+ 2.74251940505, 2.68175424311, 2.62418694186,
+ 2.56956792209, 2.51767319702, 2.46830115574,
+ 2.42126982168, 2.37641450585, 2.33358579,
+ 2.29264778675, 2.2534766331, 2.21595918157,
+ 2.17999185932, 2.14547967046, 2.11233532111,
+ 2.08047844974, 2.0498349484, 2.02033636247,
+ 1.99191935846, 1.9645252511, 1.93809958206,
+ 1.91259174386, 1.8879546434, 1.86414440028,
+ 1.84112007579, 1.81884342903, 1.79727869694,
+ 1.77639239563, 1.75615314058, 1.73653148369,
+ 1.71749976531, 1.68110365251, 1.64677447223,
+ 1.61434304574, 1.58365847132, 1.55458570812,
+ 1.5270035349, 1.50080281743, 1.47588503118,
+ 1.45216099646, 1.42954979098, 1.40797781157,
+ 1.38737796141, 1.36768894386, 1.34885464657,
+ 1.33082360294, 1.31354851955, 1.2969858605,
+ 1.28109548066, 1.2658403013, 1.25118602242,
+ 1.23710086713, 1.22355535382, 1.21052209274,
+ 1.19797560406, 1.18589215458, 1.17424961117,
+ 1.16302730872, 1.15220593111, 1.14176740369,
+ 1.1316947959, 1.12197223308, 1.11258481634,
+ 1.09476027388, 1.07811888208, 1.06256992542,
+ 1.04803270312, 1.03443522136, 1.02171308882,
+ 1.00980857969, 0.998669835485, 0.988250182301,
+ 0.978507544678, 0.96940394074, 0.960905045915,
+ 0.952979814867, 0.945600153008, 0.938740630449,
+ 0.932378232401, 0.926492141047, 0.921063544663,
+ 0.916075470469, 0.911512638216, 0.907361331987,
+ 0.903609288065, 0.900245597058, 0.897260618748,
+ 0.894645908353, 0.892394153106, 0.890499118227,
+ 0.888955601518, 0.887759395947, 0.886907259694,
+ 0.886396893265, 0
+
+ }; \end{hide}
+
+ public static double inverseF01 (double u)\begin{hide} {
+
+ /* Method of Marsaglia. REF : ? */
+
+ int n, k, j;
+ double v, x, w;
+ boolean negatif;
+
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+/* if (u >= 1.0)
+ return XBIG;
+ if (u <= 0.0)
+ return -XBIG; */
+ if (u < 0.5) {
+ negatif = true;
+ u = 2.0 * u;
+ } else {
+ negatif = false;
+ u = 2.0 * (1.0 - u);
+ }
+
+ k = 0;
+ w = u;
+ while (w < 0.5) {
+ ++k;
+ w *= 2.0;
+ }
+ j = (int) ((w - 0.5) * 64.0);
+ n = 992 - 32 * k + j;
+ if (n < 0) {
+ if (negatif)
+ return -XBIG;
+ else
+ return XBIG;
+ }
+ v = ((int) (Num.TWOEXP[k + 6] * u)) / Num.TWOEXP[k + 6];
+ v = (u - v) * B[n];
+
+ x = A[n] - v * (1.414214 - v * (A[n] - 0.4714045 * (1.0 +
+ 2.0 * A[n] * A[n]) * v));
+ if (negatif)
+ return -x;
+ else
+ return x;
+
+ }\end{hide}
+\end{code}
+%% boolean negatif;
+%% float uFl, wFl;
+%% int uInt, wInt, n;
+%% double v, x;
+%% uFl = (float) u;
+%% if (uFl >= 1)
+%% return XBIG;
+%% if (uFl <= 0)
+%% return -XBIG;
+%% if (uFl < 0.5) {
+%% negatif = true;
+%% uFl = 2.0f*uFl;
+%% } else {
+%% negatif = false;
+%% uFl = 2.0f*(1.0f - uFl);
+%% }
+%% uInt = Float.floatToIntBits (uFl);
+%% wInt = uInt & 2147221504;
+%% wFl = Float.intBitsToFloat (wInt);
+%% n = (wInt >> 18) - 3040;
+%% /* util_Assert (n <= 1024, "n >= 1024"); */
+%% v = (uFl - wFl) * B[n];
+%% x = A[n] - v * (1.414214 - v * (A[n] - 0.4714045 * (1.0 +
+%% 2.0 * A[n] * A[n]) * v));
+%% if (negatif)
+%% return -x;
+%% else
+%% return x;
+\begin{tabb}
+ Same as \method{inverseF}{double,double,double}~\texttt{(0.0, 1.0, u)}.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double mu, double sigma, double u)
+\begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ return mu + sigma*inverseF01 (u);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns an approximation of $\Phi^{-1}(u)$, where $\Phi$ is the standard
+ normal distribution function, with mean 0 and variance 1.
+ Uses the method of Marsaglia, Zaman, and Marsaglia \cite{rMAR94b},
+ with table lookups.
+ %% The method works provided that
+ %% the processor respects the IEEE-754 floating-point standard.
+ Returns 6 decimal digits of precision. This method is approximately
+ 20\%{} faster than \texttt{NormalDist.inverseF}.
+ \end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/NormalInverseGaussianDist.java b/source/umontreal/iro/lecuyer/probdist/NormalInverseGaussianDist.java
new file mode 100644
index 0000000..c4a7684
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NormalInverseGaussianDist.java
@@ -0,0 +1,368 @@
+
+
+/*
+ * Class: NormalInverseGaussianDist
+ * Description: normal inverse gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution with location parameter
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN>, scale parameter
+ * <SPAN CLASS="MATH"><I>δ</I> > 0</SPAN>, tail heavyness
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>, and
+ * asymmetry parameter <SPAN CLASS="MATH"><I>β</I></SPAN> such that
+ * <SPAN CLASS="MATH">0 <= | <I>β</I>| < <I>α</I></SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>αδe</I><SUP><I>δγ</I>+<I>β</I>(x-<I>μ</I>)</SUP><I>K</I><SUB>1</SUB>(<I>α</I>(δ^2 + (x - μ)^2)<SUP>1/2</SUP>)/<I>π</I>(δ^2 + (x - μ)^2)<SUP>1/2</SUP>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>K</I><SUB>1</SUB></SPAN> is the modified Bessel function of the second kind of order 1,
+ * and
+ * <SPAN CLASS="MATH"><I>γ</I> = (α^2 - β^2)<SUP>1/2</SUP></SPAN>.
+ *
+ * <P>
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = ∫<SUB>-∞</SUB><SUP>x</SUP><I>dtf</I> (<I>t</I>),
+ * </DIV><P></P>
+ *
+ */
+public class NormalInverseGaussianDist extends ContinuousDistribution {
+ protected double alpha;
+ protected double beta;
+ protected double gamma;
+ protected double delta;
+ protected double mu;
+
+
+
+ /**
+ * Constructor for a <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT>,
+ * <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>, <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT>.
+ *
+ */
+ public NormalInverseGaussianDist (double alpha, double beta, double mu,
+ double delta) {
+ setParams (alpha, beta, mu, delta);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, beta, mu, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, mu, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, mu, delta, x);
+ }
+
+ public double getMean() {
+ return getMean (alpha, beta, mu, delta);
+ }
+
+ public double getVariance() {
+ return getVariance (alpha, beta, mu, delta);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (alpha, beta, mu, delta);
+ }
+
+ /**
+ * Computes the density function
+ * for the <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>δ</I></SPAN>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double density (double alpha, double beta, double mu,
+ double delta, double x) {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ double z = (x - mu)/delta;
+ double w;
+ if (Math.abs(z) <= 1.0e10)
+ w = Math.sqrt (1.0 + z*z);
+ else
+ w = Math.abs(z);
+ double y = alpha*delta*w;
+ double v = delta*(gamma + beta*z);
+ double R = Num.expBesselK1(v, y);
+ return alpha * R / (Math.PI*w);
+ }
+
+
+ /**
+ * NOT IMPLEMENTED.
+ * Computes the distribution function
+ * of the <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>,
+ * <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double cdf (double alpha, double beta, double mu,
+ double delta, double x) {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ double z = (x - mu)/delta;
+ if (z > 0.0 && (gamma + (beta - alpha)*z >= XBIG))
+ return 1.0;
+ if (z < 0.0 && (gamma + (beta + alpha)*z <= -XBIGM))
+ return 0.0;
+ // double w = Math.sqrt (1.0 + z*z);
+
+ throw new UnsupportedOperationException
+ ("NormalInverseGaussianDist: cdf NOT IMPLEMENTED");
+ }
+
+
+ /**
+ * NOT IMPLEMENTED.
+ * Computes the complementary distribution function of the <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double barF (double alpha, double beta, double mu,
+ double delta, double x) {
+ return 1.0 - cdf (alpha, beta, mu, delta, x);
+ }
+
+
+ /**
+ * NOT IMPLEMENTED. Computes the inverse of the <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double mu,
+ double delta, double u) {
+ throw new UnsupportedOperationException(" Inversion NOT IMPLEMENTED");
+ }
+
+
+ /**
+ * NOT IMPLEMENTED.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters
+ * [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>, <SPAN CLASS="MATH">hat(μ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(δ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+/*
+ double[] parameters = new double[4];
+ double sum = 0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+ */
+ throw new UnsupportedOperationException("getMLE is not implemented");
+
+ // return parameters;
+ }
+
+
+ /**
+ * NOT IMPLEMENTED.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static NormalInverseGaussianDist getInstanceFromMLE (double[] x,
+ int n) {
+ double parameters[] = getMLE (x, n);
+ return new NormalInverseGaussianDist (parameters[0], parameters[1],
+ parameters[2], parameters[3]);
+ }
+
+
+ /**
+ * Returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I> + <I>δβ</I>/<I>γ</I></SPAN> of the
+ * <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the mean of the normal inverse gaussian distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I> + <I>δβ</I>/<I>γ</I></SPAN>
+ *
+ */
+ public static double getMean (double alpha, double beta, double mu,
+ double delta) {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ return mu + delta*beta/gamma;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>δα</I><SUP>2</SUP>/<I>γ</I><SUP>3</SUP></SPAN> of the <SPAN CLASS="textit">normal inverse gaussian</SPAN> distribution with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the variance of the normal inverse gaussian distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>δα</I><SUP>2</SUP>/<I>γ</I><SUP>3</SUP></SPAN>
+ *
+ */
+ public static double getVariance (double alpha, double beta, double mu,
+ double delta) {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ return delta*alpha*alpha / (gamma*gamma*gamma);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the <SPAN CLASS="textit">normal inverse gaussian</SPAN>
+ * distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the standard deviation of the normal inverse gaussian distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta,
+ double mu, double delta) {
+ return Math.sqrt (getVariance (alpha, beta, mu, delta));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double alpha, double beta, double mu,
+ double delta) {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ gamma = Math.sqrt(alpha*alpha - beta*beta);
+
+ this.mu = mu;
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }
+
+
+ /**
+ * Returns a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, beta, mu, delta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + ": alpha = " + alpha + ", beta = " + beta +
+ ", mu = " + mu + ", delta = " + delta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/NormalInverseGaussianDist.tex b/source/umontreal/iro/lecuyer/probdist/NormalInverseGaussianDist.tex
new file mode 100644
index 0000000..4b16dae
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/NormalInverseGaussianDist.tex
@@ -0,0 +1,396 @@
+\defmodule {NormalInverseGaussianDist}
+
+\newcommand{\nig}{\emph{normal inverse gaussian}}
+
+
+Extends the class \class{ContinuousDistribution} for
+the \nig{} distribution with location parameter
+$\mu$, scale parameter $\delta > 0$, tail heavyness $\alpha > 0$, and
+ asymmetry parameter $\beta$ such that $0 \le |\beta| < \alpha$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = {\alpha\delta e^{\delta \gamma + \beta (x - \mu)} K_1\left(\alpha\sqrt{\delta^2 +
+ (x - \mu)^2}\right)}/{\pi \sqrt{\delta^2 + (x - \mu)^2}},
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \frac{\alpha\delta e^{\delta \gamma + \beta (x - \mu)} K_1\left(\alpha\sqrt{\delta^2 +
+ (x - \mu)^2}\right)}{\pi \sqrt{\delta^2 + (x - \mu)^2}},
+\qquad\mbox {for } -\infty < x < \infty,
+\eqlabel{eq:fNormalInverseGaussian}
+\endeq
+\end{latexonly}%
+%
+where $K_1$ is the modified Bessel function of the second kind of order 1,
+and $\gamma = \sqrt{\alpha^2 - \beta^2}$.
+
+The distribution function is given by
+\eq
+ F(x) = \int_{-\infty}^x dt f(t),
+\eqlabel{eq:FNormalInverseGaussian}
+\endeq
+
+% The non-static versions of the methods \texttt{cdf}, \texttt{barF},
+% and \texttt{inverseF} call the static version of the same name.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalInverseGaussianDist
+ * Description: normal inverse gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class NormalInverseGaussianDist extends ContinuousDistribution\begin{hide} {
+ protected double alpha;
+ protected double beta;
+ protected double gamma;
+ protected double delta;
+ protected double mu;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public NormalInverseGaussianDist (double alpha, double beta, double mu,
+ double delta)\begin{hide} {
+ setParams (alpha, beta, mu, delta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructor for a \nig{} distribution with parameters $\alpha$ = \texttt{alpha},
+ $\beta$ = \texttt{beta}, $\mu$ = \texttt{mu} and $\delta$ = \texttt{delta}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (alpha, beta, mu, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, mu, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, mu, delta, x);
+ }
+
+ public double getMean() {
+ return getMean (alpha, beta, mu, delta);
+ }
+
+ public double getVariance() {
+ return getVariance (alpha, beta, mu, delta);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (alpha, beta, mu, delta);
+ }\end{hide}
+
+ public static double density (double alpha, double beta, double mu,
+ double delta, double x)\begin{hide} {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ double z = (x - mu)/delta;
+ double w;
+ if (Math.abs(z) <= 1.0e10)
+ w = Math.sqrt (1.0 + z*z);
+ else
+ w = Math.abs(z);
+ double y = alpha*delta*w;
+ double v = delta*(gamma + beta*z);
+ double R = Num.expBesselK1(v, y);
+ return alpha * R / (Math.PI*w);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:fNormalInverseGaussian})
+ for the \nig{} distribution with parameters $\alpha$, $\beta$, $\mu$
+ and $\delta$, evaluated at $x$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta, double mu,
+ double delta, double x)\begin{hide} {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ double z = (x - mu)/delta;
+ if (z > 0.0 && (gamma + (beta - alpha)*z >= XBIG))
+ return 1.0;
+ if (z < 0.0 && (gamma + (beta + alpha)*z <= -XBIGM))
+ return 0.0;
+ // double w = Math.sqrt (1.0 + z*z);
+
+ throw new UnsupportedOperationException
+ ("NormalInverseGaussianDist: cdf NOT IMPLEMENTED");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ NOT IMPLEMENTED.
+ Computes the distribution function (\ref{eq:FNormalInverseGaussian})
+ of the \nig{} distribution with parameters $\alpha$,
+ $\beta$, $\mu$ and $\delta$, evaluated at $x$.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double mu,
+ double delta, double x)\begin{hide} {
+ return 1.0 - cdf (alpha, beta, mu, delta, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ NOT IMPLEMENTED.
+ Computes the complementary distribution function of the \nig{} distribution
+ with parameters $\alpha$, $\beta$, $\mu$ and $\delta$, evaluated at $x$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double beta, double mu,
+ double delta, double u)\begin{hide} {
+ throw new UnsupportedOperationException(" Inversion NOT IMPLEMENTED");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ NOT IMPLEMENTED. Computes the inverse of the \nig{} distribution
+ with parameters $\alpha$, $\beta$, $\mu$ and $\delta$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+/*
+ double[] parameters = new double[4];
+ double sum = 0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+ */
+ throw new UnsupportedOperationException("getMLE is not implemented");
+
+ // return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ NOT IMPLEMENTED.
+% Estimates the parameters $(\alpha, \beta, \mu, \delta)$ of the \nig{}
+% distribution using the maximum likelihood method, from the $n$ observations
+% $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a 4-element
+% array, in regular order: [$\alpha$, $\beta$, $\mu$, $\delta$].
+% \begin{detailed}
+% The maximum likelihood estimators are the values $(\hat\mu, \hat\delta)$
+% that satisfy the equations:
+% \begin{eqnarray*}
+% \end{eqnarray*}
+% where $\bar x_n$ is the average of $x[0],\dots,x[n-1]$,
+% \cite[page 271]{tJOH95a}.
+% \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters
+ [$\hat{\alpha}$, $\hat{\beta}$, $\hat{\mu}$, $\hat{\delta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static NormalInverseGaussianDist getInstanceFromMLE (double[] x,
+ int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new NormalInverseGaussianDist (parameters[0], parameters[1],
+ parameters[2], parameters[3]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ NOT IMPLEMENTED.
+% Creates a new instance of a \nig{} distribution with parameters
+% $\alpha$, $\beta$, $\mu$, $\delta$ estimated using the maximum
+% likelihood method based on the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta, double mu,
+ double delta)\begin{hide} {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ return mu + delta*beta/gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean $E[X] = \mu + \delta\beta/\gamma$ of the
+ \nig{} distribution with parameters $\alpha$, $\beta$, $\mu$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the normal inverse gaussian distribution
+ $E[X] = \mu + \delta\beta/\gamma$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta, double mu,
+ double delta)\begin{hide} {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ double gamma = Math.sqrt(alpha*alpha - beta*beta);
+ return delta*alpha*alpha / (gamma*gamma*gamma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] =
+ \delta\alpha^2 / \gamma^3$ of the \nig{} distribution with parameters
+ $\alpha$, $\beta$, $\mu$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the normal inverse gaussian distribution
+ $\mbox{Var}[X] = \delta\alpha^2 / \gamma^3$
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta,
+ double mu, double delta)\begin{hide} {
+ return Math.sqrt (getVariance (alpha, beta, mu, delta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the \nig{}
+ distribution with parameters $\alpha$, $\beta$, $\mu$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the normal inverse gaussian distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\delta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double beta, double mu,
+ double delta)\begin{hide} {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ gamma = Math.sqrt(alpha*alpha - beta*beta);
+
+ this.mu = mu;
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\alpha$, $\beta$, $\mu$ and $\delta$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, beta, mu, delta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\beta$, $\mu$, $\delta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + ": alpha = " + alpha + ", beta = " + beta +
+ ", mu = " + mu + ", delta = " + delta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/ParetoDist.java b/source/umontreal/iro/lecuyer/probdist/ParetoDist.java
new file mode 100644
index 0000000..c6c6bb4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ParetoDist.java
@@ -0,0 +1,335 @@
+
+
+/*
+ * Class: ParetoDist
+ * Description: Pareto distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for a distribution
+ * from the <EM>Pareto</EM> family, with
+ * shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and location parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density for this type of Pareto distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>αβ</I><SUP><I>α</I></SUP>/<I>x</I><SUP><I>α</I>+1</SUP> for <I>x</I> >= <I>β</I>,
+ * </DIV><P></P>
+ * and 0 otherwise. The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - (<I>β</I>/<I>x</I>)<SUP><I>α</I></SUP> for <I>x</I> >= <I>β</I>,
+ * </DIV><P></P>
+ * and the inverse distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>β</I>(1 - <I>u</I>)<SUP>-1/<I>α</I></SUP> for 0 <= <I>u</I> < 1.
+ * </DIV><P></P>
+ *
+ */
+public class ParetoDist extends ContinuousDistribution {
+ private double alpha;
+ private double beta;
+
+
+
+ /**
+ * Constructs a <TT>ParetoDist</TT> object with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>.
+ *
+ */
+ public ParetoDist (double alpha) {
+ setParams (alpha, 1.0);
+ }
+
+
+ /**
+ * Constructs a <TT>ParetoDist</TT> object with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>.
+ *
+ */
+ public ParetoDist (double alpha, double beta) {
+ setParams (alpha, beta);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean() {
+ return ParetoDist.getMean (alpha, beta);
+ }
+
+ public double getVariance() {
+ return ParetoDist.getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation() {
+ return ParetoDist.getStandardDeviation (alpha, beta);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return x < beta ? 0 : alpha*Math.pow (beta/x, alpha)/x;
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= beta)
+ return 0.0;
+ return 1.0 - Math.pow (beta/x, alpha);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (double alpha, double beta, double x) {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= beta)
+ return 1.0;
+ return Math.pow (beta/x, alpha);
+ }
+
+
+ /**
+ * Computes the inverse of the distribution function.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double u) {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u <= 0.0)
+ return beta;
+
+ double t;
+ t = -Math.log1p (-u);
+ if ((u >= 1.0) || t/Math.log(10) >= alpha * Num.DBL_MAX_10_EXP)
+ return Double.POSITIVE_INFINITY;
+
+ return beta / Math.pow (1 - u, 1.0/alpha);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>β</I>)</SPAN> of the Pareto distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double [] parameters = new double[2];
+ parameters[1] = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < n; i++) {
+ if (x[i] < parameters[1])
+ parameters[1] = x[i];
+ }
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ sum += Math.log (x[i] / parameters[1]);
+ else
+ sum -= 709.0;
+ }
+ parameters[0] = n / sum;
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a Pareto distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static ParetoDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new ParetoDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>αβ</I>/(<I>α</I> - 1)</SPAN>
+ * of the Pareto distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the mean of the Pareto distribution
+ *
+ */
+ public static double getMean (double alpha, double beta) {
+ if (alpha <= 1.0)
+ throw new IllegalArgumentException("alpha <= 1");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return ((alpha * beta) / (alpha - 1.0));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * of the Pareto distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the variance of the Pareto distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>αβ</I><SUP>2</SUP>/[(<I>α</I> -2)(<I>α</I> - 1)]</SPAN>
+ *
+ */
+ public static double getVariance (double alpha, double beta) {
+ if (alpha <= 2)
+ throw new IllegalArgumentException("alpha <= 2");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return ((alpha * beta * beta) / ((alpha - 2.0) * (alpha - 1.0) * (alpha - 1.0)));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the Pareto
+ * distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the standard deviation of the Pareto distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta) {
+ return Math.sqrt (ParetoDist.getVariance (alpha, beta));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> for this object.
+ *
+ */
+ public void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ this.alpha = alpha;
+ this.beta = beta;
+ supportA = beta;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/ParetoDist.tex b/source/umontreal/iro/lecuyer/probdist/ParetoDist.tex
new file mode 100644
index 0000000..376f79f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/ParetoDist.tex
@@ -0,0 +1,355 @@
+\defmodule {ParetoDist}
+
+Extends the class \class{ContinuousDistribution} for a distribution
+from the {\em Pareto\/} family, with
+shape parameter $\alpha > 0$ and location parameter $\beta > 0$
+\cite[page 574]{tJOH95a}.
+The density for this type of Pareto distribution is
+\begin{latexonly}
+\eq
+ f(x) = \frac{\alpha\beta^\alpha} {x^{\alpha+1}}
+ \qquad \mbox{for }x \ge\beta,
+ \eqlabel{eq:fpareto}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ f(x) = {\alpha\beta^\alpha} / {x^{\alpha+1}}
+ \qquad \mbox{for }x \ge\beta,
+ \eqlabel{eq:fpareto}
+\endeq
+\end{htmlonly}
+and 0 otherwise. The distribution function is
+\eq
+ F(x) = 1 - \left(\beta/x\right)^\alpha
+ \qquad \mbox{for }x\ge\beta, \eqlabel{eq:Fpareto}
+\endeq
+and the inverse distribution function is
+$$
+ F^{-1}(u) = \beta (1 - u)^{-1/\alpha}
+ \qquad \mbox{for } 0 \le u < 1.
+$$
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ParetoDist
+ * Description: Pareto distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;\end{hide}
+
+public class ParetoDist extends ContinuousDistribution\begin{hide} {
+ private double alpha;
+ private double beta;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public ParetoDist (double alpha)\begin{hide} {
+ setParams (alpha, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ParetoDist} object with parameters $\alpha =$
+ \texttt{alpha} and $\beta = 1$.
+ \end{tabb}
+\begin{code}
+
+ public ParetoDist (double alpha, double beta)\begin{hide} {
+ setParams (alpha, beta);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ParetoDist} object with parameters $\alpha =$
+ \texttt{alpha} and $\beta = $ \texttt{beta}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (alpha, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean() {
+ return ParetoDist.getMean (alpha, beta);
+ }
+
+ public double getVariance() {
+ return ParetoDist.getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation() {
+ return ParetoDist.getStandardDeviation (alpha, beta);
+ }\end{hide}
+
+ public static double density (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return x < beta ? 0 : alpha*Math.pow (beta/x, alpha)/x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= beta)
+ return 0.0;
+ return 1.0 - Math.pow (beta/x, alpha);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= beta)
+ return 1.0;
+ return Math.pow (beta/x, alpha);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double beta, double u)\begin{hide} {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0,1]");
+
+ if (u <= 0.0)
+ return beta;
+
+ double t;
+ t = -Math.log1p (-u);
+ if ((u >= 1.0) || t/Math.log(10) >= alpha * Num.DBL_MAX_10_EXP)
+ return Double.POSITIVE_INFINITY;
+
+ return beta / Math.pow (1 - u, 1.0/alpha);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double [] parameters = new double[2];
+ parameters[1] = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < n; i++) {
+ if (x[i] < parameters[1])
+ parameters[1] = x[i];
+ }
+
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ if (x[i] > 0.0)
+ sum += Math.log (x[i] / parameters[1]);
+ else
+ sum -= 709.0;
+ }
+ parameters[0] = n / sum;
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\beta)$ of the Pareto distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\beta$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat\alpha, \hat\beta)$
+ that satisfy the equations:
+ \begin{eqnarray*}
+ \hat{\beta} & = & \min_i \{x_i\}\\[7pt]
+ \hat{\alpha} & = & \frac{n}{\sum_{i=1}^{n}
+ \ln\left(\frac{x_i}{\hat{\beta}\rule{0pt}{0.5em}}\right)}.
+ \end{eqnarray*}
+ See \cite[page 581]{tJOH95a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}$, $\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static ParetoDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new ParetoDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Pareto distribution with parameters $\alpha$ and $\beta$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta)\begin{hide} {
+ if (alpha <= 1.0)
+ throw new IllegalArgumentException("alpha <= 1");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return ((alpha * beta) / (alpha - 1.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \alpha\beta/(\alpha - 1)$
+ of the Pareto distribution with parameters $\alpha$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Pareto distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta)\begin{hide} {
+ if (alpha <= 2)
+ throw new IllegalArgumentException("alpha <= 2");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ return ((alpha * beta * beta) / ((alpha - 2.0) * (alpha - 1.0) * (alpha - 1.0)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+\begin{latexonly}
+ $\mbox{Var}[X] = \frac{\alpha\beta^2}{(\alpha - 2)(\alpha - 1)}$
+\end{latexonly}
+ of the Pareto distribution with parameters $\alpha$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Pareto distribution
+ $\mbox{Var}[X] = \alpha\beta^2 / [(\alpha - 2)(\alpha - 1)]$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta)\begin{hide} {
+ return Math.sqrt (ParetoDist.getVariance (alpha, beta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the Pareto
+ distribution with parameters $\alpha$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Pareto distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+
+ this.alpha = alpha;
+ this.beta = beta;
+ supportA = beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $\alpha$ and $\beta$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\beta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/PascalDist.java b/source/umontreal/iro/lecuyer/probdist/PascalDist.java
new file mode 100644
index 0000000..bba2975
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PascalDist.java
@@ -0,0 +1,204 @@
+
+
+/*
+ * Class: PascalDist
+ * Description: Pascal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * The <SPAN CLASS="textit">Pascal</SPAN> distribution is a special case of the
+ * <SPAN CLASS="textit">negative binomial</SPAN> distribution with
+ * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is a positive
+ * integer and
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN>.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = nCr(<I>n</I> + <I>x</I> - 1, <I>x</I>)<I>p</I><SUP>n</SUP>(1 - <I>p</I>)<SUP>x</SUP>, for <I>x</I> = 0, 1, 2,…
+ * </DIV><P></P>
+ * where nCr is defined in {@link BinomialDist}.
+ * This <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN> can be interpreted as the probability of having <SPAN CLASS="MATH"><I>x</I></SPAN> failures
+ * before the <SPAN CLASS="MATH"><I>n</I></SPAN>th success in a sequence of independent Bernoulli trials
+ * with probability of success <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ * For <SPAN CLASS="MATH"><I>n</I> = 1</SPAN>, this gives the <SPAN CLASS="textit">geometric</SPAN> distribution.
+ *
+ */
+public class PascalDist extends NegativeBinomialDist {
+ private static final double EPSI = 1.0E-10;
+
+ private static class Function implements MathFunction {
+ protected int m;
+ protected int max;
+ protected double mean;
+ protected int[] Fj;
+
+ public Function (int m, int max, double mean, int[] Fj) {
+ this.m = m;
+ this.max = max;
+ this.mean = mean;
+ this.Fj = new int[Fj.length];
+ System.arraycopy(Fj, 0, this.Fj, 0, Fj.length);
+ }
+
+ public double evaluate (double p) {
+ double sum = 0.0;
+ double s = (p * mean) / (1.0 - p);
+
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (s + (double) j);
+
+ return sum + m * Math.log (p);
+ }
+
+ public double evaluateN (int n, double p) {
+ double sum = 0.0;
+
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (n + j);
+
+ return sum + m * Math.log (p);
+ }
+ }
+
+
+
+ /**
+ * Creates an object that contains the probability
+ * terms and the distribution function for
+ * the Pascal distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>.
+ *
+ */
+ public PascalDist (int n, double p) {
+ setParams (n, p);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH">(<I>n</I>, <I>p</I>)</SPAN> of the Pascal distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>n</I></SPAN>, <SPAN CLASS="MATH"><I>p</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(n)</SPAN>, <SPAN CLASS="MATH">hat(p)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int m) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ double sum = 0.0;
+ int max = Integer.MIN_VALUE;
+ for (int i = 0; i < m; i++)
+ {
+ sum += x[i];
+ if (x[i] > max)
+ max = x[i];
+ }
+ double mean = (double) sum / (double) m;
+
+ double var = 0.0;
+ for (int i = 0; i < m; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) m;
+
+ if (mean >= var)
+ throw new UnsupportedOperationException("mean >= variance");
+
+ int[] Fj = new int[max];
+ for (int j = 0; j < max; j++) {
+ int prop = 0;
+ for (int i = 0; i < m; i++)
+ if (x[i] > j)
+ prop++;
+
+ Fj[j] = prop;
+ }
+
+ double[] parameters = new double[2];
+ Function f = new Function (m, max, mean, Fj);
+
+ parameters[1] = RootFinder.brentDekker (EPSI, 1 - EPSI, f, 1e-5);
+ if (parameters[1] >= 1.0)
+ parameters[1] = 1.0 - 1e-15;
+
+ parameters[0] = Math.round ((parameters[1] * mean) / (1.0 - parameters[1]));
+ if (parameters[0] == 0)
+ parameters[0] = 1;
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a Pascal distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>p</I></SPAN> estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>m</I></SPAN>
+ * observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static PascalDist getInstanceFromMLE (int[] x, int m) {
+ double parameters[] = getMLE (x, m);
+ return new PascalDist ((int) parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN1() {
+ return (int) (n + 0.5);
+ }
+
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ public void setParams (int n, double p) {
+ super.setParams ((double) n, p);
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + getN1() + ", p = " + getP();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/PascalDist.tex b/source/umontreal/iro/lecuyer/probdist/PascalDist.tex
new file mode 100644
index 0000000..c0b3cd3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PascalDist.tex
@@ -0,0 +1,234 @@
+\defmodule {PascalDist}
+
+The \emph{Pascal} distribution is a special case of the
+\emph{negative binomial\/} distribution
+\cite[page 324]{sLAW00a} with
+parameters $n$ and $p$, where $n$ is a positive
+integer and $0\le p\le 1$.
+Its mass function is
+\begin{htmlonly}
+\eq
+ p(x) = \mbox{nCr}(n + x - 1, x) p^n (1 - p)^{x},
+ \qquad\mbox{for } x = 0, 1, 2, \ldots
+\endeq
+where nCr is defined in \class{BinomialDist}.
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ p(x) = \binom{n + x - 1}{x} p^n (1 - p)^{x},
+ \qquad\mbox{for } x = 0, 1, 2, \ldots \eqlabel{eq:fmass-pascal}
+\endeq
+\end{latexonly}
+This $p(x)$ can be interpreted as the probability of having $x$ failures
+before the $n$th success in a sequence of independent Bernoulli trials
+with probability of success $p$.
+For $n=1$, this gives the \emph{geometric} distribution.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: PascalDist
+ * Description: Pascal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class PascalDist extends NegativeBinomialDist\begin{hide} {
+ private static final double EPSI = 1.0E-10;
+
+ private static class Function implements MathFunction {
+ protected int m;
+ protected int max;
+ protected double mean;
+ protected int[] Fj;
+
+ public Function (int m, int max, double mean, int[] Fj) {
+ this.m = m;
+ this.max = max;
+ this.mean = mean;
+ this.Fj = new int[Fj.length];
+ System.arraycopy(Fj, 0, this.Fj, 0, Fj.length);
+ }
+
+ public double evaluate (double p) {
+ double sum = 0.0;
+ double s = (p * mean) / (1.0 - p);
+
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (s + (double) j);
+
+ return sum + m * Math.log (p);
+ }
+
+ public double evaluateN (int n, double p) {
+ double sum = 0.0;
+
+ for (int j = 0; j < max; j++)
+ sum += Fj[j] / (n + j);
+
+ return sum + m * Math.log (p);
+ }
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public PascalDist (int n, double p)\begin{hide} {
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates an object that contains the probability
+ terms (\ref{eq:fmass-pascal}) and the distribution function for
+ the Pascal distribution with parameter $n$ and $p$.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int m)\begin{hide} {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ double sum = 0.0;
+ int max = Integer.MIN_VALUE;
+ for (int i = 0; i < m; i++)
+ {
+ sum += x[i];
+ if (x[i] > max)
+ max = x[i];
+ }
+ double mean = (double) sum / (double) m;
+
+ double var = 0.0;
+ for (int i = 0; i < m; i++)
+ var += (x[i] - mean) * (x[i] - mean);
+ var /= (double) m;
+
+ if (mean >= var)
+ throw new UnsupportedOperationException("mean >= variance");
+
+ int[] Fj = new int[max];
+ for (int j = 0; j < max; j++) {
+ int prop = 0;
+ for (int i = 0; i < m; i++)
+ if (x[i] > j)
+ prop++;
+
+ Fj[j] = prop;
+ }
+
+ double[] parameters = new double[2];
+ Function f = new Function (m, max, mean, Fj);
+
+ parameters[1] = RootFinder.brentDekker (EPSI, 1 - EPSI, f, 1e-5);
+ if (parameters[1] >= 1.0)
+ parameters[1] = 1.0 - 1e-15;
+
+ parameters[0] = Math.round ((parameters[1] * mean) / (1.0 - parameters[1]));
+ if (parameters[0] == 0)
+ parameters[0] = 1;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $(n, p)$ of the Pascal distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1, \ldots, m-1$. The estimates are returned in a two-element
+ array, in regular order: [$n$, $p$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values $(\hat{n}$, $\hat{p})$
+ that satisfy the equations
+ \begin{eqnarray*}
+ \frac{\hat{n}(1 - \hat{p})}{\hat{p}} & = & \bar{x}_m\\
+ \ln (1 + \hat{p}) & = & \sum_{j=1}^{\infty} \frac{F_j}{(\hat{n} + j - 1)}
+ \end{eqnarray*}
+ where $\bar x_m$ is the average of $x[0],\dots,x[m-1]$, and
+ $F_j = \sum_{i=j}^{\infty} f_i$ = proportion of $x$'s which are
+ greater than or equal to $j$
+ \cite[page 132]{tJOH69a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{n}$, $\hat{p}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static PascalDist getInstanceFromMLE (int[] x, int m)\begin{hide} {
+ double parameters[] = getMLE (x, m);
+ return new PascalDist ((int) parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Pascal distribution with parameters $n$ and
+ $p$ estimated using the maximum likelihood method based on the $m$
+ observations $x[i]$, $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public int getN1()\begin{hide} {
+ return (int) (n + 0.5);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParams (int n, double p)\begin{hide} {
+ super.setParams ((double) n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameter $n$ and $p$ of this object.
+\end{tabb}
+
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + getN1() + ", p = " + getP();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/Pearson5Dist.java b/source/umontreal/iro/lecuyer/probdist/Pearson5Dist.java
new file mode 100644
index 0000000..08196eb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/Pearson5Dist.java
@@ -0,0 +1,316 @@
+
+
+/*
+ * Class: Pearson5Dist
+ * Description: Pearson type V distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+
+ at Deprecated
+/**
+ * <SPAN CLASS="textbf">THIS CLASS HAS BEEN RENAMED {@link InverseGammaDist}</SPAN>.
+ *
+ * <P>
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Pearson type V</EM> distribution with shape parameter
+ *
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>β</I><SUP><I>α</I></SUP>exp<SUP>-<I>β</I>/x</SUP>)/(<I>x</I><SUP><I>α</I>+1</SUP><I>Γ</I>(<I>α</I>)) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> otherwise,
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function.
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - <I>F</I><SUB>G</SUB>(1/<I>x</I>), for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = 0</SPAN> otherwise, where <SPAN CLASS="MATH"><I>F</I><SUB>G</SUB>(<I>x</I>)</SPAN> is the distribution function
+ * of a gamma
+ * distribution with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+public class Pearson5Dist extends ContinuousDistribution {
+ protected double alpha;
+ protected double beta;
+ protected double logam; // Ln (Gamma(alpha))
+
+
+
+ /**
+ * <SPAN CLASS="textbf">THIS CLASS HAS BEEN RENAMED {@link InverseGammaDist}</SPAN>.
+ * Constructs a <TT>Pearson5Dist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>.
+ *
+ */
+ public Pearson5Dist (double alpha, double beta) {
+ setParam (alpha, beta);
+ }
+
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - logam) / x;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean () {
+ return getMean (alpha, beta);
+ }
+
+ public double getVariance () {
+ return getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation () {
+ return getStandardDeviation (alpha, beta);
+ }
+
+ /**
+ * Computes the density function of a Pearson V distribution with shape
+ * parameter <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double density (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - Num.lnGamma (alpha)) / x;
+ }
+
+
+ /**
+ * Computes the density function of a Pearson V distribution with shape
+ * parameter <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double cdf (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return GammaDist.barF (alpha, beta, 15, 1.0 / x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function of a Pearson V distribution
+ * with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double barF (double alpha, double beta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+
+ return GammaDist.cdf (alpha, beta, 15, 1.0 / x);
+ }
+
+
+ /**
+ * Computes the inverse distribution function of a Pearson V distribution
+ * with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double inverseF (double alpha, double beta, double u) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return 1.0 / GammaDist.inverseF (alpha, beta, 15, 1 - u);
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>β</I>)</SPAN> of the Pearson V distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α), hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ double[] y = new double[n];
+
+ for (int i = 0; i < n; i++) {
+ if(x[i] > 0)
+ y[i] = 1.0 / x[i];
+ else
+ y[i] = 1.0E100;
+ }
+
+ return GammaDist.getMLE (y, n);
+ }
+
+
+ /**
+ * Creates a new instance of a Pearson V distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>β</I></SPAN> estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static Pearson5Dist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new Pearson5Dist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>β</I>/(<I>α</I> - 1)</SPAN> of a Pearson V
+ * distribution with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getMean (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return (beta / (alpha - 1.0));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>β</I><SUP>2</SUP>/((<I>α</I> -1)<SUP>2</SUP>(<I>α</I> - 2)</SPAN>
+ * of a Pearson V distribution with shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale
+ * parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getVariance (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return ((beta * beta) / ((alpha - 1.0) * (alpha - 1.0) * (alpha - 2.0)));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of a Pearson V distribution with
+ * shape parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getStandardDeviation (double alpha, double beta) {
+ return Math.sqrt (getVariance (alpha, beta));
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>α</I></SPAN> parameter of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>β</I></SPAN> parameter of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public void setParam (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ supportA = 0.0;
+ this.alpha = alpha;
+ this.beta = beta;
+ logam = Num.lnGamma (alpha);
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/Pearson5Dist.tex b/source/umontreal/iro/lecuyer/probdist/Pearson5Dist.tex
new file mode 100644
index 0000000..936d254
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/Pearson5Dist.tex
@@ -0,0 +1,344 @@
+\defmodule{Pearson5Dist}
+
+\textbf{THIS CLASS HAS BEEN RENAMED \class{InverseGammaDist}}.
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Pearson type V\/} distribution with shape parameter
+$\alpha > 0$ and scale parameter $\beta > 0$.
+The density function is given by
+\begin{htmlonly}
+\eq
+ f(x) = (\beta^{\alpha}\exp^{-\beta / x}) / (x^{\alpha+1} \Gamma(\alpha))
+ \qquad \mbox{for } x > 0,
+\endeq
+ and $f(x) = 0$ otherwise,
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \left\{\begin{array}{ll} \displaystyle
+ \frac{\beta^{\alpha}e^{-\beta / x}}{x^{\alpha+ 1} \Gamma(\alpha)}
+ & \quad \mbox{for } x > 0 \\[12pt]
+ 0 & \quad \mbox{otherwise,}
+ \end{array} \right.
+ \eqlabel{eq:fpearson5}
+\endeq
+\end{latexonly}
+where $\Gamma$ is the gamma function.
+The distribution function is given by
+\begin{htmlonly}
+\eq
+ F(x) = 1 - F_{G}(1 / x),
+ \quad \mbox{for } x > 0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ F(x) = 1 - F_{G}\left(\frac{1}{x}\right)
+ \qquad \mbox{for } x > 0,
+ \eqlabel{eq:Fpearson5}
+\endeq
+\end{latexonly}
+ and $F(x) = 0$ otherwise, where $F_{G}(x)$ is the distribution function
+of a gamma
+distribution with shape parameter $\alpha$ and scale parameter $\beta$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Pearson5Dist
+ * Description: Pearson type V distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+ at Deprecated
+public class Pearson5Dist extends ContinuousDistribution\begin{hide} {
+ protected double alpha;
+ protected double beta;
+ protected double logam; // Ln (Gamma(alpha))
+
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public Pearson5Dist (double alpha, double beta)\begin{hide} {
+ setParam (alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+\textbf{THIS CLASS HAS BEEN RENAMED \class{InverseGammaDist}}.
+ Constructs a \texttt{Pearson5Dist} object with parameters
+ $\alpha$ = \texttt{alpha} and $\beta$ = \texttt{beta}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - logam) / x;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, beta, u);
+ }
+
+ public double getMean () {
+ return getMean (alpha, beta);
+ }
+
+ public double getVariance () {
+ return getVariance (alpha, beta);
+ }
+
+ public double getStandardDeviation () {
+ return getStandardDeviation (alpha, beta);
+ }\end{hide}
+
+ public static double density (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp (alpha * Math.log (beta/x) - (beta / x) - Num.lnGamma (alpha)) / x;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the density function of a Pearson V distribution with shape
+parameter $\alpha$
+ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return GammaDist.barF (alpha, beta, 15, 1.0 / x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the density function of a Pearson V distribution with shape
+parameter $\alpha$
+ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double beta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+
+ return GammaDist.cdf (alpha, beta, 15, 1.0 / x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function of a Pearson V distribution
+ with shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double beta, double u)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return 1.0 / GammaDist.inverseF (alpha, beta, 15, 1 - u);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse distribution function of a Pearson V distribution
+ with shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ double[] y = new double[n];
+
+ for (int i = 0; i < n; i++) {
+ if(x[i] > 0)
+ y[i] = 1.0 / x[i];
+ else
+ y[i] = 1.0E100;
+ }
+
+ return GammaDist.getMLE (y, n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha,\beta)$ of the Pearson V distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\beta$].
+ \begin{detailed}
+ The equations of the maximum likelihood are the same as the equations of
+ the gamma distribution, with the sample $y_i = 1/x_i$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha}, \hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static Pearson5Dist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new Pearson5Dist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Pearson V distribution with parameters $\alpha$
+ and $\beta$ estimated using the maximum likelihood method based on the $n$
+ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return (beta / (alpha - 1.0));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the mean $E[X] = \beta / (\alpha - 1)$ of a Pearson V
+ distribution with shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double getVariance (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return ((beta * beta) / ((alpha - 1.0) * (alpha - 1.0) * (alpha - 2.0)));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the variance $\mbox{Var}[X] = \beta^2 / ((\alpha -
+1)^2(\alpha - 2)$
+ of a Pearson V distribution with shape parameter $\alpha$ and scale
+parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double beta)\begin{hide} {
+ return Math.sqrt (getVariance (alpha, beta));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the standard deviation of a Pearson V distribution with
+ shape parameter $\alpha$ and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\alpha$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\beta$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParam (double alpha, double beta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ supportA = 0.0;
+ this.alpha = alpha;
+ this.beta = beta;
+ logam = Num.lnGamma (alpha);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\alpha$ and $\beta$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\beta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", beta = " + beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/Pearson6Dist.java b/source/umontreal/iro/lecuyer/probdist/Pearson6Dist.java
new file mode 100644
index 0000000..14a0a21
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/Pearson6Dist.java
@@ -0,0 +1,425 @@
+
+
+/*
+ * Class: Pearson6Dist
+ * Description: Pearson type VI distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import optimization.*;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>Pearson type VI</EM> distribution with shape parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB> > 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB> > 0</SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>x</I>/<I>β</I>)<SUP><I>α</I><SUB>1</SUB>-1</SUP>/(<I>βB</I>(<I>α</I><SUB>1</SUB>, <I>α</I><SUB>2</SUB>)[1 + <I>x</I>/<I>β</I>]<SUP><I>α</I><SUB>1</SUB>+<I>α</I><SUB>2</SUB></SUP>) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> otherwise,
+ * where <SPAN CLASS="MATH"><I>B</I></SPAN> is the beta function.
+ * The distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>F</I><SUB>B</SUB>(<I>x</I>/(<I>x</I> + <I>β</I>)) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = 0</SPAN> otherwise, where <SPAN CLASS="MATH"><I>F</I><SUB>B</SUB>(<I>x</I>)</SPAN> is the distribution function
+ * of a beta distribution with shape
+ * parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>.
+ *
+ */
+public class Pearson6Dist extends ContinuousDistribution {
+ protected double alpha1;
+ protected double alpha2;
+ protected double beta;
+ protected double logBeta; // Ln (Beta (alpha1, alpha2))
+
+ private static class Optim implements Uncmin_methods {
+ private int n;
+ private double[] x;
+
+ public Optim (double[] x, int n) {
+ this.n = n;
+ this.x = new double[n];
+ System.arraycopy (x, 0, this.x, 0, n);
+ }
+
+ public double f_to_minimize (double[] param) {
+
+ if ((param[1] <= 0.0) || (param[2] <= 0.0) || (param[3] <= 0.0))
+ return 1e200;
+
+ double sumLogY = 0.0;
+ double sumLog1_Y = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ if (x[i] > 0.0)
+ sumLogY += Math.log (x[i] / param[3]);
+ else
+ sumLogY -= 709.0;
+ sumLog1_Y += Math.log1p (x[i] / param[3]);
+ }
+
+ return (n * (Math.log (param[3]) + Num.lnBeta (param[1], param[2])) -
+ (param[1] - 1.0) * sumLogY + (param[1] + param[2]) * sumLog1_Y);
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+
+
+ /**
+ * Constructs a <TT>Pearson6Dist</TT> object with parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> =
+ * <TT>alpha1</TT>, <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> = <TT>alpha2</TT> and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>.
+ *
+ */
+ public Pearson6Dist (double alpha1, double alpha2, double beta) {
+ setParam (alpha1, alpha2, beta);
+ }
+
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp ((alpha1 - 1.0) * Math.log (x / beta) - (logBeta +
+ (alpha1 + alpha2) * Math.log1p (x / beta))) / beta;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha1, alpha2, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha1, alpha2, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha1, alpha2, beta, u);
+ }
+
+ public double getMean () {
+ return getMean (alpha1, alpha2, beta);
+ }
+
+ public double getVariance () {
+ return getVariance (alpha1, alpha2, beta);
+ }
+
+ public double getStandardDeviation () {
+ return getStandardDeviation (alpha1, alpha2, beta);
+ }
+
+ /**
+ * Computes the density function of a Pearson VI distribution with shape
+ * parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>
+ * and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double density (double alpha1, double alpha2,
+ double beta, double x) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp ((alpha1 - 1.0) * Math.log (x / beta) -
+ (Num.lnBeta (alpha1, alpha2) + (alpha1 + alpha2) * Math.log1p (x / beta))) / beta;
+ }
+
+
+ /**
+ * Computes the distribution function of a Pearson VI distribution with
+ * shape parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>
+ * and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double cdf (double alpha1, double alpha2,
+ double beta, double x) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return BetaDist.cdf (alpha1, alpha2, x / (x + beta));
+ }
+
+
+ /**
+ * Computes the complementary distribution function of a Pearson VI distribution
+ * with shape parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double barF (double alpha1, double alpha2,
+ double beta, double x) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+
+ return BetaDist.barF (alpha1, alpha2, x / (x + beta));
+ }
+
+
+ /**
+ * Computes the inverse distribution function of a Pearson VI distribution
+ * with shape parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double inverseF (double alpha1, double alpha2,
+ double beta, double u) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ double y = BetaDist.inverseF (alpha1, alpha2, u);
+
+ return ((y * beta) / (1.0 - y));
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I><SUB>1</SUB>, <I>α</I><SUB>2</SUB>, <I>β</I>)</SPAN> of the Pearson VI distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a three-element
+ * array, in regular order: [
+ * <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB>, <I>α</I><SUB>2</SUB></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(α_1), hat(α_2), hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double[] parameters = new double[3];
+ double[] xpls = new double[4];
+ double[] param = new double[4];
+ double[] fpls = new double[4];
+ double[] gpls = new double[4];
+ int[] itrcmd = new int[2];
+ double[][] h = new double[4][4];
+ double[] udiag = new double[4];
+
+ Optim system = new Optim (x, n);
+
+ double mean = 0.0;
+ double mean2 = 0.0;
+ double mean3 = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ mean += x[i];
+ mean2 += x[i] * x[i];
+ mean3 += x[i] * x[i] * x[i];
+ }
+ mean /= (double) n;
+ mean2 /= (double) n;
+ mean3 /= (double) n;
+
+ double r1 = mean2 / (mean * mean);
+ double r2 = mean2 * mean / mean3;
+
+ param[1] = - (2.0 * (-1.0 + r1 * r2)) / (-2.0 + r1 + r1 * r2);
+ if(param[1] <= 0) param[1] = 1;
+ param[2] = (- 3.0 - r2 + 4.0 * r1 * r2) / (- 1.0 - r2 + 2.0 * r1 * r2);
+ if(param[2] <= 0) param[2] = 1;
+ param[3] = (param[2] - 1.0) * mean / param[1];
+ if(param[3] <= 0) param[3] = 1;
+
+ Uncmin_f77.optif0_f77 (3, param, system, xpls, fpls, gpls, itrcmd, h, udiag);
+
+ for (int i = 0; i < 3; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a Pearson VI distribution with parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>,
+ * <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>, estimated using the maximum likelihood method based on
+ * the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static Pearson6Dist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new Pearson6Dist (parameters[0], parameters[1], parameters[2]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (<I>βα</I><SUB>1</SUB>)/(<I>α</I><SUB>2</SUB> - 1)</SPAN> of a
+ * Pearson VI distribution with shape parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, and
+ * scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getMean (double alpha1, double alpha2,
+ double beta) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 1.0)
+ throw new IllegalArgumentException("alpha2 <= 1");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return ((beta * alpha1) / (alpha2 - 1.0));
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = [<I>β</I><SUP>2</SUP><I>α</I><SUB>1</SUB>(<I>α</I><SUB>1</SUB> + <I>α</I><SUB>2</SUB> -1)]/[(<I>α</I><SUB>2</SUB> -1)<SUP>2</SUP>(<I>α</I><SUB>2</SUB> - 2)]</SPAN> of a Pearson VI distribution with shape
+ * parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getVariance (double alpha1, double alpha2,
+ double beta) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 2");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return (((beta * beta) * alpha1 * (alpha1 + alpha2 - 1.0)) /
+((alpha2 - 1.0) * (alpha2 - 1.0) * (alpha2 - 2.0)));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of a Pearson VI
+ * distribution with shape
+ * parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public static double getStandardDeviation (double alpha1, double alpha2,
+ double beta) {
+ return Math.sqrt (getVariance (alpha1, alpha2, beta));
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> parameter of this object.
+ *
+ */
+ public double getAlpha1() {
+ return alpha1;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> parameter of this object.
+ *
+ */
+ public double getAlpha2() {
+ return alpha2;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>β</I></SPAN> parameter of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public void setParam (double alpha1, double alpha2, double beta) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ supportA = 0.0;
+ this.alpha1 = alpha1;
+ this.alpha2 = alpha2;
+ this.beta = beta;
+ logBeta = Num.lnBeta (alpha1, alpha2);
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha1, alpha2, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha1 = " + alpha1 + ", alpha2 = " + alpha2 + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/Pearson6Dist.tex b/source/umontreal/iro/lecuyer/probdist/Pearson6Dist.tex
new file mode 100644
index 0000000..473dde2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/Pearson6Dist.tex
@@ -0,0 +1,455 @@
+\defmodule{Pearson6Dist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em Pearson type VI\/} distribution with shape parameters
+$\alpha_1 > 0$ and $\alpha_2 > 0$, and scale parameter $\beta > 0$.
+The density function is given by
+\begin{htmlonly}
+\eq
+ f(x) = (x / \beta)^{\alpha_{1} - 1} / (\beta \mathcal{B}(\alpha_{1},
+\alpha_{2})[1 + x/\beta]^{\alpha_{1} + \alpha_{2}})
+ \qquad \mbox{for } x > 0,
+\endeq
+ and $f(x) = 0$ otherwise,
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) =\left\{\begin{array}{ll} \displaystyle
+ \frac{\left({x}/{\beta}\right)^{\alpha_{1} - 1}}{\beta
+\mathcal{B}(\alpha_{1}, \alpha_{2})(1 + {x}/{\beta})^{\alpha_{1} +
+\alpha_{2}}}
+ & \quad \mbox{for } x > 0, \\[14pt]
+ 0 & \quad \mbox{otherwise,}
+ \end{array} \right.
+ \eqlabel{eq:fpearson6}
+\endeq
+\end{latexonly}
+where $\mathcal{B}$ is the beta function.
+The distribution function is given by
+\begin{htmlonly}
+\eq
+ F(x) = F_{B}\left(x / (x + \beta)\right)
+ \qquad \mbox{for } x > 0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ F(x) = F_{B}\left(\frac{x}{x + \beta}\right)
+ \qquad \mbox{for } x > 0,
+ \eqlabel{eq:Fpearson6}
+\endeq
+\end{latexonly}
+ and $F(x) = 0$ otherwise, where $F_{B}(x)$ is the distribution function
+of a beta distribution with shape
+parameters $\alpha_1$ and $\alpha_2$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Pearson6Dist
+ * Description: Pearson type VI distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import optimization.*;
+\end{hide}
+
+public class Pearson6Dist extends ContinuousDistribution\begin{hide} {
+ protected double alpha1;
+ protected double alpha2;
+ protected double beta;
+ protected double logBeta; // Ln (Beta (alpha1, alpha2))
+
+ private static class Optim implements Uncmin_methods {
+ private int n;
+ private double[] x;
+
+ public Optim (double[] x, int n) {
+ this.n = n;
+ this.x = new double[n];
+ System.arraycopy (x, 0, this.x, 0, n);
+ }
+
+ public double f_to_minimize (double[] param) {
+
+ if ((param[1] <= 0.0) || (param[2] <= 0.0) || (param[3] <= 0.0))
+ return 1e200;
+
+ double sumLogY = 0.0;
+ double sumLog1_Y = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ if (x[i] > 0.0)
+ sumLogY += Math.log (x[i] / param[3]);
+ else
+ sumLogY -= 709.0;
+ sumLog1_Y += Math.log1p (x[i] / param[3]);
+ }
+
+ return (n * (Math.log (param[3]) + Num.lnBeta (param[1], param[2])) -
+ (param[1] - 1.0) * sumLogY + (param[1] + param[2]) * sumLog1_Y);
+ }
+
+ public void gradient (double[] x, double[] g)
+ {
+ }
+
+ public void hessian (double[] x, double[][] h)
+ {
+ }
+ }
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public Pearson6Dist (double alpha1, double alpha2, double beta)\begin{hide} {
+ setParam (alpha1, alpha2, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{Pearson6Dist} object with parameters $\alpha_1$ =
+ \texttt{alpha1}, $\alpha_2$ = \texttt{alpha2} and $\beta$ = \texttt{beta}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ if (x <= 0.0)
+ return 0.0;
+ return Math.exp ((alpha1 - 1.0) * Math.log (x / beta) - (logBeta +
+ (alpha1 + alpha2) * Math.log1p (x / beta))) / beta;
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha1, alpha2, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha1, alpha2, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha1, alpha2, beta, u);
+ }
+
+ public double getMean () {
+ return getMean (alpha1, alpha2, beta);
+ }
+
+ public double getVariance () {
+ return getVariance (alpha1, alpha2, beta);
+ }
+
+ public double getStandardDeviation () {
+ return getStandardDeviation (alpha1, alpha2, beta);
+ }\end{hide}
+
+ public static double density (double alpha1, double alpha2,
+ double beta, double x)\begin{hide} {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return Math.exp ((alpha1 - 1.0) * Math.log (x / beta) -
+ (Num.lnBeta (alpha1, alpha2) + (alpha1 + alpha2) * Math.log1p (x / beta))) / beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the density function of a Pearson VI distribution with shape
+parameters $\alpha_1$
+ and $\alpha_2$, and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha1, double alpha2,
+ double beta, double x)\begin{hide} {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 0.0;
+
+ return BetaDist.cdf (alpha1, alpha2, x / (x + beta));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the distribution function of a Pearson VI distribution with
+shape parameters $\alpha_1$
+ and $\alpha_2$, and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double alpha1, double alpha2,
+ double beta, double x)\begin{hide} {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ if (x <= 0.0)
+ return 1.0;
+
+ return BetaDist.barF (alpha1, alpha2, x / (x + beta));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function of a Pearson VI distribution
+ with shape parameters $\alpha_1$ and $\alpha_2$, and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha1, double alpha2,
+ double beta, double u)\begin{hide} {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ double y = BetaDist.inverseF (alpha1, alpha2, u);
+
+ return ((y * beta) / (1.0 - y));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the inverse distribution function of a Pearson VI distribution
+ with shape parameters $\alpha_1$ and $\alpha_2$, and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double[] parameters = new double[3];
+ double[] xpls = new double[4];
+ double[] param = new double[4];
+ double[] fpls = new double[4];
+ double[] gpls = new double[4];
+ int[] itrcmd = new int[2];
+ double[][] h = new double[4][4];
+ double[] udiag = new double[4];
+
+ Optim system = new Optim (x, n);
+
+ double mean = 0.0;
+ double mean2 = 0.0;
+ double mean3 = 0.0;
+ for (int i = 0; i < n; i++)
+ {
+ mean += x[i];
+ mean2 += x[i] * x[i];
+ mean3 += x[i] * x[i] * x[i];
+ }
+ mean /= (double) n;
+ mean2 /= (double) n;
+ mean3 /= (double) n;
+
+ double r1 = mean2 / (mean * mean);
+ double r2 = mean2 * mean / mean3;
+
+ param[1] = - (2.0 * (-1.0 + r1 * r2)) / (-2.0 + r1 + r1 * r2);
+ if(param[1] <= 0) param[1] = 1;
+ param[2] = (- 3.0 - r2 + 4.0 * r1 * r2) / (- 1.0 - r2 + 2.0 * r1 * r2);
+ if(param[2] <= 0) param[2] = 1;
+ param[3] = (param[2] - 1.0) * mean / param[1];
+ if(param[3] <= 0) param[3] = 1;
+
+ Uncmin_f77.optif0_f77 (3, param, system, xpls, fpls, gpls, itrcmd, h, udiag);
+
+ for (int i = 0; i < 3; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha_1,\alpha_2,\beta)$ of the Pearson VI distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1,\ldots, n-1$. The estimates are returned in a three-element
+ array, in regular order: [$\alpha_1, \alpha_2$, $\beta$].
+ \begin{detailed}
+ The estimate of the parameters is given by maximizing numerically the
+ log-likelihood function, using the Uncmin package \cite{iSCHa,iVERa}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameters [$\hat{\alpha_1}, \hat{\alpha_2}, \hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static Pearson6Dist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new Pearson6Dist (parameters[0], parameters[1], parameters[2]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Pearson VI distribution with parameters $\alpha_1$,
+ $\alpha_2$ and $\beta$, estimated using the maximum likelihood method based on
+ the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha1, double alpha2,
+ double beta)\begin{hide} {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 1.0)
+ throw new IllegalArgumentException("alpha2 <= 1");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return ((beta * alpha1) / (alpha2 - 1.0));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the mean $E[X] = (\beta \alpha_1) / (\alpha_2 - 1)$ of a
+ Pearson VI distribution with shape parameters $\alpha_1$ and $\alpha_2$, and
+ scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double getVariance (double alpha1, double alpha2,
+ double beta)\begin{hide} {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 2");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+
+ return (((beta * beta) * alpha1 * (alpha1 + alpha2 - 1.0)) /
+((alpha2 - 1.0) * (alpha2 - 1.0) * (alpha2 - 2.0)));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the variance
+ $\mbox{Var}[X] = [\beta^2 \alpha_1 (\alpha_1 + \alpha_2 - 1)] /
+ [(\alpha_2 - 1)^2(\alpha_2 - 2)]$ of a Pearson VI distribution with shape
+ parameters $\alpha_1$ and $\alpha_2$, and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha1, double alpha2,
+ double beta)\begin{hide} {
+ return Math.sqrt (getVariance (alpha1, alpha2, beta));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the standard deviation of a Pearson VI
+distribution with shape
+ parameters $\alpha_1$ and $\alpha_2$, and scale parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha1()\begin{hide} {
+ return alpha1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\alpha_1$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha2()\begin{hide} {
+ return alpha2;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\alpha_2$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\beta$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParam (double alpha1, double alpha2, double beta)\begin{hide} {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ supportA = 0.0;
+ this.alpha1 = alpha1;
+ this.alpha2 = alpha2;
+ this.beta = beta;
+ logBeta = Num.lnBeta (alpha1, alpha2);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\alpha_1$, $\alpha_2$ and $\beta$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha1, alpha2, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha_1$, $\alpha_2$, $\beta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha1 = " + alpha1 + ", alpha2 = " + alpha2 + ", beta = " + beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/PiecewiseLinearEmpiricalDist.java b/source/umontreal/iro/lecuyer/probdist/PiecewiseLinearEmpiricalDist.java
new file mode 100644
index 0000000..b591b7d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PiecewiseLinearEmpiricalDist.java
@@ -0,0 +1,308 @@
+
+
+/*
+ * Class: PiecewiseLinearEmpiricalDist
+ * Description: piecewise-linear empirical distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.util.Formatter;
+import java.util.Locale;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.util.Arrays;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.BufferedReader;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for a piecewise-linear
+ * approximation of the <SPAN CLASS="textit">empirical</SPAN> distribution function,
+ * based on the observations
+ * <SPAN CLASS="MATH"><I>X</I><SUB>(1)</SUB>,..., <I>X</I><SUB>(n)</SUB></SPAN> (sorted by increasing order),
+ * and defined as follows (e.g.,).
+ * The distribution function starts at <SPAN CLASS="MATH"><I>X</I><SUB>(1)</SUB></SPAN> and climbs linearly by <SPAN CLASS="MATH">1/(<I>n</I> - 1)</SPAN>
+ * between any two successive observations. The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 1/[(<I>n</I> - 1)(<I>X</I><SUB>(i+1)</SUB> - <I>X</I><SUB>(i)</SUB>)] for <I>X</I><SUB>(i)</SUB> <= <I>x</I> < <I>X</I><SUB>(i+1)</SUB> and <I>i</I> = 1, 2,..., <I>n</I> - 1.
+ * </DIV><P></P>
+ * The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">0</TD>
+ * <TD ALIGN="LEFT"> for <I>x</I> < <I>X</I><SUB>(1)</SUB>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">(<I>i</I> - 1)/(<I>n</I> - 1) + (<I>x</I> - <I>X</I><SUB>(i)</SUB>)/[(<I>n</I> - 1)(<I>X</I><SUB>(i+1)</SUB> - <I>X</I><SUB>(i)</SUB>)]</TD>
+ * <TD ALIGN="LEFT"> for <I>X</I><SUB>(i)</SUB> <= <I>x</I> < <I>X</I><SUB>(i+1)</SUB>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * <TD ALIGN="LEFT"> elsewhere,</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ * whose inverse is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>X</I><SUB>(i)</SUB> + ((<I>n</I> - 1)<I>u</I> - <I>i</I> + 1)(<I>X</I><SUB>(i+1)</SUB> - <I>X</I><SUB>(i)</SUB>)
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH">(<I>i</I> - 1)/(<I>n</I> - 1) <= <I>u</I> <= <I>i</I>/(<I>n</I> - 1)</SPAN> and
+ * <SPAN CLASS="MATH"><I>i</I> = 1,..., <I>n</I> - 1</SPAN>.
+ *
+ */
+public class PiecewiseLinearEmpiricalDist extends ContinuousDistribution {
+ private double[] sortedObs;
+ private double[] diffObs;
+ private int n = 0;
+ private double sampleMean;
+ private double sampleVariance;
+ private double sampleStandardDeviation;
+
+
+ /**
+ * Constructs a new piecewise-linear distribution using
+ * all the observations stored in <TT>obs</TT>.
+ * These observations are copied into an internal array and then sorted.
+ *
+ */
+ public PiecewiseLinearEmpiricalDist (double[] obs) {
+ if (obs.length <= 1)
+ throw new IllegalArgumentException ("Two or more observations are needed");
+ // sortedObs = obs;
+ n = obs.length;
+ sortedObs = new double[n];
+ System.arraycopy (obs, 0, sortedObs, 0, n);
+ init();
+ }
+
+
+ /**
+ * Constructs a new empirical distribution using
+ * the observations read from the reader <TT>in</TT>. This constructor
+ * will read the first <TT>double</TT> of each line in the stream.
+ * Any line that does not start with a <TT>+</TT>, <TT>-</TT>, or a decimal digit,
+ * is ignored. The file is read until its end.
+ * One must be careful about lines starting with a blank.
+ * This format is the same as in UNURAN.
+ *
+ */
+ public PiecewiseLinearEmpiricalDist (Reader in) throws IOException {
+ BufferedReader inb = new BufferedReader (in);
+ double[] data = new double[5];
+ String li;
+ while ((li = inb.readLine()) != null) {
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Double.parseDouble (li);
+ if (n >= data.length) {
+ double[] newData = new double[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {}
+ }
+ }
+ sortedObs = new double[n];
+ System.arraycopy (data, 0, sortedObs, 0, n);
+ init();
+ }
+
+
+ public double density (double x) {
+ // This is implemented via a linear search: very inefficient!!!
+ if (x < sortedObs[0] || x >= sortedObs[n-1])
+ return 0;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedObs[i] && x < sortedObs[i+1])
+ return 1.0 / ((n-1)*diffObs[i]);
+ }
+ throw new IllegalStateException();
+ }
+
+ public double cdf (double x) {
+ // This is implemented via a linear search: very inefficient!!!
+ if (x <= sortedObs[0])
+ return 0;
+ if (x >= sortedObs[n-1])
+ return 1;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedObs[i] && x < sortedObs[i+1])
+ return i/(n-1.0) + (x - sortedObs[i])/((n-1.0)*diffObs[i]);
+ }
+ throw new IllegalStateException();
+ }
+
+ public double barF (double x) {
+ // This is implemented via a linear search: very inefficient!!!
+ if (x <= sortedObs[0])
+ return 1;
+ if (x >= sortedObs[n-1])
+ return 0;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedObs[i] && x < sortedObs[i+1])
+ return (n-1.0-i)/(n-1.0) - (x - sortedObs[i])/((n-1.0)*diffObs[i]);
+ }
+ throw new IllegalStateException();
+ }
+
+ public double inverseF (double u) {
+ if (u < 0 || u > 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return sortedObs[0];
+ if (u >= 1.0)
+ return sortedObs[n-1];
+ double p = (n - 1)*u;
+ int i = (int)Math.floor (p);
+ if (i == (n-1))
+ return sortedObs[n-1];
+ else
+ return sortedObs[i] + (p - i)*diffObs[i];
+ }
+
+ public double getMean() {
+ return sampleMean;
+ }
+
+ public double getVariance() {
+ return sampleVariance;
+ }
+
+ public double getStandardDeviation() {
+ return sampleStandardDeviation;
+ }
+
+ private void init() {
+ Arrays.sort (sortedObs);
+ // n = sortedObs.length;
+ diffObs = new double[sortedObs.length];
+ double sum = 0.0;
+ for (int i = 0; i < diffObs.length-1; i++) {
+ diffObs[i] = sortedObs[i+1] - sortedObs[i];
+ sum += sortedObs[i];
+ }
+ diffObs[n-1] = 0.0; // Can be useful in case i=n-1 in inverseF.
+ sum += sortedObs[n-1];
+ sampleMean = sum / n;
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ double coeff = (sortedObs[i] - sampleMean);
+ sum += coeff*coeff;
+ }
+ sampleVariance = sum / (n-1);
+ sampleStandardDeviation = Math.sqrt (sampleVariance);
+ supportA = sortedObs[0]*(1.0 - Num.DBL_EPSILON);
+ supportB = sortedObs[n-1]*(1.0 + Num.DBL_EPSILON);
+ }
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>n</I></SPAN>, the number of observations.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>X</I><SUB>(i)</SUB></SPAN>.
+ *
+ */
+ public double getObs (int i) {
+ return sortedObs[i];
+ }
+
+
+ /**
+ * Returns the sample mean of the observations.
+ *
+ */
+ public double getSampleMean() {
+ return sampleMean;
+ }
+
+
+ /**
+ * Returns the sample variance of the observations.
+ *
+ */
+ public double getSampleVariance() {
+ return sampleVariance;
+ }
+
+
+ /**
+ * Returns the sample standard deviation of the observations.
+ *
+ */
+ public double getSampleStandardDeviation() {
+ return sampleStandardDeviation;
+ }
+
+
+ /**
+ * Return a table containing parameters of the current distribution.
+ *
+ */
+ public double[] getParams () {
+ double[] retour = new double[n];
+ System.arraycopy (sortedObs, 0, retour, 0, n);
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString () {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + PrintfFormat.NEWLINE);
+ for(int i = 0; i<n; i++) {
+ formatter.format("%f%n", sortedObs[i]);
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/PiecewiseLinearEmpiricalDist.tex b/source/umontreal/iro/lecuyer/probdist/PiecewiseLinearEmpiricalDist.tex
new file mode 100644
index 0000000..c66d591
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PiecewiseLinearEmpiricalDist.tex
@@ -0,0 +1,306 @@
+\defmodule{PiecewiseLinearEmpiricalDist}
+
+Extends the class \class{ContinuousDistribution} for a piecewise-linear
+approximation of the \emph{empirical} distribution function,
+based on the observations $X_{(1)},\dots,X_{(n)}$ (sorted by increasing order),
+and defined as follows (e.g., \cite[page 318]{sLAW00a}).
+The distribution function starts at $X_{(1)}$ and climbs linearly by $1/(n-1)$
+between any two successive observations. The density is
+\eq
+ f(x) = \html{1/[(n-1)(X_{(i+1)} - X_{(i)})]}\latex{\frac1{(n-1)(X_{(i+1)} - X_{(i)})}}
+ \mbox{ for }X_{(i)}\le x < X_{(i+1)}\mbox{ and } i=1,2,\dots,n-1.
+\endeq
+The distribution function is
+\begin{htmlonly}
+\[\begin{array}{rll}
+ F(x) =& 0 &\qquad\mbox { for } x < X_{(1)}, \\
+ F(x) =& (i-1)/(n-1) + (x - X_{(i)})/[(n-1)(X_{(i+1)} - X_{(i)})] &\qquad\mbox { for }
+ X_{(i)} \le x < X_{(i+1)}, \\
+ F(x) =& 1 &\qquad\mbox { elsewhere,}
+\end{array}\]
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ F(x) = \left\{\begin{array}{ll}
+ 0 & \mbox { for } x < X_{(1)}, \\[8pt]
+\displaystyle \frac{i-1}{n-1} + \frac{x - X_{(i)}}{(n-1)(X_{(i+1)} - X_{(i)})}&\mbox { for }
+ X_{(i)} \le x < X_{(i+1)} \mbox{ and } i<n, \\[15pt]
+ 1 & \mbox { for } x \ge X_{(n)},
+ \end{array}\right.
+\endeq
+\end{latexonly}%
+whose inverse is
+\eq
+ F^{-1}(u) = X_{(i)} + ((n-1)u - i + 1)(X_{(i+1)} - X_{(i)})
+\endeq
+for $(i-1)/(n-1)\le u \le i/(n-1)$ and $i=1,\dots,n-1$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PiecewiseLinearEmpiricalDist
+ * Description: piecewise-linear empirical distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+
+import java.util.Formatter;
+import java.util.Locale;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.util.Arrays;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.BufferedReader;
+\end{hide}
+
+public class PiecewiseLinearEmpiricalDist extends ContinuousDistribution\begin{hide} {
+ private double[] sortedObs;
+ private double[] diffObs;
+ private int n = 0;
+ private double sampleMean;
+ private double sampleVariance;
+ private double sampleStandardDeviation;
+\end{hide}
+
+ public PiecewiseLinearEmpiricalDist (double[] obs)\begin{hide} {
+ if (obs.length <= 1)
+ throw new IllegalArgumentException ("Two or more observations are needed");
+ // sortedObs = obs;
+ n = obs.length;
+ sortedObs = new double[n];
+ System.arraycopy (obs, 0, sortedObs, 0, n);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new piecewise-linear distribution using
+ all the observations stored in \texttt{obs}.
+ These observations are copied into an internal array and then sorted.
+% The given array \texttt{obs} will be modified by this method and should
+% not be modified by the caller afterwards.
+% \hpierre{Where is it modified? I don't think it is.}
+% The given array \texttt{obs} will be used directly by the methods of this
+% class (instead of making a copy), so it should
+% not be modified after calling this constructor.
+\end{tabb}
+\begin{code}
+
+ public PiecewiseLinearEmpiricalDist (Reader in) throws IOException\begin{hide} {
+ BufferedReader inb = new BufferedReader (in);
+ double[] data = new double[5];
+ String li;
+ while ((li = inb.readLine()) != null) {
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Double.parseDouble (li);
+ if (n >= data.length) {
+ double[] newData = new double[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {}
+ }
+ }
+ sortedObs = new double[n];
+ System.arraycopy (data, 0, sortedObs, 0, n);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new empirical distribution using
+ the observations read from the reader \texttt{in}. This constructor
+ will read the first \texttt{double} of each line in the stream.
+ Any line that does not start with a \texttt{+}, \texttt{-}, or a decimal digit,
+ is ignored. The file is read until its end.
+ One must be careful about lines starting with a blank.
+ This format is the same as in UNURAN.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ // This is implemented via a linear search: very inefficient!!!
+ if (x < sortedObs[0] || x >= sortedObs[n-1])
+ return 0;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedObs[i] && x < sortedObs[i+1])
+ return 1.0 / ((n-1)*diffObs[i]);
+ }
+ throw new IllegalStateException();
+ }
+
+ public double cdf (double x) {
+ // This is implemented via a linear search: very inefficient!!!
+ if (x <= sortedObs[0])
+ return 0;
+ if (x >= sortedObs[n-1])
+ return 1;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedObs[i] && x < sortedObs[i+1])
+ return i/(n-1.0) + (x - sortedObs[i])/((n-1.0)*diffObs[i]);
+ }
+ throw new IllegalStateException();
+ }
+
+ public double barF (double x) {
+ // This is implemented via a linear search: very inefficient!!!
+ if (x <= sortedObs[0])
+ return 1;
+ if (x >= sortedObs[n-1])
+ return 0;
+ for (int i = 0; i < (n-1); i++) {
+ if (x >= sortedObs[i] && x < sortedObs[i+1])
+ return (n-1.0-i)/(n-1.0) - (x - sortedObs[i])/((n-1.0)*diffObs[i]);
+ }
+ throw new IllegalStateException();
+ }
+
+ public double inverseF (double u) {
+ if (u < 0 || u > 1)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return sortedObs[0];
+ if (u >= 1.0)
+ return sortedObs[n-1];
+ double p = (n - 1)*u;
+ int i = (int)Math.floor (p);
+ if (i == (n-1))
+ return sortedObs[n-1];
+ else
+ return sortedObs[i] + (p - i)*diffObs[i];
+ }
+
+ public double getMean() {
+ return sampleMean;
+ }
+
+ public double getVariance() {
+ return sampleVariance;
+ }
+
+ public double getStandardDeviation() {
+ return sampleStandardDeviation;
+ }
+
+ private void init() {
+ Arrays.sort (sortedObs);
+ // n = sortedObs.length;
+ diffObs = new double[sortedObs.length];
+ double sum = 0.0;
+ for (int i = 0; i < diffObs.length-1; i++) {
+ diffObs[i] = sortedObs[i+1] - sortedObs[i];
+ sum += sortedObs[i];
+ }
+ diffObs[n-1] = 0.0; // Can be useful in case i=n-1 in inverseF.
+ sum += sortedObs[n-1];
+ sampleMean = sum / n;
+ sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ double coeff = (sortedObs[i] - sampleMean);
+ sum += coeff*coeff;
+ }
+ sampleVariance = sum / (n-1);
+ sampleStandardDeviation = Math.sqrt (sampleVariance);
+ supportA = sortedObs[0]*(1.0 - Num.DBL_EPSILON);
+ supportB = sortedObs[n-1]*(1.0 + Num.DBL_EPSILON);
+ }\end{hide}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $n$, the number of observations.
+\end{tabb}
+\begin{code}
+
+ public double getObs (int i)\begin{hide} {
+ return sortedObs[i];
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $X_{(i)}$.
+\end{tabb}
+\begin{code}
+
+ public double getSampleMean()\begin{hide} {
+ return sampleMean;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the sample mean of the observations.
+\end{tabb}
+\begin{code}
+
+ public double getSampleVariance()\begin{hide} {
+ return sampleVariance;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the sample variance of the observations.
+\end{tabb}
+\begin{code}
+
+ public double getSampleStandardDeviation()\begin{hide} {
+ return sampleStandardDeviation;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the sample standard deviation of the observations.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = new double[n];
+ System.arraycopy (sortedObs, 0, retour, 0, n);
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing parameters of the current distribution.
+\end{tabb}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ formatter.format(getClass().getSimpleName() + PrintfFormat.NEWLINE);
+ for(int i = 0; i<n; i++) {
+ formatter.format("%f%n", sortedObs[i]);
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/probdist/PoissonDist.java b/source/umontreal/iro/lecuyer/probdist/PoissonDist.java
new file mode 100644
index 0000000..29a0a68
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PoissonDist.java
@@ -0,0 +1,632 @@
+
+
+/*
+ * Class: PoissonDist
+ * Description: Poisson distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.util.*;
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for the
+ * <EM>Poisson</EM> distribution with mean
+ * <SPAN CLASS="MATH"><I>λ</I> >= 0</SPAN>.
+ * The mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = <I>e</I><SUP>-<I>λ</I></SUP><I>λ</I><SUP>x</SUP>/(<I>x</I>!), for <I>x</I> = 0, 1,...
+ * </DIV><P></P>
+ * and the distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = <I>e</I><SUP>-<I>λ</I></SUP>∑<SUB>j=0</SUB><SUP>x</SUP> <I>λ</I><SUP>j</SUP>/(<I>j</I>!), for <I>x</I> = 0, 1,....
+ * </DIV><P></P>
+ * If one has to compute <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN> and/or <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN> for several values of <SPAN CLASS="MATH"><I>x</I></SPAN>
+ * with the same <SPAN CLASS="MATH"><I>λ</I></SPAN>, where <SPAN CLASS="MATH"><I>λ</I></SPAN> is not too large, then it is more
+ * efficient to instantiate an object and use the non-static methods, since
+ * the functions will then be computed once and kept in arrays.
+ *
+ * <P>
+ * For the static methods that compute <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN> and
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>,
+ * we exploit the relationship
+ *
+ * <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = 1 - <I>G</I><SUB>x+1</SUB>(<I>λ</I>)</SPAN>, where <SPAN CLASS="MATH"><I>G</I><SUB>x+1</SUB></SPAN> is the <SPAN CLASS="textit">gamma</SPAN>
+ * distribution function with parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>λ</I>) = (<I>x</I> + 1, 1)</SPAN>.
+ *
+ */
+public class PoissonDist extends DiscreteDistributionInt {
+
+ private double lambda;
+
+
+
+ public static double MAXLAMBDA = 100000;
+
+
+ /**
+ * Creates an object that contains
+ * the probability and distribution functions, for the Poisson
+ * distribution with parameter <TT>lambda</TT>, which are
+ * computed and stored in dynamic arrays inside that object.
+ *
+ */
+ public PoissonDist (double lambda) {
+ setLambda (lambda);
+ }
+
+
+ public double prob (int x) {
+ if (x < 0)
+ return 0.0;
+ if (pdf == null)
+ return prob (lambda, x);
+ if (x > xmax || x < xmin)
+ return prob (lambda, x);
+ return pdf[x - xmin];
+ }
+
+ public double cdf (int x) {
+ double Sum = 0.0;
+ int j;
+
+ if (x < 0)
+ return 0.0;
+ if (lambda == 0.0)
+ return 1.0;
+
+ /* For large lambda, we use the Chi2 distribution according to the exact
+ relation, with 2x + 2 degrees of freedom
+
+ cdf (lambda, x) = 1 - chiSquare (2x + 2, 2*lambda)
+
+ which equals also 1 - gamma (x + 1, lambda) */
+ if (cdf == null)
+ return GammaDist.barF (x + 1.0, 15, lambda);
+
+ if (x >= xmax)
+ return 1.0;
+
+ if (x < xmin) {
+ // Sum a few terms to get a few decimals far in the lower tail. One
+ // could also call GammaDist.barF instead.
+ final int RMAX = 20;
+ int i;
+ double term = prob(lambda, x);
+ Sum = term;
+ i = x;
+ while (i > 0 && i >= x - RMAX) {
+ term = term * i / lambda;
+ i--;
+ Sum += term;
+ }
+ return Sum;
+ }
+
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+ }
+
+
+ public double barF (int x) {
+ /*
+ * poisson (lambda, x) = 1 - cdf (lambda, x - 1)
+ */
+
+ if (x <= 0)
+ return 1.0;
+
+ /* For large lambda, we use the Chi2 distribution according to the exact
+ relation, with 2x + 2 degrees of freedom
+
+ cdf (lambda, x) = 1 - ChiSquare.cdf (2x + 2, 2*lambda)
+ cdf (lambda, x) = 1 - GammaDist.cdf (x + 1, lambda)
+ */
+
+ if (cdf == null)
+ return GammaDist.cdf ((double)x, 15, lambda);
+
+ if (x > xmax)
+// return GammaDist.cdf ((double)x, 15, lambda);
+ return PoissonDist.barF(lambda, x);
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ }
+
+
+ public int inverseFInt (double u) {
+ if ((cdf == null) || (u <= EPSILON))
+ return inverseF (lambda, u);
+ return super.inverseFInt (u);
+ }
+
+ public double getMean() {
+ return PoissonDist.getMean (lambda);
+ }
+
+ public double getVariance() {
+ return PoissonDist.getVariance (lambda);
+ }
+
+ public double getStandardDeviation() {
+ return PoissonDist.getStandardDeviation (lambda);
+ }
+
+ /**
+ * Computes and returns the Poisson probability
+ * <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN> for <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>..
+ *
+ */
+ public static double prob (double lambda, int x) {
+ if (x < 0)
+ return 0.0;
+
+ if (lambda >= 100.0) {
+ if ((double) x >= 10.0*lambda)
+ return 0.0;
+ } else if (lambda >= 3.0) {
+ if ((double) x >= 100.0*lambda)
+ return 0.0;
+ } else {
+ if ((double) x >= 200.0*Math.max(1.0, lambda))
+ return 0.0;
+ }
+
+ final double LAMBDALIM = 20.0;
+ double Res;
+ if (lambda < LAMBDALIM && x <= 100)
+ Res = Math.exp (-lambda)*Math.pow (lambda, x)/Num.factorial (x);
+ else {
+ double y = x*Math.log (lambda) - Num.lnGamma (x + 1.0) - lambda;
+ Res = Math.exp (y);
+ }
+ return Res;
+ }
+
+
+ /**
+ * Computes and returns the value of the Poisson
+ * distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>)</SPAN> for <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>.
+ *
+ */
+ public static double cdf (double lambda, int x) {
+ /*
+ * On our machine, computing a value using gamma is faster than the
+ * naive computation for lambdalim > 200.0, slower for lambdalim < 200.0
+ */
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (lambda == 0.0)
+ return 1.0;
+ if (x < 0)
+ return 0.0;
+
+ if (lambda >= 100.0) {
+ if ((double) x >= 10.0*lambda)
+ return 1.0;
+ } else {
+ if ((double) x >= 100.0*Math.max(1.0, lambda))
+ return 1.0;
+ }
+
+ /* If lambda > LAMBDALIM, use the Chi2 distribution according to the
+ exact relation, with 2x + 2 degrees of freedom
+
+ poisson (lambda, x) = 1 - chiSquare (2x + 2, 2*lambda)
+
+ which also equals 1 - gamma (x + 1, lambda) */
+
+ final double LAMBDALIM = 200.0;
+ if (lambda > LAMBDALIM)
+ return GammaDist.barF (x + 1.0, 15, lambda);
+
+ if (x >= lambda)
+ return 1 - PoissonDist.barF(lambda, x+1);
+
+ // Naive computation: sum all prob. from i = x
+ double sum = 1;
+ double term = 1;
+ for(int j = 1; j <= x; j++) {
+ term *= lambda/j;
+ sum += term;
+ }
+ return sum*Math.exp(-lambda);
+ }
+
+
+ /**
+ * Computes and returns the value of the complementary Poisson
+ * distribution function, for <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>.
+ * <SPAN CLASS="textit">WARNING:</SPAN> The complementary distribution function is defined as
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>.
+ *
+ */
+ public static double barF (double lambda, int x) {
+ if (lambda < 0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0)
+ return 1.0;
+
+ if (lambda >= 100.0) {
+ if ((double) x >= 10.0*lambda)
+ return 0.0;
+ } else {
+ if ((double) x >= 100 + 100.0*Math.max(1.0, lambda))
+ return 0.0;
+ }
+
+ /* If lambda > LAMBDALIM, we use the Chi2 distribution according to the
+ exact relation, with 2x + 2 degrees of freedom
+
+ cdf (lambda, x) = 1 - ChiSquare.cdf (2x + 2, 2*lambda)
+
+ which also equals 1 - GammaDist.cdf (x + 1, lambda) */
+
+ final double LAMBDALIM = 200.0;
+ if (lambda > LAMBDALIM)
+ return GammaDist.cdf ((double)x, 15, lambda);
+
+ if (x <= lambda)
+ return 1.0 - PoissonDist.cdf(lambda, x - 1);
+
+ // Naive computation: sum all prob. from i = x to i = oo
+ double term, sum;
+ final int IMAX = 20;
+
+ // Sum at least IMAX prob. terms from i = s to i = oo
+ sum = term = PoissonDist.prob(lambda, x);
+ int i = x + 1;
+ while (term > EPSILON || i <= x + IMAX) {
+ term *= lambda/i;
+ sum += term;
+ i++;
+ }
+ return sum;
+ }
+
+
+ /**
+ * Performs a linear search to get the inverse function without
+ * precomputed tables.
+ *
+ */
+ public static int inverseF (double lambda, double u) {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in range [0,1]");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (u >= 1.0)
+ return Integer.MAX_VALUE;
+ if (u <= prob (lambda, 0))
+ return 0;
+ int i;
+
+ final double LAMBDALIM = 700.0;
+ if (lambda < LAMBDALIM) {
+ double sumprev = -1.0;
+ double term = Math.exp(-lambda);
+ double sum = term;
+ i = 0;
+ while (sum < u && sum > sumprev) {
+ i++;
+ term *= lambda / i;
+ sumprev = sum;
+ sum += term;
+ }
+ return i;
+
+ } else {
+ i = (int)lambda;
+ double term = PoissonDist.prob(lambda, i);
+ while ((term >= u) && (term > Double.MIN_NORMAL)) {
+ i /= 2;
+ term = PoissonDist.prob (lambda, i);
+ }
+ if (term <= Double.MIN_NORMAL) {
+ i *= 2;
+ term = PoissonDist.prob (lambda, i);
+ while (term >= u && (term > Double.MIN_NORMAL)) {
+ term *= i / lambda;
+ i--;
+ }
+ }
+ int mid = i;
+ double sum = term;
+ double termid = term;
+
+ while (term >= EPSILON*u && i > 0) {
+ term *= i / lambda;
+ sum += term;
+ i--;
+ }
+
+ term = termid;
+ i = mid;
+ double prev = -1;
+ if (sum < u) {
+ while ((sum < u) && (sum > prev)) {
+ i++;
+ term *= lambda / i;
+ prev = sum;
+ sum += term;
+ }
+ } else {
+ // The computed CDF is too big so we substract from it.
+ sum -= term;
+ while (sum >= u) {
+ term *= i / lambda;
+ i--;
+ sum -= term;
+ }
+ }
+ }
+ return i;
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of the Poisson distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ * The maximum likelihood estimator
+ * <SPAN CLASS="MATH">hat(λ)</SPAN> satisfy the equation
+ *
+ * <SPAN CLASS="MATH">hat(λ) = bar(x)<SUB>n</SUB></SPAN>,
+ * where <SPAN CLASS="MATH">bar(x)<SUB>n</SUB></SPAN> is the average of
+ * <SPAN CLASS="MATH"><I>x</I>[0],…, <I>x</I>[<I>n</I> - 1]</SPAN>
+ * (see).
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[1];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+
+ parameters[0] = (double) sum / (double) n;
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a Poisson distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static PoissonDist getInstanceFromMLE (int[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new PoissonDist (parameters[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>λ</I></SPAN> of the
+ * Poisson distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the mean of the Poisson distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>λ</I></SPAN>
+ *
+ */
+ public static double getMean (double lambda) {
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+
+ return lambda;
+ }
+
+
+ /**
+ * Computes and returns the variance <SPAN CLASS="MATH">= <I>λ</I></SPAN>
+ * of the Poisson distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the variance of the Poisson distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>λ</I></SPAN>
+ *
+ */
+ public static double getVariance (double lambda) {
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+
+ return lambda;
+ }
+
+
+ /**
+ * Computes and returns the standard deviation of the
+ * Poisson distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ * @return the standard deviation of the Poisson distribution
+ *
+ */
+ public static double getStandardDeviation (double lambda) {
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+
+ return Math.sqrt (lambda);
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>λ</I></SPAN> associated with this object.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+
+ /**
+ * Sets the <SPAN CLASS="MATH"><I>λ</I></SPAN> associated with this object.
+ *
+ */
+ public void setLambda (double lambda) {
+ supportA = 0;
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ this.lambda = lambda;
+
+ // For lambda > MAXLAMBDAPOISSON, we do not use pre-computed arrays
+ if (lambda > MAXLAMBDA) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ double epsilon;
+ int i, mid, Nmax;
+ int imin, imax;
+ double sum;
+ double[] P; // Poisson probability terms
+ double[] F; // Poisson cumulative probabilities
+
+ // In theory, the Poisson distribution has an infinite range. But
+ // for i > Nmax, probabilities should be extremely small.
+ Nmax = (int)(lambda + 16*(2 + Math.sqrt (lambda)));
+ P = new double[1 + Nmax];
+
+ mid = (int)lambda;
+ epsilon = EPSILON * EPS_EXTRA/prob (lambda, mid);
+ // For large lambda, mass will lose a few digits of precision
+ // We shall normalize by explicitly summing all terms >= epsilon
+ sum = P[mid] = 1.0;
+
+ // Start from the maximum and compute terms > epsilon on each side.
+ i = mid;
+ while (i > 0 && P[i] > epsilon) {
+ P[i - 1] = P[i]*i/lambda;
+ i--;
+ sum += P[i];
+ }
+ xmin = imin = i;
+
+ i = mid;
+ while (P[i] > epsilon) {
+ P[i + 1] = P[i]*lambda/(i + 1);
+ i++;
+ sum += P[i];
+ if (i >= Nmax - 1) {
+ Nmax *= 2;
+ double[] nT = new double[1 + Nmax];
+ System.arraycopy (P, 0, nT, 0, P.length);
+ P = nT;
+ }
+ }
+ xmax = imax = i;
+ F = new double[1 + Nmax];
+
+ // Renormalize the sum of probabilities to 1
+ for (i = imin; i <= imax; i++)
+ P[i] /= sum;
+
+ // Compute the cumulative probabilities until F >= 0.5, and keep them in
+ // the lower part of array, i.e. F[s] contains all P[i] for i <= s
+ F[imin] = P[imin];
+ i = imin;
+ while (i < imax && F[i] < 0.5) {
+ i++;
+ F[i] = P[i] + F[i - 1];
+ }
+ // This is the boundary between F and 1 - F in the CDF
+ xmed = i;
+
+ // Compute the cumulative probabilities of the complementary distribution
+ // and keep them in the upper part of the array. i.e. F[s] contains all
+ // P[i] for i >= s
+ F[imax] = P[imax];
+ i = imax - 1;
+ do {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ } while (i > xmed);
+
+ /* Reset imin because we lose too much precision for a few terms near
+ imin when we stop adding terms < epsilon. */
+ i = imin;
+ while (i < xmed && F[i] < EPSILON)
+ i++;
+ xmin = imin = i;
+
+ /* Same thing with imax */
+ i = imax;
+ while (i > xmed && F[i] < EPSILON)
+ i--;
+ xmax = imax = i;
+
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax-imin+1);
+ System.arraycopy (F, imin, cdf, 0, imax-imin+1);
+ }
+
+
+ /**
+ * Return a table containing the parameter of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {lambda};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + ": lambda = " + lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/PoissonDist.tex b/source/umontreal/iro/lecuyer/probdist/PoissonDist.tex
new file mode 100644
index 0000000..98240bc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PoissonDist.tex
@@ -0,0 +1,695 @@
+\defmodule {PoissonDist}
+
+Extends the class \class{DiscreteDistributionInt} for the
+{\em Poisson\/} distribution
+\cite[page 325]{sLAW00a} with mean $\lambda\ge 0$.
+The mass function is
+\begin{htmlonly}
+\eq
+ p(x) = e^{-\lambda} \lambda^x/(x!),
+ \qquad\mbox{for } x=0,1,\dots
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\begin{eqnarray}
+ p(x) & =& \frac{e^{-\lambda} \lambda^x}{x!},
+ \qquad\mbox{for } x=0,1,\dots \label{eq:fmass-Poisson}
+\end{eqnarray}
+\end{latexonly}%
+and the distribution function is
+\begin{htmlonly}
+\eq
+ F(x) = e^{-\lambda} \sum_{j=0}^x\; \lambda^j/(j!),
+ \qquad\mbox{for } x=0,1,\dots .
+\endeq
+\end{htmlonly}
+\begin{latexonly}\begin{eqnarray}
+ F(x) & =& e^{-\lambda} \sum_{j=0}^x\; \frac{\lambda^j}{j!},
+ \qquad\mbox{for } x=0,1,\dots . \label{eq:FPoisson}
+\end{eqnarray}
+\end{latexonly}%
+% The static methods do not require creating an object but always recompute
+% everything from scratch.
+If one has to compute $p(x)$ and/or $F(x)$ for several values of $x$
+% call these functions several times
+with the same $\lambda$, where $\lambda$ is not too large, then it is more
+efficient to instantiate an object and use the non-static methods, since
+the functions will then be computed once and kept in arrays.
+
+For the static methods that compute $F(x)$ and $\bar{F}(x)$,
+we exploit the relationship
+$F(x) = 1 - G_{x+1}(\lambda)$, where $G_{x+1}$ is the \emph{gamma}
+distribution function with parameters $(\alpha,\lambda) = (x+1, 1)$.
+%
+\hpierre{We should explain a bit more how things are done;
+ e.g., use of the gamma function, etc.}
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: PoissonDist
+ * Description: Poisson distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.util.*;\end{hide}
+
+public class PoissonDist extends DiscreteDistributionInt\begin{hide} {
+
+ private double lambda;
+\end{hide}
+\end{code}
+\unmoved\begin{detailed}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constant}
+
+\begin{code}
+
+ public static double MAXLAMBDA = 100000;
+\end{code}
+ \begin{tabb} The value of the parameter $\lambda$ above which
+ the tables are {\em not\/} precomputed by the constructor.
+\end{tabb}
+\end{detailed}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public PoissonDist (double lambda)\begin{hide} {
+ setLambda (lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates an object that contains
+ the probability and distribution functions, for the Poisson
+ distribution with parameter \texttt{lambda}, which are
+ computed and stored in dynamic arrays inside that object.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double prob (int x) {
+ if (x < 0)
+ return 0.0;
+ if (pdf == null)
+ return prob (lambda, x);
+ if (x > xmax || x < xmin)
+ return prob (lambda, x);
+ return pdf[x - xmin];
+ }
+
+ public double cdf (int x) {
+ double Sum = 0.0;
+ int j;
+
+ if (x < 0)
+ return 0.0;
+ if (lambda == 0.0)
+ return 1.0;
+
+ /* For large lambda, we use the Chi2 distribution according to the exact
+ relation, with 2x + 2 degrees of freedom
+
+ cdf (lambda, x) = 1 - chiSquare (2x + 2, 2*lambda)
+
+ which equals also 1 - gamma (x + 1, lambda) */
+ if (cdf == null)
+ return GammaDist.barF (x + 1.0, 15, lambda);
+
+ if (x >= xmax)
+ return 1.0;
+
+ if (x < xmin) {
+ // Sum a few terms to get a few decimals far in the lower tail. One
+ // could also call GammaDist.barF instead.
+ final int RMAX = 20;
+ int i;
+ double term = prob(lambda, x);
+ Sum = term;
+ i = x;
+ while (i > 0 && i >= x - RMAX) {
+ term = term * i / lambda;
+ i--;
+ Sum += term;
+ }
+ return Sum;
+ }
+
+ if (x <= xmed)
+ return cdf[x - xmin];
+ else
+ // We keep the complementary distribution in the upper part of cdf
+ return 1.0 - cdf[x + 1 - xmin];
+ }
+
+
+ public double barF (int x) {
+ /*
+ * poisson (lambda, x) = 1 - cdf (lambda, x - 1)
+ */
+
+ if (x <= 0)
+ return 1.0;
+
+ /* For large lambda, we use the Chi2 distribution according to the exact
+ relation, with 2x + 2 degrees of freedom
+
+ cdf (lambda, x) = 1 - ChiSquare.cdf (2x + 2, 2*lambda)
+ cdf (lambda, x) = 1 - GammaDist.cdf (x + 1, lambda)
+ */
+
+ if (cdf == null)
+ return GammaDist.cdf ((double)x, 15, lambda);
+
+ if (x > xmax)
+// return GammaDist.cdf ((double)x, 15, lambda);
+ return PoissonDist.barF(lambda, x);
+ if (x <= xmin)
+ return 1.0;
+ if (x > xmed)
+ // We keep the complementary distribution in the upper part of cdf
+ return cdf[x - xmin];
+ else
+ return 1.0 - cdf[x - 1 - xmin];
+ }
+
+
+ public int inverseFInt (double u) {
+ if ((cdf == null) || (u <= EPSILON))
+ return inverseF (lambda, u);
+ return super.inverseFInt (u);
+ }
+
+ public double getMean() {
+ return PoissonDist.getMean (lambda);
+ }
+
+ public double getVariance() {
+ return PoissonDist.getVariance (lambda);
+ }
+
+ public double getStandardDeviation() {
+ return PoissonDist.getStandardDeviation (lambda);
+ }\end{hide}
+
+ public static double prob (double lambda, int x)\begin{hide} {
+ if (x < 0)
+ return 0.0;
+
+ if (lambda >= 100.0) {
+ if ((double) x >= 10.0*lambda)
+ return 0.0;
+ } else if (lambda >= 3.0) {
+ if ((double) x >= 100.0*lambda)
+ return 0.0;
+ } else {
+ if ((double) x >= 200.0*Math.max(1.0, lambda))
+ return 0.0;
+ }
+
+ final double LAMBDALIM = 20.0;
+ double Res;
+ if (lambda < LAMBDALIM && x <= 100)
+ Res = Math.exp (-lambda)*Math.pow (lambda, x)/Num.factorial (x);
+ else {
+ double y = x*Math.log (lambda) - Num.lnGamma (x + 1.0) - lambda;
+ Res = Math.exp (y);
+ }
+ return Res;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the Poisson probability
+ $p(x)$ for $\lambda = $ \texttt{lambda}\html{.}\latex{, as
+ defined in (\ref{eq:fmass-Poisson})}.
+\begin{detailed}
+ If $\lambda\ge 20$, this (static) method uses the logarithm of the gamma
+ function, defined in (\ref{eq:Gamma}), to estimate the density.
+\end{detailed}
+ \end{tabb}
+\begin{code}
+
+ public static double cdf (double lambda, int x)\begin{hide} {
+ /*
+ * On our machine, computing a value using gamma is faster than the
+ * naive computation for lambdalim > 200.0, slower for lambdalim < 200.0
+ */
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (lambda == 0.0)
+ return 1.0;
+ if (x < 0)
+ return 0.0;
+
+ if (lambda >= 100.0) {
+ if ((double) x >= 10.0*lambda)
+ return 1.0;
+ } else {
+ if ((double) x >= 100.0*Math.max(1.0, lambda))
+ return 1.0;
+ }
+
+ /* If lambda > LAMBDALIM, use the Chi2 distribution according to the
+ exact relation, with 2x + 2 degrees of freedom
+
+ poisson (lambda, x) = 1 - chiSquare (2x + 2, 2*lambda)
+
+ which also equals 1 - gamma (x + 1, lambda) */
+
+ final double LAMBDALIM = 200.0;
+ if (lambda > LAMBDALIM)
+ return GammaDist.barF (x + 1.0, 15, lambda);
+
+ if (x >= lambda)
+ return 1 - PoissonDist.barF(lambda, x+1);
+
+ // Naive computation: sum all prob. from i = x
+ double sum = 1;
+ double term = 1;
+ for(int j = 1; j <= x; j++) {
+ term *= lambda/j;
+ sum += term;
+ }
+ return sum*Math.exp(-lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the value of the Poisson
+ distribution function $F(x)$ for $\lambda = $ \texttt{lambda}\html{.}\latex{,
+as defined in (\ref{eq:FPoisson}).}
+\begin{detailed}
+ To compute $F(x)$, all non-negligible terms of the sum are added
+ if $\lambda \le 200$; otherwise, the relationship
+ $F_\lambda (x) = 1 - G_{x + 1}(\lambda)$ is used,
+ where $G_{x+1}$ is the gamma distribution function with parameter
+ $\alpha = x+1$ (see \class{GammaDist}).
+\end{detailed}
+ \hrichard{Cette documentation est ici, parce que ce paragraphe s'applique
+ seulement \`a la version \texttt{static}.}
+ \hpierre{Suggestions: lorsque $x>\lambda$, calculer plutot
+ les termes de $1-F (x)$ ? Et si $\lambda > 200$ mais $x$ est petit,
+ ne vaut-il pas mieux utiliser la somme? Peut-etre mettre la limite
+ sur $x$ a la place? }
+ \hrichard{ Si $\lambda$ est grand, on risque d'avoir d\'epassement de
+ capacit\'e avec le facteur $e^{-\lambda}$ et l'autre facteur aussi,
+ si on somme les termes
+ explicitement. D'ailleurs, avec grand $\lambda$, la probabilit\'e
+ d'avoir des petits $x$ est n\'egligeable. C'est pourquoi j'ai mis la
+ limite sur $\lambda$ et non sur $x$.\\ Pour $x>\lambda$, la queue
+ de Poisson \'etant plus allong\'ee vers le haut que vers le bas, il
+ n'est pas \'evident que ce serait plus efficace de calculer
+ $1-F (x)$, et ce serait beaucoup plus compliqu\'e.}
+ \hpierre{Et pourquoi prendre $10^5$ au lieu de 150 dans PoissonDist?
+ Si on a 5 a 10 appels a faire, il me semble qu'appeler ``gamma''
+ est beaucoup plus efficace que de calculer des tableaux de taille
+ $10^5$? }
+ \hrichard{Dans le cas o\`u $\lambda = 10^5$, on ne calcule pas des
+ tableaux de taille $10^5$, mais 2 tableaux de taille 4849. Les
+ autres termes sont $< 10^{-16}$ et ne sont ni calcul\'es ni
+ conserv\'es.}
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double lambda, int x)\begin{hide} {
+ if (lambda < 0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (x <= 0)
+ return 1.0;
+
+ if (lambda >= 100.0) {
+ if ((double) x >= 10.0*lambda)
+ return 0.0;
+ } else {
+ if ((double) x >= 100 + 100.0*Math.max(1.0, lambda))
+ return 0.0;
+ }
+
+ /* If lambda > LAMBDALIM, we use the Chi2 distribution according to the
+ exact relation, with 2x + 2 degrees of freedom
+
+ cdf (lambda, x) = 1 - ChiSquare.cdf (2x + 2, 2*lambda)
+
+ which also equals 1 - GammaDist.cdf (x + 1, lambda) */
+
+ final double LAMBDALIM = 200.0;
+ if (lambda > LAMBDALIM)
+ return GammaDist.cdf ((double)x, 15, lambda);
+
+ if (x <= lambda)
+ return 1.0 - PoissonDist.cdf(lambda, x - 1);
+
+ // Naive computation: sum all prob. from i = x to i = oo
+ double term, sum;
+ final int IMAX = 20;
+
+ // Sum at least IMAX prob. terms from i = s to i = oo
+ sum = term = PoissonDist.prob(lambda, x);
+ int i = x + 1;
+ while (term > EPSILON || i <= x + IMAX) {
+ term *= lambda/i;
+ sum += term;
+ i++;
+ }
+ return sum;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the value of the complementary Poisson
+ distribution function, for $\lambda = $ \texttt{lambda}.
+% Computes and adds the non-negligible terms in the tail.
+ \emph{WARNING:} The complementary distribution function is defined as
+ $\bar F(x) = P[X \ge x]$.
+ \end{tabb}
+\begin{code}
+
+ public static int inverseF (double lambda, double u)\begin{hide} {
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in range [0,1]");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ if (u >= 1.0)
+ return Integer.MAX_VALUE;
+ if (u <= prob (lambda, 0))
+ return 0;
+ int i;
+
+ final double LAMBDALIM = 700.0;
+ if (lambda < LAMBDALIM) {
+ double sumprev = -1.0;
+ double term = Math.exp(-lambda);
+ double sum = term;
+ i = 0;
+ while (sum < u && sum > sumprev) {
+ i++;
+ term *= lambda / i;
+ sumprev = sum;
+ sum += term;
+ }
+ return i;
+
+ } else {
+ i = (int)lambda;
+ double term = PoissonDist.prob(lambda, i);
+ while ((term >= u) && (term > Double.MIN_NORMAL)) {
+ i /= 2;
+ term = PoissonDist.prob (lambda, i);
+ }
+ if (term <= Double.MIN_NORMAL) {
+ i *= 2;
+ term = PoissonDist.prob (lambda, i);
+ while (term >= u && (term > Double.MIN_NORMAL)) {
+ term *= i / lambda;
+ i--;
+ }
+ }
+ int mid = i;
+ double sum = term;
+ double termid = term;
+
+ while (term >= EPSILON*u && i > 0) {
+ term *= i / lambda;
+ sum += term;
+ i--;
+ }
+
+ term = termid;
+ i = mid;
+ double prev = -1;
+ if (sum < u) {
+ while ((sum < u) && (sum > prev)) {
+ i++;
+ term *= lambda / i;
+ prev = sum;
+ sum += term;
+ }
+ } else {
+ // The computed CDF is too big so we substract from it.
+ sum -= term;
+ while (sum >= u) {
+ term *= i / lambda;
+ i--;
+ sum -= term;
+ }
+ }
+ }
+ return i;
+ }\end{hide}
+\end{code}
+\begin{tabb} Performs a linear search to get the inverse function without
+ precomputed tables.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[];
+ parameters = new double[1];
+ double sum = 0.0;
+ for (int i = 0; i < n; i++) {
+ sum += x[i];
+ }
+
+ parameters[0] = (double) sum / (double) n;
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $\lambda$ of the Poisson distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+% \begin{detailed}
+ The maximum likelihood estimator $\hat{\lambda}$ satisfy the equation
+ $\hat{\lambda} = \bar{x}_n$,
+ where $\bar{x}_n$ is the average of $x[0], \ldots, x[n-1]$
+ (see \cite[page 326]{sLAW00a}).
+% \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameter [$\hat{\lambda}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static PoissonDist getInstanceFromMLE (int[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new PoissonDist (parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Poisson distribution with parameter $\lambda$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double lambda)\begin{hide} {
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = \lambda$ of the
+ Poisson distribution with parameter $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Poisson distribution $E[X] = \lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double lambda)\begin{hide} {
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $= \lambda$
+ of the Poisson distribution with parameter $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Poisson distribution
+ $\mbox{Var}[X] = \lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double lambda)\begin{hide} {
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+
+ return Math.sqrt (lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation of the
+ Poisson distribution with parameter $\lambda$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Poisson distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\lambda$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public void setLambda (double lambda)\begin{hide} {
+ supportA = 0;
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ this.lambda = lambda;
+
+ // For lambda > MAXLAMBDAPOISSON, we do not use pre-computed arrays
+ if (lambda > MAXLAMBDA) {
+ pdf = null;
+ cdf = null;
+ return;
+ }
+
+ double epsilon;
+ int i, mid, Nmax;
+ int imin, imax;
+ double sum;
+ double[] P; // Poisson probability terms
+ double[] F; // Poisson cumulative probabilities
+
+ // In theory, the Poisson distribution has an infinite range. But
+ // for i > Nmax, probabilities should be extremely small.
+ Nmax = (int)(lambda + 16*(2 + Math.sqrt (lambda)));
+ P = new double[1 + Nmax];
+
+ mid = (int)lambda;
+ epsilon = EPSILON * EPS_EXTRA/prob (lambda, mid);
+ // For large lambda, mass will lose a few digits of precision
+ // We shall normalize by explicitly summing all terms >= epsilon
+ sum = P[mid] = 1.0;
+
+ // Start from the maximum and compute terms > epsilon on each side.
+ i = mid;
+ while (i > 0 && P[i] > epsilon) {
+ P[i - 1] = P[i]*i/lambda;
+ i--;
+ sum += P[i];
+ }
+ xmin = imin = i;
+
+ i = mid;
+ while (P[i] > epsilon) {
+ P[i + 1] = P[i]*lambda/(i + 1);
+ i++;
+ sum += P[i];
+ if (i >= Nmax - 1) {
+ Nmax *= 2;
+ double[] nT = new double[1 + Nmax];
+ System.arraycopy (P, 0, nT, 0, P.length);
+ P = nT;
+ }
+ }
+ xmax = imax = i;
+ F = new double[1 + Nmax];
+
+ // Renormalize the sum of probabilities to 1
+ for (i = imin; i <= imax; i++)
+ P[i] /= sum;
+
+ // Compute the cumulative probabilities until F >= 0.5, and keep them in
+ // the lower part of array, i.e. F[s] contains all P[i] for i <= s
+ F[imin] = P[imin];
+ i = imin;
+ while (i < imax && F[i] < 0.5) {
+ i++;
+ F[i] = P[i] + F[i - 1];
+ }
+ // This is the boundary between F and 1 - F in the CDF
+ xmed = i;
+
+ // Compute the cumulative probabilities of the complementary distribution
+ // and keep them in the upper part of the array. i.e. F[s] contains all
+ // P[i] for i >= s
+ F[imax] = P[imax];
+ i = imax - 1;
+ do {
+ F[i] = P[i] + F[i + 1];
+ i--;
+ } while (i > xmed);
+
+ /* Reset imin because we lose too much precision for a few terms near
+ imin when we stop adding terms < epsilon. */
+ i = imin;
+ while (i < xmed && F[i] < EPSILON)
+ i++;
+ xmin = imin = i;
+
+ /* Same thing with imax */
+ i = imax;
+ while (i > xmed && F[i] < EPSILON)
+ i--;
+ xmax = imax = i;
+
+ pdf = new double[imax + 1 - imin];
+ cdf = new double[imax + 1 - imin];
+ System.arraycopy (P, imin, pdf, 0, imax-imin+1);
+ System.arraycopy (F, imin, cdf, 0, imax-imin+1);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the $\lambda$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {lambda};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameter of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString () {
+ return getClass().getSimpleName() + ": lambda = " + lambda;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/PowerDist.java b/source/umontreal/iro/lecuyer/probdist/PowerDist.java
new file mode 100644
index 0000000..57f8ee2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PowerDist.java
@@ -0,0 +1,394 @@
+
+
+/*
+ * Class: PowerDist
+ * Description: power distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>power</EM> distribution with shape parameter
+ * <SPAN CLASS="MATH"><I>c</I> > 0</SPAN>, over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>, where <SPAN CLASS="MATH"><I>a</I> < <I>b</I></SPAN>.
+ * It has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>c</I>(<I>x</I> - <I>a</I>)<SUP>c-1</SUP>/(<I>b</I> - <I>a</I>)<SUP>c</SUP>
+ * </DIV><P></P>
+ * for <SPAN CLASS="MATH"><I>a</I> < <I>x</I> < <I>b</I></SPAN>, and 0 elsewhere. It has distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = (<I>x</I> - <I>a</I>)<SUP>c</SUP>/(<I>b</I> - <I>a</I>)<SUP>c</SUP> for <I>a</I> <= <I>x</I> <= <I>b</I>,
+ * </DIV><P></P>
+ * with <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = 0</SPAN> for <SPAN CLASS="MATH"><I>x</I> <= <I>a</I></SPAN> and <SPAN CLASS="MATH"><I>F</I>(<I>x</I>) = 1</SPAN> for <SPAN CLASS="MATH"><I>x</I> >= <I>b</I></SPAN>.
+ *
+ */
+public class PowerDist extends ContinuousDistribution {
+ private double a;
+ private double b;
+ private double c;
+
+
+
+
+ /**
+ * Constructs a <TT>PowerDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT>, <SPAN CLASS="MATH"><I>b</I> =</SPAN> <TT>b</TT> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>.
+ *
+ */
+ public PowerDist (double a, double b, double c) {
+ setParams (a, b, c);
+ }
+
+
+ /**
+ * Constructs a <TT>PowerDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>a</I> = 0</SPAN>, <SPAN CLASS="MATH"><I>b</I> =</SPAN> <TT>b</TT> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>.
+ *
+ */
+ public PowerDist (double b, double c) {
+ setParams (0.0, b, c);
+ }
+
+
+ /**
+ * Constructs a <TT>PowerDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>a</I> = 0</SPAN>, <SPAN CLASS="MATH"><I>b</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>.
+ *
+ */
+ public PowerDist (double c) {
+ setParams (0.0, 1.0, c);
+ }
+
+
+ public double density (double x) {
+ return density (a, b, c, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, b, c, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, b, c, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, b, c, u);
+ }
+
+ public double getMean() {
+ return PowerDist.getMean (a, b, c);
+ }
+
+ public double getVariance() {
+ return PowerDist.getVariance (a, b, c);
+ }
+
+ public double getStandardDeviation() {
+ return PowerDist.getStandardDeviation (a, b, c);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @param c shape parameter
+ *
+ * @param x the value at which the density is evaluated
+ *
+ * @return returns the density function
+ *
+ */
+ public static double density (double a, double b, double c, double x) {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+ if (x >= b)
+ return 0.0;
+ double z = (x-a)/(b-a);
+ return c*Math.pow(z, c-1.0) / (b-a);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @param c shape parameter
+ *
+ * @param x the value at which the distribution is evaluated
+ *
+ * @return returns the distribution function
+ *
+ */
+ public static double cdf (double a, double b, double c, double x) {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+ if (x >= b)
+ return 1.0;
+ return Math.pow((x-a)/(b-a), c);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @param c shape parameter
+ *
+ * @param x the value at which the complementary distribution is evaluated
+ *
+ * @return returns the complementary distribution function
+ *
+ */
+ public static double barF (double a, double b, double c, double x) {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 1.0;
+ if (x >= b)
+ return 0.0;
+ return 1.0 - Math.pow((x-a)/(b-a),c);
+ }
+
+
+ /**
+ * Computes the inverse of the distribution function.
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @param c shape parameter
+ *
+ * @param u the value at which the inverse distribution is evaluated
+ *
+ * @return returns the inverse of the distribution function
+ *
+ */
+ public static double inverseF (double a, double b, double c, double u) {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u == 0.0)
+ return a;
+ if (u == 1.0)
+ return b;
+
+ return a + (b-a) * Math.pow(u,1.0/c);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>c</I></SPAN> of the power distribution from the <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>, using the maximum
+ * likelihood method and assuming that <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> are known.
+ * The estimate is returned in a one-element array: [<SPAN CLASS="MATH"><I>c</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @return returns the shape parameter [<SPAN CLASS="MATH">hat(c)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n, double a, double b) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double d = b - a;
+ double somme = 0;
+ for (int i = 0 ; i < n ; ++i) somme += Math.log((x[i] - a)/d);
+
+ double [] parametres = new double [1];
+ parametres[0] = -1.0 / (somme/n);
+ return parametres;
+ }
+
+
+ /**
+ * Creates a new instance of a power distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN>,
+ * with <SPAN CLASS="MATH"><I>c</I></SPAN> estimated using the maximum likelihood method based on the
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ *
+ */
+ public static PowerDist getInstanceFromMLE (double[] x, int n,
+ double a, double b) {
+ double parameters[] = getMLE (x, n, a, b);
+ return new PowerDist (a, b, parameters[0]);
+ }
+
+
+ /**
+ * Returns the mean
+ * <SPAN CLASS="MATH"><I>a</I> + (<I>b</I> - <I>a</I>)<I>c</I>/(<I>c</I> + 1)</SPAN> of the power distribution
+ * with parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @param c shape parameter
+ *
+ * @return returns the mean
+ *
+ */
+ public static double getMean (double a, double b, double c) {
+ return a + (b-a) * c / (c+1.0);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">(<I>b</I> - <I>a</I>)<SUP>2</SUP><I>c</I>/[(<I>c</I> + 1)<SUP>2</SUP>(<I>c</I> + 2)]</SPAN>
+ * of the power distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @param c shape parameter
+ *
+ * @return returns the variance
+ *
+ */
+ public static double getVariance (double a, double b, double c) {
+ return (b-a)*(b-a)*c / ((c+1.0)*(c+1.0)*(c+2.0));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the power distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ * @return the standard deviation of the power distribution
+ *
+ */
+ public static double getStandardDeviation (double a, double b, double c) {
+ return Math.sqrt (PowerDist.getVariance (a, b, c));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ * @return the left limit of interval <SPAN CLASS="MATH"><I>a</I></SPAN>
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @return the right limit of interval <SPAN CLASS="MATH"><I>b</I></SPAN>
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ * @return the shape parameter <SPAN CLASS="MATH"><I>c</I></SPAN>
+ *
+ */
+ public double getC() {
+ return c;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>c</I></SPAN> for this object.
+ *
+ * @param a left limit of interval
+ *
+ * @param b right limit of interval
+ *
+ * @param c shape parameter
+ *
+ *
+ */
+ public void setParams (double a, double b, double c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>c</I></SPAN>].
+ *
+ * @return [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I>,</SPAN>c]
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {a, b, c};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + " : b = " + b + " : c = " + c;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/PowerDist.tex b/source/umontreal/iro/lecuyer/probdist/PowerDist.tex
new file mode 100644
index 0000000..30cae80
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/PowerDist.tex
@@ -0,0 +1,399 @@
+\defmodule {PowerDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em power\/} distribution
+\cite[page 161]{tEVA00a} with shape parameter
+$c > 0$, over the interval $[a,b]$, where $a < b$.
+\begin{htmlonly}
+It has density
+\eq
+ f(x) = c(x - a)^{c - 1}/(b - a)^{c}
+\endeq
+for $a < x < b$, and 0 elsewhere. It has distribution function
+\eq
+ F(x) = (x - a)^{c}/(b - a)^{c}
+ \qquad \mbox{for } a \le x \le b,
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+This distribution has density
+\eq
+ f(x) = \frac{c(x-a)^{c - 1}} {(b - a)^{c}},
+ \qquad \mbox{for } a \le x \le b, \eqlabel{eq:fpower}
+\endeq
+and $f(x) = 0$ elsewhere. Its distribution function is
+\eq
+ F(x) = \frac {(x - a)^{c}} {(b - a)^{c}},
+ \qquad \mbox{for } a \le x \le b, \eqlabel{eq:Fpower}
+\endeq
+\end{latexonly}
+with $F(x) = 0$ for $x \le a$ and $F(x) = 1$ for $x \ge b$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: PowerDist
+ * Description: power distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+public class PowerDist extends ContinuousDistribution\begin{hide} {
+ private double a;
+ private double b;
+ private double c;
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public PowerDist (double a, double b, double c)\begin{hide} {
+ setParams (a, b, c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{PowerDist} object with parameters
+ $a =$ \texttt{a}, $b =$ \texttt{b} and $c =$ \texttt{c}.
+ \end{tabb}
+\begin{code}
+
+ public PowerDist (double b, double c)\begin{hide} {
+ setParams (0.0, b, c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{PowerDist} object with parameters
+ $a = 0$, $b =$ \texttt{b} and $c =$ \texttt{c}.
+ \end{tabb}
+\begin{code}
+
+ public PowerDist (double c)\begin{hide} {
+ setParams (0.0, 1.0, c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{PowerDist} object with parameters
+ $a = 0$, $b =1$ and $c =$ \texttt{c}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (a, b, c, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, b, c, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, b, c, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, b, c, u);
+ }
+
+ public double getMean() {
+ return PowerDist.getMean (a, b, c);
+ }
+
+ public double getVariance() {
+ return PowerDist.getVariance (a, b, c);
+ }
+
+ public double getStandardDeviation() {
+ return PowerDist.getStandardDeviation (a, b, c);
+ }\end{hide}
+
+ public static double density (double a, double b, double c, double x)\begin{hide} {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+ if (x >= b)
+ return 0.0;
+ double z = (x-a)/(b-a);
+ return c*Math.pow(z, c-1.0) / (b-a);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:fpower}).
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \param{c}{shape parameter}
+ \param{x}{the value at which the density is evaluated}
+ \return{returns the density function}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double a, double b, double c, double x)\begin{hide} {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 0.0;
+ if (x >= b)
+ return 1.0;
+ return Math.pow((x-a)/(b-a), c);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function (\ref{eq:Fpower}).
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \param{c}{shape parameter}
+ \param{x}{the value at which the distribution is evaluated}
+ \return{returns the distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double a, double b, double c, double x)\begin{hide} {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (x <= a)
+ return 1.0;
+ if (x >= b)
+ return 0.0;
+ return 1.0 - Math.pow((x-a)/(b-a),c);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \param{c}{shape parameter}
+ \param{x}{the value at which the complementary distribution is evaluated}
+ \return{returns the complementary distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double a, double b, double c, double u)\begin{hide} {
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u == 0.0)
+ return a;
+ if (u == 1.0)
+ return b;
+
+ return a + (b-a) * Math.pow(u,1.0/c);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the distribution function.
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \param{c}{shape parameter}
+ \param{u}{the value at which the inverse distribution is evaluated}
+ \return{returns the inverse of the distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n, double a, double b)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double d = b - a;
+ double somme = 0;
+ for (int i = 0 ; i < n ; ++i) somme += Math.log((x[i] - a)/d);
+
+ double [] parametres = new double [1];
+ parametres[0] = -1.0 / (somme/n);
+ return parametres;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $c$ of the power distribution from the $n$
+ observations $x[i]$, $i = 0, 1, \ldots, n-1$, using the maximum
+ likelihood method and assuming that $a$ and $b$ are known.
+ The estimate is returned in a one-element array: [$c$].
+ \begin{detailed}
+ The maximum likelihood estimator is the value
+ $\hat{c}$ that satisfies the equation
+ \begin{eqnarray*}
+ \frac1{\hat c} = -\frac1n \sum_{i=1}^{n} \ln\left(\frac{x_i - a}{b - a}
+ \right)
+ \end{eqnarray*}
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \return{returns the shape parameter [$\hat{c}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static PowerDist getInstanceFromMLE (double[] x, int n,
+ double a, double b)\begin{hide} {
+ double parameters[] = getMLE (x, n, a, b);
+ return new PowerDist (a, b, parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a power distribution with parameters $a$ and $b$,
+ with $c$ estimated using the maximum likelihood method based on the
+ $n$ observations $x[i]$, $i = 0, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double a, double b, double c)\begin{hide} {
+ return a + (b-a) * c / (c+1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean $a + (b-a)c/(c+1)$ of the power distribution
+with parameters $a$, $b$ and $c$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \param{c}{shape parameter}
+ \return{returns the mean}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double a, double b, double c)\begin{hide} {
+ return (b-a)*(b-a)*c / ((c+1.0)*(c+1.0)*(c+2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $(b-a)^2 c / [(c+1)^2(c+2)]$
+ of the power distribution with parameters $a$, $b$ and $c$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \param{c}{shape parameter}
+ \return{returns the variance}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double a, double b, double c)\begin{hide} {
+ return Math.sqrt (PowerDist.getVariance (a, b, c));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the power distribution with parameters $a$, $b$ and $c$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the power distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $a$.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the left limit of interval $a$}
+\end{htmlonly}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $b$.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the right limit of interval $b$}
+\end{htmlonly}
+\begin{code}
+
+ public double getC()\begin{hide} {
+ return c;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $c$.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the shape parameter $c$}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double a, double b, double c)\begin{hide} {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $a$, $b$ and $c$ for this object.
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{left limit of interval}
+ \param{b}{right limit of interval}
+ \param{c}{shape parameter}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {a, b, c};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$a$, $b$, $c$].
+\end{tabb}
+\begin{htmlonly}
+ \return{[$a$, $b, $c]}
+\end{htmlonly}
+\begin{hide}\begin{code}
+
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + " : b = " + b + " : c = " + c;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{a \texttt{String} containing information about the current distribution}
+\end{htmlonly}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/RayleighDist.java b/source/umontreal/iro/lecuyer/probdist/RayleighDist.java
new file mode 100644
index 0000000..ca17526
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/RayleighDist.java
@@ -0,0 +1,427 @@
+
+
+/*
+ * Class: RayleighDist
+ * Description: Rayleigh distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+
+/**
+ * This class extends the class {@link ContinuousDistribution} for
+ * the <EM>Rayleigh</EM> distribution with
+ * location parameter <SPAN CLASS="MATH"><I>a</I></SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>x</I>-<I>a</I>)/<I>β</I><SUP>2</SUP> <I>e</I><SUP>-(x-a)<SUP>2</SUP>/(2<I>β</I><SUP>2</SUP>)</SUP> for <I>x</I> >= <I>a</I>,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> for <SPAN CLASS="MATH"><I>x</I> < <I>a</I></SPAN>.
+ * The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - <I>e</I><SUP>-(x-a)<SUP>2</SUP>/(2<I>β</I><SUP>2</SUP>)</SUP> for <I>x</I> >= <I>a</I>,
+ * </DIV><P></P>
+ * and the inverse distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>x</I> = <I>a</I> + <I>β</I>(-2ln(1-u))<SUP>1/2</SUP> for 0 <= <I>u</I> <= 1.
+ * </DIV><P></P>
+ *
+ */
+public class RayleighDist extends ContinuousDistribution {
+ private double a;
+ private double beta;
+
+
+
+
+
+ /**
+ * Constructs a <TT>RayleighDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>a</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>.
+ *
+ */
+ public RayleighDist (double beta) {
+ setParams (0.0, beta);
+ }
+
+
+ /**
+ * Constructs a <TT>RayleighDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT>, and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>.
+ *
+ */
+ public RayleighDist (double a, double beta) {
+ setParams (a, beta);
+ }
+
+
+ public double density (double x) {
+ return density (a, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, beta, u);
+ }
+
+ public double getMean() {
+ return RayleighDist.getMean (a, beta);
+ }
+
+ public double getVariance() {
+ return RayleighDist.getVariance (beta);
+ }
+
+ public double getStandardDeviation() {
+ return RayleighDist.getStandardDeviation (beta);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ * @param a the location parameter
+ *
+ * @param beta the scale parameter
+ *
+ * @param x the value at which the density is evaluated
+ *
+ * @return the density function
+ *
+ */
+ public static double density (double a, double beta, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= a)
+ return 0.0;
+ final double Z = (x - a)/beta;
+ return Z/beta * Math.exp(-Z*Z/2.0);
+ }
+
+
+ /**
+ * Same as <TT>density (0, beta, x)</TT>.
+ *
+ * @param beta the scale parameter
+ *
+ * @param x the value at which the density is evaluated
+ *
+ * @return returns the density function
+ *
+ */
+ public static double density (double beta, double x) {
+ return density (0.0, beta, x);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ * @param a the location parameter
+ *
+ * @param beta the scale parameter
+ *
+ * @param x the value at which the distribution is evaluated
+ *
+ * @return returns the distribution function
+ *
+ */
+ public static double cdf (double a, double beta, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= a)
+ return 0.0;
+ final double Z = (x - a)/beta;
+ if (Z >= 10.0)
+ return 1.0;
+ return -Math.expm1(-Z*Z/2.0);
+ }
+
+
+ /**
+ * Same as <TT>cdf (0, beta, x)</TT>.
+ *
+ * @param beta the scale parameter
+ *
+ * @param x the value at which the distribution is evaluated
+ *
+ * @return returns the distribution function
+ *
+ */
+ public static double cdf (double beta, double x) {
+ return cdf (0.0, beta, x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ * @param a the location parameter
+ *
+ * @param beta the scale parameter
+ *
+ * @param x the value at which the complementary distribution is evaluated
+ *
+ * @return returns the complementary distribution function
+ *
+ */
+ public static double barF (double a, double beta, double x) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= a)
+ return 1.0;
+ double z = (x - a)/beta;
+ if (z >= 44.0)
+ return 0.0;
+ return Math.exp(-z*z/2.0);
+ }
+
+
+ /**
+ * Same as <TT>barF (0, beta, x)</TT>.
+ *
+ * @param beta the scale parameter
+ *
+ * @param x the value at which the complementary distribution is evaluated
+ *
+ * @return returns the complementary distribution function
+ *
+ */
+ public static double barF (double beta, double x) {
+ return barF (0.0, beta, x);
+ }
+
+
+ /**
+ * Computes the inverse of the distribution function.
+ *
+ * @param a the location parameter
+ *
+ * @param beta the scale parameter
+ *
+ * @param u the value at which the inverse distribution is evaluated
+ *
+ * @return returns the inverse of the distribution function
+ *
+ */
+ public static double inverseF (double a, double beta, double u) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u <= 0.0)
+ return a;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ return a + beta * Math.sqrt(-2.0 * Math.log1p(-u));
+ }
+
+
+ /**
+ * Same as <TT>inverseF (0, beta, u)</TT>.
+ *
+ * @param beta the scale parameter
+ *
+ * @param u the value at which the inverse distribution is evaluated
+ *
+ * @return returns the inverse of the distribution function
+ *
+ */
+ public static double inverseF (double beta, double u) {
+ return inverseF (0.0, beta, u);
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of the Rayleigh distribution
+ * using the maximum likelihood method, assuming that <SPAN CLASS="MATH"><I>a</I></SPAN> is known,
+ * from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ * The estimate is returned in a one-element array: [<SPAN CLASS="MATH">hat(β)</SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param a the location parameter
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(β)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n, double a) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double somme = 0;
+ for (int i = 0 ; i < n ; ++i) somme += (x[i]-a)*(x[i]-a);
+
+ double [] parametres = new double [1];
+ parametres[0] = Math.sqrt(somme/(2.0*n));
+ return parametres;
+ }
+
+
+ /**
+ * Creates a new instance of a Rayleigh distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and
+ * <SPAN CLASS="MATH">hat(β)</SPAN>. This last is estimated using the maximum likelihood method
+ * based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param a the location parameter
+ *
+ *
+ */
+ public static RayleighDist getInstanceFromMLE (double[] x, int n,
+ double a) {
+ double parameters[] = getMLE (x, n, a);
+ return new RayleighDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Returns the mean
+ * <SPAN CLASS="MATH"><I>a</I> + <I>β</I>(π/2)<SUP>1/2</SUP></SPAN> of the
+ * Rayleigh distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @param a the location parameter
+ *
+ * @param beta the scale parameter
+ *
+ * @return the mean of the Rayleigh distribution
+ *
+ */
+ public static double getMean (double a, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ return (a + beta * Math.sqrt(Math.PI/2.0));
+ }
+
+
+ /**
+ * Returns the variance
+ * of the Rayleigh distribution with parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @param beta the scale parameter
+ *
+ * @return the variance of the Rayleigh distribution
+ *
+ */
+ public static double getVariance (double beta) {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+ return (2.0 - 0.5*Math.PI) * beta * beta;
+ }
+
+
+ /**
+ * Returns the standard deviation
+ * <SPAN CLASS="MATH"><I>β</I>(2 - π/2)<SUP>1/2</SUP></SPAN> of
+ * the Rayleigh distribution with parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @param beta the scale parameter
+ *
+ * @return the standard deviation of the Rayleigh distribution
+ *
+ */
+ public static double getStandardDeviation (double beta) {
+ return Math.sqrt (RayleighDist.getVariance (beta));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ * @return the location parameter <SPAN CLASS="MATH"><I>a</I></SPAN>
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ * @return the scale parameter <SPAN CLASS="MATH"><I>beta</I></SPAN>
+ *
+ */
+ public double getSigma() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> for this object.
+ *
+ * @param a the location parameter
+ *
+ * @param beta the scale parameter
+ *
+ *
+ */
+ public void setParams (double a, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.a = a;
+ this.beta = beta;
+ supportA = a;
+ }
+
+
+ /**
+ * Return an array containing the parameters of the current distribution
+ * in the order: [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>].
+ *
+ * @return [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>]
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {a, beta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + ", beta = " + beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/RayleighDist.tex b/source/umontreal/iro/lecuyer/probdist/RayleighDist.tex
new file mode 100644
index 0000000..3232622
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/RayleighDist.tex
@@ -0,0 +1,417 @@
+\defmodule {RayleighDist}
+
+This class extends the class \class{ContinuousDistribution} for
+the {\em Rayleigh\/} distribution \cite{tEVA00a} with
+ location parameter $a$, and scale parameter $\beta > 0$.
+The density function is
+\eq
+ f(x) = \latex{\frac{(x-a)}{\beta^2}}\html{{(x-a)}/{\beta^2}}\,
+ e^{-(x-a)^2/(2\beta^2)}
+ \qquad\mbox{for } x \ge a, \eqlabel {eq:frayleigh}
+\endeq
+and $f(x) = 0$ for $x < a$.
+The distribution function is
+\eq
+ F(x) = 1 - e^{-(x - a)^2/(2\beta^2)}
+ \qquad\mbox{for } x \ge a, \eqlabel{eq:Frayleigh}
+\endeq
+and the inverse distribution function is
+\eq
+ F^{-1}(u) = x = a + \beta\sqrt{-2\latex{\ln}\html{ln}(1-u)}
+ \qquad \mbox{for } 0 \le u \le 1. \eqlabel{eq:Invrayleigh}
+\endeq
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: RayleighDist
+ * Description: Rayleigh distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+public class RayleighDist extends ContinuousDistribution\begin{hide} {
+ private double a;
+ private double beta;
+
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public RayleighDist (double beta)\begin{hide} {
+ setParams (0.0, beta);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{RayleighDist} object with parameters
+ $a = 0$ and $\beta$ = \texttt{beta}.
+ \end{tabb}
+\begin{code}
+
+ public RayleighDist (double a, double beta)\begin{hide} {
+ setParams (a, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{RayleighDist} object with parameters
+ $a =$ \texttt{a}, and $\beta$ = \texttt{beta}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (a, beta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, beta, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, beta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, beta, u);
+ }
+
+ public double getMean() {
+ return RayleighDist.getMean (a, beta);
+ }
+
+ public double getVariance() {
+ return RayleighDist.getVariance (beta);
+ }
+
+ public double getStandardDeviation() {
+ return RayleighDist.getStandardDeviation (beta);
+ }\end{hide}
+
+ public static double density (double a, double beta, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= a)
+ return 0.0;
+ final double Z = (x - a)/beta;
+ return Z/beta * Math.exp(-Z*Z/2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:frayleigh}).
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{beta}{the scale parameter}
+ \param{x}{the value at which the density is evaluated}
+ \return{the density function}
+\end{htmlonly}
+\begin{code}
+
+ public static double density (double beta, double x)\begin{hide} {
+ return density (0.0, beta, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{density (0, beta, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{beta}{the scale parameter}
+ \param{x}{the value at which the density is evaluated}
+ \return{returns the density function}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double a, double beta, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= a)
+ return 0.0;
+ final double Z = (x - a)/beta;
+ if (Z >= 10.0)
+ return 1.0;
+ return -Math.expm1(-Z*Z/2.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function (\ref{eq:Frayleigh}).
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{beta}{the scale parameter}
+ \param{x}{the value at which the distribution is evaluated}
+ \return{returns the distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double cdf (double beta, double x)\begin{hide} {
+ return cdf (0.0, beta, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{cdf (0, beta, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{beta}{the scale parameter}
+ \param{x}{the value at which the distribution is evaluated}
+ \return{returns the distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double a, double beta, double x)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (x <= a)
+ return 1.0;
+ double z = (x - a)/beta;
+ if (z >= 44.0)
+ return 0.0;
+ return Math.exp(-z*z/2.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{beta}{the scale parameter}
+ \param{x}{the value at which the complementary distribution is evaluated}
+ \return{returns the complementary distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double barF (double beta, double x)\begin{hide} {
+ return barF (0.0, beta, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{barF (0, beta, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{beta}{the scale parameter}
+ \param{x}{the value at which the complementary distribution is evaluated}
+ \return{returns the complementary distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double a, double beta, double u)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u <= 0.0)
+ return a;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ return a + beta * Math.sqrt(-2.0 * Math.log1p(-u));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the distribution function (\ref{eq:Invrayleigh}).
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{beta}{the scale parameter}
+ \param{u}{the value at which the inverse distribution is evaluated}
+ \return{returns the inverse of the distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double inverseF (double beta, double u)\begin{hide} {
+ return inverseF (0.0, beta, u);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{inverseF (0, beta, u)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{beta}{the scale parameter}
+ \param{u}{the value at which the inverse distribution is evaluated}
+ \return{returns the inverse of the distribution function}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n, double a)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double somme = 0;
+ for (int i = 0 ; i < n ; ++i) somme += (x[i]-a)*(x[i]-a);
+
+ double [] parametres = new double [1];
+ parametres[0] = Math.sqrt(somme/(2.0*n));
+ return parametres;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $\beta$ of the Rayleigh distribution
+ using the maximum likelihood method, assuming that $a$ is known,
+ from the $n$ observations $x[i]$, $i = 0, 1, \ldots, n-1$.
+ The estimate is returned in a one-element array: [$\hat\beta$].
+ \begin{detailed}
+ The maximum likelihood estimator is the value
+ $\hat{\beta}$ that satisfies the equation
+ $$
+ \hat{\beta} = \sqrt{\frac1{2n}\sum_{i=1}^{n} x_i^2}
+ $$
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{a}{the location parameter}
+ \return{returns the parameter [$\hat{\beta}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static RayleighDist getInstanceFromMLE (double[] x, int n,
+ double a)\begin{hide} {
+ double parameters[] = getMLE (x, n, a);
+ return new RayleighDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Rayleigh distribution with parameters $a$ and
+ $\hat\beta$. This last is estimated using the maximum likelihood method
+ based on the $n$ observations $x[i]$, $i = 0, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{a}{the location parameter}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double a, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ return (a + beta * Math.sqrt(Math.PI/2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean $a + \beta\sqrt{\pi/2}$ of the
+ Rayleigh distribution with parameters $a$ and $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{beta}{the scale parameter}
+ \return{the mean of the Rayleigh distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double beta)\begin{hide} {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+ return (2.0 - 0.5*Math.PI) * beta * beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance
+ of the Rayleigh distribution with parameter $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \param{beta}{the scale parameter}
+ \return{the variance of the Rayleigh distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double beta)\begin{hide} {
+ return Math.sqrt (RayleighDist.getVariance (beta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation $\beta\sqrt{2 - \pi/2}$ of
+ the Rayleigh distribution with parameter $\beta$.
+\end{tabb}
+\begin{htmlonly}
+ \param{beta}{the scale parameter}
+ \return{the standard deviation of the Rayleigh distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $a$.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the location parameter $a$}
+\end{htmlonly}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the scale parameter $beta$}
+\end{htmlonly}
+\begin{code}
+
+ public void setParams (double a, double beta)\begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.a = a;
+ this.beta = beta;
+ supportA = a;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $a$ and $\beta$ for this object.
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{the location parameter}
+ \param{beta}{the scale parameter}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {a, beta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameters of the current distribution
+ in the order: [$a$, $\beta$].
+\end{tabb}
+\begin{htmlonly}
+ \return{[$a$, $\beta$]}
+\end{htmlonly}
+\begin{hide}\begin{code}
+
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + ", beta = " + beta;
+ }
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{a \texttt{String} containing information about the current distribution}
+\end{htmlonly}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/StudentDist.java b/source/umontreal/iro/lecuyer/probdist/StudentDist.java
new file mode 100644
index 0000000..e85ee11
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/StudentDist.java
@@ -0,0 +1,438 @@
+
+/*
+ * Class: StudentDistDist
+ * Description: Student-t distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since March 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <SPAN CLASS="textit">Student</SPAN> <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution
+ * with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom, where <SPAN CLASS="MATH"><I>n</I></SPAN> is a positive integer.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = [<I>Γ</I>((<I>n</I> + 1)/2)/(<I>Γ</I>(<I>n</I>/2)(πn)<SUP>1/2</SUP>)][1 + <I>x</I><SUP>2</SUP>/<I>n</I>]<SUP>-(n+1)/2</SUP> for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined in
+ * {@link GammaDist}.
+ *
+ */
+public class StudentDist extends ContinuousDistribution {
+ protected int n;
+ private double factor;
+ private static final int NLIM1 = 100000;
+/*
+ private static double cdfPeizer (int n, double x) {
+ // Peizer-Pratt normal approximation for the cdf (n, u)
+ // \cite{tPEI68a}
+ double v = Math.log1p(x*x/n) / (n - 5.0/6.0);
+ double z = -(n - 2.0/3.0 + 0.1/n) * Math.sqrt(v);
+ double u = NormalDist.cdf01 (z);
+ if (x >= 0.0)
+ return 1.0 - u;
+ return u;
+ }
+
+ private static double invPeizer (int n, double u) {
+ // Peizer-Pratt normal approximation for the inverseF (n, u)
+ // \cite{tPEI68a}
+ double z = NormalDist.inverseF01 (u);
+ double q = z / (n - 2.0/3.0 + 0.1/n);
+ double v = q*q*(n - 5.0/6.0);
+ double t = Math.sqrt(n * Math.expm1(v));
+ if (u >= 0.5)
+ return t;
+ else
+ return -t;
+ }
+*/
+
+ private static double cdfGaver (int n, double x) {
+ // Gaver-Kafadar normal approximation for the cdf
+ // \cite{tGAV84a}
+ double v = Math.log1p(x * x / n) / (n - 1.5);
+ double z = -(n - 1) * Math.sqrt(v);
+ double u = NormalDist.cdf01 (z);
+ if (x >= 0.0)
+ return 1.0 - u;
+ return u;
+ }
+
+
+ private static double invGaver (int n, double u) {
+ // Gaver-Kafadar normal approximation for the inverse
+ // \cite{tGAV84a}
+ double z = NormalDist.inverseF01 (u);
+ double q = z / (n - 1.0);
+ double v = q * q * (n - 1.5);
+ double t = Math.sqrt(n * Math.expm1(v));
+ if (u >= 0.5)
+ return t;
+ else
+ return -t;
+ }
+
+
+ private static class Function implements MathFunction {
+ private int n;
+ private double[] xi;
+
+ public Function (double[] x, int n) {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0)
+ return 1e200;
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += Math.log (density ((int) Math.round (x), xi[i]));
+ return sum;
+ }
+ }
+
+
+ /**
+ * Constructs a <TT>StudentDist</TT> object with <TT>n</TT> degrees of freedom.
+ *
+ */
+ public StudentDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ return factor*Math.pow (1.0 / (1.0 + x*x/n), (n + 1)/2.0);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return StudentDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return StudentDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return StudentDist.getStandardDeviation (n);
+ }
+
+ /**
+ * Computes the density function of a
+ * Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ *
+ */
+ public static double density (int n, double x) {
+ double factor = Num.gammaRatioHalf(n/2.0)/ Math.sqrt (n*Math.PI);
+ return factor*Math.pow (1.0 / (1.0 + x*x/n), (n + 1)/2.0);
+ }
+
+
+ /**
+ * Computes the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution function <SPAN CLASS="MATH"><I>u</I> = <I>F</I>(<I>x</I>)</SPAN> with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ * Gives 13 decimal digits of precision for
+ * <SPAN CLASS="MATH"><I>n</I> <= 10<SUP>5</SUP></SPAN>.
+ * For <SPAN CLASS="MATH"><I>n</I> > 10<SUP>5</SUP></SPAN>, gives at least 6 decimal digits of precision everywhere, and
+ * at least 9 decimal digits of precision for all
+ * <SPAN CLASS="MATH"><I>u</I> > 10<SUP>-15</SUP></SPAN>.
+ *
+ */
+ public static double cdf (int n, double x) {
+ if (n < 1)
+ throw new IllegalArgumentException ("n < 1");
+ if (n == 1)
+ return CauchyDist.cdf(0, 1, x);
+
+ if (x > 1.0e10)
+ return 1.0;
+ if (n > NLIM1)
+ return cdfGaver(n, x);
+
+ double r = Math.abs(x);
+ if (r < 1.0e20)
+ r = Math.sqrt (n + x*x);
+
+ double z;
+ if (x >= 0.0)
+ z = 0.5*(1.0 + x/r);
+ else
+ z = 0.5*n/(r*(r - x));
+
+ if (n == 2)
+ return z;
+ return BetaSymmetricalDist.cdf (0.5*n, 15, z);
+ }
+
+
+ /**
+ * Same as {@link #cdf(int,double) cdf}<TT>(n, x)</TT>.
+ *
+ */
+ @Deprecated
+ public static double cdf2 (int n, int d, double x) {
+ if (d <= 0)
+ throw new IllegalArgumentException ("student2: d <= 0");
+ return cdf (n, x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH"><I>v</I> = bar(F)(<I>x</I>)</SPAN>
+ * with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom. Gives 13 decimal digits of precision for
+ * <SPAN CLASS="MATH"><I>n</I> <= 10<SUP>5</SUP></SPAN>.
+ * For <SPAN CLASS="MATH"><I>n</I> > 10<SUP>5</SUP></SPAN>, gives at least 6 decimal digits of precision everywhere, and
+ * at least 9 decimal digits of precision for all
+ * <SPAN CLASS="MATH"><I>v</I> > 10<SUP>-15</SUP></SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ if (n < 1)
+ throw new IllegalArgumentException ("n < 1");
+ if (n == 1)
+ return CauchyDist.barF(0, 1, x);
+
+ if (n == 2) {
+ double z = Math.abs(x);
+ if (z < 1.0e20)
+ z = Math.sqrt(2.0 + x*x);
+ if (x <= 0.) {
+ if (x < -1.0e10)
+ return 1.0;
+ return 0.5* (1.0 - x / z);
+ } else
+ return 1.0 / (z * (z + x));
+ }
+
+ return cdf (n, -x);
+ }
+
+
+ /**
+ * Returns the inverse
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN> of
+ * Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution function with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ * Gives 13 decimal digits of precision for
+ * <SPAN CLASS="MATH"><I>n</I> <= 10<SUP>5</SUP></SPAN>,
+ * and at least 9 decimal digits of precision for <SPAN CLASS="MATH"><I>n</I> > 10<SUP>5</SUP></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n < 1)
+ throw new IllegalArgumentException ("Student: n < 1");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("Student: u not in [0, 1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ if (1 == n)
+ return CauchyDist.inverseF(0, 1, u);
+
+ if (2 == n)
+ return (2.0*u - 1.0) / Math.sqrt(2.0*u*(1.0 - u));
+
+ if (n > NLIM1)
+ return invGaver(n, u);
+ double z = BetaSymmetricalDist.inverseF (0.5*n, u);
+ return (z - 0.5) * Math.sqrt(n / (z*(1.0 - z)));
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>. The estimate is returned in a one-element
+ * array.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH">hat(n)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int m) {
+ double sum = 0.0;
+ double[] parameters = new double[1];
+
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ double var = 0.0;
+ for (int i = 0; i < m; i++)
+ var += x[i] * x[i];
+ var /= (double) m;
+
+ Function f = new Function (x, m);
+
+ double n0 = Math.round ((2.0 * var) / (var - 1.0));
+ double fn0 = f.evaluate (n0);
+ double min = fn0;
+ double fn1 = f.evaluate (n0 + 1.0);
+ double fn_1 = f.evaluate (n0 - 1.0);
+
+ parameters[0] = n0;
+
+ if (fn_1 > fn0) {
+ double n = n0 - 1.0;
+ double y;
+ while (((y = f.evaluate (n)) > min) && (n >= 1.0)) {
+ min = y;
+ parameters[0] = n;
+ n -= 1.0;
+ }
+
+ } else if (fn1 > fn0) {
+ double n = n0 + 1.0;
+ double y;
+ while ((y = f.evaluate (n)) > min) {
+ min = y;
+ parameters[0] = n;
+ n += 1.0;
+ }
+ }
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>m</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param m the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static StudentDist getInstanceFromMLE (double[] x, int m) {
+ double parameters[] = getMLE (x, m);
+ return new StudentDist ((int) parameters[0]);
+ }
+
+
+ /**
+ * Returns the mean <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = 0</SPAN> of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution with
+ * parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the mean of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = 0</SPAN>
+ *
+ */
+ public static double getMean (int n) {
+ if (n < 2)
+ throw new IllegalArgumentException ("n <= 1");
+ return 0;
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>n</I>/(<I>n</I> - 2)</SPAN>
+ * of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the variance of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = <I>n</I>/(<I>n</I> - 2)</SPAN>
+ *
+ */
+ public static double getVariance (int n) {
+ if (n < 3)
+ throw new IllegalArgumentException("n <= 2");
+ return (n / (n - 2.0));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the standard deviation of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution
+ *
+ */
+ public static double getStandardDeviation (int n) {
+ return Math.sqrt (StudentDist.getVariance (n));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> associated with this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> associated with this object.
+ *
+ */
+ public void setN (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ factor = Num.gammaRatioHalf(n/2.0) / Math.sqrt (n*Math.PI);
+ }
+
+
+ /**
+ * Return a table containing the parameter of the current distribution.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/StudentDist.tex b/source/umontreal/iro/lecuyer/probdist/StudentDist.tex
new file mode 100644
index 0000000..3ae318c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/StudentDist.tex
@@ -0,0 +1,448 @@
+\defmodule {StudentDist}
+
+Extends the class \class{ContinuousDistribution} for
+the \emph{Student} $t$-distribution \cite[page 362]{tJOH95b}
+with $n$ degrees of freedom, where $n$ is a positive integer.
+Its density is
+\begin{htmlonly}
+\eq
+ f (x) = [\Gamma((n + 1)/2)/
+ (\Gamma (n/2) \sqrt{\pi n})]
+ [1 + x^2/n]^{-(n+1)/2}
+ \qquad\mbox {for } -\infty < x < \infty,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f (x) = \frac{\Gamma\left ((n + 1)/2 \right)}
+ {\Gamma (n/2) \sqrt{\pi n}}
+ \left(1 + \frac{x^2}{n}\right)^{-(n+1)/2}
+ \qquad\mbox {for } -\infty < x < \infty,
+ \eqlabel{eq:fstudent}
+\endeq
+\end{latexonly}
+where $\Gamma(x)$ is the gamma function defined in
+\latex{(\ref{eq:Gamma})}\html{\class{GammaDist}}.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: StudentDistDist
+ * Description: Student-t distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since March 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class StudentDist extends ContinuousDistribution\begin{hide} {
+ protected int n;
+ private double factor;
+ private static final int NLIM1 = 100000;
+/*
+ private static double cdfPeizer (int n, double x) {
+ // Peizer-Pratt normal approximation for the cdf (n, u)
+ // \cite{tPEI68a}
+ double v = Math.log1p(x*x/n) / (n - 5.0/6.0);
+ double z = -(n - 2.0/3.0 + 0.1/n) * Math.sqrt(v);
+ double u = NormalDist.cdf01 (z);
+ if (x >= 0.0)
+ return 1.0 - u;
+ return u;
+ }
+
+ private static double invPeizer (int n, double u) {
+ // Peizer-Pratt normal approximation for the inverseF (n, u)
+ // \cite{tPEI68a}
+ double z = NormalDist.inverseF01 (u);
+ double q = z / (n - 2.0/3.0 + 0.1/n);
+ double v = q*q*(n - 5.0/6.0);
+ double t = Math.sqrt(n * Math.expm1(v));
+ if (u >= 0.5)
+ return t;
+ else
+ return -t;
+ }
+*/
+
+ private static double cdfGaver (int n, double x) {
+ // Gaver-Kafadar normal approximation for the cdf
+ // \cite{tGAV84a}
+ double v = Math.log1p(x * x / n) / (n - 1.5);
+ double z = -(n - 1) * Math.sqrt(v);
+ double u = NormalDist.cdf01 (z);
+ if (x >= 0.0)
+ return 1.0 - u;
+ return u;
+ }
+
+
+ private static double invGaver (int n, double u) {
+ // Gaver-Kafadar normal approximation for the inverse
+ // \cite{tGAV84a}
+ double z = NormalDist.inverseF01 (u);
+ double q = z / (n - 1.0);
+ double v = q * q * (n - 1.5);
+ double t = Math.sqrt(n * Math.expm1(v));
+ if (u >= 0.5)
+ return t;
+ else
+ return -t;
+ }
+
+
+ private static class Function implements MathFunction {
+ private int n;
+ private double[] xi;
+
+ public Function (double[] x, int n) {
+ this.n = n;
+ this.xi = new double[n];
+ System.arraycopy (x, 0, this.xi, 0, n);
+ }
+
+ public double evaluate (double x) {
+ if (x <= 0.0)
+ return 1e200;
+ double sum = 0.0;
+ for (int i = 0; i < n; i++)
+ sum += Math.log (density ((int) Math.round (x), xi[i]));
+ return sum;
+ }
+ }
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public StudentDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{StudentDist} object with \texttt{n} degrees of freedom.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return factor*Math.pow (1.0 / (1.0 + x*x/n), (n + 1)/2.0);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return StudentDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return StudentDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return StudentDist.getStandardDeviation (n);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ double factor = Num.gammaRatioHalf(n/2.0)/ Math.sqrt (n*Math.PI);
+ return factor*Math.pow (1.0 / (1.0 + x*x/n), (n + 1)/2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function (\ref{eq:fstudent}) of a
+ Student $t$-distribution with $n$ degrees of freedom.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ if (n < 1)
+ throw new IllegalArgumentException ("n < 1");
+ if (n == 1)
+ return CauchyDist.cdf(0, 1, x);
+
+ if (x > 1.0e10)
+ return 1.0;
+ if (n > NLIM1)
+ return cdfGaver(n, x);
+
+ double r = Math.abs(x);
+ if (r < 1.0e20)
+ r = Math.sqrt (n + x*x);
+
+ double z;
+ if (x >= 0.0)
+ z = 0.5*(1.0 + x/r);
+ else
+ z = 0.5*n/(r*(r - x));
+
+ if (n == 2)
+ return z;
+ return BetaSymmetricalDist.cdf (0.5*n, 15, z);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the Student $t$-distribution function $u=F(x)$ with $n$ degrees of freedom.
+ Gives 13 decimal digits of precision for $n \le 10^5$.
+ For $n > 10^5$, gives at least 6 decimal digits of precision everywhere, and
+ at least 9 decimal digits of precision for all $u > 10^{-15}$.
+ \end{tabb}
+\begin{code}
+
+ @Deprecated
+ public static double cdf2 (int n, int d, double x)\begin{hide} {
+ if (d <= 0)
+ throw new IllegalArgumentException ("student2: d <= 0");
+ return cdf (n, x);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+Same as \method{cdf}{int,double}\texttt{(n, x)}.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double x)\begin{hide} {
+ if (n < 1)
+ throw new IllegalArgumentException ("n < 1");
+ if (n == 1)
+ return CauchyDist.barF(0, 1, x);
+
+ if (n == 2) {
+ double z = Math.abs(x);
+ if (z < 1.0e20)
+ z = Math.sqrt(2.0 + x*x);
+ if (x <= 0.) {
+ if (x < -1.0e10)
+ return 1.0;
+ return 0.5* (1.0 - x / z);
+ } else
+ return 1.0 / (z * (z + x));
+ }
+
+ return cdf (n, -x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function $v = \bar{F}(x)$
+with $n$ degrees of freedom. Gives 13 decimal digits of precision for $n \le 10^5$.
+ For $n > 10^5$, gives at least 6 decimal digits of precision everywhere, and
+ at least 9 decimal digits of precision for all $v > 10^{-15}$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n < 1)
+ throw new IllegalArgumentException ("Student: n < 1");
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("Student: u not in [0, 1]");
+ if (u <= 0.0)
+ return Double.NEGATIVE_INFINITY;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ if (1 == n)
+ return CauchyDist.inverseF(0, 1, u);
+
+ if (2 == n)
+ return (2.0*u - 1.0) / Math.sqrt(2.0*u*(1.0 - u));
+
+ if (n > NLIM1)
+ return invGaver(n, u);
+ double z = BetaSymmetricalDist.inverseF (0.5*n, u);
+ return (z - 0.5) * Math.sqrt(n / (z*(1.0 - z)));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the inverse $x = F^{-1}(u)$ of
+ Student $t$-distribution function with $n$ degrees of freedom.
+ Gives 13 decimal digits of precision for $n \le 10^5$,
+ and at least 9 decimal digits of precision for $n > 10^5$.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int m)\begin{hide} {
+ double sum = 0.0;
+ double[] parameters = new double[1];
+
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+
+ double var = 0.0;
+ for (int i = 0; i < m; i++)
+ var += x[i] * x[i];
+ var /= (double) m;
+
+ Function f = new Function (x, m);
+
+ double n0 = Math.round ((2.0 * var) / (var - 1.0));
+ double fn0 = f.evaluate (n0);
+ double min = fn0;
+ double fn1 = f.evaluate (n0 + 1.0);
+ double fn_1 = f.evaluate (n0 - 1.0);
+
+ parameters[0] = n0;
+
+ if (fn_1 > fn0) {
+ double n = n0 - 1.0;
+ double y;
+ while (((y = f.evaluate (n)) > min) && (n >= 1.0)) {
+ min = y;
+ parameters[0] = n;
+ n -= 1.0;
+ }
+
+ } else if (fn1 > fn0) {
+ double n = n0 + 1.0;
+ double y;
+ while ((y = f.evaluate (n)) > min) {
+ min = y;
+ parameters[0] = n;
+ n += 1.0;
+ }
+ }
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $n$ of the Student $t$-distribution
+ using the maximum likelihood method, from the $m$ observations
+ $x[i]$, $i = 0, 1,\ldots, m-1$. The estimate is returned in a one-element
+ array.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+ \return{returns the parameter [$\hat{n}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static StudentDist getInstanceFromMLE (double[] x, int m)\begin{hide} {
+ double parameters[] = getMLE (x, m);
+ return new StudentDist ((int) parameters[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Student $t$-distribution with parameter $n$
+ estimated using the maximum likelihood method based on the $m$ observations
+ $x[i]$, $i = 0, 1, \ldots, m-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{m}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (int n)\begin{hide} {
+ if (n < 2)
+ throw new IllegalArgumentException ("n <= 1");
+ return 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean $E[X] = 0$ of the Student $t$-distribution with
+ parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Student $t$-distribution $E[X] = 0$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int n)\begin{hide} {
+ if (n < 3)
+ throw new IllegalArgumentException("n <= 2");
+ return (n / (n - 2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = n/(n - 2)$
+ of the Student $t$-distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Student $t$-distribution
+ $\mbox{Var}[X] = n / (n - 2)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int n)\begin{hide} {
+ return Math.sqrt (StudentDist.getVariance (n));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the Student $t$-distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Student $t$-distribution}
+\end{htmlonly}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ associated with this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ factor = Num.gammaRatioHalf(n/2.0) / Math.sqrt (n*Math.PI);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ associated with this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameter of the current distribution.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/StudentDistQuick.java b/source/umontreal/iro/lecuyer/probdist/StudentDistQuick.java
new file mode 100644
index 0000000..ce77523
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/StudentDistQuick.java
@@ -0,0 +1,217 @@
+
+
+/*
+ * Class: StudentDistQuick
+ * Description: Student t-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+// import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link StudentDist} for
+ * the <SPAN CLASS="textit">Student</SPAN> <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution. Uses methods that are faster but less precise
+ * than {@link StudentDist}.
+ *
+ */
+public class StudentDistQuick extends StudentDist {
+ private static final int STUDENT_N1 = 20;
+ private static final double STUDENT_X1 = 8.01;
+ private static final int STUDENT_KMAX = 200;
+ private static final double STUDENT_EPS = 0.5E-16;
+
+
+ /**
+ * Constructs a <TT>StudentDistQuick</TT> object with <TT>n</TT> degrees of freedom.
+ *
+ */
+ public StudentDistQuick (int n) {
+ super (n);
+ }
+
+ /* These public methods are necessary so that the methods cdf,
+ * barF and inverseF used are those of the present
+ * class and not those of the mother class.
+ */
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ /**
+ * Returns an approximation
+ * of the Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution function with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ * Is very poor in the tails but good in the central part of the range.
+ *
+ */
+ public static double cdf (int n, double x) {
+ if (n <= 2)
+ return StudentDist.cdf(n, x);
+ if (x == Double.NEGATIVE_INFINITY)
+ return 0.0;
+ if (x == Double.POSITIVE_INFINITY)
+ return 1.0;
+ double b, y, z, z2, prec, v;
+ int k;
+
+ // first case: small n and small x
+ if (n <= STUDENT_N1 && x <= STUDENT_X1) {
+ b = 1.0 + x*x/n;
+ y = x/Math.sqrt ((double)n);
+ z = 1.0;
+ for (k = n - 2; k >= 2; k -= 2)
+ z = 1.0 + z*(k - 1)/(k*b);
+ if (n % 2 == 0) {
+ v = (1.0 + z*y/Math.sqrt (b))/2.0;
+ } else {
+ if (y > -1.0)
+ v = (0.5 + (Math.atan (y) + z*y/b)/Math.PI);
+ else
+ v = (Math.atan(-1.0/y) + z * y / b) / Math.PI;
+ }
+ if (v > 1.0e-18)
+ return v;
+ else
+ return 0.0;
+ }
+
+ // second case: large n and small x
+ else if (x < STUDENT_X1) {
+ double a = n - 0.5;
+ b = 48.0*a*a;
+ z2 = a*Math.log1p (x*x/n);
+ z = Math.sqrt (z2);
+ y = (((((64.0*z2 + 788.0)*z2 + 9801.0)*z2 + 89775.0)*z2 +
+ 543375.0)*z2 + 1788885.0)*z/(210.0*b*b*b);
+ y -= (((4.0*z2 + 33.0)*z2 + 240.0)*z2 + 855.0)*z/(10.0*b*b);
+ y += z + (z2 + 3.0)*z/b;
+ if (x >= 0.0)
+ return NormalDist.barF01 (-y);
+ else
+ return NormalDist.barF01 (y);
+ }
+
+ // third case: large x
+ else {
+ // Compute the Student probability density
+ b = 1.0 + x*x/n;
+ y = Num.gammaRatioHalf (n/2.0);
+ y *= 1.0/(Math.sqrt (Math.PI*n)*Math.pow (b, (n + 1)/2.0));
+
+ y *= 2.0*Math.sqrt (n*b);
+ z = y/n;
+ k = 2;
+ z2 = prec = 10.0;
+ while (k < STUDENT_KMAX && prec > STUDENT_EPS) {
+ y *= (k - 1)/(k*b);
+ z += y/(n + k);
+ prec = Math.abs (z - z2);
+ z2 = z;
+ k += 2;
+ }
+ if (k >= STUDENT_KMAX)
+ System.err.println ("student: k >= STUDENT_KMAX");
+ if (x >= 0.0)
+ return 1.0 - z/2.0;
+ else
+ return z/2.0;
+ }
+ }
+
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ if (n <= 2)
+ return StudentDist.barF(n, x);
+ return cdf (n, -x);
+ }
+
+
+ /**
+ * Returns an approximation of <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN>, where <SPAN CLASS="MATH"><I>F</I></SPAN> is the
+ * Student <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution function with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom.
+ * Gives at least 5 decimal digits of precision when <SPAN CLASS="MATH"><I>n</I> >= 3</SPAN>
+ * .
+ * Uses exact formulae for <SPAN CLASS="MATH"><I>n</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>n</I> = 2</SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n <= 2)
+ return StudentDist.inverseF(n, u);
+ final double PI = Math.PI;
+ double a, b, c, d, e, p, t, x, y;
+
+ e = (double) n;
+ if (u > 0.5)
+ p = 2.0 * (1.0 - u);
+ else
+ p = 2.0 * u;
+
+ a = 1. / (e - 0.5);
+ b = 48. / (a * a);
+ c = ((20700. / b * a - 98.) * a - 16.) * a + 96.36;
+ d = e * Math.sqrt (a * PI / 2.) * ((94.5 / (b + c) - 3.) / b + 1.);
+ y = Math.pow ((d * p), (2.0 / e));
+ if (y > (a + 0.05)) {
+ if (p == 1.0)
+ x = 0.0;
+ else
+ x = NormalDist.inverseF01 (p * 0.5);
+ y = x * x;
+ if (n < 5)
+ c = c + 0.3 * (e - 4.5) * (x + 0.6);
+
+ c = (((0.05 * d * x - 5.) * x - 7.) * x - 2.) * x + b + c;
+ y = (((((0.4 * y + 6.3) * y + 36.) * y + 94.5) /
+ c - y - 3.) / b + 1.) * x;
+ y = a * (y * y);
+ y = Math.expm1 (y);
+
+ } else {
+ y = ((1. / (((e + 6.) / (e * y) - 0.089 * d - 0.822) *
+ (e + 2.) * 3.) + 0.5 / (e + 4.)) * y - 1.) *
+ (e + 1.) / (e + 2.) + 1. / y;
+ }
+
+ t = Math.sqrt (e * y);
+ if (u < 0.5)
+ return -t;
+ else
+ return t;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/StudentDistQuick.tex b/source/umontreal/iro/lecuyer/probdist/StudentDistQuick.tex
new file mode 100644
index 0000000..2dbcc32
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/StudentDistQuick.tex
@@ -0,0 +1,228 @@
+\defmodule {StudentDistQuick}
+
+Extends the class \class{StudentDist} for
+the \emph{Student} $t$-distribution. Uses methods that are faster but less precise
+than \class{StudentDist}.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: StudentDistQuick
+ * Description: Student t-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+// import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class StudentDistQuick extends StudentDist\begin{hide} {
+ private static final int STUDENT_N1 = 20;
+ private static final double STUDENT_X1 = 8.01;
+ private static final int STUDENT_KMAX = 200;
+ private static final double STUDENT_EPS = 0.5E-16;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public StudentDistQuick (int n)\begin{hide} {
+ super (n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{StudentDistQuick} object with \texttt{n} degrees of freedom.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ /* These public methods are necessary so that the methods cdf,
+ * barF and inverseF used are those of the present
+ * class and not those of the mother class.
+ */
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }\end{hide}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ if (n <= 2)
+ return StudentDist.cdf(n, x);
+ if (x == Double.NEGATIVE_INFINITY)
+ return 0.0;
+ if (x == Double.POSITIVE_INFINITY)
+ return 1.0;
+ double b, y, z, z2, prec, v;
+ int k;
+
+ // first case: small n and small x
+ if (n <= STUDENT_N1 && x <= STUDENT_X1) {
+ b = 1.0 + x*x/n;
+ y = x/Math.sqrt ((double)n);
+ z = 1.0;
+ for (k = n - 2; k >= 2; k -= 2)
+ z = 1.0 + z*(k - 1)/(k*b);
+ if (n % 2 == 0) {
+ v = (1.0 + z*y/Math.sqrt (b))/2.0;
+ } else {
+ if (y > -1.0)
+ v = (0.5 + (Math.atan (y) + z*y/b)/Math.PI);
+ else
+ v = (Math.atan(-1.0/y) + z * y / b) / Math.PI;
+ }
+ if (v > 1.0e-18)
+ return v;
+ else
+ return 0.0;
+ }
+
+ // second case: large n and small x
+ else if (x < STUDENT_X1) {
+ double a = n - 0.5;
+ b = 48.0*a*a;
+ z2 = a*Math.log1p (x*x/n);
+ z = Math.sqrt (z2);
+ y = (((((64.0*z2 + 788.0)*z2 + 9801.0)*z2 + 89775.0)*z2 +
+ 543375.0)*z2 + 1788885.0)*z/(210.0*b*b*b);
+ y -= (((4.0*z2 + 33.0)*z2 + 240.0)*z2 + 855.0)*z/(10.0*b*b);
+ y += z + (z2 + 3.0)*z/b;
+ if (x >= 0.0)
+ return NormalDist.barF01 (-y);
+ else
+ return NormalDist.barF01 (y);
+ }
+
+ // third case: large x
+ else {
+ // Compute the Student probability density
+ b = 1.0 + x*x/n;
+ y = Num.gammaRatioHalf (n/2.0);
+ y *= 1.0/(Math.sqrt (Math.PI*n)*Math.pow (b, (n + 1)/2.0));
+
+ y *= 2.0*Math.sqrt (n*b);
+ z = y/n;
+ k = 2;
+ z2 = prec = 10.0;
+ while (k < STUDENT_KMAX && prec > STUDENT_EPS) {
+ y *= (k - 1)/(k*b);
+ z += y/(n + k);
+ prec = Math.abs (z - z2);
+ z2 = z;
+ k += 2;
+ }
+ if (k >= STUDENT_KMAX)
+ System.err.println ("student: k >= STUDENT_KMAX");
+ if (x >= 0.0)
+ return 1.0 - z/2.0;
+ else
+ return z/2.0;
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns \latex{the}\html{an} approximation\latex{ of \cite[page 96]{tKEN80a}}
+ of the Student $t$-distribution function with $n$ degrees of freedom.
+ Is very poor in the tails but good in the central part of the range.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double x)\begin{hide} {
+ if (n <= 2)
+ return StudentDist.barF(n, x);
+ return cdf (n, -x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the complementary distribution function $\bar{F}(x)$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n <= 2)
+ return StudentDist.inverseF(n, u);
+ final double PI = Math.PI;
+ double a, b, c, d, e, p, t, x, y;
+
+ e = (double) n;
+ if (u > 0.5)
+ p = 2.0 * (1.0 - u);
+ else
+ p = 2.0 * u;
+
+ a = 1. / (e - 0.5);
+ b = 48. / (a * a);
+ c = ((20700. / b * a - 98.) * a - 16.) * a + 96.36;
+ d = e * Math.sqrt (a * PI / 2.) * ((94.5 / (b + c) - 3.) / b + 1.);
+ y = Math.pow ((d * p), (2.0 / e));
+ if (y > (a + 0.05)) {
+ if (p == 1.0)
+ x = 0.0;
+ else
+ x = NormalDist.inverseF01 (p * 0.5);
+ y = x * x;
+ if (n < 5)
+ c = c + 0.3 * (e - 4.5) * (x + 0.6);
+
+ c = (((0.05 * d * x - 5.) * x - 7.) * x - 2.) * x + b + c;
+ y = (((((0.4 * y + 6.3) * y + 36.) * y + 94.5) /
+ c - y - 3.) / b + 1.) * x;
+ y = a * (y * y);
+ y = Math.expm1 (y);
+
+ } else {
+ y = ((1. / (((e + 6.) / (e * y) - 0.089 * d - 0.822) *
+ (e + 2.) * 3.) + 0.5 / (e + 4.)) * y - 1.) *
+ (e + 1.) / (e + 2.) + 1. / y;
+ }
+
+ t = Math.sqrt (e * y);
+ if (u < 0.5)
+ return -t;
+ else
+ return t;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns an approximation of $F^{-1}(u)$, where $F$ is the
+ Student $t$-distribution function with $n$ degrees of freedom.
+ Gives at least 5 decimal digits of precision when $n \ge 3$
+ \latex{(see \cite{tHIL70a})}.
+ Uses exact formulae for $n=1$ and $n=2$.
+ \end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/TriangularDist.java b/source/umontreal/iro/lecuyer/probdist/TriangularDist.java
new file mode 100644
index 0000000..1248080
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/TriangularDist.java
@@ -0,0 +1,432 @@
+
+
+/*
+ * Class: TriangularDist
+ * Description: triangular distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import java.util.Arrays;
+
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>triangular</EM> distribution with domain <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> and <SPAN CLASS="textit">mode</SPAN>
+ * (or shape parameter) <SPAN CLASS="MATH"><I>m</I></SPAN>, where
+ * <SPAN CLASS="MATH"><I>a</I> <= <I>m</I> <= <I>b</I></SPAN>.
+ * The density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">2(<I>x</I> - <I>a</I>)/[(<I>b</I> - <I>a</I>)(<I>m</I> - <I>a</I>)]</TD>
+ * <TD ALIGN="LEFT"> for <I>a</I> <= <I>x</I> <= <I>m</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">2(<I>b</I> - <I>x</I>)/[(<I>b</I> - <I>a</I>)(<I>b</I> - <I>m</I>)]</TD>
+ * <TD ALIGN="LEFT"> for <I>m</I> <= <I>x</I> <= <I>b</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">0</TD>
+ * <TD ALIGN="LEFT"> elsewhere,</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ * the distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">0</TD>
+ * <TD ALIGN="LEFT"> for <I>x</I> < <I>a</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">(<I>x</I> - <I>a</I>)<SUP>2</SUP>/[(<I>b</I> - <I>a</I>)(<I>m</I> - <I>a</I>)]</TD>
+ * <TD ALIGN="LEFT"> if <I>a</I> <= <I>x</I> <= <I>m</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">1 - (<I>b</I> - <I>x</I>)<SUP>2</SUP>/[(<I>b</I> - <I>a</I>)(<I>b</I> - <I>m</I>)]</TD>
+ * <TD ALIGN="LEFT"> if <I>m</I> <= <I>x</I> <= <I>b</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I>(<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">1</TD>
+ * <TD ALIGN="LEFT"> for <I>x</I> > <I>b</I>,</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ * and the inverse distribution function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I><SUP>-1</SUP>(<I>u</I>) =</TD>
+ * <TD ALIGN="LEFT"><I>a</I> + ((b - a)(m - a)u)<SUP>1/2</SUP></TD>
+ * <TD ALIGN="LEFT"> if 0 <= <I>u</I> <= (<I>m</I> - <I>a</I>)/(<I>b</I> - <I>a</I>),</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>F</I><SUP>-1</SUP>(<I>u</I>) =</TD>
+ * <TD ALIGN="LEFT"><I>b</I> - ((b - a)(b - m)(1 - u))<SUP>1/2</SUP></TD>
+ * <TD ALIGN="LEFT"> if (<I>m</I> - <I>a</I>)/(<I>b</I> - <I>a</I> <= <I>u</I> <= 1.</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ *
+ */
+public class TriangularDist extends ContinuousDistribution {
+ private double a;
+ private double b;
+ private double m;
+
+
+
+ /**
+ * Constructs a <TT>TriangularDist</TT> object with default parameters
+ * <SPAN CLASS="MATH"><I>a</I> = 0</SPAN>, <SPAN CLASS="MATH"><I>b</I> = 1</SPAN>, and <SPAN CLASS="MATH"><I>m</I> = 0.5</SPAN>.
+ *
+ */
+ public TriangularDist() {
+ setParams (0.0, 1.0, 0.5);
+ }
+
+
+ /**
+ * Constructs a <TT>TriangularDist</TT> object with parameters <SPAN CLASS="MATH"><I>a</I> = 0</SPAN> ,
+ * <SPAN CLASS="MATH"><I>b</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> = <TT>m</TT>.
+ *
+ */
+ public TriangularDist (double m) {
+ setParams (0.0, 1.0, m);
+ }
+
+
+ /**
+ * Constructs a <TT>TriangularDist</TT> object with
+ * parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN>.
+ *
+ */
+ public TriangularDist (double a, double b, double m) {
+ setParams (a, b, m);
+ }
+
+
+ public double density (double x) {
+ return density (a, b, m, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, b, m, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, b, m, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (a, b, m, u);
+ }
+
+ public double getMean() {
+ return TriangularDist.getMean (a, b, m);
+ }
+
+ public double getVariance() {
+ return TriangularDist.getVariance (a, b, m);
+ }
+
+ public double getStandardDeviation() {
+ return TriangularDist.getStandardDeviation (a, b, m);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (double a, double b, double m, double x) {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (x < a || x > b)
+ return 0.0;
+ else if (x <= m && m != a)
+ return 2.0*(x - a)/((b - a)*(m - a));
+ else
+ return 2.0*(b - x)/((b - a)*(b - m));
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double a, double b, double m, double x) {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (x <= a)
+ return 0.0;
+ else if (x <= m && m != a)
+ return (x - a)*(x - a)/((b - a)*(m - a));
+ else if (x < b)
+ return 1.0 - (b - x)*(b - x)/((b - a)*(b - m));
+ else
+ return 1.0;
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (double a, double b, double m, double x) {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (x <= a)
+ return 1.0;
+ else if (x <= m && m != a)
+ return 1.0 - (x - a)*(x - a)/((b - a)*(m - a));
+ else if (x < b)
+ return (b - x)*(b - x)/((b - a)*(b - m));
+ else
+ return 0.0;
+ }
+
+
+ /**
+ * Computes the inverse distribution function.
+ *
+ */
+ public static double inverseF (double a, double b, double m, double u) {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return a;
+ if (u >= 1.0)
+ return b;
+ // the code is taken and adapted from unuran
+ // file /distributions/c_triangular_gen.c
+ double h = (m - a)/(b - a);
+ return u <= h && m != a ? a + Math.sqrt ((b - a)*(m - a)*u)
+ : b - Math.sqrt ((b - a)*(b - m)*(1 - u));
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH"><I>m</I></SPAN> of the triangular distribution using the
+ * maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimated parameter is returned in a one-element
+ * array: [<SPAN CLASS="MATH">hat(m)</SPAN>]. See.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @param a lower limit of range
+ *
+ * @param b upper limit of range
+ *
+ * @return returns the parameter [<SPAN CLASS="MATH"><I>m</I></SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n, double a, double b) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ double[] Y = new double[n]; // sorted x[i]
+ System.arraycopy (x, 0, Y, 0, n);
+ Arrays.sort (Y);
+
+ int rmax = -1;
+ double prodmax = -1.0e300;
+ final double ba = b - a;
+ double z;
+ int i;
+ for (int r = 0; r < n; r++) {
+ z = (Y[r] - a) / ba;
+ if ((z <= (double)r/n) || (z >= (double)(r + 1)/n))
+ continue; // MLE cannot be there
+ double prod = 1.0;
+ double d = Y[r] - a;
+ for (i = 0; i < r; i++)
+ prod *= (Y[i] - a)/d;
+
+ d = b - Y[r];
+ for (i = r+1; i < n; i++)
+ prod *= (b - Y[i])/d;
+
+ if (prod > prodmax) {
+ prodmax = prod;
+ rmax = r;
+ }
+ }
+
+ if (rmax < 0)
+ throw new UnsupportedOperationException (
+ " data cannot fit a triangular distribution");
+
+ double[] param = new double[1];
+ param[0] = Y[rmax];
+ return param;
+ }
+
+
+ /**
+ * Creates a new instance of a triangular distribution with parameters
+ * <TT>a</TT> and <TT>b</TT>. <SPAN CLASS="MATH"><I>m</I></SPAN> is estimated using the maximum
+ * likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @param a lower limit of range
+ *
+ * @param b upper limit of range
+ *
+ *
+ */
+ public static TriangularDist getInstanceFromMLE (double[] x, int n,
+ double a, double b) {
+ double param[] = getMLE (x, n, a, b);
+ return new TriangularDist (a, b, param[0]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (<I>a</I> + <I>b</I> + <I>m</I>)/3</SPAN>
+ * of the triangular distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>m</I></SPAN>.
+ *
+ * @return the mean of the triangular distribution
+ *
+ */
+ public static double getMean (double a, double b, double m) {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+
+ return ((a + b + m) / 3.0);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (<I>a</I><SUP>2</SUP> + <I>b</I><SUP>2</SUP> + <I>m</I><SUP>2</SUP> - <I>ab</I> - <I>am</I> - <I>bm</I>)/18</SPAN>
+ * of the triangular distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>m</I></SPAN>.
+ *
+ * @return the variance of the triangular distribution
+ *
+ */
+ public static double getVariance (double a, double b, double m) {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+
+ return ((a * a + b * b + m * m - a * b - a * m - b * m) / 18.0);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the triangular distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>m</I></SPAN>.
+ *
+ * @return the standard deviation of the triangular distribution
+ *
+ */
+ public static double getStandardDeviation (double a, double b, double m) {
+ return Math.sqrt (TriangularDist.getVariance (a, b, m));
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>a</I></SPAN> for this object.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN> for this object.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>m</I></SPAN> for this object.
+ *
+ */
+ public double getM() {
+ return m;
+ }
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> for this object.
+ *
+ */
+ public void setParams (double a, double b, double m) {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ supportA = a;
+ supportB = b;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>m</I></SPAN>].
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {a, b, m};
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + ", b = " + b + ", m = " + m;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/TriangularDist.tex b/source/umontreal/iro/lecuyer/probdist/TriangularDist.tex
new file mode 100644
index 0000000..f2241ff
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/TriangularDist.tex
@@ -0,0 +1,430 @@
+\defmodule {TriangularDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em triangular\/} distribution\latex{ (see \cite[page 297]{tJOH95b}
+and \cite[page 317]{sLAW00a})} with domain $[a,b]$ and \emph{mode}
+(or shape parameter) $m$, where $a\le m\le b$.
+The density function is
+\begin{htmlonly}
+\[\begin{array}{rll}
+ f(x) =& 2(x-a)/[(b-a)(m-a)]&\qquad\mbox{ for }a\le x\le m,\\
+ f(x) =& 2(b-x)/[(b-a)(b-m)]&\qquad\mbox{ for }m\le x\le b, \\
+ f(x) =& 0&\qquad\mbox{ elsewhere,}
+\end{array}\]
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \left \{\begin{array}{ll}
+ \frac {2(x-a)}{(b-a)(m-a)} & \mbox { if } a\le x\le m, \\ [5pt]
+ \frac {2(b-x)}{(b-a)(b-m)} & \mbox { if } m\le x\le b, \\ [5pt]
+ 0 & \mbox { elsewhere, }
+ \end{array}\right. \eqlabel{eq:ftrian}
+\endeq
+\end{latexonly}
+the distribution function is
+\begin{htmlonly}
+\[\begin{array}{rll}
+F (x) =& 0 &\qquad\mbox { for } x < a, \\ [5pt]
+F (x) =& (x - a)^2/[(b - a)(m - a)] &\qquad\mbox { if } a\le x\le m, \\ [5pt]
+F (x) =& 1 - (b - x)^2/[(b - a)(b - m)] &\qquad\mbox { if } m\le x\le b, \\[5pt]
+F (x) =& 1 &\qquad\mbox { for } x > b,
+\end{array}\]
+\end{htmlonly}
+\begin{latexonly}
+\eq
+F (x) = \left\{\begin{array}{ll}
+ 0 & \mbox { for } x < a, \\ [5pt]
+ \frac{(x - a)^2}{(b - a)(m - a)} & \mbox { if } a\le x\le m, \\ [5pt]
+ 1 - \frac{(b - x)^2}{(b - a)(b - m)} & \mbox { if } m\le x\le b, \\[5pt]
+ 1 & \mbox { for } x > b,
+\end{array}\right.
+\endeq
+\end{latexonly}
+and the inverse distribution function is given by
+\begin{htmlonly}
+\[\begin{array}{rll}
+F^{-1}(u) =& a + \sqrt{(b - a)(m - a)u} &\qquad\mbox { if } 0\le u\le (m-a)/(b-a),
+\\ [5pt]
+F^{-1}(u)=& b - \sqrt{(b - a)(b - m)(1 - u)} &\qquad\mbox { if } (m-a)/(b-a\le u
+\le 1.
+\end{array}\]
+\end{htmlonly}
+\begin{latexonly}
+\eq
+F^{-1}(u) = \left\{\begin{array}{ll}
+ a + \sqrt{(b - a)(m - a)u} & \mbox { if } 0\le u\le \frac{m-a}{b-a},
+\\ [5pt]
+ b - \sqrt{(b - a)(b - m)(1 - u)} & \mbox { if } \frac{m-a}{b-a}\le u
+\le 1.
+\end{array}\right.
+\endeq
+\end{latexonly}
+
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: TriangularDist
+ * Description: triangular distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+
+import java.util.Arrays;
+\end{hide}
+
+
+public class TriangularDist extends ContinuousDistribution\begin{hide} {
+ private double a;
+ private double b;
+ private double m;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public TriangularDist()\begin{hide} {
+ setParams (0.0, 1.0, 0.5);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{TriangularDist} object with default parameters
+ $a=0$, $b=1$, and $m=0.5$.
+ \end{tabb}
+\begin{code}
+
+ public TriangularDist (double m)\begin{hide} {
+ setParams (0.0, 1.0, m);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{TriangularDist} object with parameters $a = 0$ ,
+ $b = 1$ and $m$ = \texttt{m}.
+ \end{tabb}
+\begin{code}
+
+ public TriangularDist (double a, double b, double m)\begin{hide} {
+ setParams (a, b, m);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{TriangularDist} object with
+ parameters $a$, $b$ and $m$.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (a, b, m, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, b, m, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, b, m, x);
+ }
+
+ public double inverseF (double u){
+ return inverseF (a, b, m, u);
+ }
+
+ public double getMean() {
+ return TriangularDist.getMean (a, b, m);
+ }
+
+ public double getVariance() {
+ return TriangularDist.getVariance (a, b, m);
+ }
+
+ public double getStandardDeviation() {
+ return TriangularDist.getStandardDeviation (a, b, m);
+ }\end{hide}
+
+ public static double density (double a, double b, double m, double x)\begin{hide} {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (x < a || x > b)
+ return 0.0;
+ else if (x <= m && m != a)
+ return 2.0*(x - a)/((b - a)*(m - a));
+ else
+ return 2.0*(b - x)/((b - a)*(b - m));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double a, double b, double m, double x)\begin{hide} {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (x <= a)
+ return 0.0;
+ else if (x <= m && m != a)
+ return (x - a)*(x - a)/((b - a)*(m - a));
+ else if (x < b)
+ return 1.0 - (b - x)*(b - x)/((b - a)*(b - m));
+ else
+ return 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double a, double b, double m, double x)\begin{hide} {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (x <= a)
+ return 1.0;
+ else if (x <= m && m != a)
+ return 1.0 - (x - a)*(x - a)/((b - a)*(m - a));
+ else if (x < b)
+ return (b - x)*(b - x)/((b - a)*(b - m));
+ else
+ return 0.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double a, double b, double m, double u)\begin{hide} {
+ if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u is not in [0,1]");
+ if (u <= 0.0)
+ return a;
+ if (u >= 1.0)
+ return b;
+ // the code is taken and adapted from unuran
+ // file /distributions/c_triangular_gen.c
+ double h = (m - a)/(b - a);
+ return u <= h && m != a ? a + Math.sqrt ((b - a)*(m - a)*u)
+ : b - Math.sqrt ((b - a)*(b - m)*(1 - u));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n, double a, double b)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ double[] Y = new double[n]; // sorted x[i]
+ System.arraycopy (x, 0, Y, 0, n);
+ Arrays.sort (Y);
+
+ int rmax = -1;
+ double prodmax = -1.0e300;
+ final double ba = b - a;
+ double z;
+ int i;
+ for (int r = 0; r < n; r++) {
+ z = (Y[r] - a) / ba;
+ if ((z <= (double)r/n) || (z >= (double)(r + 1)/n))
+ continue; // MLE cannot be there
+ double prod = 1.0;
+ double d = Y[r] - a;
+ for (i = 0; i < r; i++)
+ prod *= (Y[i] - a)/d;
+
+ d = b - Y[r];
+ for (i = r+1; i < n; i++)
+ prod *= (b - Y[i])/d;
+
+ if (prod > prodmax) {
+ prodmax = prod;
+ rmax = r;
+ }
+ }
+
+ if (rmax < 0)
+ throw new UnsupportedOperationException (
+ " data cannot fit a triangular distribution");
+
+ double[] param = new double[1];
+ param[0] = Y[rmax];
+ return param;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $m$ of the triangular distribution using the
+ maximum likelihood method, from the $n$ observations $x[i]$,
+ $i = 0, 1,\ldots, n-1$. The estimated parameter is returned in a one-element
+ array: [$\hat m$]. See \cite{tOLI72a,tHUA07a,tKOT04a}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \param{a}{lower limit of range}
+ \param{b}{upper limit of range}
+ \return{returns the parameter [$m$]}
+\end{htmlonly}
+\begin{code}
+
+ public static TriangularDist getInstanceFromMLE (double[] x, int n,
+ double a, double b)\begin{hide} {
+ double param[] = getMLE (x, n, a, b);
+ return new TriangularDist (a, b, param[0]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a triangular distribution with parameters
+ \texttt{a} and \texttt{b}. $m$ is estimated using the maximum
+ likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \param{a}{lower limit of range}
+ \param{b}{upper limit of range}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double a, double b, double m)\begin{hide} {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+
+ return ((a + b + m) / 3.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = (a + b + m)/3$
+ of the triangular distribution with parameters $a$, $b$, $m$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the triangular distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double a, double b, double m)\begin{hide} {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+
+ return ((a * a + b * b + m * m - a * b - a * m - b * m) / 18.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+ $\mbox{Var}[X] = (a^2 + b^2 + m^2 - ab - am - bm)/18$
+ of the triangular distribution with parameters $a$, $b$, $m$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the triangular distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double a, double b, double m)\begin{hide} {
+ return Math.sqrt (TriangularDist.getVariance (a, b, m));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the triangular distribution with parameters $a$, $b$, $m$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the triangular distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $a$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $b$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double getM()\begin{hide} {
+ return m;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $m$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double a, double b, double m)\begin{hide} {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ supportA = a;
+ supportB = b;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $a$, $b$ and $m$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {a, b, m};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$a$, $b$, $m$].
+\end{tabb}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : a = " + a + ", b = " + b + ", m = " + m;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/TruncatedDist.java b/source/umontreal/iro/lecuyer/probdist/TruncatedDist.java
new file mode 100644
index 0000000..9880097
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/TruncatedDist.java
@@ -0,0 +1,292 @@
+
+
+/*
+ * Class: TruncatedDist
+ * Description: an arbitrary continuous distribution truncated
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * This container class takes an arbitrary continuous distribution and truncates
+ * it to an interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>, where <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> can be finite or infinite.
+ * If the original density and distribution function are <SPAN CLASS="MATH"><I>f</I><SUB>0</SUB></SPAN> and <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB></SPAN>,
+ * the new ones are <SPAN CLASS="MATH"><I>f</I></SPAN> and <SPAN CLASS="MATH"><I>F</I></SPAN>, defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>f</I><SUB>0</SUB>(<I>x</I>)/(<I>F</I><SUB>0</SUB>(<I>b</I>) - <I>F</I><SUB>0</SUB>(<I>a</I>)) for <I>a</I> <= <I>x</I> <= <I>b</I>
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> elsewhere, and
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = (<I>F</I><SUB>0</SUB>(<I>x</I>) - <I>F</I><SUB>0</SUB>(<I>a</I>))/(<I>F</I><SUB>0</SUB>(<I>b</I>) - <I>F</I><SUB>0</SUB>(<I>a</I>)) for <I>a</I> <= <I>x</I> <= <I>b</I>.
+ * </DIV><P></P>
+ * The inverse distribution function of the truncated distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>F</I><SUB>0</SUB><SUP>-1</SUP>(<I>F</I><SUB>0</SUB>(<I>a</I>) + (<I>F</I><SUB>0</SUB>(<I>b</I>) - <I>F</I><SUB>0</SUB>(<I>a</I>))<I>u</I>)
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB><SUP>-1</SUP></SPAN> is the inverse distribution function of the
+ * original distribution.
+ *
+ */
+public class TruncatedDist extends ContinuousDistribution {
+ public static int NUMINTERVALS = 500;
+
+ private ContinuousDistribution dist0; // The original (non-truncated) dist.
+ private double fa; // F(a)
+ private double fb; // F(b)
+ private double barfb; // bar F(b)
+ private double fbfa; // F(b) - F(a)
+ private double a;
+ private double b;
+ private double approxMean;
+ private double approxVariance;
+ private double approxStandardDeviation;
+
+
+ /**
+ * Constructs a new distribution by truncating distribution <TT>dist</TT>
+ * to the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>. Restrictions: <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> must be finite.
+ *
+ */
+ public TruncatedDist (ContinuousDistribution dist, double a, double b) {
+ setParams (dist, a, b);
+ }
+
+
+ public double density (double x) {
+ if ((x < a) || (x > b))
+ return 0;
+ return dist0.density (x) / fbfa;
+ }
+
+ public double cdf (double x) {
+ if (x <= a)
+ return 0;
+ else if (x >= b)
+ return 1;
+ else
+ return (dist0.cdf (x) - fa) / fbfa;
+ }
+
+ public double barF (double x) {
+ if (x <= a)
+ return 1;
+ else if (x >= b)
+ return 0;
+ else
+ return (dist0.barF (x) - barfb) / fbfa;
+ }
+
+ public double inverseF (double u) {
+ if (u == 0)
+ return a;
+ if (u == 1)
+ return b;
+ return dist0.inverseF (fa + fbfa * u);
+ }
+
+ /**
+ * Returns an approximation of the mean computed with the
+ * Simpson <SPAN CLASS="MATH">1/3</SPAN> numerical integration rule.
+ *
+ * @exception UnsupportedOperationException the mean of the truncated distribution is unknown
+ *
+ *
+ */
+ public double getMean() {
+ if (Double.isNaN (approxMean))
+ throw new UnsupportedOperationException("Undefined mean");
+ return approxMean;
+ }
+
+
+ /**
+ * Returns an approximation of the variance computed with the
+ * Simpson <SPAN CLASS="MATH">1/3</SPAN> numerical integration rule.
+ *
+ * @exception UnsupportedOperationException the mean of the truncated distribution is unknown
+ *
+ *
+ */
+ public double getVariance() {
+ if (Double.isNaN (approxVariance))
+ throw new UnsupportedOperationException("Unknown variance");
+ return approxVariance;
+ }
+
+
+ /**
+ * Returns the square root of the approximate variance.
+ *
+ * @exception UnsupportedOperationException the mean of the truncated distribution is unknown
+ *
+ *
+ */
+ public double getStandardDeviation() {
+ if (Double.isNaN (approxStandardDeviation))
+ throw new UnsupportedOperationException("Unknown standard deviation");
+ return approxStandardDeviation;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB>(<I>a</I>)</SPAN>.
+ *
+ */
+ public double getFa() {
+ return fa;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB>(<I>b</I>)</SPAN>.
+ *
+ */
+ public double getFb() {
+ return fb;
+ }
+
+
+ /**
+ * Returns the value of
+ * <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB>(<I>b</I>) - <I>F</I><SUB>0</SUB>(<I>a</I>)</SPAN>,
+ * the area under the truncated density function.
+ *
+ */
+ public double getArea() {
+ return fbfa;
+ }
+
+
+
+ /**
+ * Sets the parameters <TT>dist</TT>, <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> for this object. See the
+ * constructor for details.
+ *
+ */
+ public void setParams (ContinuousDistribution dist, double a, double b) {
+ if (a >= b)
+ throw new IllegalArgumentException ("a must be smaller than b.");
+ this.dist0 = dist;
+ if (a < dist.getXinf())
+ a = dist.getXinf();
+ if (b > dist.getXsup())
+ b = dist.getXsup();
+ supportA = this.a = a;
+ supportB = this.b = b;
+ fa = dist.cdf (a);
+ fb = dist.cdf (b);
+ fbfa = fb - fa;
+ barfb = dist.barF (b);
+
+ if (((a <= dist.getXinf()) && (b >= dist.getXsup())) ||
+ ((a == Double.NEGATIVE_INFINITY) && (b == Double.POSITIVE_INFINITY))) {
+ approxMean = dist.getMean();
+ approxVariance = dist.getVariance();
+ approxStandardDeviation = dist.getStandardDeviation();
+
+ } else {
+ // The mean is the integral of xf*(x) over [a, b].
+ MomentFunction func1 = new MomentFunction (dist, 1);
+ approxMean = MathFunctionUtil.simpsonIntegral (func1, a, b, NUMINTERVALS) / fbfa;
+
+ // Estimate the integral of (x-E[X])^2 f*(x) over [a, b]
+ MomentFunction func2 = new MomentFunction (dist, 2, approxMean);
+ approxVariance = MathFunctionUtil.simpsonIntegral (func2, a, b, NUMINTERVALS) / fbfa;
+
+ approxStandardDeviation = Math.sqrt (approxVariance);
+ }
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in order: [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB>(<I>a</I>)</SPAN>, <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB>(<I>b</I>)</SPAN>,
+ * <SPAN CLASS="MATH"><I>F</I><SUB>0</SUB>(<I>b</I>) - <I>F</I><SUB>0</SUB>(<I>a</I>)</SPAN>].
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {a, b, fa, fb, fbfa};
+ return retour;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current distribution.
+ *
+ */
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + ", b = " + b + ", F(a) = " + fa + ", F(b) = " + fb + ", F(b)-F(a) = " + fbfa;
+ }
+
+
+ private static class MomentFunction implements MathFunction {
+ private ContinuousDistribution dist;
+ private int moment;
+ private double offset;
+
+ public MomentFunction (ContinuousDistribution dist, int moment) {
+ this.dist = dist;
+ this.moment = moment;
+ offset = 0;
+ }
+
+ public MomentFunction (ContinuousDistribution dist, int moment, double offset) {
+ this (dist, moment);
+ this.offset = offset;
+ }
+
+ public double evaluate (double x) {
+ double res = dist.density (x);
+ final double offsetX = x - offset;
+ for (int i = 0; i < moment; i++)
+ res *= offsetX;
+ return res;
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/TruncatedDist.tex b/source/umontreal/iro/lecuyer/probdist/TruncatedDist.tex
new file mode 100644
index 0000000..2e5b580
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/TruncatedDist.tex
@@ -0,0 +1,307 @@
+\defmodule{TruncatedDist}
+
+This container class takes an arbitrary continuous distribution and truncates
+it to an interval $[a,b]$, where $a$ and $b$ can be finite or infinite.
+If the original density and distribution function are $f_0$ and $F_0$,
+the new ones are $f$ and $F$, defined by
+\begin{latexonly}%
+\[
+ f(x) = \frac {f_0(x)}{F_0(b) - F_0(a)} \qquad\mbox { for } a\le x\le b
+\]
+\end{latexonly}
+\begin{htmlonly}
+\[
+ f(x) = f_0(x) / (F_0(b) - F_0(a)) \qquad\mbox { for } a\le x\le b
+\]
+\end{htmlonly}%
+and $f(x)=0$ elsewhere, and
+\begin{latexonly}%
+\[
+ F(x) = \frac {F_0(x)-F_0(a)}{F_0(b)-F_0(a)} \qquad\mbox { for } a\le x\le b.
+\]
+\end{latexonly}
+\begin{htmlonly}
+\[
+ F(x) = (F_0(x)-F_0(a))/(F_0(b)-F_0(a)) \qquad\mbox { for } a\le x\le b.
+\]
+\end{htmlonly}
+
+The inverse distribution function of the truncated distribution is
+\[
+ {F^{-1}}(u) = F_0^{-1}(F_0(a) + (F_0(b) - F_0(a))u)
+\]
+where $F_0^{-1}$ is the inverse distribution function of the
+original distribution.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: TruncatedDist
+ * Description: an arbitrary continuous distribution truncated
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;\begin{hide}
+import umontreal.iro.lecuyer.functions.MathFunctionUtil;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class TruncatedDist extends ContinuousDistribution\begin{hide} {
+ public static int NUMINTERVALS = 500;
+
+ private ContinuousDistribution dist0; // The original (non-truncated) dist.
+ private double fa; // F(a)
+ private double fb; // F(b)
+ private double barfb; // bar F(b)
+ private double fbfa; // F(b) - F(a)
+ private double a;
+ private double b;
+ private double approxMean;
+ private double approxVariance;
+ private double approxStandardDeviation;\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public TruncatedDist (ContinuousDistribution dist, double a, double b)\begin{hide} {
+ setParams (dist, a, b);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new distribution by truncating distribution \texttt{dist}
+ to the interval $[a,b]$. Restrictions: $a$ and $b$ must be finite.
+% If \texttt{a = Double.NEGATIVE\_INFINITY}, $F_0(a)$ is assumed to be 0.
+% If \texttt{b = Double.POSITIVE\_INFINITY}, $F_0(b)$ is assumed to be 1.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}\begin{code}
+
+ public double density (double x) {
+ if ((x < a) || (x > b))
+ return 0;
+ return dist0.density (x) / fbfa;
+ }
+
+ public double cdf (double x) {
+ if (x <= a)
+ return 0;
+ else if (x >= b)
+ return 1;
+ else
+ return (dist0.cdf (x) - fa) / fbfa;
+ }
+
+ public double barF (double x) {
+ if (x <= a)
+ return 1;
+ else if (x >= b)
+ return 0;
+ else
+ return (dist0.barF (x) - barfb) / fbfa;
+ }
+
+ public double inverseF (double u) {
+ if (u == 0)
+ return a;
+ if (u == 1)
+ return b;
+ return dist0.inverseF (fa + fbfa * u);
+ }\end{code}\end{hide}
+\begin{code}
+
+ public double getMean()\begin{hide} {
+ if (Double.isNaN (approxMean))
+ throw new UnsupportedOperationException("Undefined mean");
+ return approxMean;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an approximation of the mean computed with the
+ Simpson $1/3$ numerical integration rule.
+\end{tabb}
+\begin{htmlonly}
+ \exception{UnsupportedOperationException}{the mean of the truncated distribution is unknown}
+\end{htmlonly}
+\begin{code}
+
+ public double getVariance()\begin{hide} {
+ if (Double.isNaN (approxVariance))
+ throw new UnsupportedOperationException("Unknown variance");
+ return approxVariance;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns an approximation of the variance computed with the
+ Simpson $1/3$ numerical integration rule.
+\end{tabb}
+\begin{htmlonly}
+ \exception{UnsupportedOperationException}{the mean of the truncated distribution is unknown}
+\end{htmlonly}
+\begin{code}
+
+ public double getStandardDeviation()\begin{hide} {
+ if (Double.isNaN (approxStandardDeviation))
+ throw new UnsupportedOperationException("Unknown standard deviation");
+ return approxStandardDeviation;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the square root of the approximate variance.
+\end{tabb}
+\begin{htmlonly}
+ \exception{UnsupportedOperationException}{the mean of the truncated distribution is unknown}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $a$.
+\end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $b$.
+\end{tabb}
+\begin{code}
+
+ public double getFa()\begin{hide} {
+ return fa;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $F_0(a)$.
+\end{tabb}
+\begin{code}
+
+ public double getFb()\begin{hide} {
+ return fb;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $F_0(b)$.
+\end{tabb}
+\begin{code}
+
+ public double getArea()\begin{hide} {
+ return fbfa;
+ }
+\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $F_0(b) - F_0(a)$,
+ the area under the truncated density function.
+\end{tabb}
+\begin{code}
+
+ public void setParams (ContinuousDistribution dist, double a, double b)\begin{hide} {
+ if (a >= b)
+ throw new IllegalArgumentException ("a must be smaller than b.");
+ this.dist0 = dist;
+ if (a < dist.getXinf())
+ a = dist.getXinf();
+ if (b > dist.getXsup())
+ b = dist.getXsup();
+ supportA = this.a = a;
+ supportB = this.b = b;
+ fa = dist.cdf (a);
+ fb = dist.cdf (b);
+ fbfa = fb - fa;
+ barfb = dist.barF (b);
+
+ if (((a <= dist.getXinf()) && (b >= dist.getXsup())) ||
+ ((a == Double.NEGATIVE_INFINITY) && (b == Double.POSITIVE_INFINITY))) {
+ approxMean = dist.getMean();
+ approxVariance = dist.getVariance();
+ approxStandardDeviation = dist.getStandardDeviation();
+
+ } else {
+ // The mean is the integral of xf*(x) over [a, b].
+ MomentFunction func1 = new MomentFunction (dist, 1);
+ approxMean = MathFunctionUtil.simpsonIntegral (func1, a, b, NUMINTERVALS) / fbfa;
+
+ // Estimate the integral of (x-E[X])^2 f*(x) over [a, b]
+ MomentFunction func2 = new MomentFunction (dist, 2, approxMean);
+ approxVariance = MathFunctionUtil.simpsonIntegral (func2, a, b, NUMINTERVALS) / fbfa;
+
+ approxStandardDeviation = Math.sqrt (approxVariance);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameters \texttt{dist}, $a$ and $b$ for this object. See the
+ constructor for details.
+\end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {a, b, fa, fb, fbfa};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in order: [$a$, $b$, $F_0(a)$, $F_0(b)$, $F_0(b) - F_0(a)$].
+\end{tabb}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : a = " + a + ", b = " + b + ", F(a) = " + fa + ", F(b) = " + fb + ", F(b)-F(a) = " + fbfa;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static class MomentFunction implements MathFunction {
+ private ContinuousDistribution dist;
+ private int moment;
+ private double offset;
+
+ public MomentFunction (ContinuousDistribution dist, int moment) {
+ this.dist = dist;
+ this.moment = moment;
+ offset = 0;
+ }
+
+ public MomentFunction (ContinuousDistribution dist, int moment, double offset) {
+ this (dist, moment);
+ this.offset = offset;
+ }
+
+ public double evaluate (double x) {
+ double res = dist.density (x);
+ final double offsetX = x - offset;
+ for (int i = 0; i < moment; i++)
+ res *= offsetX;
+ return res;
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/UniformDist.java b/source/umontreal/iro/lecuyer/probdist/UniformDist.java
new file mode 100644
index 0000000..0142a1d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/UniformDist.java
@@ -0,0 +1,316 @@
+
+
+/*
+ * Class: UniformDist
+ * Description: uniform distribution over the reals
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for
+ * the <EM>uniform</EM> distribution
+ * over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 1/(<I>b</I> - <I>a</I>) for <I>a</I> <= <I>x</I> <= <I>b</I>
+ * </DIV><P></P>
+ * and 0 elsewhere. The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = (<I>x</I> - <I>a</I>)/(<I>b</I> - <I>a</I>) for <I>a</I> <= <I>x</I> <= <I>b</I>
+ * </DIV><P></P>
+ * and its inverse is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>a</I> + (<I>b</I> - <I>a</I>)<I>u</I> for 0 <= <I>u</I> <= 1.
+ * </DIV><P></P>
+ *
+ */
+public class UniformDist extends ContinuousDistribution {
+ private double a;
+ private double b;
+
+
+ /**
+ * Constructs a uniform distribution over the interval
+ * <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>) = (0, 1)</SPAN>.
+ *
+ */
+ public UniformDist() {
+ setParams (0.0, 1.0);
+ }
+
+
+ /**
+ * Constructs a uniform distribution over the interval <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>)</SPAN>.
+ *
+ */
+ public UniformDist (double a, double b) {
+ setParams (a, b);
+ }
+
+
+ public double density (double x) {
+ return density (a, b, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, b, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, b, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, b, u);
+ }
+
+ public double getMean() {
+ return UniformDist.getMean (a, b);
+ }
+
+ public double getVariance() {
+ return UniformDist.getVariance (a, b);
+ }
+
+ public double getStandardDeviation() {
+ return UniformDist.getStandardDeviation (a, b);
+ }
+
+ /**
+ * Computes the uniform density function
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I>)</SPAN>.
+ *
+ */
+ public static double density (double a, double b, double x) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ if (x <= a || x >= b)
+ return 0.0;
+ return 1.0 / (b - a);
+ }
+
+
+ /**
+ * Computes the uniform distribution function as in.
+ *
+ */
+ public static double cdf (double a, double b, double x) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ if (x <= a)
+ return 0.0;
+ if (x >= b)
+ return 1.0;
+ return (x - a)/(b - a);
+ }
+
+
+ /**
+ * Computes the uniform complementary distribution function
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double barF (double a, double b, double x) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ if (x <= a)
+ return 1.0;
+ if (x >= b)
+ return 0.0;
+ return (b - x)/(b - a);
+ }
+
+
+ /**
+ * Computes the inverse of the uniform distribution function.
+ *
+ */
+ public static double inverseF (double a, double b, double u) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+
+ if (u <= 0.0)
+ return a;
+ if (u >= 1.0)
+ return b;
+ return a + (b - a)*u;
+ }
+
+
+ /**
+ * Estimates the parameter <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>)</SPAN> of the uniform distribution
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(a)</SPAN>, <SPAN CLASS="MATH">hat(b)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[2];
+ parameters[0] = Double.POSITIVE_INFINITY;
+ parameters[1] = Double.NEGATIVE_INFINITY;
+ for (int i = 0; i < n; i++) {
+ if (x[i] < parameters[0])
+ parameters[0] = x[i];
+ if (x[i] > parameters[1])
+ parameters[1] = x[i];
+ }
+
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a uniform distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static UniformDist getInstanceFromMLE (double[] x, int n) {
+ double parameters[] = getMLE (x, n);
+ return new UniformDist (parameters[0], parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (<I>a</I> + <I>b</I>)/2</SPAN>
+ * of the uniform distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @return the mean of the uniform distribution
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (<I>a</I> + <I>b</I>)/2</SPAN>
+ *
+ */
+ public static double getMean (double a, double b) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+
+ return ((a + b) / 2);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (<I>b</I> - <I>a</I>)<SUP>2</SUP>/12</SPAN>
+ * of the uniform distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @return the variance of the uniform distribution
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = (<I>b</I> - <I>a</I>)<SUP>2</SUP>/12</SPAN>
+ *
+ */
+ public static double getVariance (double a, double b) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+
+ return ((b - a) * (b - a) / 12);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the uniform distribution with parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ * @return the standard deviation of the uniform distribution
+ *
+ */
+ public static double getStandardDeviation (double a, double b) {
+ return Math.sqrt (UniformDist.getVariance (a, b));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> for this object.
+ *
+ */
+ public void setParams (double a, double b) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ this.a = a;
+ this.b = b;
+ supportA = a;
+ supportB = b;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {a, b};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : a = " + a + ", b = " + b;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/UniformDist.tex b/source/umontreal/iro/lecuyer/probdist/UniformDist.tex
new file mode 100644
index 0000000..046f375
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/UniformDist.tex
@@ -0,0 +1,326 @@
+\defmodule {UniformDist}
+
+Extends the class \class{ContinuousDistribution} for
+the {\em uniform\/} distribution \cite[page 276]{tJOH95b}
+over the interval $[a,b]$.
+Its density is
+\eq
+ f(x) = 1/(b-a) \qquad\mbox{ for } a\le x\le b \eqlabel{eq:funiform}
+\endeq
+and 0 elsewhere. The distribution function is
+\eq
+ F(x) = (x-a)/(b-a) \qquad\mbox { for } a\le x\le b
+ \eqlabel{eq:cdfuniform}
+\endeq
+and its inverse is
+\eq
+ F^{-1}(u) = a + (b - a)u
+ \qquad\mbox{for }0 \le u \le 1. \eqlabel{eq:cdinvfuniform}
+\endeq
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: UniformDist
+ * Description: uniform distribution over the reals
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class UniformDist extends ContinuousDistribution\begin{hide} {
+ private double a;
+ private double b;
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public UniformDist()\begin{hide} {
+ setParams (0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a uniform distribution over the interval $(a,b) = (0,1)$.
+ \end{tabb}
+\begin{code}
+
+ public UniformDist (double a, double b)\begin{hide} {
+ setParams (a, b);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a uniform distribution over the interval $(a,b)$.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (a, b, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (a, b, x);
+ }
+
+ public double barF (double x) {
+ return barF (a, b, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (a, b, u);
+ }
+
+ public double getMean() {
+ return UniformDist.getMean (a, b);
+ }
+
+ public double getVariance() {
+ return UniformDist.getVariance (a, b);
+ }
+
+ public double getStandardDeviation() {
+ return UniformDist.getStandardDeviation (a, b);
+ }\end{hide}
+
+ public static double density (double a, double b, double x)\begin{hide} {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ if (x <= a || x >= b)
+ return 0.0;
+ return 1.0 / (b - a);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the uniform density function
+ $f(x)$\latex{ in (\ref{eq:funiform})}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double a, double b, double x)\begin{hide} {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ if (x <= a)
+ return 0.0;
+ if (x >= b)
+ return 1.0;
+ return (x - a)/(b - a);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the uniform distribution function as in (\ref{eq:cdfuniform}).
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double a, double b, double x)\begin{hide} {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ if (x <= a)
+ return 1.0;
+ if (x >= b)
+ return 0.0;
+ return (b - x)/(b - a);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the uniform complementary distribution function
+ $\bar{F}(x)$.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double a, double b, double u)\begin{hide} {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+
+ if (u <= 0.0)
+ return a;
+ if (u >= 1.0)
+ return b;
+ return a + (b - a)*u;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the uniform distribution function
+ (\ref{eq:cdinvfuniform}).
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[2];
+ parameters[0] = Double.POSITIVE_INFINITY;
+ parameters[1] = Double.NEGATIVE_INFINITY;
+ for (int i = 0; i < n; i++) {
+ if (x[i] < parameters[0])
+ parameters[0] = x[i];
+ if (x[i] > parameters[1])
+ parameters[1] = x[i];
+ }
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameter $(a, b)$ of the uniform distribution
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$a$, $b$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values
+ $(\hat{a}$, $\hat{b})$ that satisfy the equations
+ \begin{eqnarray*}
+ \hat{a} & = & \min_i \{x_i\}\\
+ \hat{b} & = & \max_i \{x_i\}.
+ \end{eqnarray*}
+ See \cite[page 300]{sLAW00a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{a}$, $\hat{b}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static UniformDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double parameters[] = getMLE (x, n);
+ return new UniformDist (parameters[0], parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a uniform distribution with parameters $a$ and $b$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double a, double b)\begin{hide} {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+
+ return ((a + b) / 2);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = (a + b)/2$
+ of the uniform distribution with parameters $a$ and $b$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the uniform distribution $E[X] = (a + b) / 2$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double a, double b)\begin{hide} {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+
+ return ((b - a) * (b - a) / 12);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance $\mbox{Var}[X] = (b - a)^2/12$
+ of the uniform distribution with parameters $a$ and $b$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the uniform distribution $\mbox{Var}[X] = (b - a)^2 / 12$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double a, double b)\begin{hide} {
+ return Math.sqrt (UniformDist.getVariance (a, b));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the uniform distribution with parameters $a$ and $b$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the uniform distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $a$.
+ \end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $b$.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double a, double b)\begin{hide} {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ this.a = a;
+ this.b = b;
+ supportA = a;
+ supportB = b;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the parameters $a$ and $b$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {a, b};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$a$, $b$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : a = " + a + ", b = " + b;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/UniformIntDist.java b/source/umontreal/iro/lecuyer/probdist/UniformIntDist.java
new file mode 100644
index 0000000..76aac0e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/UniformIntDist.java
@@ -0,0 +1,312 @@
+
+
+/*
+ * Class: UniformIntDist
+ * Description: discrete uniform distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+
+/**
+ * Extends the class {@link DiscreteDistributionInt} for
+ * the <EM>discrete uniform</EM> distribution over the range <SPAN CLASS="MATH">[<I>i</I>, <I>j</I>]</SPAN>.
+ * Its mass function is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = 1/(<I>j</I> - <I>i</I> + 1) for <I>x</I> = <I>i</I>, <I>i</I> + 1,…, <I>j</I>
+ * </DIV><P></P>
+ * and 0 elsewhere. The distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = (floor(<I>x</I>) - <I>i</I> + 1)/(<I>j</I> - <I>i</I> + 1) for <I>i</I> <= <I>x</I> <= <I>j</I>
+ * </DIV><P></P>
+ * and its inverse is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = <I>i</I> + (<I>j</I> - <I>i</I> + 1)<I>u</I> for 0 <= <I>u</I> <= 1.
+ * </DIV><P></P>
+ *
+ */
+public class UniformIntDist extends DiscreteDistributionInt {
+ protected int i;
+ protected int j;
+
+
+ /**
+ * Constructs a discrete uniform distribution over the interval <SPAN CLASS="MATH">[<I>i</I>, <I>j</I>]</SPAN>.
+ *
+ */
+ public UniformIntDist (int i, int j) {
+ setParams (i, j);
+ }
+
+
+ public double prob (int x) {
+ return prob (i, j, x);
+ }
+
+ public double cdf (int x) {
+ return cdf (i, j, x);
+ }
+
+ public double barF (int x) {
+ return barF (i, j, x);
+ }
+
+ public int inverseFInt (double u) {
+ return inverseF (i, j, u);
+ }
+
+ public double getMean() {
+ return getMean (i, j);
+ }
+
+ public double getVariance() {
+ return getVariance (i, j);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (i, j);
+ }
+
+ /**
+ * Computes the discrete uniform probability <SPAN CLASS="MATH"><I>p</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double prob (int i, int j, int x) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ if (x < i || x > j)
+ return 0.0;
+
+ return (1.0 / (j - i + 1.0));
+ }
+
+
+ /**
+ * Computes the discrete uniform distribution function
+ * defined in.
+ *
+ */
+ public static double cdf (int i, int j, int x) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ if (x < i)
+ return 0.0;
+ if (x >= j)
+ return 1.0;
+
+ return ((x - i + 1) / (j - i + 1.0));
+ }
+
+
+ /**
+ * Computes the discrete uniform complementary distribution function
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>)</SPAN>.
+ * <SPAN CLASS="textit">WARNING:</SPAN> The complementary distribution function is defined as
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>) = <I>P</I>[<I>X</I> >= <I>x</I>]</SPAN>.
+ *
+ */
+ public static double barF (int i, int j, int x) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ if (x <= i)
+ return 1.0;
+ if (x > j)
+ return 0.0;
+
+ return ((j - x + 1.0) / (j - i + 1.0));
+ }
+
+
+ /**
+ * Computes the inverse of the discrete uniform distribution function.
+ *
+ */
+ public static int inverseF (int i, int j, double u) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+
+ if (u <= 0.0)
+ return i;
+ if (u >= 1.0)
+ return j;
+
+ return i + (int) (u * (j - i + 1.0));
+ }
+
+
+ /**
+ * Estimates the parameters <SPAN CLASS="MATH">(<I>i</I>, <I>j</I>)</SPAN> of the uniform distribution
+ * over integers using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>k</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>k</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>i</I></SPAN>, <SPAN CLASS="MATH"><I>j</I></SPAN>].
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @return returns the parameters [
+ * <SPAN CLASS="MATH">hat(ı)</SPAN>,
+ * <SPAN CLASS="MATH">hat(&jnodot;)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int[] x, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[2];
+ parameters[0] = (double) Integer.MAX_VALUE;
+ parameters[1] = (double) Integer.MIN_VALUE;
+ for (int i = 0; i < n; i++) {
+ if ((double) x[i] < parameters[0])
+ parameters[0] = (double) x[i];
+ if ((double) x[i] > parameters[1])
+ parameters[1] = (double) x[i];
+ }
+ return parameters;
+ }
+
+
+ /**
+ * Creates a new instance of a discrete uniform distribution over integers with parameters
+ * <SPAN CLASS="MATH"><I>i</I></SPAN> and <SPAN CLASS="MATH"><I>j</I></SPAN> estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>k</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>k</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static UniformIntDist getInstanceFromMLE (int[] x, int n) {
+
+ double parameters[] = getMLE (x, n);
+
+ return new UniformIntDist ((int) parameters[0], (int) parameters[1]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (<I>i</I> + <I>j</I>)/2</SPAN>
+ * of the discrete uniform distribution.
+ *
+ * @return the mean of the discrete uniform distribution
+ *
+ */
+ public static double getMean (int i, int j) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ return ((i + j) / 2.0);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = [(<I>j</I> - <I>i</I> + 1)<SUP>2</SUP> -1]/12</SPAN>
+ * of the discrete uniform distribution.
+ *
+ * @return the variance of the discrete uniform distribution
+ *
+ */
+ public static double getVariance (int i, int j) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ return (((j - i + 1.0) * (j - i + 1.0) - 1.0) / 12.0);
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the discrete uniform distribution.
+ *
+ * @return the standard deviation of the discrete uniform distribution
+ *
+ */
+ public static double getStandardDeviation (int i, int j) {
+ return Math.sqrt (UniformIntDist.getVariance (i, j));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ *
+ */
+ public int getI() {
+ return i;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>j</I></SPAN>.
+ *
+ */
+ public int getJ() {
+ return j;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>i</I></SPAN> and <SPAN CLASS="MATH"><I>j</I></SPAN> for this object.
+ *
+ */
+ public void setParams (int i, int j) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ supportA = this.i = i;
+ supportB = this.j = j;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>i</I></SPAN>, <SPAN CLASS="MATH"><I>j</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {i, j};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : i = " + i + ", j = " + j;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/UniformIntDist.tex b/source/umontreal/iro/lecuyer/probdist/UniformIntDist.tex
new file mode 100644
index 0000000..716c1d2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/UniformIntDist.tex
@@ -0,0 +1,346 @@
+\defmodule {UniformIntDist}
+
+Extends the class \class{DiscreteDistributionInt} for
+the {\em discrete uniform\/} distribution over the range $[i,j]$.
+Its mass function is given by
+\begin{htmlonly}
+\eq
+ p(x) = 1 / (j - i + 1) \qquad\mbox{ for } x = i, i + 1, \ldots, j
+ \eqlabel{eq:fmassuniformint}
+\endeq
+and 0 elsewhere. The distribution function is
+\eq
+ F(x) = (\lfloor x\rfloor -i+1)/(j-i+1) \qquad\mbox { for } i\le x\le j
+ \eqlabel{eq:cdfuniformint}
+\endeq
+and its inverse is
+\eq
+ F^{-1}(u) = i + (j - i + 1)u
+ \qquad\mbox{for }0 \le u \le 1. \eqlabel{eq:invuniformint}
+\endeq
+\end{htmlonly}%
+\begin{latexonly}
+\eq
+ p(x) = \frac{1}{j - i + 1} \qquad\mbox{ for } x = i, i + 1, \ldots, j
+ \eqlabel{eq:fmassuniformint}
+\endeq
+and 0 elsewhere. The distribution function is
+\eq
+ F(x) = \left\{\begin{array}{ll}
+ 0, & \mbox { for } x < i\\[5pt]
+ \displaystyle \frac{\lfloor x\rfloor -i+1}{j-i+1}, & \mbox { for } i\le x < j\\[12pt]
+ 1, & \mbox { for } x \ge j.
+ \end{array}\right.
+ \eqlabel{eq:cdfuniformint}
+\endeq
+and its inverse is
+\eq
+ F^{-1}(u) = i + \lfloor (j - i + 1)u\rfloor
+ \qquad\mbox{for }0 \le u \le 1. \eqlabel{eq:invuniformint}
+\endeq
+\end{latexonly}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: UniformIntDist
+ * Description: discrete uniform distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+
+
+public class UniformIntDist extends DiscreteDistributionInt\begin{hide} {
+ protected int i;
+ protected int j;
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public UniformIntDist (int i, int j)\begin{hide} {
+ setParams (i, j);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a discrete uniform distribution over the interval $[i,j]$.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double prob (int x) {
+ return prob (i, j, x);
+ }
+
+ public double cdf (int x) {
+ return cdf (i, j, x);
+ }
+
+ public double barF (int x) {
+ return barF (i, j, x);
+ }
+
+ public int inverseFInt (double u) {
+ return inverseF (i, j, u);
+ }
+
+ public double getMean() {
+ return getMean (i, j);
+ }
+
+ public double getVariance() {
+ return getVariance (i, j);
+ }
+
+ public double getStandardDeviation() {
+ return getStandardDeviation (i, j);
+ }\end{hide}
+
+ public static double prob (int i, int j, int x)\begin{hide} {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ if (x < i || x > j)
+ return 0.0;
+
+ return (1.0 / (j - i + 1.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the discrete uniform probability $p(x)$%
+\latex{ defined in (\ref{eq:fmassuniformint})}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int i, int j, int x)\begin{hide} {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ if (x < i)
+ return 0.0;
+ if (x >= j)
+ return 1.0;
+
+ return ((x - i + 1) / (j - i + 1.0));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the discrete uniform distribution function
+defined in (\ref{eq:cdfuniformint}).
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int i, int j, int x)\begin{hide} {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ if (x <= i)
+ return 1.0;
+ if (x > j)
+ return 0.0;
+
+ return ((j - x + 1.0) / (j - i + 1.0));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the discrete uniform complementary distribution function
+ $\bar{F}(x)$.
+ \emph{WARNING:} The complementary distribution function is defined as
+ $\bar F(x) = P[X \ge x]$.
+ \end{tabb}
+\begin{code}
+
+ public static int inverseF (int i, int j, double u)\begin{hide} {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ if (u > 1.0 || u < 0.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+
+ if (u <= 0.0)
+ return i;
+ if (u >= 1.0)
+ return j;
+
+ return i + (int) (u * (j - i + 1.0));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the discrete uniform distribution function
+ (\ref{eq:invuniformint}).
+ \end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int[] x, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double parameters[] = new double[2];
+ parameters[0] = (double) Integer.MAX_VALUE;
+ parameters[1] = (double) Integer.MIN_VALUE;
+ for (int i = 0; i < n; i++) {
+ if ((double) x[i] < parameters[0])
+ parameters[0] = (double) x[i];
+ if ((double) x[i] > parameters[1])
+ parameters[1] = (double) x[i];
+ }
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(i, j)$ of the uniform distribution
+ over integers using the maximum likelihood method, from the $n$ observations
+ $x[k]$, $k = 0, 1, \ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$i$, $j$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values
+ $(\hat{\imath}$, $\hat{\jmath})$ that satisfy the equations
+ \begin{eqnarray*}
+ \hat{\imath} & = & \mbox{min} \{x_k\}\\
+ \hat{\jmath} & = & \mbox{max} \{x_k\}
+ \end{eqnarray*}
+ where $\bar x_n$ is the average of $x[0],\dots,x[n-1]$.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \return{returns the parameters [$\hat{\imath}$, $\hat{\jmath}$]}
+\end{htmlonly}
+\begin{code}
+
+ public static UniformIntDist getInstanceFromMLE (int[] x, int n)\begin{hide} {
+
+ double parameters[] = getMLE (x, n);
+
+ return new UniformIntDist ((int) parameters[0], (int) parameters[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a discrete uniform distribution over integers with parameters
+ $i$ and $j$ estimated using the maximum likelihood method based on the $n$ observations
+ $x[k]$, $k = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (int i, int j)\begin{hide} {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ return ((i + j) / 2.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean $E[X] = (i + j)/2$
+ of the discrete uniform distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the discrete uniform distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int i, int j)\begin{hide} {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ return (((j - i + 1.0) * (j - i + 1.0) - 1.0) / 12.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+ $\mbox{Var}[X] = [(j - i + 1)^2 - 1]/{12}$
+ of the discrete uniform distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the discrete uniform distribution}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int i, int j)\begin{hide} {
+ return Math.sqrt (UniformIntDist.getVariance (i, j));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the discrete uniform distribution.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the discrete uniform distribution}
+\end{htmlonly}
+\begin{code}
+
+ public int getI()\begin{hide} {
+ return i;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $i$.
+ \end{tabb}
+\begin{code}
+
+ public int getJ()\begin{hide} {
+ return j;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $j$.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (int i, int j)\begin{hide} {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+
+ supportA = this.i = i;
+ supportB = this.j = j;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the parameters $i$ and $j$ for this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {i, j};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$i$, $j$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : i = " + i + ", j = " + j;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/WatsonGDist.java b/source/umontreal/iro/lecuyer/probdist/WatsonGDist.java
new file mode 100644
index 0000000..30748b7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/WatsonGDist.java
@@ -0,0 +1,714 @@
+
+
+/*
+ * Class: WatsonGDist
+ * Description: Watson G distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * Watson <SPAN CLASS="MATH"><I>G</I></SPAN> distribution (see).
+ * Given a sample of <SPAN CLASS="MATH"><I>n</I></SPAN> independent uniforms <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> over <SPAN CLASS="MATH">[0, 1]</SPAN>,
+ * the <SPAN CLASS="MATH"><I>G</I></SPAN> statistic is defined by
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>G</I><SUB>n</SUB></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>(n)<SUP>1/2</SUP>max<SUB>1 <= j <= n</SUB>{<I>j</I>/<I>n</I> - <I>U</I><SUB>(j)</SUB> + bar(U)<SUB>n</SUB> -1/2}</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"> </TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>(n)<SUP>1/2</SUP>(<I>D</I><SUB>n</SUB><SUP>+</SUP> + bar(U)<SUB>n</SUB> - 1/2),</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * where the <SPAN CLASS="MATH"><I>U</I><SUB>(j)</SUB></SPAN> are the <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN> sorted in increasing order,
+ * <SPAN CLASS="MATH">bar(U)<SUB>n</SUB></SPAN> is the average of the observations <SPAN CLASS="MATH"><I>U</I><SUB>i</SUB></SPAN>,
+ * and <SPAN CLASS="MATH"><I>D</I><SUB>n</SUB><SUP>+</SUP></SPAN> is the Kolmogorov-Smirnov+ statistic.
+ * The distribution function (the cumulative probabilities)
+ * is defined as
+ * <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>) = <I>P</I>[<I>G</I><SUB>n</SUB> <= <I>x</I>]</SPAN>.
+ *
+ */
+public class WatsonGDist extends ContinuousDistribution {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+
+
+ /**
+ * Constructs a <EM>Watson</EM> distribution for a sample of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public WatsonGDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ /**
+ * Computes the density function
+ * for a <EM>Watson</EM> <SPAN CLASS="MATH"><I>G</I></SPAN> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+ final double MINARG = 0.15;
+ final double MAXARG = 1.5;
+
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= MINARG || x >= XBIGM)
+ return 0.0;
+
+ final double Res;
+ if (x > MAXARG)
+ Res = 20 * Math.exp (19.0 - 20.0*x)
+ - 15.26 * Math.exp (13.34 - 15.26*x) / Math.sqrt ((double)n);
+ else {
+ final double EPS = 1.0 / 20.0;
+ Res = (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+ if (Res <= 0.0)
+ return 0.0;
+ return Res;
+ }
+
+ // Tables for a spline approximation
+ // of the WatsonG distribution
+ // Empirical correction in 1/sqrt (n)
+ private static double YWA[] = new double[143];
+ private static double MWA[] = new double[143];
+ private static double CoWA[] = new double[143];
+
+ static {
+ /*
+ * Initialization for watsonG
+ */
+ int j;
+ YWA[0] = 1.8121832847E-39;
+ YWA[1] = 2.0503176304E-32;
+ YWA[2] = 4.6139577764E-27;
+ YWA[3] = 6.5869745929E-23;
+ YWA[4] = 1.2765816107E-19;
+ YWA[5] = 5.6251923105E-17;
+ YWA[6] = 8.0747150511E-15;
+ YWA[7] = 4.8819994144E-13;
+ YWA[8] = 1.4996052497E-11;
+ YWA[9] = 2.6903519441E-10;
+ YWA[10] = 3.1322929018E-9;
+ YWA[11] = 2.5659643046E-8;
+ YWA[12] = 1.5749759318E-7;
+ YWA[13] = 7.6105096466E-7;
+ YWA[14] = 3.0113293541E-6;
+ YWA[15] = 1.0070166837E-5;
+ YWA[16] = 2.9199826692E-5;
+ YWA[17] = 7.4970409372E-5;
+ YWA[18] = 1.7340586581E-4;
+ YWA[19] = 3.6654236297E-4;
+ YWA[20] = 7.165864865E-4;
+ YWA[21] = 1.3087767385E-3;
+ YWA[22] = 2.2522044209E-3;
+ YWA[23] = 3.6781862572E-3;
+ YWA[24] = 5.7361958631E-3;
+ YWA[25] = 8.5877444706E-3;
+ YWA[26] = 1.23988738E-2;
+ YWA[27] = 1.73320516E-2;
+ YWA[28] = 2.35382479E-2;
+ YWA[29] = 3.11498548E-2;
+ YWA[30] = 4.02749297E-2;
+ YWA[31] = 5.09930445E-2;
+ YWA[32] = 6.33528333E-2;
+ YWA[33] = 7.73711747E-2;
+ YWA[34] = 9.30338324E-2;
+ YWA[35] = 1.10297306E-1;
+ YWA[36] = 1.290916098E-1;
+ YWA[37] = 1.493236984E-1;
+ YWA[38] = 1.708812741E-1;
+ YWA[39] = 1.936367476E-1;
+ YWA[40] = 2.174511609E-1;
+ YWA[41] = 2.42177928E-1;
+ YWA[42] = 2.676662852E-1;
+ YWA[43] = 2.937643828E-1;
+ YWA[44] = 3.203219784E-1;
+ YWA[45] = 3.471927188E-1;
+ YWA[46] = 3.742360163E-1;
+ YWA[47] = 4.013185392E-1;
+ YWA[48] = 4.283153467E-1;
+ YWA[49] = 4.551107027E-1;
+ YWA[50] = 4.815986082E-1;
+ YWA[51] = 5.076830902E-1;
+ YWA[52] = 5.332782852E-1;
+ YWA[53] = 5.583083531E-1;
+ YWA[54] = 5.827072528E-1;
+ YWA[55] = 6.064184099E-1;
+ YWA[56] = 6.293943006E-1;
+ YWA[57] = 6.515959739E-1;
+ YWA[58] = 6.729925313E-1;
+ YWA[59] = 6.935605784E-1;
+ YWA[60] = 7.132836621E-1;
+ YWA[61] = 7.321517033E-1;
+ YWA[62] = 7.501604333E-1;
+ YWA[63] = 7.673108406E-1;
+ YWA[64] = 7.836086337E-1;
+ YWA[65] = 7.99063723E-1;
+ YWA[66] = 8.136897251E-1;
+ YWA[67] = 8.275034914E-1;
+ YWA[68] = 8.405246632E-1;
+ YWA[69] = 8.527752531E-1;
+ YWA[70] = 8.642792535E-1;
+ YWA[71] = 8.750622738E-1;
+ YWA[72] = 8.851512032E-1;
+ YWA[73] = 8.945739017E-1;
+ YWA[74] = 9.033589176E-1;
+ YWA[75] = 9.115352296E-1;
+ YWA[76] = 9.19132015E-1;
+ YWA[77] = 9.261784413E-1;
+ YWA[78] = 9.327034806E-1;
+ YWA[79] = 9.387357465E-1;
+ YWA[80] = 9.44303351E-1;
+ YWA[81] = 9.494337813E-1;
+ YWA[82] = 9.541537951E-1;
+ YWA[83] = 9.584893325E-1;
+ YWA[84] = 9.624654445E-1;
+ YWA[85] = 9.661062352E-1;
+ YWA[86] = 9.694348183E-1;
+ YWA[87] = 9.724732859E-1;
+ YWA[88] = 9.752426872E-1;
+ YWA[89] = 9.777630186E-1;
+ YWA[90] = 9.800532221E-1;
+ YWA[91] = 9.821311912E-1;
+ YWA[92] = 9.840137844E-1;
+ YWA[93] = 9.85716844E-1;
+ YWA[94] = 9.872552203E-1;
+ YWA[95] = 9.886428002E-1;
+ YWA[96] = 9.898925389E-1;
+ YWA[97] = 9.910164946E-1;
+ YWA[98] = 9.920258656E-1;
+ YWA[99] = 9.929310287E-1;
+ YWA[100] = 9.937415788E-1;
+ YWA[101] = 9.944663692E-1;
+ YWA[102] = 9.95113552E-1;
+ YWA[103] = 9.956906185E-1;
+ YWA[104] = 9.962044387E-1;
+ YWA[105] = 9.966613009E-1;
+ YWA[106] = 9.970669496E-1;
+ YWA[107] = 9.974266225E-1;
+ YWA[108] = 9.977450862E-1;
+ YWA[109] = 9.980266707E-1;
+ YWA[110] = 9.982753021E-1;
+ YWA[111] = 9.984945338E-1;
+ YWA[112] = 9.98687576E-1;
+ YWA[113] = 9.98857324E-1;
+ YWA[114] = 9.990063842E-1;
+ YWA[115] = 9.991370993E-1;
+ YWA[116] = 9.992515708E-1;
+ YWA[117] = 9.99351681E-1;
+ YWA[118] = 9.994391129E-1;
+ YWA[119] = 9.995153688E-1;
+ YWA[120] = 9.995817875E-1;
+ YWA[121] = 9.996395602E-1;
+ YWA[122] = 9.996897446E-1;
+ YWA[123] = 9.997332791E-1;
+ YWA[124] = 9.997709943E-1;
+ YWA[125] = 9.998036243E-1;
+ YWA[126] = 9.998318172E-1;
+ YWA[127] = 9.998561438E-1;
+ YWA[128] = 9.998771066E-1;
+ YWA[129] = 9.998951466E-1;
+ YWA[130] = 9.999106508E-1;
+ YWA[131] = 9.99923958E-1;
+ YWA[132] = 9.999353645E-1;
+ YWA[133] = 9.999451288E-1;
+ YWA[134] = 9.999534765E-1;
+ YWA[135] = 9.999606035E-1;
+ YWA[136] = 9.999666805E-1;
+ YWA[137] = 9.999718553E-1;
+ YWA[138] = 9.999762562E-1;
+ YWA[139] = 9.999799939E-1;
+ YWA[140] = 9.999831643E-1;
+ YWA[141] = 9.999858E-1;
+ YWA[142] = 9.999883E-1;
+
+ MWA[0] = 0.0;
+ MWA[1] = 6.909E-15;
+ MWA[2] = 2.763E-14;
+ MWA[3] = 1.036E-13;
+ MWA[4] = 3.792E-13;
+ MWA[5] = 4.773E-12;
+ MWA[6] = 4.59E-10;
+ MWA[7] = 2.649E-8;
+ MWA[8] = 7.353E-7;
+ MWA[9] = 1.14E-5;
+ MWA[10] = 1.102E-4;
+ MWA[11] = 7.276E-4;
+ MWA[12] = 3.538E-3;
+ MWA[13] = 0.01342;
+ MWA[14] = 0.04157;
+ MWA[15] = 0.1088;
+ MWA[16] = 0.2474;
+ MWA[17] = 0.4999;
+ MWA[18] = 0.913;
+ MWA[19] = 1.53;
+ MWA[20] = 2.381;
+ MWA[21] = 3.475;
+ MWA[22] = 4.795;
+ MWA[23] = 6.3;
+ MWA[24] = 7.928;
+ MWA[25] = 9.602;
+ MWA[26] = 11.24;
+ MWA[27] = 12.76;
+ MWA[28] = 14.1;
+ MWA[29] = 15.18;
+ MWA[30] = 15.98;
+ MWA[31] = 16.47;
+ MWA[32] = 16.64;
+ MWA[33] = 16.49;
+ MWA[34] = 16.05;
+ MWA[35] = 15.35;
+ MWA[36] = 14.41;
+ MWA[37] = 13.28;
+ MWA[38] = 12.0;
+ MWA[39] = 10.6;
+ MWA[40] = 9.13;
+ MWA[41] = 7.618;
+ MWA[42] = 6.095;
+ MWA[43] = 4.588;
+ MWA[44] = 3.122;
+ MWA[45] = 1.713;
+ MWA[46] = 0.3782;
+ MWA[47] = -0.8726;
+ MWA[48] = -2.031;
+ MWA[49] = -3.091;
+ MWA[50] = -4.051;
+ MWA[51] = -4.91;
+ MWA[52] = -5.668;
+ MWA[53] = -6.327;
+ MWA[54] = -6.893;
+ MWA[55] = -7.367;
+ MWA[56] = -7.756;
+ MWA[57] = -8.064;
+ MWA[58] = -8.297;
+ MWA[59] = -8.46;
+ MWA[60] = -8.56;
+ MWA[61] = -8.602;
+ MWA[62] = -8.591;
+ MWA[63] = -8.533;
+ MWA[64] = -8.433;
+ MWA[65] = -8.296;
+ MWA[66] = -8.127;
+ MWA[67] = -7.93;
+ MWA[68] = -7.709;
+ MWA[69] = -7.469;
+ MWA[70] = -7.212;
+ MWA[71] = -6.943;
+ MWA[72] = -6.663;
+ MWA[73] = -6.378;
+ MWA[74] = -6.087;
+ MWA[75] = -5.795;
+ MWA[76] = -5.503;
+ MWA[77] = -5.213;
+ MWA[78] = -4.927;
+ MWA[79] = -4.646;
+ MWA[80] = -4.371;
+ MWA[81] = -4.103;
+ MWA[82] = -3.843;
+ MWA[83] = -3.593;
+ MWA[84] = -3.352;
+ MWA[85] = -3.12;
+ MWA[86] = -2.899;
+ MWA[87] = -2.689;
+ MWA[88] = -2.489;
+ MWA[89] = -2.3;
+ MWA[90] = -2.121;
+ MWA[91] = -1.952;
+ MWA[92] = -1.794;
+ MWA[93] = -1.645;
+ MWA[94] = -1.506;
+ MWA[95] = -1.377;
+ MWA[96] = -1.256;
+ MWA[97] = -1.144;
+ MWA[98] = -1.041;
+ MWA[99] = -0.9449;
+ MWA[100] = -0.8564;
+ MWA[101] = -0.775;
+ MWA[102] = -0.7001;
+ MWA[103] = -0.6315;
+ MWA[104] = -0.5687;
+ MWA[105] = -0.5113;
+ MWA[106] = -0.459;
+ MWA[107] = -0.4114;
+ MWA[108] = -0.3681;
+ MWA[109] = -0.3289;
+ MWA[110] = -0.2934;
+ MWA[111] = -0.2614;
+ MWA[112] = -0.2325;
+ MWA[113] = -0.2064;
+ MWA[114] = -0.183;
+ MWA[115] = -0.1621;
+ MWA[116] = -0.1433;
+ MWA[117] = -0.1265;
+ MWA[118] = -0.1115;
+ MWA[119] = -9.813E-2;
+ MWA[120] = -8.624E-2;
+ MWA[121] = -7.569E-2;
+ MWA[122] = -6.632E-2;
+ MWA[123] = -5.803E-2;
+ MWA[124] = -5.071E-2;
+ MWA[125] = -4.424E-2;
+ MWA[126] = -3.855E-2;
+ MWA[127] = -3.353E-2;
+ MWA[128] = -2.914E-2;
+ MWA[129] = -2.528E-2;
+ MWA[130] = -0.0219;
+ MWA[131] = -1.894E-2;
+ MWA[132] = -1.637E-2;
+ MWA[133] = -1.412E-2;
+ MWA[134] = -1.217E-2;
+ MWA[135] = -1.046E-2;
+ MWA[136] = -8.988E-3;
+ MWA[137] = -7.72E-3;
+ MWA[138] = -6.567E-3;
+ MWA[139] = -5.802E-3;
+ MWA[140] = -0.0053;
+ MWA[141] = -4.7E-4;
+ MWA[142] = -4.3E-4;
+
+ for (j = 5; j <= 11; j++) {
+ CoWA[j] = 0.0;
+ }
+ CoWA[12] = 1.25E-5;
+ CoWA[13] = 3.87E-5;
+ CoWA[14] = 1.004E-4;
+ CoWA[15] = 2.703E-4;
+ CoWA[16] = 6.507E-4;
+ CoWA[17] = 1.3985E-3;
+ CoWA[18] = 2.8353E-3;
+ CoWA[19] = 5.1911E-3;
+ CoWA[20] = 8.9486E-3;
+ CoWA[21] = 1.41773E-2;
+ CoWA[22] = 2.16551E-2;
+ CoWA[23] = 3.1489E-2;
+ CoWA[24] = 4.34123E-2;
+ CoWA[25] = 5.78719E-2;
+ CoWA[26] = 7.46921E-2;
+ CoWA[27] = 9.45265E-2;
+ CoWA[28] = 1.165183E-1;
+ CoWA[29] = 1.406353E-1;
+ CoWA[30] = 1.662849E-1;
+ CoWA[31] = 1.929895E-1;
+ CoWA[32] = 2.189347E-1;
+ CoWA[33] = 2.457772E-1;
+ CoWA[34] = 2.704794E-1;
+ CoWA[35] = 2.947906E-1;
+ CoWA[36] = 3.169854E-1;
+ CoWA[37] = 3.377435E-1;
+ CoWA[38] = 3.573555E-1;
+ CoWA[39] = 3.751205E-1;
+ CoWA[40] = 3.906829E-1;
+ CoWA[41] = 4.039806E-1;
+ CoWA[42] = 4.142483E-1;
+ CoWA[43] = 4.22779E-1;
+ CoWA[44] = 4.288013E-1;
+ CoWA[45] = 4.330353E-1;
+ CoWA[46] = 4.34452E-1;
+ CoWA[47] = 4.338138E-1;
+ CoWA[48] = 4.31504E-1;
+ CoWA[49] = 4.272541E-1;
+ CoWA[50] = 4.220568E-1;
+ CoWA[51] = 4.158229E-1;
+ CoWA[52] = 4.083281E-1;
+ CoWA[53] = 3.981182E-1;
+ CoWA[54] = 3.871678E-1;
+ CoWA[55] = 3.755527E-1;
+ CoWA[56] = 3.628823E-1;
+ CoWA[57] = 3.520135E-1;
+ CoWA[58] = 3.400924E-1;
+ CoWA[59] = 3.280532E-1;
+ CoWA[60] = 3.139477E-1;
+ CoWA[61] = 2.997087E-1;
+ CoWA[62] = 2.849179E-1;
+ CoWA[63] = 2.710475E-1;
+ CoWA[64] = 2.576478E-1;
+ CoWA[65] = 2.449155E-1;
+ CoWA[66] = 2.317447E-1;
+ CoWA[67] = 2.193161E-1;
+ CoWA[68] = 2.072622E-1;
+ CoWA[69] = 1.956955E-1;
+ CoWA[70] = 1.846514E-1;
+ CoWA[71] = 1.734096E-1;
+ CoWA[72] = 1.622678E-1;
+ CoWA[73] = 1.520447E-1;
+ CoWA[74] = 1.416351E-1;
+ CoWA[75] = 1.32136E-1;
+ CoWA[76] = 1.231861E-1;
+ CoWA[77] = 1.150411E-1;
+ CoWA[78] = 1.071536E-1;
+ CoWA[79] = 9.9465E-2;
+ CoWA[80] = 9.22347E-2;
+ CoWA[81] = 8.54394E-2;
+ CoWA[82] = 7.87697E-2;
+ CoWA[83] = 7.23848E-2;
+ CoWA[84] = 6.6587E-2;
+ CoWA[85] = 6.15849E-2;
+ CoWA[86] = 5.6573E-2;
+ CoWA[87] = 5.17893E-2;
+ CoWA[88] = 4.70011E-2;
+ CoWA[89] = 4.2886E-2;
+ CoWA[90] = 3.91224E-2;
+ CoWA[91] = 3.53163E-2;
+ CoWA[92] = 3.20884E-2;
+ CoWA[93] = 2.92264E-2;
+ CoWA[94] = 2.66058E-2;
+ CoWA[95] = 2.37352E-2;
+ CoWA[96] = 2.14669E-2;
+ CoWA[97] = 1.94848E-2;
+ CoWA[98] = 1.75591E-2;
+ CoWA[99] = 1.58232E-2;
+ CoWA[100] = 1.40302E-2;
+ CoWA[101] = 1.24349E-2;
+ CoWA[102] = 1.11856E-2;
+ CoWA[103] = 9.9765E-3;
+ CoWA[104] = 8.9492E-3;
+ CoWA[105] = 8.0063E-3;
+ CoWA[106] = 7.1509E-3;
+ CoWA[107] = 6.3196E-3;
+ CoWA[108] = 5.6856E-3;
+ CoWA[109] = 5.0686E-3;
+ CoWA[110] = 4.5085E-3;
+ CoWA[111] = 3.9895E-3;
+ CoWA[112] = 3.4804E-3;
+ CoWA[113] = 3.0447E-3;
+ CoWA[114] = 2.7012E-3;
+ CoWA[115] = 2.2984E-3;
+ CoWA[116] = 2.0283E-3;
+ CoWA[117] = 1.7399E-3;
+ CoWA[118] = 1.5032E-3;
+ CoWA[119] = 1.3267E-3;
+ CoWA[120] = 1.1531E-3;
+ CoWA[121] = 9.92E-4;
+ CoWA[122] = 9.211E-4;
+ CoWA[123] = 8.296E-4;
+ CoWA[124] = 6.991E-4;
+ CoWA[125] = 5.84E-4;
+ CoWA[126] = 5.12E-4;
+ CoWA[127] = 4.314E-4;
+ CoWA[128] = 3.593E-4;
+ CoWA[129] = 3.014E-4;
+ CoWA[130] = 2.401E-4;
+ CoWA[131] = 2.004E-4;
+ CoWA[132] = 1.614E-4;
+ CoWA[133] = 1.257E-4;
+ CoWA[134] = 1.112E-4;
+ CoWA[135] = 9.22E-5;
+ CoWA[136] = 8.77E-5;
+ CoWA[137] = 6.22E-5;
+ CoWA[138] = 4.93E-5;
+ CoWA[139] = 3.92E-5;
+ CoWA[140] = 3.15E-5;
+ CoWA[141] = 1.03E-5;
+ CoWA[142] = 9.6E-6;
+ }
+
+ /**
+ * Computes the Watson <SPAN CLASS="MATH"><I>G</I></SPAN> distribution function <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>)</SPAN>, with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * A cubic spline interpolation is used for the asymptotic distribution when
+ *
+ * <SPAN CLASS="MATH"><I>n</I> -> ∞</SPAN>, and an empirical correction of order
+ * <SPAN CLASS="MATH">1/(n)<SUP>1/2</SUP></SPAN>, obtained
+ * empirically from <SPAN CLASS="MATH">10<SUP>7</SUP></SPAN> simulation runs with <SPAN CLASS="MATH"><I>n</I> = 256</SPAN> is then added.
+ * The absolute error is estimated to be less than
+ * 0.01, 0.005, 0.002, 0.0008, 0.0005, 0.0005, 0.0005 for
+ * <SPAN CLASS="MATH"><I>n</I> = 16</SPAN>, 32, 64, 128, 256, 512, 1024, respectively.
+ *
+ */
+ public static double cdf (int n, double x) {
+ /*
+ * Approximation of the cumulative distribution function of the
+ * watsonG statistics by the cubic spline function.
+ * Y[.] - tabular value of the statistic;
+ * M[.] - tabular value of the first derivative;
+ */
+ if (n <= 1)
+ throw new IllegalArgumentException ("n < 2");
+
+ final double MINARG = 0.15;
+ if (x <= MINARG)
+ return 0.0;
+ if (x >= 10.0)
+ return 1.0;
+
+ double R, Res;
+ final double MAXARG = 1.5;
+ if (x > MAXARG) {
+ R = Math.exp (19.0 - 20.0*x);
+ Res = 1.0 - R;
+ // Empirical Correction in 1/sqrt (n)
+ R = Math.exp (13.34 - 15.26*x)/Math.sqrt ((double)n);
+ Res += R;
+ // The correction in 1/sqrt (n) is not always precise
+ if (Res >= 1.0)
+ return 1.0;
+ else
+ return Res;
+ }
+
+ final double MINTAB = 0.1;
+ final double STEP = 0.01;
+ int i, j;
+ double Tj;
+ double Ti;
+ double P;
+ double H;
+
+ // Search of the correct slot in the interpolation table
+ i = (int)((x - MINTAB)/STEP + 1);
+ Ti = MINTAB + i*STEP;
+ Tj = Ti - STEP;
+
+ // Approximation within the slot
+ j = i - 1;
+ H = x - Tj;
+ R = Ti - x;
+ P = STEP*STEP/6.0;
+ Res = ((MWA[j]*R*R*R + MWA[i]*H*H*H)/6.0)/STEP;
+ Res += ((YWA[j] - MWA[j]*P)*R + (YWA[i] - MWA[i]*P)*H)/STEP;
+
+ // Empirical correction in 1/sqrt (n)
+ Res += (CoWA[i]*H + CoWA[j]*R)/(STEP*Math.sqrt ((double)n));
+
+ if (Res >= 1.0)
+ return 1.0;
+ return Res;
+ }
+
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)<SUB>n</SUB>(<I>x</I>)</SPAN>
+ * with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ return 1.0 - cdf(n,x);
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUB>n</SUB><SUP>-1</SUP>(<I>u</I>)</SPAN>, where <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB></SPAN> is the
+ * <EM>Watson</EM> <SPAN CLASS="MATH"><I>G</I></SPAN> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n < 2");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 10.0, f, 1e-5);
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public void setN (int n) {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n < 2");
+ this.n = n;
+ supportA = 0.0;
+ supportB = 10.0;
+ }
+
+
+ /**
+ * Return an array containing the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/WatsonGDist.tex b/source/umontreal/iro/lecuyer/probdist/WatsonGDist.tex
new file mode 100644
index 0000000..c27667c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/WatsonGDist.tex
@@ -0,0 +1,711 @@
+\defmodule {WatsonGDist}
+
+Extends the class \class{ContinuousDistribution} for the
+Watson $G$ distribution (see \cite{tDAR83a,tWAT76a}).
+Given a sample of $n$ independent uniforms $U_i$ over $[0,1]$,
+the $G$ statistic is defined by
+ \begin {eqnarray}
+ G_n &=& \sqrt{n} \max_{\rule{0pt}{7pt} 1\le j \le n} \left\{ j/n -
+ U_{(j)} + \bar U_n - 1/2 \right\} \eqlabel {eq:WatsonG} \\[6pt]
+ &=& \sqrt{n}\left (D_n^+ + \bar U_n - 1/2\right), \nonumber
+ \end {eqnarray}
+ where the $U_{(j)}$ are the $U_i$ sorted in increasing order,
+ $\bar U_n$ is the average of the observations $U_{i}$,
+ and $D_n^+$ is the Kolmogorov-Smirnov+ statistic.
+ The distribution function (the cumulative probabilities)
+ is defined as $F_n(x) = P[G_n \le x]$.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: WatsonGDist
+ * Description: Watson G distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class WatsonGDist extends ContinuousDistribution\begin{hide} {
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public WatsonGDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a {\em Watson\/} distribution for a sample of size $n$.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ final double MINARG = 0.15;
+ final double MAXARG = 1.5;
+
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= MINARG || x >= XBIGM)
+ return 0.0;
+
+ final double Res;
+ if (x > MAXARG)
+ Res = 20 * Math.exp (19.0 - 20.0*x)
+ - 15.26 * Math.exp (13.34 - 15.26*x) / Math.sqrt ((double)n);
+ else {
+ final double EPS = 1.0 / 20.0;
+ Res = (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+ }
+
+ if (Res <= 0.0)
+ return 0.0;
+ return Res;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function
+ for a {\em Watson\/} $G$ distribution with parameter $n$.
+\end{tabb}
+
+\begin{code}\begin{hide}
+ // Tables for a spline approximation
+ // of the WatsonG distribution
+ // Empirical correction in 1/sqrt (n)
+ private static double YWA[] = new double[143];
+ private static double MWA[] = new double[143];
+ private static double CoWA[] = new double[143];
+
+ static {
+ /*
+ * Initialization for watsonG
+ */
+ int j;
+ YWA[0] = 1.8121832847E-39;
+ YWA[1] = 2.0503176304E-32;
+ YWA[2] = 4.6139577764E-27;
+ YWA[3] = 6.5869745929E-23;
+ YWA[4] = 1.2765816107E-19;
+ YWA[5] = 5.6251923105E-17;
+ YWA[6] = 8.0747150511E-15;
+ YWA[7] = 4.8819994144E-13;
+ YWA[8] = 1.4996052497E-11;
+ YWA[9] = 2.6903519441E-10;
+ YWA[10] = 3.1322929018E-9;
+ YWA[11] = 2.5659643046E-8;
+ YWA[12] = 1.5749759318E-7;
+ YWA[13] = 7.6105096466E-7;
+ YWA[14] = 3.0113293541E-6;
+ YWA[15] = 1.0070166837E-5;
+ YWA[16] = 2.9199826692E-5;
+ YWA[17] = 7.4970409372E-5;
+ YWA[18] = 1.7340586581E-4;
+ YWA[19] = 3.6654236297E-4;
+ YWA[20] = 7.165864865E-4;
+ YWA[21] = 1.3087767385E-3;
+ YWA[22] = 2.2522044209E-3;
+ YWA[23] = 3.6781862572E-3;
+ YWA[24] = 5.7361958631E-3;
+ YWA[25] = 8.5877444706E-3;
+ YWA[26] = 1.23988738E-2;
+ YWA[27] = 1.73320516E-2;
+ YWA[28] = 2.35382479E-2;
+ YWA[29] = 3.11498548E-2;
+ YWA[30] = 4.02749297E-2;
+ YWA[31] = 5.09930445E-2;
+ YWA[32] = 6.33528333E-2;
+ YWA[33] = 7.73711747E-2;
+ YWA[34] = 9.30338324E-2;
+ YWA[35] = 1.10297306E-1;
+ YWA[36] = 1.290916098E-1;
+ YWA[37] = 1.493236984E-1;
+ YWA[38] = 1.708812741E-1;
+ YWA[39] = 1.936367476E-1;
+ YWA[40] = 2.174511609E-1;
+ YWA[41] = 2.42177928E-1;
+ YWA[42] = 2.676662852E-1;
+ YWA[43] = 2.937643828E-1;
+ YWA[44] = 3.203219784E-1;
+ YWA[45] = 3.471927188E-1;
+ YWA[46] = 3.742360163E-1;
+ YWA[47] = 4.013185392E-1;
+ YWA[48] = 4.283153467E-1;
+ YWA[49] = 4.551107027E-1;
+ YWA[50] = 4.815986082E-1;
+ YWA[51] = 5.076830902E-1;
+ YWA[52] = 5.332782852E-1;
+ YWA[53] = 5.583083531E-1;
+ YWA[54] = 5.827072528E-1;
+ YWA[55] = 6.064184099E-1;
+ YWA[56] = 6.293943006E-1;
+ YWA[57] = 6.515959739E-1;
+ YWA[58] = 6.729925313E-1;
+ YWA[59] = 6.935605784E-1;
+ YWA[60] = 7.132836621E-1;
+ YWA[61] = 7.321517033E-1;
+ YWA[62] = 7.501604333E-1;
+ YWA[63] = 7.673108406E-1;
+ YWA[64] = 7.836086337E-1;
+ YWA[65] = 7.99063723E-1;
+ YWA[66] = 8.136897251E-1;
+ YWA[67] = 8.275034914E-1;
+ YWA[68] = 8.405246632E-1;
+ YWA[69] = 8.527752531E-1;
+ YWA[70] = 8.642792535E-1;
+ YWA[71] = 8.750622738E-1;
+ YWA[72] = 8.851512032E-1;
+ YWA[73] = 8.945739017E-1;
+ YWA[74] = 9.033589176E-1;
+ YWA[75] = 9.115352296E-1;
+ YWA[76] = 9.19132015E-1;
+ YWA[77] = 9.261784413E-1;
+ YWA[78] = 9.327034806E-1;
+ YWA[79] = 9.387357465E-1;
+ YWA[80] = 9.44303351E-1;
+ YWA[81] = 9.494337813E-1;
+ YWA[82] = 9.541537951E-1;
+ YWA[83] = 9.584893325E-1;
+ YWA[84] = 9.624654445E-1;
+ YWA[85] = 9.661062352E-1;
+ YWA[86] = 9.694348183E-1;
+ YWA[87] = 9.724732859E-1;
+ YWA[88] = 9.752426872E-1;
+ YWA[89] = 9.777630186E-1;
+ YWA[90] = 9.800532221E-1;
+ YWA[91] = 9.821311912E-1;
+ YWA[92] = 9.840137844E-1;
+ YWA[93] = 9.85716844E-1;
+ YWA[94] = 9.872552203E-1;
+ YWA[95] = 9.886428002E-1;
+ YWA[96] = 9.898925389E-1;
+ YWA[97] = 9.910164946E-1;
+ YWA[98] = 9.920258656E-1;
+ YWA[99] = 9.929310287E-1;
+ YWA[100] = 9.937415788E-1;
+ YWA[101] = 9.944663692E-1;
+ YWA[102] = 9.95113552E-1;
+ YWA[103] = 9.956906185E-1;
+ YWA[104] = 9.962044387E-1;
+ YWA[105] = 9.966613009E-1;
+ YWA[106] = 9.970669496E-1;
+ YWA[107] = 9.974266225E-1;
+ YWA[108] = 9.977450862E-1;
+ YWA[109] = 9.980266707E-1;
+ YWA[110] = 9.982753021E-1;
+ YWA[111] = 9.984945338E-1;
+ YWA[112] = 9.98687576E-1;
+ YWA[113] = 9.98857324E-1;
+ YWA[114] = 9.990063842E-1;
+ YWA[115] = 9.991370993E-1;
+ YWA[116] = 9.992515708E-1;
+ YWA[117] = 9.99351681E-1;
+ YWA[118] = 9.994391129E-1;
+ YWA[119] = 9.995153688E-1;
+ YWA[120] = 9.995817875E-1;
+ YWA[121] = 9.996395602E-1;
+ YWA[122] = 9.996897446E-1;
+ YWA[123] = 9.997332791E-1;
+ YWA[124] = 9.997709943E-1;
+ YWA[125] = 9.998036243E-1;
+ YWA[126] = 9.998318172E-1;
+ YWA[127] = 9.998561438E-1;
+ YWA[128] = 9.998771066E-1;
+ YWA[129] = 9.998951466E-1;
+ YWA[130] = 9.999106508E-1;
+ YWA[131] = 9.99923958E-1;
+ YWA[132] = 9.999353645E-1;
+ YWA[133] = 9.999451288E-1;
+ YWA[134] = 9.999534765E-1;
+ YWA[135] = 9.999606035E-1;
+ YWA[136] = 9.999666805E-1;
+ YWA[137] = 9.999718553E-1;
+ YWA[138] = 9.999762562E-1;
+ YWA[139] = 9.999799939E-1;
+ YWA[140] = 9.999831643E-1;
+ YWA[141] = 9.999858E-1;
+ YWA[142] = 9.999883E-1;
+
+ MWA[0] = 0.0;
+ MWA[1] = 6.909E-15;
+ MWA[2] = 2.763E-14;
+ MWA[3] = 1.036E-13;
+ MWA[4] = 3.792E-13;
+ MWA[5] = 4.773E-12;
+ MWA[6] = 4.59E-10;
+ MWA[7] = 2.649E-8;
+ MWA[8] = 7.353E-7;
+ MWA[9] = 1.14E-5;
+ MWA[10] = 1.102E-4;
+ MWA[11] = 7.276E-4;
+ MWA[12] = 3.538E-3;
+ MWA[13] = 0.01342;
+ MWA[14] = 0.04157;
+ MWA[15] = 0.1088;
+ MWA[16] = 0.2474;
+ MWA[17] = 0.4999;
+ MWA[18] = 0.913;
+ MWA[19] = 1.53;
+ MWA[20] = 2.381;
+ MWA[21] = 3.475;
+ MWA[22] = 4.795;
+ MWA[23] = 6.3;
+ MWA[24] = 7.928;
+ MWA[25] = 9.602;
+ MWA[26] = 11.24;
+ MWA[27] = 12.76;
+ MWA[28] = 14.1;
+ MWA[29] = 15.18;
+ MWA[30] = 15.98;
+ MWA[31] = 16.47;
+ MWA[32] = 16.64;
+ MWA[33] = 16.49;
+ MWA[34] = 16.05;
+ MWA[35] = 15.35;
+ MWA[36] = 14.41;
+ MWA[37] = 13.28;
+ MWA[38] = 12.0;
+ MWA[39] = 10.6;
+ MWA[40] = 9.13;
+ MWA[41] = 7.618;
+ MWA[42] = 6.095;
+ MWA[43] = 4.588;
+ MWA[44] = 3.122;
+ MWA[45] = 1.713;
+ MWA[46] = 0.3782;
+ MWA[47] = -0.8726;
+ MWA[48] = -2.031;
+ MWA[49] = -3.091;
+ MWA[50] = -4.051;
+ MWA[51] = -4.91;
+ MWA[52] = -5.668;
+ MWA[53] = -6.327;
+ MWA[54] = -6.893;
+ MWA[55] = -7.367;
+ MWA[56] = -7.756;
+ MWA[57] = -8.064;
+ MWA[58] = -8.297;
+ MWA[59] = -8.46;
+ MWA[60] = -8.56;
+ MWA[61] = -8.602;
+ MWA[62] = -8.591;
+ MWA[63] = -8.533;
+ MWA[64] = -8.433;
+ MWA[65] = -8.296;
+ MWA[66] = -8.127;
+ MWA[67] = -7.93;
+ MWA[68] = -7.709;
+ MWA[69] = -7.469;
+ MWA[70] = -7.212;
+ MWA[71] = -6.943;
+ MWA[72] = -6.663;
+ MWA[73] = -6.378;
+ MWA[74] = -6.087;
+ MWA[75] = -5.795;
+ MWA[76] = -5.503;
+ MWA[77] = -5.213;
+ MWA[78] = -4.927;
+ MWA[79] = -4.646;
+ MWA[80] = -4.371;
+ MWA[81] = -4.103;
+ MWA[82] = -3.843;
+ MWA[83] = -3.593;
+ MWA[84] = -3.352;
+ MWA[85] = -3.12;
+ MWA[86] = -2.899;
+ MWA[87] = -2.689;
+ MWA[88] = -2.489;
+ MWA[89] = -2.3;
+ MWA[90] = -2.121;
+ MWA[91] = -1.952;
+ MWA[92] = -1.794;
+ MWA[93] = -1.645;
+ MWA[94] = -1.506;
+ MWA[95] = -1.377;
+ MWA[96] = -1.256;
+ MWA[97] = -1.144;
+ MWA[98] = -1.041;
+ MWA[99] = -0.9449;
+ MWA[100] = -0.8564;
+ MWA[101] = -0.775;
+ MWA[102] = -0.7001;
+ MWA[103] = -0.6315;
+ MWA[104] = -0.5687;
+ MWA[105] = -0.5113;
+ MWA[106] = -0.459;
+ MWA[107] = -0.4114;
+ MWA[108] = -0.3681;
+ MWA[109] = -0.3289;
+ MWA[110] = -0.2934;
+ MWA[111] = -0.2614;
+ MWA[112] = -0.2325;
+ MWA[113] = -0.2064;
+ MWA[114] = -0.183;
+ MWA[115] = -0.1621;
+ MWA[116] = -0.1433;
+ MWA[117] = -0.1265;
+ MWA[118] = -0.1115;
+ MWA[119] = -9.813E-2;
+ MWA[120] = -8.624E-2;
+ MWA[121] = -7.569E-2;
+ MWA[122] = -6.632E-2;
+ MWA[123] = -5.803E-2;
+ MWA[124] = -5.071E-2;
+ MWA[125] = -4.424E-2;
+ MWA[126] = -3.855E-2;
+ MWA[127] = -3.353E-2;
+ MWA[128] = -2.914E-2;
+ MWA[129] = -2.528E-2;
+ MWA[130] = -0.0219;
+ MWA[131] = -1.894E-2;
+ MWA[132] = -1.637E-2;
+ MWA[133] = -1.412E-2;
+ MWA[134] = -1.217E-2;
+ MWA[135] = -1.046E-2;
+ MWA[136] = -8.988E-3;
+ MWA[137] = -7.72E-3;
+ MWA[138] = -6.567E-3;
+ MWA[139] = -5.802E-3;
+ MWA[140] = -0.0053;
+ MWA[141] = -4.7E-4;
+ MWA[142] = -4.3E-4;
+
+ for (j = 5; j <= 11; j++) {
+ CoWA[j] = 0.0;
+ }
+ CoWA[12] = 1.25E-5;
+ CoWA[13] = 3.87E-5;
+ CoWA[14] = 1.004E-4;
+ CoWA[15] = 2.703E-4;
+ CoWA[16] = 6.507E-4;
+ CoWA[17] = 1.3985E-3;
+ CoWA[18] = 2.8353E-3;
+ CoWA[19] = 5.1911E-3;
+ CoWA[20] = 8.9486E-3;
+ CoWA[21] = 1.41773E-2;
+ CoWA[22] = 2.16551E-2;
+ CoWA[23] = 3.1489E-2;
+ CoWA[24] = 4.34123E-2;
+ CoWA[25] = 5.78719E-2;
+ CoWA[26] = 7.46921E-2;
+ CoWA[27] = 9.45265E-2;
+ CoWA[28] = 1.165183E-1;
+ CoWA[29] = 1.406353E-1;
+ CoWA[30] = 1.662849E-1;
+ CoWA[31] = 1.929895E-1;
+ CoWA[32] = 2.189347E-1;
+ CoWA[33] = 2.457772E-1;
+ CoWA[34] = 2.704794E-1;
+ CoWA[35] = 2.947906E-1;
+ CoWA[36] = 3.169854E-1;
+ CoWA[37] = 3.377435E-1;
+ CoWA[38] = 3.573555E-1;
+ CoWA[39] = 3.751205E-1;
+ CoWA[40] = 3.906829E-1;
+ CoWA[41] = 4.039806E-1;
+ CoWA[42] = 4.142483E-1;
+ CoWA[43] = 4.22779E-1;
+ CoWA[44] = 4.288013E-1;
+ CoWA[45] = 4.330353E-1;
+ CoWA[46] = 4.34452E-1;
+ CoWA[47] = 4.338138E-1;
+ CoWA[48] = 4.31504E-1;
+ CoWA[49] = 4.272541E-1;
+ CoWA[50] = 4.220568E-1;
+ CoWA[51] = 4.158229E-1;
+ CoWA[52] = 4.083281E-1;
+ CoWA[53] = 3.981182E-1;
+ CoWA[54] = 3.871678E-1;
+ CoWA[55] = 3.755527E-1;
+ CoWA[56] = 3.628823E-1;
+ CoWA[57] = 3.520135E-1;
+ CoWA[58] = 3.400924E-1;
+ CoWA[59] = 3.280532E-1;
+ CoWA[60] = 3.139477E-1;
+ CoWA[61] = 2.997087E-1;
+ CoWA[62] = 2.849179E-1;
+ CoWA[63] = 2.710475E-1;
+ CoWA[64] = 2.576478E-1;
+ CoWA[65] = 2.449155E-1;
+ CoWA[66] = 2.317447E-1;
+ CoWA[67] = 2.193161E-1;
+ CoWA[68] = 2.072622E-1;
+ CoWA[69] = 1.956955E-1;
+ CoWA[70] = 1.846514E-1;
+ CoWA[71] = 1.734096E-1;
+ CoWA[72] = 1.622678E-1;
+ CoWA[73] = 1.520447E-1;
+ CoWA[74] = 1.416351E-1;
+ CoWA[75] = 1.32136E-1;
+ CoWA[76] = 1.231861E-1;
+ CoWA[77] = 1.150411E-1;
+ CoWA[78] = 1.071536E-1;
+ CoWA[79] = 9.9465E-2;
+ CoWA[80] = 9.22347E-2;
+ CoWA[81] = 8.54394E-2;
+ CoWA[82] = 7.87697E-2;
+ CoWA[83] = 7.23848E-2;
+ CoWA[84] = 6.6587E-2;
+ CoWA[85] = 6.15849E-2;
+ CoWA[86] = 5.6573E-2;
+ CoWA[87] = 5.17893E-2;
+ CoWA[88] = 4.70011E-2;
+ CoWA[89] = 4.2886E-2;
+ CoWA[90] = 3.91224E-2;
+ CoWA[91] = 3.53163E-2;
+ CoWA[92] = 3.20884E-2;
+ CoWA[93] = 2.92264E-2;
+ CoWA[94] = 2.66058E-2;
+ CoWA[95] = 2.37352E-2;
+ CoWA[96] = 2.14669E-2;
+ CoWA[97] = 1.94848E-2;
+ CoWA[98] = 1.75591E-2;
+ CoWA[99] = 1.58232E-2;
+ CoWA[100] = 1.40302E-2;
+ CoWA[101] = 1.24349E-2;
+ CoWA[102] = 1.11856E-2;
+ CoWA[103] = 9.9765E-3;
+ CoWA[104] = 8.9492E-3;
+ CoWA[105] = 8.0063E-3;
+ CoWA[106] = 7.1509E-3;
+ CoWA[107] = 6.3196E-3;
+ CoWA[108] = 5.6856E-3;
+ CoWA[109] = 5.0686E-3;
+ CoWA[110] = 4.5085E-3;
+ CoWA[111] = 3.9895E-3;
+ CoWA[112] = 3.4804E-3;
+ CoWA[113] = 3.0447E-3;
+ CoWA[114] = 2.7012E-3;
+ CoWA[115] = 2.2984E-3;
+ CoWA[116] = 2.0283E-3;
+ CoWA[117] = 1.7399E-3;
+ CoWA[118] = 1.5032E-3;
+ CoWA[119] = 1.3267E-3;
+ CoWA[120] = 1.1531E-3;
+ CoWA[121] = 9.92E-4;
+ CoWA[122] = 9.211E-4;
+ CoWA[123] = 8.296E-4;
+ CoWA[124] = 6.991E-4;
+ CoWA[125] = 5.84E-4;
+ CoWA[126] = 5.12E-4;
+ CoWA[127] = 4.314E-4;
+ CoWA[128] = 3.593E-4;
+ CoWA[129] = 3.014E-4;
+ CoWA[130] = 2.401E-4;
+ CoWA[131] = 2.004E-4;
+ CoWA[132] = 1.614E-4;
+ CoWA[133] = 1.257E-4;
+ CoWA[134] = 1.112E-4;
+ CoWA[135] = 9.22E-5;
+ CoWA[136] = 8.77E-5;
+ CoWA[137] = 6.22E-5;
+ CoWA[138] = 4.93E-5;
+ CoWA[139] = 3.92E-5;
+ CoWA[140] = 3.15E-5;
+ CoWA[141] = 1.03E-5;
+ CoWA[142] = 9.6E-6;
+ }\end{hide}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ /*
+ * Approximation of the cumulative distribution function of the
+ * watsonG statistics by the cubic spline function.
+ * Y[.] - tabular value of the statistic;
+ * M[.] - tabular value of the first derivative;
+ */
+ if (n <= 1)
+ throw new IllegalArgumentException ("n < 2");
+
+ final double MINARG = 0.15;
+ if (x <= MINARG)
+ return 0.0;
+ if (x >= 10.0)
+ return 1.0;
+
+ double R, Res;
+ final double MAXARG = 1.5;
+ if (x > MAXARG) {
+ R = Math.exp (19.0 - 20.0*x);
+ Res = 1.0 - R;
+ // Empirical Correction in 1/sqrt (n)
+ R = Math.exp (13.34 - 15.26*x)/Math.sqrt ((double)n);
+ Res += R;
+ // The correction in 1/sqrt (n) is not always precise
+ if (Res >= 1.0)
+ return 1.0;
+ else
+ return Res;
+ }
+
+ final double MINTAB = 0.1;
+ final double STEP = 0.01;
+ int i, j;
+ double Tj;
+ double Ti;
+ double P;
+ double H;
+
+ // Search of the correct slot in the interpolation table
+ i = (int)((x - MINTAB)/STEP + 1);
+ Ti = MINTAB + i*STEP;
+ Tj = Ti - STEP;
+
+ // Approximation within the slot
+ j = i - 1;
+ H = x - Tj;
+ R = Ti - x;
+ P = STEP*STEP/6.0;
+ Res = ((MWA[j]*R*R*R + MWA[i]*H*H*H)/6.0)/STEP;
+ Res += ((YWA[j] - MWA[j]*P)*R + (YWA[i] - MWA[i]*P)*H)/STEP;
+
+ // Empirical correction in 1/sqrt (n)
+ Res += (CoWA[i]*H + CoWA[j]*R)/(STEP*Math.sqrt ((double)n));
+
+ if (Res >= 1.0)
+ return 1.0;
+ return Res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the Watson $G$ distribution function $F_n(x)$, with parameter $n$.
+ A cubic spline interpolation is used for the asymptotic distribution when
+ $n\to \infty$, and an empirical correction of order $1/\sqrt{n}$, obtained
+ empirically from $10^7$ simulation runs with $n = 256$ is then added.
+ The absolute error is estimated to be less than
+ 0.01, 0.005, 0.002, 0.0008, 0.0005, 0.0005, 0.0005 for
+ $n = 16$, 32, 64, 128, 256, 512, 1024, respectively.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double x)\begin{hide} {
+ return 1.0 - cdf(n,x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function $\bar F_n(x)$
+ with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n < 2");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u == 1.0)
+ return Double.POSITIVE_INFINITY;
+ if (u == 0.0)
+ return 0.0;
+
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 10.0, f, 1e-5);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes $x = F_n^{-1}(u)$, where $F_n$ is the
+ {\em Watson\/} $G$ distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n <= 1)
+ throw new IllegalArgumentException ("n < 2");
+ this.n = n;
+ supportA = 0.0;
+ supportB = 10.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameter $n$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/WatsonUDist.java b/source/umontreal/iro/lecuyer/probdist/WatsonUDist.java
new file mode 100644
index 0000000..b215db6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/WatsonUDist.java
@@ -0,0 +1,392 @@
+
+
+/*
+ * Class: WatsonUDist
+ * Description: Watson U distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution} for the
+ * <SPAN CLASS="textit">Watson U</SPAN> distribution (see).
+ * Given a sample of <SPAN CLASS="MATH"><I>n</I></SPAN> independent uniforms <SPAN CLASS="MATH"><I>u</I><SUB>i</SUB></SPAN> over <SPAN CLASS="MATH">[0, 1]</SPAN>,
+ * the <SPAN CLASS="textit">Watson</SPAN> statistic <SPAN CLASS="MATH"><I>U</I><SUB>n</SUB><SUP>2</SUP></SPAN> is defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>W</I><SUB>n</SUB><SUP>2</SUP> = 1/12<I>n</I> + ∑<SUB>j=1</SUB><SUP>n</SUP>{<I>u</I><SUB>(j)</SUB> - (<I>j</I>-0.5)/<I>n</I>}<SUP>2</SUP>,
+ * <BR><I>U</I><SUB>n</SUB><SUP>2</SUP> = <I>W</I><SUB>n</SUB><SUP>2</SUP> - <I>n</I>(bar(u)<SUB>n</SUB> -1/2)<SUP>2</SUP>.
+ * </DIV><P></P>
+ * where the <SPAN CLASS="MATH"><I>u</I><SUB>(j)</SUB></SPAN> are the <SPAN CLASS="MATH"><I>u</I><SUB>i</SUB></SPAN> sorted in increasing order, and
+ * <SPAN CLASS="MATH">bar(u)<SUB>n</SUB></SPAN> is the average of the observations <SPAN CLASS="MATH"><I>u</I><SUB>i</SUB></SPAN>.
+ * The distribution function (the cumulative probabilities)
+ * is defined as
+ * <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB>(<I>x</I>) = <I>P</I>[<I>U</I><SUB>n</SUB><SUP>2</SUP> <= <I>x</I>]</SPAN>.
+ *
+ */
+public class WatsonUDist extends ContinuousDistribution {
+ private static final double XSEPARE = 0.15;
+ private static final double PI = Math.PI;
+ private static final int JMAX = 10;
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+ private static double cdfn (int n, double x) {
+ // The 1/n correction for the cdf, for x < XSEPARE
+ double terme;
+ double v = Math.exp (-0.125/x);
+ double somme = 0;
+ int j = 0;
+
+ do {
+ double a = (2*j + 1)*(2*j + 1);
+ terme = Math.pow (v, (double)(2*j + 1)*(2*j + 1));
+ double der = terme*(a - 4.0*x)/(8.0*x*x);
+ somme += (5.0*x - 1.0/12.0) * der / 12.0;
+ der = terme* (a*a - 24.0*a*x + 48.0*x*x)/ (64.0*x*x*x*x);
+ somme += x*x*der/6.0;
+ ++j;
+ } while (!(terme <= Math.abs(somme) * Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println (x + ": watsonU: somme 1/n has not converged");
+
+ v = -2.0*somme/(n*Math.sqrt (2.0*PI*x));
+ return v;
+ }
+
+
+ /**
+ * Constructs a <EM>Watson U</EM> distribution for a sample of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public WatsonUDist (int n) {
+ setN (n);
+ }
+
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return WatsonUDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return WatsonUDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return WatsonUDist.getStandardDeviation (n);
+ }
+
+ /**
+ * Computes the density of the <EM>Watson U</EM> distribution
+ * with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double density (int n, double x) {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= 1.0/(12.0*n) || x >= n/12.0 || x >= XBIG)
+ return 0.0;
+
+ final double EPS = 1.0 / 100.0;
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+
+/*
+// This is the asymptotic density for n -> infinity
+ int j;
+ double signe;
+ double v;
+ double terme;
+ double somme;
+
+ if (x > XSEPARE) {
+ // this series converges rapidly for x > 0.15
+ v = Math.exp (-(x*2.0*PI*PI));
+ signe = 1.0;
+ somme = 0.0;
+ j = 1;
+ do {
+ terme = j*j*Math.pow (v, (double)j*j);
+ somme += signe*terme;
+ signe = -signe;
+ ++j;
+ } while (!(terme < Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println ("watsonU: sum1 has not converged");
+ return 4.0*PI*PI*somme;
+ }
+
+ // this series converges rapidly for x <= 0.15
+ v = Math.exp (-0.125/x);
+ somme = v;
+ double somme2 = v;
+ j = 2;
+ do {
+ terme = Math.pow (v, (double)(2*j - 1)*(2*j - 1));
+ somme += terme;
+ somme2 += terme * (2*j - 1)*(2*j - 1);
+ ++j;
+ } while (!(terme <= somme2 * Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println ("watsonU: sum2 has not converged");
+
+ final double RACINE = Math.sqrt (2.0*PI*x);
+ return -somme/(x*RACINE) + 2*somme2 * 0.125/ ((x*x) * RACINE);
+*/
+ }
+
+
+ /**
+ * Computes the Watson <SPAN CLASS="MATH"><I>U</I></SPAN> distribution function, i.e. returns
+ *
+ * <SPAN CLASS="MATH"><I>P</I>[<I>U</I><SUB>n</SUB><SUP>2</SUP> <= <I>x</I>]</SPAN>, where <SPAN CLASS="MATH"><I>U</I><SUB>n</SUB><SUP>2</SUP></SPAN> is the Watson statistic
+ * defined in. We use the asymptotic distribution for
+ *
+ * <SPAN CLASS="MATH"><I>n</I> -> ∞</SPAN>, plus a correction in <SPAN CLASS="MATH"><I>O</I>(1/<I>n</I>)</SPAN>, as given in.
+ *
+ */
+ public static double cdf (int n, double x) {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= 1.0/(12.0*n))
+ return 0.0;
+ if (x > 3.95 || x >= n/12.0)
+ return 1.0;
+
+ if (2 == n) {
+ if (x <= 1.0/24.0)
+ return 0.0;
+ if (x >= 1.0/6.0)
+ return 1.0;
+ return 2.0*Math.sqrt(2.0*x - 1.0/12.0);
+ }
+
+ if (x > XSEPARE)
+ return 1.0 - barF (n, x);
+
+ // this series converges rapidly for x <= 0.15
+ double terme;
+ double v = Math.exp (-0.125/x);
+ double somme = v;
+ int j = 2;
+
+ do {
+ terme = Math.pow (v, (double)(2*j - 1)*(2*j - 1));
+ somme += terme;
+ ++j;
+ } while (!(terme <= somme * Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println (x + ": watsonU: sum2 has not converged");
+
+ v = 2.0*somme/Math.sqrt (2.0*PI*x);
+ v += cdfn(n, x);
+ if (v >= 1.0)
+ return 1.0;
+ if (v <= 0.0)
+ return 0.0;
+ return v;
+ }
+
+
+ /**
+ * Computes the complementary distribution function
+ * <SPAN CLASS="MATH">bar(F)<SUB>n</SUB>(<I>x</I>)</SPAN>,
+ * where <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB></SPAN> is the <EM>Watson</EM> <SPAN CLASS="MATH"><I>U</I></SPAN> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double barF (int n, double x) {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= 1.0/(12.0*n))
+ return 1.0;
+ if (x >= XBIG || x >= n/12.0)
+ return 0.0;
+
+ if (2 == n)
+ return 1.0 - 2.0*Math.sqrt(2.0*x - 1.0/12.0);
+
+ if (x > XSEPARE) {
+ // this series converges rapidly for x > 0.15
+ double terme, ter;
+ double v = Math.exp (-2.0*PI*PI*x);
+ double signe = 1.0;
+ double somme = 0.0;
+ double son = 0.0;
+ int j = 1;
+
+ do {
+ terme = Math.pow (v, (double)j*j);
+ somme += signe*terme;
+ double h = 2*j*PI*x;
+ ter = (5.0*x - h*h - 1.0/12.0)*j*j;
+ son += signe*terme*ter;
+ signe = -signe;
+ ++j;
+ } while (!(terme < Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println (x + ": watsonU: sum1 has not converged");
+ v = 2.0*somme + PI*PI*son/(3.0*n);
+ if (v <= 0.0)
+ return 0.0;
+ if (v >= 1.0)
+ return 1.0;
+ return v;
+ }
+
+ return (1.0 - cdf(n, x));
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><I>x</I> = <I>F</I><SUB>n</SUB><SUP>-1</SUP>(<I>u</I>)</SPAN>, where <SPAN CLASS="MATH"><I>F</I><SUB>n</SUB></SPAN> is the
+ * <EM>Watson</EM> <SPAN CLASS="MATH"><I>U</I></SPAN> distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ */
+ public static double inverseF (int n, double u) {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u >= 1.0)
+ return n/12.0;
+ if (u <= 0.0)
+ return 1.0/(12.0*n);
+
+ if (2 == n)
+ return 1.0/24.0 + u*u/8.0;
+
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 2.0, f, 1e-7);
+ }
+
+
+ /**
+ * Returns the mean of the <EM>Watson</EM> <SPAN CLASS="MATH"><I>U</I></SPAN> distribution with
+ * parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return Returns the mean
+ *
+ */
+ public static double getMean (int n) {
+ return 1.0/12.0;
+ }
+
+
+ /**
+ * Returns the variance of the <EM>Watson</EM> <SPAN CLASS="MATH"><I>U</I></SPAN> distribution with
+ * parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the variance
+ *
+ */
+ public static double getVariance (int n) {
+ return (n - 1)/(360.0*n);
+ }
+
+
+ /**
+ * Returns the standard deviation of the <EM>Watson</EM> <SPAN CLASS="MATH"><I>U</I></SPAN>
+ * distribution with parameter <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @return the standard deviation
+ *
+ */
+ public static double getStandardDeviation (int n) {
+ return Math.sqrt (WatsonUDist.getVariance (n));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public void setN (int n) {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+ this.n = n;
+ supportA = 1.0/(12.0*n);
+ supportB = n/12.0;
+ }
+
+
+ /**
+ * Return an array containing the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {n};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : n = " + n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/WatsonUDist.tex b/source/umontreal/iro/lecuyer/probdist/WatsonUDist.tex
new file mode 100644
index 0000000..38eecd8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/WatsonUDist.tex
@@ -0,0 +1,427 @@
+\defmodule {WatsonUDist}
+
+Extends the class \class{ContinuousDistribution} for the
+\emph{Watson U} distribution (see \cite{tDUR73a,tSTE70a,tSTE86b}).
+Given a sample of $n$ independent uniforms $u_i$ over $[0,1]$,
+the \emph{Watson} statistic $U_n^2$ is defined by
+\begin{latexonly}%
+\begin {eqnarray*}
+ W_n^2 &=& \frac{1}{12n} +
+ \sum_{j=1}^n \left\{u_{(j)} - \frac{(j- 1/2)}{n}\right\}^2, \\
+ U_n^2 &=& W_n^2 - n\left (\bar u_n - 1/2\right)^2.
+ \eqlabel {eq:WatsonU}
+\end {eqnarray*}
+\end{latexonly}%
+\begin{htmlonly}
+\eq
+ W_n^2 = {1/ 12n} +
+ \sum_{j=1}^n \left\{u_{(j)} - {(j- 0.5)/ n}\right\}^2, \\
+ U_n^2 = W_n^2 - n\left (\bar u_n - 1/2\right)^2.
+\endeq
+\end{htmlonly}%
+ where the $u_{(j)}$ are the $u_i$ sorted in increasing order, and
+ $\bar u_n$ is the average of the observations $u_{i}$.
+ The distribution function (the cumulative probabilities)
+ is defined as $F_n(x) = P[U_n^2 \le x]$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: WatsonUDist
+ * Description: Watson U distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class WatsonUDist extends ContinuousDistribution\begin{hide} {
+ private static final double XSEPARE = 0.15;
+ private static final double PI = Math.PI;
+ private static final int JMAX = 10;
+ protected int n;
+
+ private static class Function implements MathFunction {
+ protected int n;
+ protected double u;
+
+ public Function (int n, double u) {
+ this.n = n;
+ this.u = u;
+ }
+
+ public double evaluate (double x) {
+ return u - cdf(n,x);
+ }
+ }
+
+ private static double cdfn (int n, double x) {
+ // The 1/n correction for the cdf, for x < XSEPARE
+ double terme;
+ double v = Math.exp (-0.125/x);
+ double somme = 0;
+ int j = 0;
+
+ do {
+ double a = (2*j + 1)*(2*j + 1);
+ terme = Math.pow (v, (double)(2*j + 1)*(2*j + 1));
+ double der = terme*(a - 4.0*x)/(8.0*x*x);
+ somme += (5.0*x - 1.0/12.0) * der / 12.0;
+ der = terme* (a*a - 24.0*a*x + 48.0*x*x)/ (64.0*x*x*x*x);
+ somme += x*x*der/6.0;
+ ++j;
+ } while (!(terme <= Math.abs(somme) * Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println (x + ": watsonU: somme 1/n has not converged");
+
+ v = -2.0*somme/(n*Math.sqrt (2.0*PI*x));
+ return v;
+ }\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public WatsonUDist (int n)\begin{hide} {
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a {\em Watson U\/} distribution for a sample of size $n$.
+
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (n, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (n, x);
+ }
+
+ public double barF (double x) {
+ return barF (n, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (n, u);
+ }
+
+ public double getMean() {
+ return WatsonUDist.getMean (n);
+ }
+
+ public double getVariance() {
+ return WatsonUDist.getVariance (n);
+ }
+
+ public double getStandardDeviation() {
+ return WatsonUDist.getStandardDeviation (n);
+ }\end{hide}
+
+ public static double density (int n, double x)\begin{hide} {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= 1.0/(12.0*n) || x >= n/12.0 || x >= XBIG)
+ return 0.0;
+
+ final double EPS = 1.0 / 100.0;
+ return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS);
+
+/*
+// This is the asymptotic density for n -> infinity
+ int j;
+ double signe;
+ double v;
+ double terme;
+ double somme;
+
+ if (x > XSEPARE) {
+ // this series converges rapidly for x > 0.15
+ v = Math.exp (-(x*2.0*PI*PI));
+ signe = 1.0;
+ somme = 0.0;
+ j = 1;
+ do {
+ terme = j*j*Math.pow (v, (double)j*j);
+ somme += signe*terme;
+ signe = -signe;
+ ++j;
+ } while (!(terme < Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println ("watsonU: sum1 has not converged");
+ return 4.0*PI*PI*somme;
+ }
+
+ // this series converges rapidly for x <= 0.15
+ v = Math.exp (-0.125/x);
+ somme = v;
+ double somme2 = v;
+ j = 2;
+ do {
+ terme = Math.pow (v, (double)(2*j - 1)*(2*j - 1));
+ somme += terme;
+ somme2 += terme * (2*j - 1)*(2*j - 1);
+ ++j;
+ } while (!(terme <= somme2 * Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println ("watsonU: sum2 has not converged");
+
+ final double RACINE = Math.sqrt (2.0*PI*x);
+ return -somme/(x*RACINE) + 2*somme2 * 0.125/ ((x*x) * RACINE);
+*/
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density of the {\em Watson U\/} distribution
+ with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int n, double x)\begin{hide} {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= 1.0/(12.0*n))
+ return 0.0;
+ if (x > 3.95 || x >= n/12.0)
+ return 1.0;
+
+ if (2 == n) {
+ if (x <= 1.0/24.0)
+ return 0.0;
+ if (x >= 1.0/6.0)
+ return 1.0;
+ return 2.0*Math.sqrt(2.0*x - 1.0/12.0);
+ }
+
+ if (x > XSEPARE)
+ return 1.0 - barF (n, x);
+
+ // this series converges rapidly for x <= 0.15
+ double terme;
+ double v = Math.exp (-0.125/x);
+ double somme = v;
+ int j = 2;
+
+ do {
+ terme = Math.pow (v, (double)(2*j - 1)*(2*j - 1));
+ somme += terme;
+ ++j;
+ } while (!(terme <= somme * Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println (x + ": watsonU: sum2 has not converged");
+
+ v = 2.0*somme/Math.sqrt (2.0*PI*x);
+ v += cdfn(n, x);
+ if (v >= 1.0)
+ return 1.0;
+ if (v <= 0.0)
+ return 0.0;
+ return v;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the Watson $U$ distribution function, i.e. returns
+ $P[U_n^2 \le x]$, where $U_n^2$ is the Watson statistic
+ defined in (\ref{eq:WatsonU}). We use the asymptotic distribution for
+$n \to\infty$, plus a correction in $O(1/n)$, as given in \cite{tCSO96a}.
+% It is given by
+% \begin{equation}
+% P(U_\infty^2 \le x) \ = \ 1 + 2 \sum_{j=1}^\infty (-1)^j e^{-2 j^2 \pi^2 x}
+% \eqlabel{eq:DistWU1}
+% \end{equation}
+% This sum converges extremely fast except for small $x$, where alternating
+% successive terms give rise to numerical instability.
+% But with the Poisson summation formula \cite{mLAN73a},
+% the sum can be transformed to
+% \begin{equation}
+% P(U_\infty^2 \le x) \ = \ \sqrt{\frac2{\pi x}}\; \sum_{j=0}^\infty
+% e^{- (2 j+1)^2/8 x} \eqlabel{eq:DistWU2}
+% \end{equation}
+% which can be used for small $x$.
+% The absolute difference between the returned value and $P[U_n^2 \le x]$
+% is estimated to be less than 0.01 for $n \ge 8$, for the asymptotic formula.
+
+% With the 1/n correction, there should be 4 decimals digits of precision.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (int n, double x)\begin{hide} {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+
+ if (x <= 1.0/(12.0*n))
+ return 1.0;
+ if (x >= XBIG || x >= n/12.0)
+ return 0.0;
+
+ if (2 == n)
+ return 1.0 - 2.0*Math.sqrt(2.0*x - 1.0/12.0);
+
+ if (x > XSEPARE) {
+ // this series converges rapidly for x > 0.15
+ double terme, ter;
+ double v = Math.exp (-2.0*PI*PI*x);
+ double signe = 1.0;
+ double somme = 0.0;
+ double son = 0.0;
+ int j = 1;
+
+ do {
+ terme = Math.pow (v, (double)j*j);
+ somme += signe*terme;
+ double h = 2*j*PI*x;
+ ter = (5.0*x - h*h - 1.0/12.0)*j*j;
+ son += signe*terme*ter;
+ signe = -signe;
+ ++j;
+ } while (!(terme < Num.DBL_EPSILON || j > JMAX));
+ if (j > JMAX)
+ System.err.println (x + ": watsonU: sum1 has not converged");
+ v = 2.0*somme + PI*PI*son/(3.0*n);
+ if (v <= 0.0)
+ return 0.0;
+ if (v >= 1.0)
+ return 1.0;
+ return v;
+ }
+
+ return (1.0 - cdf(n, x));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the complementary distribution function $\bar F_n(x)$,
+ where $F_n$ is the {\em Watson\/} $U$ distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (int n, double u)\begin{hide} {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u must be in [0,1]");
+ if (u >= 1.0)
+ return n/12.0;
+ if (u <= 0.0)
+ return 1.0/(12.0*n);
+
+ if (2 == n)
+ return 1.0/24.0 + u*u/8.0;
+
+ Function f = new Function (n,u);
+ return RootFinder.brentDekker (0.0, 2.0, f, 1e-7);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes $x = F_n^{-1}(u)$, where $F_n$ is the
+ {\em Watson\/} $U$ distribution with parameter $n$.
+\end{tabb}
+\begin{code}
+
+ public static double getMean (int n)\begin{hide} {
+ return 1.0/12.0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean of the {\em Watson\/} $U$ distribution with
+ parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{Returns the mean}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (int n)\begin{hide} {
+ return (n - 1)/(360.0*n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the variance of the {\em Watson\/} $U$ distribution with
+ parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (int n)\begin{hide} {
+ return Math.sqrt (WatsonUDist.getVariance (n));
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the standard deviation of the {\em Watson\/} $U$
+ distribution with parameter $n$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation}
+\end{htmlonly}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $n$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setN (int n)\begin{hide} {
+ if (n < 2)
+ throw new IllegalArgumentException ("n < 2");
+ this.n = n;
+ supportA = 1.0/(12.0*n);
+ supportB = n/12.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameter $n$ of this object.
+ \end{tabb}
+ \begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {n};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return an array containing the parameter $n$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : n = " + n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/WeibullDist.java b/source/umontreal/iro/lecuyer/probdist/WeibullDist.java
new file mode 100644
index 0000000..d542842
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/WeibullDist.java
@@ -0,0 +1,490 @@
+
+/*
+ * Class: WeibullDist
+ * Description: Weibull distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdist;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+/**
+ * This class extends the class {@link ContinuousDistribution} for
+ * the <EM>Weibull</EM> distribution with shape parameter
+ *
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>, location parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>, and scale parameter
+ *
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * The density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>αλ</I><SUP><I>α</I></SUP>(<I>x</I> - <I>δ</I>)<SUP><I>α</I>-1</SUP><I>e</I><SUP>-(<I>λ</I>(x-<I>δ</I>))<SUP><I>α</I></SUP></SUP> for <I>x</I> > <I>δ</I>.
+ * </DIV><P></P>
+ * the distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>) = 1 - <I>e</I><SUP>-(<I>λ</I>(x-<I>δ</I>))<SUP><I>α</I></SUP></SUP> for <I>x</I> > <I>δ</I>,
+ * </DIV><P></P>
+ * and the inverse distribution function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I><SUP>-1</SUP>(<I>u</I>) = (- ln(1 - <I>u</I>))<SUP>1/<I>α</I></SUP>/<I>λ</I> + <I>δ</I> for 0 <= <I>u</I> < 1.
+ * </DIV><P></P>
+ *
+ */
+public class WeibullDist extends ContinuousDistribution {
+ private double alpha;
+ private double lambda;
+ private double delta;
+
+ private static class Function implements MathFunction {
+ private int n;
+ private double xi[];
+ private double lnXi[];
+ private double sumLnXi = 0.0;
+ private final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+
+ public Function (double x[], int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ this.lnXi = new double[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ this.xi[i] = x[i];
+ if (x[i] > 0.0)
+ this.lnXi[i] = Math.log (x[i]);
+ else
+ this.lnXi[i] = LN_EPS;
+ sumLnXi += this.lnXi[i];
+ }
+ }
+
+ public double evaluate (double x)
+ {
+ if (x <= 0.0) return 1.0e200;
+ double sumXiLnXi = 0.0;
+ double sumXi = 0.0;
+ double xalpha;
+
+ for (int i = 0; i < n; i++)
+ {
+ xalpha = Math.pow (this.xi[i], x);
+ sumXiLnXi += xalpha * lnXi[i];
+ sumXi += xalpha;
+ }
+
+ return (x * (n * sumXiLnXi - sumLnXi * sumXi) - n * sumXi);
+ }
+ }
+
+
+
+
+ /**
+ * Constructs a <TT>WeibullDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT>, <SPAN CLASS="MATH"><I>λ</I></SPAN> = 1, and <SPAN CLASS="MATH"><I>δ</I></SPAN> = 0.
+ *
+ */
+ public WeibullDist (double alpha) {
+ setParams (alpha, 1.0, 0.0);
+ }
+
+
+ /**
+ * Constructs a <TT>WeibullDist</TT> object with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>,
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>, and <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT>.
+ *
+ */
+ public WeibullDist (double alpha, double lambda, double delta) {
+ setParams (alpha, lambda, delta);
+ }
+
+
+ public double density (double x) {
+ return density (alpha, lambda, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, delta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, lambda, delta, u);
+ }
+
+ public double getMean() {
+ return WeibullDist.getMean (alpha, lambda, delta);
+ }
+
+ public double getVariance() {
+ return WeibullDist.getVariance (alpha, lambda, delta);
+ }
+
+ public double getStandardDeviation() {
+ return WeibullDist.getStandardDeviation (alpha, lambda, delta);
+ }
+
+ /**
+ * Computes the density function.
+ *
+ */
+ public static double density (double alpha, double lambda,
+ double delta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= delta)
+ return 0.0;
+ double y = Math.log(lambda*(x - delta)) * alpha;
+ if (y >= 7.0)
+ return 0.0;
+ y = Math.exp(y);
+
+ return alpha * (y / (x - delta)) * Math.exp(-y);
+ }
+
+
+ /**
+ * Same as <TT>density (alpha, 1, 0, x)</TT>.
+ *
+ */
+ public static double density (double alpha, double x) {
+ return density (alpha, 1.0, 0.0, x);
+ }
+
+
+ /**
+ * Computes the distribution function.
+ *
+ */
+ public static double cdf (double alpha, double lambda,
+ double delta, double x) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= delta)
+ return 0.0;
+ if ((lambda*(x - delta) >= XBIG) && (alpha >= 1.0))
+ return 1.0;
+ double y = Math.log(lambda*(x - delta)) * alpha;
+ if (y >= 3.65)
+ return 1.0;
+ y = Math.exp(y);
+ return -Math.expm1 (-y); // in JDK-1.5
+ }
+
+
+ /**
+ * Same as <TT>cdf (alpha, 1, 0, x)</TT>.
+ *
+ */
+ public static double cdf (double alpha, double x) {
+ return cdf (alpha, 1.0, 0.0, x);
+ }
+
+
+ /**
+ * Computes the complementary distribution function.
+ *
+ */
+ public static double barF (double alpha, double lambda,
+ double delta, double x) {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= delta)
+ return 1.0;
+ if (alpha >= 1.0 && x >= Num.DBL_MAX_EXP*2)
+ return 0.0;
+
+ double temp = Math.log (lambda*(x - delta)) * alpha;
+ if (temp >= Num.DBL_MAX_EXP * Num.LN2)
+ return 0.0;
+ temp = Math.exp(temp);
+ return Math.exp (-temp);
+ }
+
+
+ /**
+ * Same as <TT>barF (alpha, 1, 0, x)</TT>.
+ *
+ */
+ public static double barF (double alpha, double x) {
+ return barF (alpha, 1.0, 0.0, x);
+ }
+
+
+ /**
+ * Computes the inverse of the distribution function.
+ *
+ */
+ public static double inverseF (double alpha, double lambda,
+ double delta, double u) {
+ double t;
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u <= 0.0)
+ return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ t = -Math.log1p (-u);
+ if (Math.log (t)/Math.log (10) >= alpha*Num.DBL_MAX_10_EXP)
+ throw new ArithmeticException
+ ("inverse function cannot be positive infinity");
+
+ return Math.pow (t, 1.0/alpha)/lambda + delta;
+ }
+
+
+ /**
+ * Same as <TT>inverseF (alpha, 1, 0, x)</TT>.
+ *
+ */
+ public static double inverseF (double alpha, double x) {
+ return inverseF (alpha, 1.0, 0.0, x);
+ }
+
+ private static double[] getMaximumLikelihoodEstimate (double[] x, int n,
+ double delta) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (delta != 0.0)
+ throw new IllegalArgumentException ("delta must be equal to 0");
+// Verifier cette fonction si delta != 0.
+
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+ double sumLn = 0.0;
+ double sumLn2 = 0.0;
+ double lnxi;
+ for (int i = 0; i < n; i++) {
+ if (x[i] <= delta)
+ lnxi = LN_EPS;
+ else
+ lnxi = Math.log (x[i]);
+ sumLn += lnxi;
+ sumLn2 += lnxi * lnxi;
+ }
+
+ double alpha0 = Math.sqrt ((double) n / ((6.0 / (Math.PI * Math.PI)) *
+ (sumLn2 - sumLn * sumLn / (double) n)));
+ double a = alpha0 - 20.0;
+ if (a <= delta)
+ a = delta + 1.0e-5;
+
+ double param[] = new double[3];
+ param[2] = 0.0;
+ Function f = new Function (x, n);
+ param[0] = RootFinder.brentDekker (a, alpha0 + 20.0, f, 1e-5);
+
+ double sumXalpha = 0.0;
+ for (int i = 0; i < n; i++)
+ sumXalpha += Math.pow (x[i], param[0]);
+ param[1] = Math.pow ((double) n / sumXalpha, 1.0 / param[0]);
+
+ return param;
+ }
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH">(<I>α</I>, <I>λ</I>)</SPAN> of the Weibull distribution,
+ * assuming that
+ * <SPAN CLASS="MATH"><I>δ</I> = 0</SPAN>,
+ * using the maximum likelihood method, from the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>. The estimates are returned in a two-element
+ * array, in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>].
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(α)</SPAN>,
+ * <SPAN CLASS="MATH">hat(λ)</SPAN>,
+ * <SPAN CLASS="MATH">hat(δ)</SPAN> = 0]
+ *
+ */
+ public static double[] getMLE (double[] x, int n)
+ {
+ return getMaximumLikelihoodEstimate (x, n, 0.0);
+ }
+
+
+ /**
+ * Creates a new instance of a Weibull distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>,
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> = 0</SPAN>
+ * estimated using the maximum likelihood method based on the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * <SPAN CLASS="MATH"><I>x</I>[<I>i</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ *
+ */
+ public static WeibullDist getInstanceFromMLE (double[] x, int n) {
+ double param[] = getMLE (x, n);
+ return new WeibullDist (param[0], param[1], param[2]);
+ }
+
+
+ /**
+ * Computes and returns the mean
+ * of the Weibull distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the mean of the Weibull distribution
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>δ</I> + <I>Γ</I>(1 + 1/<I>α</I>)/<I>λ</I></SPAN>
+ *
+ */
+ public static double getMean (double alpha, double lambda, double delta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (delta + Math.exp (Num.lnGamma(1.0 + 1.0 / alpha)) / lambda);
+ }
+
+
+ /**
+ * Computes and returns the variance
+ * of the Weibull distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the variance of the Weibull distribution
+ *
+ * <SPAN CLASS="MATH">Var[<I>X</I>] = 1/<I>λ</I><SUP>2</SUP>| <I>Γ</I>(2/<I>α</I> +1) - <I>Γ</I><SUP>2</SUP>(1/<I>α</I> + 1)|</SPAN>
+ *
+ */
+ public static double getVariance (double alpha, double lambda,
+ double delta) {
+ double gAlpha;
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ gAlpha = Math.exp (Num.lnGamma (1.0 / alpha + 1.0));
+
+ return (Math.abs (Math.exp (Num.lnGamma(2 / alpha + 1)) - gAlpha * gAlpha) / (lambda * lambda));
+ }
+
+
+ /**
+ * Computes and returns the standard deviation
+ * of the Weibull distribution with parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ * @return the standard deviation of the Weibull distribution
+ *
+ */
+ public static double getStandardDeviation (double alpha, double lambda,
+ double delta) {
+ return Math.sqrt (WeibullDist.getVariance (alpha, lambda, delta));
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> for this
+ * object.
+ *
+ */
+ public void setParams (double alpha, double lambda, double delta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ this.alpha = alpha;
+ this.lambda = lambda;
+ this.delta = delta;
+ supportA = delta;
+ }
+
+
+ /**
+ * Return a table containing the parameters of the current distribution.
+ * This table is put in regular order: [<SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>].
+ *
+ *
+ */
+ public double[] getParams () {
+ double[] retour = {alpha, lambda, delta};
+ return retour;
+ }
+
+
+ public String toString () {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda + ", delta = " + delta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdist/WeibullDist.tex b/source/umontreal/iro/lecuyer/probdist/WeibullDist.tex
new file mode 100644
index 0000000..6f0a36f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/WeibullDist.tex
@@ -0,0 +1,495 @@
+\defmodule {WeibullDist}
+
+This class extends the class \class{ContinuousDistribution} for
+the {\em Weibull\/} distribution \cite[page 628]{tJOH95a} with shape parameter
+$\alpha > 0$, location parameter $\delta$, and scale parameter
+$\lambda > 0$.
+The density function is
+\eq
+ f(x) = \alpha\lambda^{\alpha}(x-\delta)^{\alpha-1}
+ e^{-(\lambda(x-\delta))^{\alpha}}
+ \qquad\mbox{for }x>\delta\latex{,}\html{.} \eqlabel {eq:fweibull}
+\endeq
+the distribution function is
+\eq
+ F(x) = 1 - e^{-(\lambda (x - \delta))^\alpha}
+ \qquad\mbox{for }x>\delta, \eqlabel{eq:Fweibull}
+\endeq
+and the inverse distribution function is
+$$
+ F^{-1}(u) = (-\ln (1-u))^{1/\alpha}/\lambda
+ + \delta \qquad \mbox{for } 0 \le u < 1.
+$$
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: WeibullDist
+ * Description: Weibull distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdist;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+public class WeibullDist extends ContinuousDistribution\begin{hide} {
+ private double alpha;
+ private double lambda;
+ private double delta;
+
+ private static class Function implements MathFunction {
+ private int n;
+ private double xi[];
+ private double lnXi[];
+ private double sumLnXi = 0.0;
+ private final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+
+ public Function (double x[], int n)
+ {
+ this.n = n;
+ this.xi = new double[n];
+ this.lnXi = new double[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ this.xi[i] = x[i];
+ if (x[i] > 0.0)
+ this.lnXi[i] = Math.log (x[i]);
+ else
+ this.lnXi[i] = LN_EPS;
+ sumLnXi += this.lnXi[i];
+ }
+ }
+
+ public double evaluate (double x)
+ {
+ if (x <= 0.0) return 1.0e200;
+ double sumXiLnXi = 0.0;
+ double sumXi = 0.0;
+ double xalpha;
+
+ for (int i = 0; i < n; i++)
+ {
+ xalpha = Math.pow (this.xi[i], x);
+ sumXiLnXi += xalpha * lnXi[i];
+ sumXi += xalpha;
+ }
+
+ return (x * (n * sumXiLnXi - sumLnXi * sumXi) - n * sumXi);
+ }
+ }
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public WeibullDist (double alpha)\begin{hide} {
+ setParams (alpha, 1.0, 0.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{WeibullDist} object with parameters
+ $\alpha$ = \texttt{alpha}, $\lambda$ = 1, and $\delta$ = 0.
+ \end{tabb}
+\begin{code}
+
+ public WeibullDist (double alpha, double lambda, double delta)\begin{hide} {
+ setParams (alpha, lambda, delta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{WeibullDist} object with parameters
+ $\alpha =$ \texttt{alpha},
+ $\lambda $ = \texttt{lambda}, and $\delta$ = \texttt{delta}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double x) {
+ return density (alpha, lambda, delta, x);
+ }
+
+ public double cdf (double x) {
+ return cdf (alpha, lambda, delta, x);
+ }
+
+ public double barF (double x) {
+ return barF (alpha, lambda, delta, x);
+ }
+
+ public double inverseF (double u) {
+ return inverseF (alpha, lambda, delta, u);
+ }
+
+ public double getMean() {
+ return WeibullDist.getMean (alpha, lambda, delta);
+ }
+
+ public double getVariance() {
+ return WeibullDist.getVariance (alpha, lambda, delta);
+ }
+
+ public double getStandardDeviation() {
+ return WeibullDist.getStandardDeviation (alpha, lambda, delta);
+ }\end{hide}
+
+ public static double density (double alpha, double lambda,
+ double delta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= delta)
+ return 0.0;
+ double y = Math.log(lambda*(x - delta)) * alpha;
+ if (y >= 7.0)
+ return 0.0;
+ y = Math.exp(y);
+
+ return alpha * (y / (x - delta)) * Math.exp(-y);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the density function.
+\end{tabb}
+\begin{code}
+
+ public static double density (double alpha, double x)\begin{hide} {
+ return density (alpha, 1.0, 0.0, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{density (alpha, 1, 0, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double lambda,
+ double delta, double x)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= delta)
+ return 0.0;
+ if ((lambda*(x - delta) >= XBIG) && (alpha >= 1.0))
+ return 1.0;
+ double y = Math.log(lambda*(x - delta)) * alpha;
+ if (y >= 3.65)
+ return 1.0;
+ y = Math.exp(y);
+ return -Math.expm1 (-y); // in JDK-1.5
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double cdf (double alpha, double x)\begin{hide} {
+ return cdf (alpha, 1.0, 0.0, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{cdf (alpha, 1, 0, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double lambda,
+ double delta, double x)\begin{hide} {
+ if (alpha <= 0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (x <= delta)
+ return 1.0;
+ if (alpha >= 1.0 && x >= Num.DBL_MAX_EXP*2)
+ return 0.0;
+
+ double temp = Math.log (lambda*(x - delta)) * alpha;
+ if (temp >= Num.DBL_MAX_EXP * Num.LN2)
+ return 0.0;
+ temp = Math.exp(temp);
+ return Math.exp (-temp);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the complementary distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double barF (double alpha, double x)\begin{hide} {
+ return barF (alpha, 1.0, 0.0, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{barF (alpha, 1, 0, x)}.
+\end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double lambda,
+ double delta, double u)\begin{hide} {
+ double t;
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ if (u < 0.0 || u > 1.0)
+ throw new IllegalArgumentException ("u not in [0, 1]");
+ if (u <= 0.0)
+ return 0.0;
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ t = -Math.log1p (-u);
+ if (Math.log (t)/Math.log (10) >= alpha*Num.DBL_MAX_10_EXP)
+ throw new ArithmeticException
+ ("inverse function cannot be positive infinity");
+
+ return Math.pow (t, 1.0/alpha)/lambda + delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Computes the inverse of the distribution function.
+ \end{tabb}
+\begin{code}
+
+ public static double inverseF (double alpha, double x)\begin{hide} {
+ return inverseF (alpha, 1.0, 0.0, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{inverseF (alpha, 1, 0, x)}.
+\end{tabb}
+\begin{code}\begin{hide}
+ private static double[] getMaximumLikelihoodEstimate (double[] x, int n,
+ double delta) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (delta != 0.0)
+ throw new IllegalArgumentException ("delta must be equal to 0");
+// Verifier cette fonction si delta != 0.
+
+ final double LN_EPS = Num.LN_DBL_MIN - Num.LN2;
+ double sumLn = 0.0;
+ double sumLn2 = 0.0;
+ double lnxi;
+ for (int i = 0; i < n; i++) {
+ if (x[i] <= delta)
+ lnxi = LN_EPS;
+ else
+ lnxi = Math.log (x[i]);
+ sumLn += lnxi;
+ sumLn2 += lnxi * lnxi;
+ }
+
+ double alpha0 = Math.sqrt ((double) n / ((6.0 / (Math.PI * Math.PI)) *
+ (sumLn2 - sumLn * sumLn / (double) n)));
+ double a = alpha0 - 20.0;
+ if (a <= delta)
+ a = delta + 1.0e-5;
+
+ double param[] = new double[3];
+ param[2] = 0.0;
+ Function f = new Function (x, n);
+ param[0] = RootFinder.brentDekker (a, alpha0 + 20.0, f, 1e-5);
+
+ double sumXalpha = 0.0;
+ for (int i = 0; i < n; i++)
+ sumXalpha += Math.pow (x[i], param[0]);
+ param[1] = Math.pow ((double) n / sumXalpha, 1.0 / param[0]);
+
+ return param;
+ }\end{hide}
+
+ public static double[] getMLE (double[] x, int n)\begin{hide}
+ {
+ return getMaximumLikelihoodEstimate (x, n, 0.0);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $(\alpha, \lambda)$ of the Weibull distribution,
+ assuming that $\delta = 0$,
+ using the maximum likelihood method, from the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$. The estimates are returned in a two-element
+ array, in regular order: [$\alpha$, $\lambda$].
+ \begin{detailed}
+ The maximum likelihood estimators are the values
+ $(\hat{\alpha}$, $\hat{\lambda})$ that satisfy the equations
+ \begin{eqnarray*}
+ \frac{\sum_{i=1}^{n} x_i^{\hat{\alpha}} \ln(x_i)}{\sum_{i=1}^{n}
+ x_i^{\hat{\alpha}}} - \frac{1}{\hat{\alpha}} & = & \frac{\sum_{i=1}^{n} \ln(x_i)}{n}\\
+ \hat{\lambda} & = & \left( \frac{n}{\sum_{i=1}^{n} x_i^{\hat{\alpha}}}
+ \right)^{1/\hat{\alpha}}
+ \end{eqnarray*}
+ See \cite[page 303]{sLAW00a}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \return{returns the parameter [$\hat{\alpha}$, $\hat{\lambda}$, $\hat{\delta}$ = 0]}
+\end{htmlonly}
+\begin{code}
+
+ public static WeibullDist getInstanceFromMLE (double[] x, int n)\begin{hide} {
+ double param[] = getMLE (x, n);
+ return new WeibullDist (param[0], param[1], param[2]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new instance of a Weibull distribution with parameters $\alpha$,
+ $\lambda$ and $\delta = 0$
+ estimated using the maximum likelihood method based on the $n$ observations
+ $x[i]$, $i = 0, 1, \ldots, n-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMean (double alpha, double lambda, double delta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ return (delta + Math.exp (Num.lnGamma(1.0 + 1.0 / alpha)) / lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the mean
+\begin{latexonly}
+ $E[X] = \delta + \Gamma(1 + 1/\alpha)/\lambda$
+\end{latexonly}
+ of the Weibull distribution with parameters $\alpha$, $\lambda$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the mean of the Weibull distribution
+ $E[X] = \delta + \Gamma(1 + 1/\alpha) / \lambda$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getVariance (double alpha, double lambda,
+ double delta)\begin{hide} {
+ double gAlpha;
+
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ gAlpha = Math.exp (Num.lnGamma (1.0 / alpha + 1.0));
+
+ return (Math.abs (Math.exp (Num.lnGamma(2 / alpha + 1)) - gAlpha * gAlpha) / (lambda * lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the variance
+\begin{latexonly}
+ $\mbox{Var}[X] = | \Gamma(2/\alpha + 1) - \Gamma^2(1/\alpha + 1) | /\lambda^2$
+\end{latexonly}
+ of the Weibull distribution with parameters $\alpha$, $\lambda$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the variance of the Weibull distribution
+ $\mbox{Var}[X] = 1 / \lambda^2 | \Gamma(2/\alpha + 1) - \Gamma^2(1/\alpha + 1) |$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getStandardDeviation (double alpha, double lambda,
+ double delta)\begin{hide} {
+ return Math.sqrt (WeibullDist.getVariance (alpha, lambda, delta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the standard deviation
+ of the Weibull distribution with parameters $\alpha$, $\lambda$ and $\delta$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the Weibull distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\lambda$.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\delta$.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double lambda, double delta)\begin{hide} {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+
+ this.alpha = alpha;
+ this.lambda = lambda;
+ this.delta = delta;
+ supportA = delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\alpha$, $\lambda$ and $\delta$ for this
+ object.
+ \end{tabb}
+\begin{code}
+
+ public double[] getParams ()\begin{hide} {
+ double[] retour = {alpha, lambda, delta};
+ return retour;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return a table containing the parameters of the current distribution.
+ This table is put in regular order: [$\alpha$, $\lambda$, $\delta$].
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public String toString ()\begin{hide} {
+ return getClass().getSimpleName() + " : alpha = " + alpha + ", lambda = " + lambda + ", delta = " + delta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current distribution.
+\end{tabb}\end{hide}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdist/continuous.tex b/source/umontreal/iro/lecuyer/probdist/continuous.tex
new file mode 100644
index 0000000..c94c136
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/continuous.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Continuous Distributions}
diff --git a/source/umontreal/iro/lecuyer/probdist/discrete.tex b/source/umontreal/iro/lecuyer/probdist/discrete.tex
new file mode 100644
index 0000000..1cfdf97
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/discrete.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Discrete Distributions over Integers}
diff --git a/source/umontreal/iro/lecuyer/probdist/discretereal.tex b/source/umontreal/iro/lecuyer/probdist/discretereal.tex
new file mode 100644
index 0000000..7595e59
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/discretereal.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Discrete Distributions over Real Numbers}
diff --git a/source/umontreal/iro/lecuyer/probdist/edf.tex b/source/umontreal/iro/lecuyer/probdist/edf.tex
new file mode 100644
index 0000000..9e85595
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/edf.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Empirical Distribution Functions (EDF)}
diff --git a/source/umontreal/iro/lecuyer/probdist/general.tex b/source/umontreal/iro/lecuyer/probdist/general.tex
new file mode 100644
index 0000000..df08c3a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/general.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{General Classes}
diff --git a/source/umontreal/iro/lecuyer/probdist/guideprobdist.bbl b/source/umontreal/iro/lecuyer/probdist/guideprobdist.bbl
new file mode 100644
index 0000000..9087b53
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/guideprobdist.bbl
@@ -0,0 +1,348 @@
+\begin{thebibliography}{10}
+
+\bibitem{tAND52a}
+T.~W. Anderson and D.~A. Darling.
+\newblock Asymptotic theory of certain goodness of fit criteria based on
+ stochastic processes.
+\newblock {\em Annals of Mathematical Statistics}, 23:193--212, 1952.
+
+\bibitem{tBES75a}
+D.~J. Best and D.~E. Roberts.
+\newblock Algorithm {AS} 91: The percentage points of the $\chi^{2}$
+ distribution.
+\newblock {\em Applied Statistics}, 24:385--388, 1975.
+
+\bibitem{tBAT70a}
+G.~P. Bhattacharjee.
+\newblock The incomplete gamma integral.
+\newblock {\em Applied Statistics}, 19:285--287, 1970.
+\newblock AS32.
+
+\bibitem{tBIR69a}
+Z.~W. Birnbaum and S.~C. Saunders.
+\newblock A new family of life distributions.
+\newblock {\em Journal of Applied Probability}, 6:319--327, 1969.
+
+\bibitem{tBLA76a}
+J.~M. Blair, C.~A. Edwards, and J.~H. Johnson.
+\newblock Rational {C}hebyshev approximations for the inverse of the error
+ function.
+\newblock {\em Mathematics of Computation}, 30:827--830, 1976.
+
+\bibitem{tBOL64a}
+L.~N. Bol'shev.
+\newblock Some applications of {P}earson transformations.
+\newblock {\em Review of the Internat. Stat. Institute}, 32:14--16, 1964.
+
+\bibitem{sBRA87a}
+P.~Bratley, B.~L. Fox, and L.~E. Schrage.
+\newblock {\em A Guide to Simulation}.
+\newblock Springer-Verlag, New York, NY, second edition, 1987.
+
+\bibitem{tBRO07a}
+J.~R. Brown and M.~E. Harvey.
+\newblock Rational arithmetic {M}athematica functions to evaluate the one-sided
+ one-sample {K-S} cumulative sample distribution.
+\newblock {\em Journal of Statistical Software}, 19(6):1--32, 2007.
+
+\bibitem{tBRO08a}
+J.~R. Brown and M.~E. Harvey.
+\newblock Rational arithmetic {{M}athematica} functions to evaluate the
+ two-sided one sample {K-S} cumulative sample distribution.
+\newblock {\em Journal of Statistical Software}, 26(2):1--40, 2008.
+
+\bibitem{tCAM51a}
+B.~H. Camp.
+\newblock Approximation to the point binomial.
+\newblock {\em Ann. Math. Stat.}, 22:130--131, 1951.
+
+\bibitem{tCSO96a}
+S.~Cs\"org\H{o} and J.~J. Faraway.
+\newblock The exact and asymptotic distributions of {C}ram\'er-von mises
+ statistics.
+\newblock {\em Journal of the Royal Statistical Society, Series B},
+ 58:221--234, 1996.
+
+\bibitem{tCUY08a}
+A.~M. Cuyt, V.~B. Petersen, B.~Verdonk, H.~Waadeland, and W.~B. Jones.
+\newblock {\em Handbook of Continued Fractions for Special Functions}.
+\newblock Springer Netherlands, 2008.
+
+\bibitem{tDAR60a}
+D.~A. Darling.
+\newblock On the theorems of {K}olmogorov-{S}mirnov.
+\newblock {\em Theory of Probability and Its Applications}, V(4):356--360,
+ 1960.
+
+\bibitem{tDAR83a}
+D.~A. Darling.
+\newblock On the asymptotic distribution of {W}atson's statistic.
+\newblock {\em The Annals of Statistics}, 11(4):1263--1266, 1983.
+
+\bibitem{rDER09a}
+G.~Derflinger, W.~H\"{o}rmann, and J.~Leydold.
+\newblock Random variate generation by numerical inversion when only the
+ density is known.
+\newblock {\em Preprint of the Department of Statistics and Mathematics 78,
+ Wirtschaftsuniversit\"at Wien, Austria}, 2008.
+\newblock See \url{http://epub.wu-wien.ac.at/english/}.
+
+\bibitem{tDID92a}
+A.~R. DiDonato and A.~H. Morris.
+\newblock Significant digit computation of the incomplete beta function ratios.
+\newblock {\em ACM Transactions on Mathematical Software}, 18(3):360--377,
+ 1992.
+
+\bibitem{tDUR73a}
+J.~Durbin.
+\newblock {\em Distribution Theory for Tests Based on the Sample Distribution
+ Function}.
+\newblock SIAM CBMS-NSF Regional Conference Series in Applied Mathematics.
+ SIAM, Philadelphia, PA, 1973.
+
+\bibitem{tEVA00a}
+M.~Evans, N.~Hastings, and B.~Peacock.
+\newblock {\em Statistical Distributions}.
+\newblock Wiley, 3rd edition, 2000.
+
+\bibitem{mEVA00a}
+M.~Evans and T.~Swartz.
+\newblock {\em Approximating Integrals via {M}onte {C}arlo and Deterministic
+ Methods}.
+\newblock Oxford University Press, Oxford, UK, 2000.
+
+\bibitem{tFLY06a}
+M.~R. Flynn.
+\newblock Fitting human exposure data with the {J}ohnson ${S_B}$ distribution.
+\newblock {\em Journal of Exposure Science and Environmental Epidemiology},
+ 16:56--62, 2006.
+
+\bibitem{rGEN98a}
+J.~E. Gentle.
+\newblock {\em Random Number Generation and {M}onte {C}arlo Methods}.
+\newblock Springer, New York, NY, 1998.
+
+\bibitem{pGER10a}
+I.~B. Gertsbakh and Y.~Shpungin.
+\newblock {\em Models of Network Reliability}.
+\newblock {CRC Press}, Boca Raton, FL, 2010.
+
+\bibitem{tGOL73a}
+R.~B. Goldstein.
+\newblock Algorithm 451: Chi-square quantiles.
+\newblock {\em Communications of the ACM}, 16:483--485, 1973.
+
+\bibitem{tGRA12a}
+A.~W. Grace and I.~A. Wood.
+\newblock Approximating the tail of the {A}nderson–{D}arling distribution.
+\newblock {\em Computational Statistics and Data Analysis}, 56(12):4301--4311,
+ 2012.
+
+\bibitem{tHIL70a}
+G.~W. Hill.
+\newblock Algorithm 395: Student's $t$-distribution.
+\newblock {\em Communications of the ACM}, 13:617--619, 1970.
+
+\bibitem{tHUA07a}
+J.~S. Huang and P.~S. Shen.
+\newblock More maximum likelihood oddities.
+\newblock {\em Journal of Statistical Planning and Inference},
+ 137(7):2151--2155, 2007.
+
+\bibitem{tJOH49a}
+N.~L. Johnson.
+\newblock Systems of frequency curves generated by methods of translation.
+\newblock {\em Biometrika}, 36:149--176, 1949.
+
+\bibitem{tJOH69a}
+N.~L. Johnson and S.~Kotz.
+\newblock {\em Distributions in Statistics: Discrete Distributions}.
+\newblock Houghton Mifflin, Boston, 1969.
+
+\bibitem{tJOH95a}
+N.~L. Johnson, S.~Kotz, and N.~Balakrishnan.
+\newblock {\em Continuous Univariate Distributions}, volume~1.
+\newblock Wiley, 2nd edition, 1994.
+
+\bibitem{tJOH95b}
+N.~L. Johnson, S.~Kotz, and N.~Balakrishnan.
+\newblock {\em Continuous Univariate Distributions}, volume~2.
+\newblock Wiley, 2nd edition, 1995.
+
+\bibitem{sKAC85a}
+V.~Kachitvichyanukul and B.~Schmeiser.
+\newblock Computer generation of hypergeometric random variates.
+\newblock {\em J. Statist. Comput. Simul.}, 22:127--145, 1985.
+
+\bibitem{tKEN80a}
+W.~J. {Kennedy Jr.} and J.~E. Gentle.
+\newblock {\em Statistical Computing}.
+\newblock Dekker, New York, NY, 1980.
+
+\bibitem{tKNO74a}
+M.~Knott.
+\newblock The distribution of the {C}ram\'er-von {M}ises statistic for small
+ sample sizes.
+\newblock {\em Journal of the Royal Statistical Society B}, 36:430--438, 1974.
+
+\bibitem{tKOT04a}
+S.~Kotz and J.~R. van Dorp.
+\newblock {\em {BEYOND BETA}, Other Continuous Families of Distributions with
+ Bounded Support and Applications}.
+\newblock World Scientific Publishing co., Singapore, 2004.
+
+\bibitem{pLAT99a}
+G.~Latouche and V.~Ramaswami.
+\newblock {\em Introduction to matrix analytic methods in stochastic modeling}.
+\newblock Society for Industrial and Applied Mathematics, Philadelphia, Pa.,
+ 1999.
+
+\bibitem{sLAW00a}
+A.~M. Law and W.~D. Kelton.
+\newblock {\em Simulation Modeling and Analysis}.
+\newblock McGraw-Hill, New York, NY, third edition, 2000.
+
+\bibitem{rLEC06a}
+P.~L'Ecuyer and R.~Simard.
+\newblock Inverting the symmetrical beta distribution.
+\newblock {\em {ACM} Transactions on Mathematical Software}, 32(4):509--520,
+ 2006.
+
+\bibitem{tLEO61a}
+F.~C. Leone, L.~S. Nelson, and R.~B. Nottingham.
+\newblock The folded normal distribution.
+\newblock {\em Technometrics}, 3(4):543--550, 1961.
+
+\bibitem{tLEW61a}
+P.~A.~W. Lewis.
+\newblock Distribution of the {A}nderson-{D}arling statistic.
+\newblock {\em Annals of Mathematical Statistics}, 32:1118--1124, 1961.
+
+\bibitem{iLEY02a}
+J.~Leydold and W.~H\"ormann.
+\newblock {\em {UNU.RAN}---A Library for Universal Non-Uniform Random Number
+ Generators}, 2002.
+\newblock Available at \url{http://statistik.wu-wien.ac.at/unuran}.
+
+\bibitem{tMAR78a}
+K.~V. Mardia and P.~J. Zemroch.
+\newblock {\em Tables of the {F} and Related Distributions with Algorithms}.
+\newblock Academic Press, London, 1978.
+
+\bibitem{tMAR04a}
+G.~Marsaglia and J.~Marsaglia.
+\newblock Evaluating the {A}nderson-{D}arling distribution.
+\newblock {\em Journal of Statistical Software}, 9(2):1--5, 2004.
+\newblock See \url{http://www.jstatsoft.org/v09/i02/}.
+
+\bibitem{tMAR03a}
+G.~Marsaglia, W.~W. Tsang, and J.~Wang.
+\newblock Evaluating {K}olmogorov's distribution.
+\newblock {\em Journal of Statistical Software}, 8(18):1--4, 2003.
+\newblock URL is \url{http://www.jstatsoft.org/v08/i18/}.
+
+\bibitem{rMAR94b}
+G.~Marsaglia, A.~Zaman, and J.~C.~W. Marsaglia.
+\newblock Rapid evaluation of the inverse normal distribution function.
+\newblock {\em Statistics and Probability Letters}, 19:259--266, 1994.
+
+\bibitem{tMOL70a}
+W.~Molenaar.
+\newblock {\em Approximations to the Poisson, Binomial and Hypergeometric
+ Distribution Functions}, volume~31 of {\em Mathematical Center Tract}.
+\newblock Mathematisch Centrum, Amsterdam, 1970.
+
+\bibitem{iMOS00a}
+S.~L. Moshier.
+\newblock Cephes math library, 2000.
+\newblock See \url{http://www.moshier.net}.
+
+\bibitem{pNEU81a}
+M.~F. Neuts.
+\newblock {\em Matrix-Geometric Solutions in Stochastic Models}.
+\newblock John Hopkins, University Press, Baltimore, 1981.
+
+\bibitem{tOLI72a}
+E.~H. Oliver.
+\newblock A maximum likelihood oddity.
+\newblock {\em The American Statistician}, 26(3):43–--44, 1972.
+
+\bibitem{tPEA59a}
+E.~S. Pearson.
+\newblock Note on an approximation to the distribution of non-central $\chi^2$.
+\newblock {\em Biometrika}, 46:364, 1959.
+
+\bibitem{tPEI68a}
+D.~B. Peizer and J.~W. Pratt.
+\newblock A normal approximation for binomial, {F}, beta, and other common
+ related tail probabilities.
+\newblock {\em Journal of the American Statistical Association}, 63:1416--1456,
+ 1968.
+
+\bibitem{tPEL76a}
+W.~Pelz and I.~J. Good.
+\newblock Approximating the lower tail-areas of the {K}olmogorov-{S}mirnov
+ one-sample statistic.
+\newblock {\em Journal of the Royal Statistical Society B}, 38(2):152--156,
+ 1976.
+
+\bibitem{tPEN00a}
+S.~Penev and T.~Raykov.
+\newblock A {W}iener germ approximation of the noncentral chi square
+ distribution and of its quantiles.
+\newblock {\em Computational Statistics}, 15(2):219--228, 2000.
+
+\bibitem{tPOM74a}
+J.~Pomeranz.
+\newblock Exact cumulative distribution of the {K}olmogorov-{S}mirnov statistic
+ for small samples (algorithm 487).
+\newblock {\em Communications of the ACM}, 17(12):703--704, 1974.
+
+\bibitem{pROS07b}
+S.~M. Ross.
+\newblock {\em Introduction to Probability Models}.
+\newblock Academic Press, ninth edition, 2007.
+
+\bibitem{iSCHa}
+R.~B. Schnabel.
+\newblock {\em {UNCMIN}---Unconstrained Optimization Package, FORTRAN}.
+\newblock University of Colorado at Boulder.
+\newblock See \url{http://www.ici.ro/camo/unconstr/uncmin.htm}.
+
+\bibitem{tSCH78a}
+J.~L. Schonfelder.
+\newblock Chebyshev expansions for the error and related functions.
+\newblock {\em Mathematics of Computation}, 32:1232--1240, 1978.
+
+\bibitem{tSIM11a}
+R.~Simard and P.~L'Ecuyer.
+\newblock Computing the two-sided {Kolmogorov-Smirnov} distribution.
+\newblock {\em Journal of Statistical Software}, 39(11), 2011.
+\newblock URL is \url{http://www.jstatsoft.org/v39/i11}.
+
+\bibitem{tSTE70a}
+M.~A. Stephens.
+\newblock Use of the {K}olmogorov-{S}mirnov, {C}ram{\'e}r-{V}on {M}ises and
+ related statistics without extensive tables.
+\newblock {\em Journal of the Royal Statistical Society, Series B},
+ 33(1):115--122, 1970.
+
+\bibitem{tSTE86b}
+M.~S. Stephens.
+\newblock Tests based on {EDF} statistics.
+\newblock In R.~B. D'Agostino and M.~S. Stephens, editors, {\em Goodness-of-Fit
+ Techniques}. Marcel Dekker, New York and Basel, 1986.
+
+\bibitem{iVERa}
+S.~P. Verrill.
+\newblock {\em {UNCMIN}---Unconstrained Optimization Package, Java}.
+\newblock US Forest Service, Forest Products Laboratory.
+\newblock Available at \url{http://www1.fpl.fs.fed.us/optimization.html}.
+
+\bibitem{tWAT76a}
+G.~S. Watson.
+\newblock Optimal invariant tests for uniformity.
+\newblock In {\em Studies in Probability and Statistics}, pages 121--127. North
+ Holland, Amsterdam, 1976.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/probdist/guideprobdist.tex b/source/umontreal/iro/lecuyer/probdist/guideprobdist.tex
new file mode 100644
index 0000000..ba12e00
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/guideprobdist.tex
@@ -0,0 +1,133 @@
+\documentclass[12pt]{article}
+
+\usepackage{amsmath}
+\usepackage{amssymb}
+\usepackage{url}
+\usepackage{alltt}
+\usepackage{html}
+% \usepackage{tcode}
+\usepackage{ssj}
+
+\mytwoheads\dateheadtrue
+\detailedfalse
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+
+\title{probdist}{Probability Distributions}
+
+This package provides tools to compute densities, mass functions,
+distribution functions and their inverses, and reliability functions,
+for various continuous and discrete probability distributions.
+It also offers facilities for estimating the parameters of some
+distributions from a data set.
+
+\vfill
+\end{titlepage}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+
+\include{overview}
+
+%% General
+\include{general}
+\include{Distribution}
+\include{DiscreteDistributionInt}
+\include{ContinuousDistribution}
+\include{DistributionFactory}
+\include{InverseDistFromDensity}
+
+%% Discrete distributions over the integers
+\include{discrete}
+\include{BernoulliDist}
+\include{BinomialDist}
+\include{GeometricDist}
+\include{HypergeometricDist}
+\include{LogarithmicDist}
+\include{NegativeBinomialDist}
+\include{PascalDist}
+\include{PoissonDist}
+\include{UniformIntDist}
+\include{ConstantIntDist}
+
+%% Discrete distributions over the real numbers
+\include{discretereal}
+\include{DiscreteDistribution}
+\include{ConstantDist}
+\include{EmpiricalDist}
+
+%% Continuous distributions
+\include{continuous}
+\include{BetaDist}
+\include{BetaSymmetricalDist}
+\include{CauchyDist}
+\include{ChiDist}
+\include{ChiSquareDist}
+\include{ChiSquareDistQuick}
+\include{ChiSquareNoncentralDist}
+\include{ErlangDist}
+\include{ExponentialDist}
+\include{ExponentialDistFromMean}
+\include{ExtremeValueDist}
+\include{FatigueLifeDist}
+\include{FisherFDist}
+\include{FoldedNormalDist}
+\include{FrechetDist}
+\include{GammaDist}
+\include{GammaDistFromMoments}
+\include{GumbelDist}
+\include{HalfNormalDist}
+\include{HyperbolicSecantDist}
+\include{HypoExponentialDist}
+\include{HypoExponentialDistQuick}
+\include{HypoExponentialDistEqual}
+\include{InverseGammaDist}
+\include{InverseGaussianDist}
+\include{JohnsonSystem}
+\include{JohnsonSLDist}
+\include{JohnsonSBDist}
+\include{JohnsonSUDist}
+\include{LaplaceDist}
+\include{LogisticDist}
+\include{LoglogisticDist}
+\include{LognormalDist}
+\include{LognormalDistFromMoments}
+\include{NakagamiDist}
+\include{NormalDist}
+\include{NormalDistQuick}
+\include{NormalInverseGaussianDist}
+\include{ParetoDist}
+\include{Pearson5Dist}
+\include{Pearson6Dist}
+\include{PiecewiseLinearEmpiricalDist}
+\include{PowerDist}
+\include{RayleighDist}
+\include{StudentDist}
+\include{StudentDistQuick}
+\include{TriangularDist}
+\include{UniformDist}
+\include{WeibullDist}
+
+\include{TruncatedDist}
+
+%% Empirical distribution functions
+\include{edf}
+\include{AndersonDarlingDist}
+\include{AndersonDarlingDistQuick}
+\include{CramerVonMisesDist}
+\include{KolmogorovSmirnovPlusDist}
+\include{KolmogorovSmirnovDist}
+\include{KolmogorovSmirnovDistQuick}
+\include{WatsonGDist}
+\include{WatsonUDist}
+
+\bibliographystyle{plain}
+\bibliography{stat,random,simul,math,ift,prob}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/probdist/overview.tex b/source/umontreal/iro/lecuyer/probdist/overview.tex
new file mode 100644
index 0000000..f0a1fbd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/overview.tex
@@ -0,0 +1,135 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+
+This package contains a set of Java classes providing methods to
+compute mass, density, distribution, complementary
+distribution, and inverse distribution functions for some discrete
+and continuous probability distributions.
+It also provides methods to estimate the parameters of some distributions
+from empirical data.
+It does not generate random variates;
+for that, see the package \externalclass{umontreal.iro.lecuyer}{randvar}.
+It is possible to plot the density or the cumulative probabilities of a
+distribution function either on screen, or in a \LaTeX{} file, but for this,
+one has to use the package
+\externalclass{umontreal.iro.lecuyer}{charts}.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection* {Distributions}
+
+We recall that the {\em distribution function\/} of a {\em continuous\/}
+random variable $X$ with {\em density\/} $f$ over the real line is
+\eq
+ F(x) = P[X\le x] = \int_{-\infty}^x f(s)ds \label{eq:FDist}
+\endeq
+while that of a {\em discrete\/} random variable $X$ with
+{\em mass function\/} $p$ over a fixed set of real numbers
+$x_0 < x_1 < x_2 < \cdots$ is
+\eq
+ F(x) = P[X\le x] = \sum_{x_i\le x} p(x_i), \label{eq:FDistDisc}
+\endeq
+where $p(x_i) = P[X = x_i]$.
+For a discrete distribution over the set of integers, one has
+\eq
+ F (x) = P[X\le x] = \sum_{s=-\infty}^x p(s), \label{eq:FDistDiscInt}
+\endeq
+where $p(s) = P[X=s]$.
+
+We define $\bar{F}$, the {\em complementary distribution function\/}
+of $X$, by
+\eq
+ \bar{F} (x) = P[X\ge x].
+\endeq
+With this definition of $\bar{F}$, one has
+$\bar{F}(x) = 1 - F (x)$ for continuous distributions and
+$\bar{F}(x) = 1 - F (x-1)$ for discrete distributions over the integers.
+This definition is \emph{non-standard} for the discrete case: we have
+$\bar{F} (x) = P[X\ge x]$ instead of $\bar{F} (x) = P[X > x] = 1-F(x)$.
+We find it more convenient especially for computing $p$-values in
+goodness-of-fit tests.
+
+The {\em inverse distribution function\/} is defined as
+\eq
+ F^{-1}(u) = \inf \{x\in\RR : F (x)\ge u\}, \label{eq:inverseF}
+\endeq
+for $0\le u\le 1$.
+This function $F^{-1}$ is often used, among other things, to generate
+the random variable $X$ by inversion, by passing a
+$U (0,1)$ random variate as the value of $u$.
+
+The package \texttt{probdist} offers two types of tools for computing
+$p$, $f$, $F$, $\bar{F}$, and $F^{-1}$:
+\emph{static methods}, for which no object needs to
+be created, and methods associated with \emph{distribution objects}.
+% Both types of methods are provided by the class that corresponds to
+% the desired distribution.
+% A number of static methods for computing $F$, $\bar{F}$, and $F^{-1}$,
+% are regrouped in the static classes \texttt{FDist}, \texttt{FBar}, and
+% \texttt{FInverse}, respectively.
+Standard distributions are implemented each in their own class.
+% (Poisson, binomial, normal, chi-square, etc.)
+Constructing an object from one of these classes can be convenient
+if $F$, $\bar{F}$, etc., has to be evaluated several times for the same
+distribution. In certain cases (for the Poisson distribution, for example),
+creating the distribution object would precompute tables that
+would speed up significantly all subsequent method calls for computing
+$F$, $\bar{F}$, etc.
+This trades memory, plus a one-time setup cost, for speed.
+In addition to the non-static methods, the distribution classes also
+provide static methods that do not require the creation of an object.
+
+The distribution classes extend one of the (abstract) classes
+\externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistribution} and
+\externalclass{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}
+(which both implement the interface
+\externalclass{umontreal.iro.lecuyer.probdist}{Distribution})
+for discrete and continuous distributions over the real numbers,
+or \externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt},
+for discrete distributions over the non-negative integers.
+
+For example, the class
+\externalclass{umontreal.iro.lecuyer.probdist}{PoissonDist} extends
+% \externalclass{umontreal.iro.lecuyer.probdist}{BinomialDist} extends
+\externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt}.
+Calling a static method from this class
+% \externalclass{umontreal.iro.lecuyer.probdist}{PoissonDist}
+will compute the corresponding probability from scratch.
+Constructing a \externalclass{umontreal.iro.lecuyer.probdist}{PoissonDist}
+object, on the other hand,
+will precompute tables that contain the probability terms and the
+distribution function for a given parameter $\lambda$ (the mean of the
+Poisson distribution). These tables will then be used whenever
+a method is called for the corresponding object.
+This second approach is recommended if some of $F$, $\bar{F}$, etc.,
+has to be computed several times for the same parameter $\lambda$.
+As a rule of thumb, creating objects and using their methods
+is faster than just using static methods as soon as two or three
+calls are made, unless the parameters are large.
+
+In fact, only the non-negligible probability terms
+(those that exceed the threshold
+\clsexternalmethod{umontreal.iro.lecuyer.probdist}{Discrete\-DistributionInt}{EPSILON}{})
+are stored in the tables. For $F$ and $\bar{F}$, a single table actually
+contains $F (x)$ for $F (x) \le 1/2$ and $1-F (x)$ for $F (x) > 1/2$.
+When the distribution parameters are so large that the tables would
+take too much space, these are not created and the methods
+automatically call their static equivalents instead of using tables.
+
+% For the continuous distributions,
+Objects that implement the interface
+\externalclass{umontreal.iro.lecuyer.probdist}{Distribution}
+(and sometimes the abstract class
+\externalclass{umontreal.iro.lecuyer.probdist}{ContinuousDistribution})
+are required by some methods in
+package \externalclass{umontreal.iro.lecuyer}{randvar}
+and also in classes
+\externalclass{umontreal.iro.lecuyer.gof}{GofStat} and
+\externalclass{umontreal.iro.lecuyer.gof}{GofFormat} of
+ package \externalclass{umontreal.iro.lecuyer}{gof}.
+
+
+Some of the classes also provide methods that compute parameter estimations
+of the corresponding distribution from a set of empirical observations,
+in most cases based on the maximum likelihood method.
+
diff --git a/source/umontreal/iro/lecuyer/probdist/rvclasses.tex b/source/umontreal/iro/lecuyer/probdist/rvclasses.tex
new file mode 100644
index 0000000..3dffd69
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/rvclasses.tex
@@ -0,0 +1,7 @@
+\thispagestyle {nomark}
+\addcontentsline{toc}{section}{\protect\numberline{3.}
+ {The Random Variables Classes}}
+\centerline {\Large\bf The Distribution Classes}
+\vfill\vfill
+%\eject\null\vfill % Page blanche.
+
diff --git a/source/umontreal/iro/lecuyer/probdist/scclasses.tex b/source/umontreal/iro/lecuyer/probdist/scclasses.tex
new file mode 100644
index 0000000..3a64c69
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/scclasses.tex
@@ -0,0 +1,7 @@
+\thispagestyle {nomark}
+\addcontentsline{toc}{section}{\protect\numberline{5.}
+ {The Statistic Collection Classes}}
+\centerline {\Large\bf The Statistical Collector's Classes}
+\vfill\vfill
+%\eject\null\vfill % Page blanche.
+
diff --git a/source/umontreal/iro/lecuyer/probdist/staticclasses.tex b/source/umontreal/iro/lecuyer/probdist/staticclasses.tex
new file mode 100644
index 0000000..df3eeeb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdist/staticclasses.tex
@@ -0,0 +1,7 @@
+\thispagestyle {nomark}
+\addcontentsline{toc}{section}{\protect\numberline{2.}
+ {The Static Classes}}
+\centerline {\Large\bf The Static Distribution Classes}
+\vfill\vfill
+%\eject\null\vfill % Page blanche.
+
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDist.java b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDist.java
new file mode 100644
index 0000000..50419f3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDist.java
@@ -0,0 +1,510 @@
+
+
+/*
+ * Class: BiNormalDist
+ * Description: bivariate normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.probdist.NormalDistQuick;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution2Dim} for the <EM>bivariate
+ * normal</EM> distribution. It has means
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>μ</I><SUB>1</SUB></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>E</I>[<I>Y</I>] = <I>μ</I><SUB>2</SUB></SPAN>, and variances var
+ * <SPAN CLASS="MATH">[<I>X</I>] = <I>σ</I><SUB>1</SUB><SUP>2</SUP></SPAN>,
+ * var
+ * <SPAN CLASS="MATH">[<I>Y</I>] = <I>σ</I><SUB>2</SUB><SUP>2</SUP></SPAN> such that
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> > 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB> > 0</SPAN>.
+ * The correlation between <SPAN CLASS="MATH"><I>X</I></SPAN> and <SPAN CLASS="MATH"><I>Y</I></SPAN> is <SPAN CLASS="MATH"><I>r</I></SPAN>.
+ * Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>, <I>y</I>) = <I>e</I><SUP>-T</SUP>/(2<I>πσ</I><SUB>1</SUB><I>σ</I><SUB>2</SUB>(1-r^2)<SUP>1/2</SUP>)
+ * </DIV><P></P>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>T</I> = [((<I>x</I> - <I>μ</I><SUB>1</SUB>)/<I>σ</I><SUB>1</SUB>)<SUP>2</SUP> -2<I>r</I>((<I>x</I> - <I>μ</I><SUB>1</SUB>)/<I>σ</I><SUB>1</SUB>)((<I>y</I> - <I>μ</I><SUB>2</SUB>)/<I>σ</I><SUB>2</SUB>) + ((<I>y</I> - <I>μ</I><SUB>2</SUB>)/<I>σ</I><SUB>2</SUB>)<SUP>2</SUP>]/(2(1 - <I>r</I><SUP>2</SUP>))
+ * </DIV><P></P>
+ * and the corresponding distribution function is (the <TT>cdf</TT> method)
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>Φ</I>(<I>μ</I><SUB>1</SUB>, <I>σ</I><SUB>1</SUB>, <I>x</I>, <I>μ</I><SUB>2</SUB>, <I>σ</I><SUB>2</SUB>, <I>y</I>, <I>r</I>) = ∫<SUB>-∞</SUB><SUP>x</SUP><I>dx</I>∫<SUB>-∞</SUB><SUP>y</SUP><I>dy</I> <I>e</I><SUP>-T</SUP>/(2<I>πσ</I><SUB>1</SUB><I>σ</I><SUB>2</SUB>(1 - r^2)<SUP>1/2</SUP>).
+ * </DIV><P></P>
+ * We also define the upper distribution function (the <TT>barF</TT> method) as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * bar(Φ)(<I>μ</I><SUB>1</SUB>, <I>σ</I><SUB>1</SUB>, <I>x</I>, <I>μ</I><SUB>2</SUB>, <I>σ</I><SUB>2</SUB>, <I>y</I>, <I>r</I>) = ∫<SUP>∞</SUP><SUB>x</SUB><I>dx</I>∫<SUP>∞</SUP><SUB>y</SUB><I>dy</I> <I>e</I><SUP>-T</SUP>/(2<I>πσ</I><SUB>1</SUB><I>σ</I><SUB>2</SUB>(1 - r^2)<SUP>1/2</SUP>).
+ * </DIV><P></P>
+ * When
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB> = <I>μ</I><SUB>2</SUB> = 0</SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> = <I>σ</I><SUB>2</SUB> = 1</SPAN>, we have the <EM>standard binormal</EM>
+ * distribution, with corresponding distribution function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>Φ</I>(<I>x</I>, <I>y</I>, <I>r</I>) = ∫<SUP>∞</SUP><SUB>x</SUB><I>dx</I>∫<SUP>∞</SUP><SUB>y</SUB><I>dy</I> <I>e</I><SUP>-S</SUP>/(2<I>π</I>(1 - r^2)<SUP>1/2</SUP>)
+ * </DIV><P></P>
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>S</I> = (<I>x</I><SUP>2</SUP> -2<I>rxy</I> + <I>y</I><SUP>2</SUP>)/(2(1 - <I>r</I><SUP>2</SUP>)).
+ * </DIV><P></P>
+ *
+ */
+public class BiNormalDist extends ContinuousDistribution2Dim {
+ protected int ndigit; // Number of decimal digits of accuracy
+ protected double mu1, mu2;
+ protected double sigma1, sigma2;
+ protected double rho;
+ protected double racRho; // sqrt(1 - rho^2)
+ protected double detS; // 2*PI*sigma1*sigma2*sqrt(1 - rho^2)
+ protected static final double RHO_SMALL = 1.0e-8; // neglect small rhos
+
+ private static final double Z[] = {
+ 0.04691008, 0.23076534, 0.5, 0.76923466, 0.95308992 };
+
+ private static final double W[] = {
+ 0.018854042, 0.038088059, 0.0452707394, 0.038088059, 0.018854042 };
+
+ private static final double AGauss[] = {
+ -0.72657601, 0.71070688, -0.142248368, 0.127414796 };
+
+
+
+
+ /**
+ * Constructs a <TT>BiNormalDist</TT> object with default parameters
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB> = <I>μ</I><SUB>2</SUB> = 0</SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> = <I>σ</I><SUB>2</SUB> = 1</SPAN> and correlation <SPAN CLASS="MATH"><I>ρ</I> =</SPAN><TT> rho</TT>.
+ *
+ */
+ public BiNormalDist (double rho) {
+ setParams (0.0, 1.0, 0.0, 1.0, rho);
+ }
+
+
+ /**
+ * Constructs a <TT>BiNormalDist</TT> object with parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>,
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> =
+ * <TT>sigma2</TT> and <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>.
+ *
+ */
+ public BiNormalDist (double mu1, double sigma1,
+ double mu2, double sigma2, double rho) {
+ setParams (mu1, sigma1, mu2, sigma2, rho);
+ }
+
+
+ public double density (double x, double y) {
+ if (Math.abs(rho) == 1.)
+ throw new IllegalArgumentException ("|rho| = 1");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ double T = (X*X - 2.0*rho*X*Y + Y*Y) / (2.0*racRho*racRho);
+ return Math.exp(-T) / detS;
+ }
+
+ /**
+ * Computes the <EM>standard binormal</EM> density function with
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB> = <I>μ</I><SUB>2</SUB> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> = <I>σ</I><SUB>2</SUB> = 1</SPAN>.
+ *
+ */
+ public static double density (double x, double y, double rho) {
+ return density (0.0, 1.0, x, 0.0, 1.0, y, rho);
+ }
+
+
+ /**
+ * Computes the <EM>binormal</EM> density function
+ * with parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>, <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> =
+ * <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> = <TT>sigma2</TT> and <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>.
+ *
+ */
+ public static double density (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ if (sigma1 <= 0.)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0.)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) >= 1.)
+ throw new IllegalArgumentException ("|rho| >= 1");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ double fRho = (1.0 - rho)*(1.0 + rho);
+ double T = (X*X - 2.0*rho*X*Y + Y*Y) / (2.0*fRho);
+ return Math.exp (-T)/ (2.0*Math.PI*sigma1*sigma2*Math.sqrt(fRho));
+
+ }
+
+
+ protected static double Gauss (double z) {
+ // Computes normal probabilities to within accuracy of 10^(-7)
+ // Drezner Z., and G.O. Wesolowsky (1990) On the computation of the
+ // bivariate normal integral. J. Statist. Comput. Simul. 35:101-107.
+
+ final double x = 1.0/(1.0 + 0.23164189 * Math.abs(z));
+ double G = 0.53070271;
+ for (int i = 0; i < 4; ++i)
+ G = G*x + AGauss[i];
+ G = G * x * Math.exp(-z*z/2.0);
+ if (z > 0.0)
+ G = 1.0 - G;
+ return G;
+ }
+
+
+ protected static double specialCDF (double x, double y, double rho, double xbig) {
+ // Compute the bivariate normal CDF for limiting cases and returns
+ // its value. If non limiting case, returns -2 as flag.
+ // xbig is practical infinity
+
+ if (Math.abs (rho) > 1.0)
+ throw new IllegalArgumentException ("|rho| > 1");
+ if (x == 0.0 && y == 0.0)
+ return 0.25 + Math.asin(rho)/(2.0*Math.PI);
+
+ if (rho == 1.0) {
+ if (y < x)
+ x = y;
+ return NormalDist.cdf01(x);
+ }
+ if (rho == -1.0) {
+ if (y <= -x)
+ return 0.0;
+ else
+ return NormalDist.cdf01(x) - NormalDist.cdf01(-y);
+ }
+ if (Math.abs (rho) < RHO_SMALL)
+ return NormalDist.cdf01(x) * NormalDist.cdf01(y);
+
+ if ((x <= -xbig) || (y <= -xbig))
+ return 0.0;
+ if (x >= xbig)
+ return NormalDist.cdf01(y);
+ if (y >= xbig)
+ return NormalDist.cdf01(x);
+
+ return -2.0;
+ }
+
+ /**
+ * Computes the standard <EM>binormal</EM> distribution
+ * using the fast Drezner-Wesolowsky method described in.
+ * The absolute error is expected to be smaller than
+ * <SPAN CLASS="MATH">2⋅10<SUP>-7</SUP></SPAN>.
+ *
+ */
+ public static double cdf (double x, double y, double rho) {
+ double bvn = specialCDF (x, y, rho, 20.0);
+ if (bvn >= 0.0)
+ return bvn;
+ bvn = 0.0;
+
+ /* prob(x <= h1, y <= h2), where x and y are standard binormal,
+ with rho = corr(x,y), error < 2e-7.
+ Drezner Z., and G.O. Wesolowsky (1990) On the computation of the
+ bivariate normal integral. J. Statist. Comput. Simul. 35:101-107. */
+
+ int i;
+ double r1, r2, r3, rr, aa, ab, h3, h5, h6, h7;
+ final double h1 = -x;
+ double h2 = -y;
+ final double h12 = (h1 * h1 + h2 * h2) / 2.;
+
+ if (Math.abs (rho) >= 0.7) {
+ r2 = (1. - rho) * (1. + rho);
+ r3 = Math.sqrt (r2);
+ if (rho < 0.)
+ h2 = -h2;
+ h3 = h1 * h2;
+ if (h3 < 300.)
+ h7 = Math.exp (-h3 / 2.);
+ else
+ h7 = 0.;
+ if (r2 != 0.) {
+ h6 = Math.abs (h1 - h2);
+ h5 = h6 * h6 / 2.;
+ h6 /= r3;
+ aa = .5 - h3 / 8.;
+ ab = 3. - 2. * aa * h5;
+ bvn = .13298076 * h6 * ab * (1.0 - Gauss(h6))
+ - Math.exp (-h5 / r2) * (ab + aa * r2) * 0.053051647;
+// if (h7 > 0. && -h3 < 500.0)
+ for (i = 0; i < 5; i++) {
+ r1 = r3 * Z[i];
+ rr = r1 * r1;
+ r2 = Math.sqrt (1. - rr);
+ bvn -= W[i] * Math.exp (-h5 / rr) *
+ (Math.exp (-h3 / (1. + r2)) / r2 / h7 - 1. - aa * rr);
+ }
+ }
+
+ if (rho > 0.)
+ bvn = bvn * r3 * h7 + (1.0 - Gauss (Math.max (h1, h2)));
+ else if (rho < 0.)
+ bvn = (h1 < h2 ? Gauss (h2) - Gauss (h1) : 0.) - bvn * r3 * h7;
+
+ } else {
+ h3 = h1 * h2;
+ for (i = 0; i < 5; i++) {
+ r1 = rho * Z[i];
+ r2 = 1. - r1 * r1;
+ bvn += W[i] * Math.exp ((r1 * h3 - h12) / r2) / Math.sqrt (r2);
+ }
+ bvn = (1.0 - Gauss (h1)) * (1.0 - Gauss (h2)) + rho * bvn;
+ }
+
+ if (bvn <= 0.0)
+ return 0.0;
+ if (bvn <= 1.0)
+ return bvn;
+ return 1.0;
+ }
+
+
+ public double cdf (double x, double y) {
+ return cdf ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }
+
+ /**
+ * Computes the <EM>binormal</EM> distribution function with parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>,
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> =
+ * <TT>sigma2</TT> and <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>.
+ * Uses the fast Drezner-Wesolowsky method described in.
+ * The absolute error is expected to be smaller than
+ * <SPAN CLASS="MATH">2⋅10<SUP>-7</SUP></SPAN>.
+ *
+ */
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return cdf (X, Y, rho);
+ }
+
+
+ /**
+ * Computes the standard upper <EM>binormal</EM> distribution
+ * with
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB> = <I>μ</I><SUB>2</SUB> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> = <I>σ</I><SUB>2</SUB> = 1</SPAN>.
+ * Uses the fast Drezner-Wesolowsky method described in.
+ * The absolute error is expected to be smaller than
+ * <SPAN CLASS="MATH">2⋅10<SUP>-7</SUP></SPAN>.
+ *
+ */
+ public static double barF (double x, double y, double rho) {
+ return cdf (-x, -y, rho);
+ }
+
+
+ public double barF (double x, double y) {
+ return barF ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }
+
+ /**
+ * Computes the upper <EM>binormal</EM> distribution function with parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>,
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> =
+ * <TT>sigma2</TT> and <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>.
+ * Uses the fast Drezner-Wesolowsky method described in.
+ * The absolute error is expected to be smaller than
+ * <SPAN CLASS="MATH">2⋅10<SUP>-7</SUP></SPAN>.
+ *
+ */
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return barF (X, Y, rho);
+ }
+
+
+ public double[] getMean() {
+ return getMean (mu1, mu2, sigma1, sigma2, rho);
+ }
+
+ /**
+ * Return the mean vector
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (<I>μ</I><SUB>1</SUB>, <I>μ</I><SUB>2</SUB>)</SPAN> of the binormal distribution.
+ *
+ */
+ public static double[] getMean(double mu1, double sigma1,
+ double mu2, double sigma2, double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double mean[] = new double[2];
+
+ mean[0] = mu1;
+ mean[1] = mu2;
+
+ return mean;
+ }
+
+
+ public double[][] getCovariance() {
+ return getCovariance (mu1, sigma1, mu2, sigma2, rho);
+ }
+
+ /**
+ * Return the covariance matrix of the binormal distribution.
+ *
+ */
+ public static double[][] getCovariance (double mu1, double sigma1,
+ double mu2, double sigma2,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double cov[][] = new double[2][2];
+
+ cov[0][0] = sigma1 * sigma1;
+ cov[0][1] = rho * sigma1 * sigma2;
+ cov[1][0] = cov[0][1];
+ cov[1][1] = sigma2 * sigma2;
+
+ return cov;
+ }
+
+
+ public double[][] getCorrelation() {
+ return getCovariance (mu1, sigma1, mu2, sigma2, rho);
+ }
+
+ /**
+ * Return the correlation matrix of the binormal distribution.
+ *
+ */
+ public static double[][] getCorrelation (double mu1, double sigma1,
+ double mu2, double sigma2,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double corr[][] = new double[2][2];
+
+ corr[0][0] = 1.0;
+ corr[0][1] = rho;
+ corr[1][0] = rho;
+ corr[1][1] = 1.0;
+
+ return corr;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN>.
+ *
+ */
+ public double getMu1() {
+ return mu1;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN>.
+ *
+ */
+ public double getMu2() {
+ return mu2;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN>.
+ *
+ */
+ public double getSigma1() {
+ return sigma1;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN>.
+ *
+ */
+ public double getSigma2() {
+ return sigma2;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>,
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> =
+ * <TT>sigma2</TT> and <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT> of this object.
+ *
+ */
+ protected void setParams (double mu1, double sigma1,
+ double mu2, double sigma2, double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+ this.dimension = 2;
+ this.mu1 = mu1;
+ this.sigma1 = sigma1;
+ this.mu2 = mu2;
+ this.sigma2 = sigma2;
+ this.rho = rho;
+ racRho = Math.sqrt((1.0 - rho)*(1.0 + rho));
+ detS = 2.0*Math.PI*sigma1*sigma2*racRho;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDist.tex
new file mode 100644
index 0000000..d5d32f2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDist.tex
@@ -0,0 +1,525 @@
+\defmodule {BiNormalDist}
+
+Extends the class \class{ContinuousDistribution2Dim} for the {\em bivariate
+normal\/} distribution\latex{ \cite[page 84]{tJOH72a}}. It has means $E[X] =\mu_1$,
+ $E[Y] =\mu_2$, and variances var$[X] = \sigma_1^2$,
+var$[Y] = \sigma_2^2$ such that $\sigma_1 > 0$ and $\sigma_2 > 0$.
+ The correlation between $X$ and $Y$ is \latex{$\rho$}\html{$r$}.
+Its density function is
+\eq
+ f (x, y) = \latex{\frac{1}{2\pi\sigma_1\sigma_2\sqrt{1-\rho^2}}}e^{-T}
+ \html{/(2\pi\sigma_1\sigma_2\sqrt{1-r^2})} \eqlabel{eq:f1binormal}
+\endeq
+$$
+ T = \latex{\frac{1}{2(1-\rho^2)}\left[\left(\frac{x-\mu_1}{\sigma_1}\right)^2
+ - 2\rho\left(\frac{x-\mu_1}{\sigma_1}\right)
+ \left(\frac{y-\mu_2}{\sigma_2}\right)
+ + \left(\frac{y-\mu_2}{\sigma_2}\right)^2\right]}
+%
+ \html{[((x-\mu_1)/\sigma_1)^2
+ - 2r((x-\mu_1)/\sigma_1)
+ ((y-\mu_2)/\sigma_2)
+ + ((y-\mu_2)/\sigma_2)^2]/(2(1-r^2))}
+$$
+and the corresponding distribution function is (the \texttt{cdf} method)
+\begin{latexonly}
+\eq
+ \Phi(\mu_1, \sigma_1, x, \mu_2, \sigma_2, y, \rho) =
+ \frac{1}{2\pi\sigma_1\sigma_2\sqrt{1-\rho^2}} \int_{-\infty}^x dx
+ \int_{-\infty}^y dy\, e^{-T}. \eqlabel{eq:cdf1binormal}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ \Phi(\mu_1, \sigma_1, x, \mu_2, \sigma_2, y, r) =
+ \int_{-\infty}^x dx \int_{-\infty}^y dy\, e^{-T} /(2\pi\sigma_1\sigma_2\sqrt{1 - r^{2}}).
+\endeq
+\end{htmlonly}
+We also define the upper distribution function (the \texttt{barF} method) as
+\begin{latexonly}
+\eq
+ \overline\Phi(\mu_1, \sigma_1, x, \mu_2, \sigma_2, y, \rho) =
+ \frac{1}{2\pi\sigma_1\sigma_2\sqrt{1-\rho^2}} \int^{\infty}_x dx
+ \int^{\infty}_y dy\, e^{-T}. \eqlabel{eq:cdf3binormal}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ \bar \Phi(\mu_1, \sigma_1, x, \mu_2, \sigma_2, y, r) =
+ \int^{\infty}_x dx \int^{\infty}_y dy\, e^{-T} /(2\pi\sigma_1\sigma_2\sqrt{1 - r^2}).
+\endeq
+\end{htmlonly}
+ When $\mu_1=\mu_2=0$ and
+ $\sigma_1=\sigma_2=1$, we have the {\em standard binormal\/}
+distribution, with corresponding distribution function
+\begin{htmlonly}
+\eq
+ \Phi(x, y, r) =
+ \int^{\infty}_x dx \int^{\infty}_y dy\, e^{-S} /(2\pi\sqrt{1 - r^2})
+\endeq
+$$
+ S = (x^2 - 2r x y + y^2) / (2(1-r^2)).
+$$
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ \Phi(x, y, \rho) = \frac{1}{2\pi\sqrt{1-\rho^2}} \int_{-\infty}^x dx
+ \int_{-\infty}^y dy\, e^{-S} \eqlabel{eq:cdf2binormal}
+\endeq
+$$
+ S = \frac{x^2 - 2\rho x y + y^2}{2(1-\rho^2)}.
+$$
+\end{latexonly}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BiNormalDist
+ * Description: bivariate normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.probdist.NormalDistQuick;
+\end{hide}
+
+public class BiNormalDist extends ContinuousDistribution2Dim \begin{hide} {
+ protected int ndigit; // Number of decimal digits of accuracy
+ protected double mu1, mu2;
+ protected double sigma1, sigma2;
+ protected double rho;
+ protected double racRho; // sqrt(1 - rho^2)
+ protected double detS; // 2*PI*sigma1*sigma2*sqrt(1 - rho^2)
+ protected static final double RHO_SMALL = 1.0e-8; // neglect small rhos
+
+ private static final double Z[] = {
+ 0.04691008, 0.23076534, 0.5, 0.76923466, 0.95308992 };
+
+ private static final double W[] = {
+ 0.018854042, 0.038088059, 0.0452707394, 0.038088059, 0.018854042 };
+
+ private static final double AGauss[] = {
+ -0.72657601, 0.71070688, -0.142248368, 0.127414796 };
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BiNormalDist (double rho) \begin{hide} {
+ setParams (0.0, 1.0, 0.0, 1.0, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{BiNormalDist} object with default parameters $\mu_1 = \mu_2 =
+ 0$, $\sigma_1 = \sigma_2 = 1$ and correlation $\rho = $\texttt{ rho}.
+ \end{tabb}
+\begin{code}
+
+ public BiNormalDist (double mu1, double sigma1,
+ double mu2, double sigma2, double rho) \begin{hide} {
+ setParams (mu1, sigma1, mu2, sigma2, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{BiNormalDist} object with parameters $\mu_1$ = \texttt{mu1},
+ $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1}, $\sigma_2$ =
+ \texttt{sigma2} and $\rho$ = \texttt{rho}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x, double y) {
+ if (Math.abs(rho) == 1.)
+ throw new IllegalArgumentException ("|rho| = 1");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ double T = (X*X - 2.0*rho*X*Y + Y*Y) / (2.0*racRho*racRho);
+ return Math.exp(-T) / detS;
+ }\end{hide}
+
+ public static double density (double x, double y, double rho) \begin{hide} {
+ return density (0.0, 1.0, x, 0.0, 1.0, y, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the {\em standard binormal\/} density function
+ (\ref{eq:f1binormal}) with $\mu_1 = \mu_2 = 0$ and $\sigma_1 = \sigma_2 = 1$.
+\end{tabb}
+\begin{code}
+
+ public static double density (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) \begin{hide} {
+ if (sigma1 <= 0.)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0.)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) >= 1.)
+ throw new IllegalArgumentException ("|rho| >= 1");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ double fRho = (1.0 - rho)*(1.0 + rho);
+ double T = (X*X - 2.0*rho*X*Y + Y*Y) / (2.0*fRho);
+ return Math.exp (-T)/ (2.0*Math.PI*sigma1*sigma2*Math.sqrt(fRho));
+
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the {\em binormal\/} density function (\ref{eq:f1binormal})
+ with parameters $\mu_1$ = \texttt{mu1}, $\mu_2$ = \texttt{mu2}, $\sigma_1$ =
+ \texttt{sigma1}, $\sigma_2$ = \texttt{sigma2} and $\rho$ = \texttt{rho}.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ protected static double Gauss (double z) {
+ // Computes normal probabilities to within accuracy of 10^(-7)
+ // Drezner Z., and G.O. Wesolowsky (1990) On the computation of the
+ // bivariate normal integral. J. Statist. Comput. Simul. 35:101-107.
+
+ final double x = 1.0/(1.0 + 0.23164189 * Math.abs(z));
+ double G = 0.53070271;
+ for (int i = 0; i < 4; ++i)
+ G = G*x + AGauss[i];
+ G = G * x * Math.exp(-z*z/2.0);
+ if (z > 0.0)
+ G = 1.0 - G;
+ return G;
+ }
+
+
+ protected static double specialCDF (double x, double y, double rho, double xbig) {
+ // Compute the bivariate normal CDF for limiting cases and returns
+ // its value. If non limiting case, returns -2 as flag.
+ // xbig is practical infinity
+
+ if (Math.abs (rho) > 1.0)
+ throw new IllegalArgumentException ("|rho| > 1");
+ if (x == 0.0 && y == 0.0)
+ return 0.25 + Math.asin(rho)/(2.0*Math.PI);
+
+ if (rho == 1.0) {
+ if (y < x)
+ x = y;
+ return NormalDist.cdf01(x);
+ }
+ if (rho == -1.0) {
+ if (y <= -x)
+ return 0.0;
+ else
+ return NormalDist.cdf01(x) - NormalDist.cdf01(-y);
+ }
+ if (Math.abs (rho) < RHO_SMALL)
+ return NormalDist.cdf01(x) * NormalDist.cdf01(y);
+
+ if ((x <= -xbig) || (y <= -xbig))
+ return 0.0;
+ if (x >= xbig)
+ return NormalDist.cdf01(y);
+ if (y >= xbig)
+ return NormalDist.cdf01(x);
+
+ return -2.0;
+ }\end{hide}
+
+ public static double cdf (double x, double y, double rho) \begin{hide} {
+ double bvn = specialCDF (x, y, rho, 20.0);
+ if (bvn >= 0.0)
+ return bvn;
+ bvn = 0.0;
+
+ /* prob(x <= h1, y <= h2), where x and y are standard binormal,
+ with rho = corr(x,y), error < 2e-7.
+ Drezner Z., and G.O. Wesolowsky (1990) On the computation of the
+ bivariate normal integral. J. Statist. Comput. Simul. 35:101-107. */
+
+ int i;
+ double r1, r2, r3, rr, aa, ab, h3, h5, h6, h7;
+ final double h1 = -x;
+ double h2 = -y;
+ final double h12 = (h1 * h1 + h2 * h2) / 2.;
+
+ if (Math.abs (rho) >= 0.7) {
+ r2 = (1. - rho) * (1. + rho);
+ r3 = Math.sqrt (r2);
+ if (rho < 0.)
+ h2 = -h2;
+ h3 = h1 * h2;
+ if (h3 < 300.)
+ h7 = Math.exp (-h3 / 2.);
+ else
+ h7 = 0.;
+ if (r2 != 0.) {
+ h6 = Math.abs (h1 - h2);
+ h5 = h6 * h6 / 2.;
+ h6 /= r3;
+ aa = .5 - h3 / 8.;
+ ab = 3. - 2. * aa * h5;
+ bvn = .13298076 * h6 * ab * (1.0 - Gauss(h6))
+ - Math.exp (-h5 / r2) * (ab + aa * r2) * 0.053051647;
+// if (h7 > 0. && -h3 < 500.0)
+ for (i = 0; i < 5; i++) {
+ r1 = r3 * Z[i];
+ rr = r1 * r1;
+ r2 = Math.sqrt (1. - rr);
+ bvn -= W[i] * Math.exp (-h5 / rr) *
+ (Math.exp (-h3 / (1. + r2)) / r2 / h7 - 1. - aa * rr);
+ }
+ }
+
+ if (rho > 0.)
+ bvn = bvn * r3 * h7 + (1.0 - Gauss (Math.max (h1, h2)));
+ else if (rho < 0.)
+ bvn = (h1 < h2 ? Gauss (h2) - Gauss (h1) : 0.) - bvn * r3 * h7;
+
+ } else {
+ h3 = h1 * h2;
+ for (i = 0; i < 5; i++) {
+ r1 = rho * Z[i];
+ r2 = 1. - r1 * r1;
+ bvn += W[i] * Math.exp ((r1 * h3 - h12) / r2) / Math.sqrt (r2);
+ }
+ bvn = (1.0 - Gauss (h1)) * (1.0 - Gauss (h2)) + rho * bvn;
+ }
+
+ if (bvn <= 0.0)
+ return 0.0;
+ if (bvn <= 1.0)
+ return bvn;
+ return 1.0;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the standard {\em binormal\/} distribution (\ref{eq:cdf2binormal})
+ using the fast Drezner-Wesolowsky method described in \cite{tDRE90a}.
+ The absolute error is expected to be smaller than $2 \cdot 10^{-7}$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double cdf (double x, double y) {
+ return cdf ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }\end{hide}
+
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) \begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return cdf (X, Y, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the {\em binormal\/} distribution function
+ (\ref{eq:cdf1binormal}) with parameters $\mu_1$ = \texttt{mu1},
+ $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1}, $\sigma_2$ =
+ \texttt{sigma2} and $\rho$ = \texttt{rho}.
+ Uses the fast Drezner-Wesolowsky method described in \cite{tDRE90a}.
+ The absolute error is expected to be smaller than $2 \cdot 10^{-7}$.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double x, double y, double rho) \begin{hide} {
+ return cdf (-x, -y, rho);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the standard upper {\em binormal\/} distribution
+ with $\mu_1 = \mu_2 = 0$ and $\sigma_1 = \sigma_2 = 1$.
+ Uses the fast Drezner-Wesolowsky method described in \cite{tDRE90a}.
+ The absolute error is expected to be smaller than $2 \cdot 10^{-7}$.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ public double barF (double x, double y) {
+ return barF ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }\end{hide}
+
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) \begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return barF (X, Y, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the upper {\em binormal\/} distribution function
+ (\ref{eq:cdf3binormal}) with parameters $\mu_1$ = \texttt{mu1},
+ $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1}, $\sigma_2$ =
+ \texttt{sigma2} and $\rho$ = \texttt{rho}.
+ Uses the fast Drezner-Wesolowsky method described in \cite{tDRE90a}.
+ The absolute error is expected to be smaller than $2 \cdot 10^{-7}$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double[] getMean() {
+ return getMean (mu1, mu2, sigma1, sigma2, rho);
+ }\end{hide}
+
+ public static double[] getMean(double mu1, double sigma1,
+ double mu2, double sigma2, double rho)\begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double mean[] = new double[2];
+
+ mean[0] = mu1;
+ mean[1] = mu2;
+
+ return mean;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return the mean vector $E[X] = (\mu_1, \mu_2)$ of the binormal distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double[][] getCovariance() {
+ return getCovariance (mu1, sigma1, mu2, sigma2, rho);
+ }\end{hide}
+
+ public static double[][] getCovariance (double mu1, double sigma1,
+ double mu2, double sigma2,
+ double rho)\begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double cov[][] = new double[2][2];
+
+ cov[0][0] = sigma1 * sigma1;
+ cov[0][1] = rho * sigma1 * sigma2;
+ cov[1][0] = cov[0][1];
+ cov[1][1] = sigma2 * sigma2;
+
+ return cov;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return the covariance matrix of the binormal distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double[][] getCorrelation() {
+ return getCovariance (mu1, sigma1, mu2, sigma2, rho);
+ }\end{hide}
+
+ public static double[][] getCorrelation (double mu1, double sigma1,
+ double mu2, double sigma2,
+ double rho)\begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double corr[][] = new double[2][2];
+
+ corr[0][0] = 1.0;
+ corr[0][1] = rho;
+ corr[1][0] = rho;
+ corr[1][1] = 1.0;
+
+ return corr;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Return the correlation matrix of the binormal distribution.
+\end{tabb}
+\begin{code}
+
+ public double getMu1() \begin{hide} {
+ return mu1;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu_1$.
+ \end{tabb}
+\begin{code}
+
+ public double getMu2() \begin{hide} {
+ return mu2;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu_2$.
+ \end{tabb}
+\begin{code}
+
+ public double getSigma1() \begin{hide} {
+ return sigma1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\sigma_1$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma2() \begin{hide} {
+ return sigma2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\sigma_2$.
+\end{tabb}
+\begin{code}
+
+ protected void setParams (double mu1, double sigma1,
+ double mu2, double sigma2, double rho) \begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+ this.dimension = 2;
+ this.mu1 = mu1;
+ this.sigma1 = sigma1;
+ this.mu2 = mu2;
+ this.sigma2 = sigma2;
+ this.rho = rho;
+ racRho = Math.sqrt((1.0 - rho)*(1.0 + rho));
+ detS = 2.0*Math.PI*sigma1*sigma2*racRho;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\mu_1$ = \texttt{mu1},
+ $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1}, $\sigma_2$ =
+ \texttt{sigma2} and $\rho$ = \texttt{rho} of this object.
+ \end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDonnellyDist.java b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDonnellyDist.java
new file mode 100644
index 0000000..e9b2833
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDonnellyDist.java
@@ -0,0 +1,342 @@
+
+
+/*
+ * Class: BiNormalDonnellyDist
+ * Description: bivariate normal distribution using Donnelly's code
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Extends the class {@link BiNormalDist} for the <EM>bivariate
+ * normal</EM> distribution
+ * using a translation of Donnelly's F<SMALL>ORTRAN</SMALL> code.
+ *
+ */
+public class BiNormalDonnellyDist extends BiNormalDist {
+
+private static final double TWOPI = 2.0*Math.PI;
+private static final double SQRTPI = Math.sqrt(Math.PI);
+private static final int KMAX = 6;
+
+private static double BB[] = new double[KMAX + 1];
+private static final double CB[] = {
+ 0.9999936, -0.9992989, 0.9872976,
+ -0.9109973, 0.6829098, -0.3360210, 0.07612251 };
+
+
+private static double BorthT (double h, double a)
+{
+ int k;
+ final double w = a * h / Num.RAC2;
+ final double w2 = w * w;
+ double z = w * Math.exp(-w2);
+
+ BB[0] = SQRTPI * (Gauss (Num.RAC2 * w) - 0.5);
+ for (k = 1; k <= KMAX; ++k) {
+ BB[k] = ((2 * k - 1) * BB[k - 1] - z) / 2.0;
+ z *= w2;
+ }
+
+ final double h2 = h * h / 2.0;
+ z = h / Num.RAC2;
+ double sum = 0;
+ for (k = 0; k <= KMAX; ++k) {
+ sum += CB[k] * BB[k] / z;
+ z *= h2;
+ }
+ return sum * Math.exp (-h2) / TWOPI;
+}
+
+
+
+
+
+ /**
+ * Constructor with default parameters
+ *
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB> = <I>μ</I><SUB>2</SUB> = 0</SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> = <I>σ</I><SUB>2</SUB> = 1</SPAN>, correlation
+ * <SPAN CLASS="MATH"><I>ρ</I> =</SPAN><TT> rho</TT>, and <SPAN CLASS="MATH"><I>d</I> =</SPAN> <TT>ndig</TT> digits of accuracy
+ * (the absolute error is smaller than <SPAN CLASS="MATH">10<SUP>-d</SUP></SPAN>). Restriction: <SPAN CLASS="MATH"><I>d</I> <= 15</SPAN>.
+ *
+ */
+ public BiNormalDonnellyDist (double rho, int ndig) {
+ super (rho);
+ if (ndig > 15)
+ throw new IllegalArgumentException ("ndig > 15");
+ decPrec = ndigit = ndig;
+ }
+
+
+ /**
+ * Same as {@link #BiNormalDonnellyDist(double,int) BiNormalDonnellyDist} <TT>(rho, 15)</TT>.
+ *
+ */
+ public BiNormalDonnellyDist (double rho) {
+ this (rho, 15);
+ }
+
+
+ /**
+ * Constructor with parameters
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>, <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>,
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> = <TT>sigma2</TT>, <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>, and <SPAN CLASS="MATH"><I>d</I> =</SPAN> <TT>ndig</TT>
+ * digits of accuracy. Restriction: <SPAN CLASS="MATH"><I>d</I> <= 15</SPAN>.
+ *
+ */
+ public BiNormalDonnellyDist (double mu1, double sigma1, double mu2,
+ double sigma2, double rho, int ndig) {
+ super (mu1, sigma1, mu2, sigma2, rho);
+ if (ndig > 15)
+ throw new IllegalArgumentException ("ndig > 15");
+ decPrec = ndigit = ndig;
+ }
+
+
+ /**
+ * Same as {@link #BiNormalDonnellyDist(double,double,double,double,double,int) BiNormalDonnellyDist} <TT>(mu1, sigma1, mu2, sigma2, rho, 15)</TT>.
+ *
+ */
+ public BiNormalDonnellyDist (double mu1, double sigma1, double mu2,
+ double sigma2, double rho) {
+ this (mu1, sigma1, mu2, sigma2, rho, 15);
+ }
+
+
+ /**
+ * Computes the standard <EM>binormal</EM> distribution
+ * with the method described in, where <TT>ndig</TT> is the
+ * number of decimal digits of accuracy provided (<TT>ndig</TT> <SPAN CLASS="MATH"> <= 15</SPAN>).
+ * The code was translated from the Fortran program written by T. G. Donnelly
+ * and copyrighted by the ACM (see
+ * <TT><A NAME="tex2html1"
+ * HREF="http://www.acm.org/pubs/copyright_policy/#Notice">http://www.acm.org/pubs/copyright_policy/#Notice</A></TT>). The absolute error
+ * is expected to be smaller than <SPAN CLASS="MATH">10<SUP>-d</SUP></SPAN>, where <SPAN CLASS="MATH"><I>d</I> =</SPAN> <TT>ndig</TT>.
+ *
+ */
+ public static double cdf (double x, double y, double rho, int ndig) {
+ /*
+ * This is a translation of the FORTRAN code published by Thomas G. Donnelly
+ * in the CACM, Vol. 16, Number 10, p. 638, (1973)
+ */
+
+ if (ndig > 15)
+ throw new IllegalArgumentException ("ndig > 15");
+ double b = specialCDF (x, y, rho, 13.0);
+ if (b >= 0.0)
+ return b;
+ b = 0;
+
+ final boolean SINGLE_FLAG = ndig <= 7 ? true : false;
+ final double TWO_PI = 2.0 * Math.PI;
+ final double r = rho;
+ final double ah = -x;
+ final double ak = -y;
+
+ double a2, ap, cn, conex, ex, g2, gh, gk, gw = 0, h2, h4, rr, s1, s2,
+ sgn, sn, sp, sqr, t, temp, w2, wh = 0, wk = 0;
+ int is = -1;
+
+ if (SINGLE_FLAG) {
+ gh = Gauss (x) / 2.0;
+ gk = Gauss (y) / 2.0;
+ } else {
+ gh = NormalDist.cdf01 (x) / 2.0;
+ gk = NormalDist.cdf01 (y) / 2.0;
+ }
+ boolean flag = true; // Easiest way to translate a Fortran goto
+
+ rr = (1 - r) * (1 + r);
+ if (rr < 0)
+ throw new IllegalArgumentException ("|rho| > 1");
+ sqr = Math.sqrt(rr);
+ final double con = Math.PI * Num.TEN_NEG_POW[ndig];
+ final double EPSILON = 0.5*Num.TEN_NEG_POW[ndig];
+
+ if (ah != 0) {
+ b = gh;
+ if (ah * ak < 0)
+ b -= 0.5;
+ else if (ah * ak == 0) {
+ flag = false;
+ }
+ } else if (ak == 0) {
+ return Math.asin (r) / TWO_PI + 0.25;
+ }
+
+ if (flag)
+ b += gk;
+ if (ah != 0) {
+ flag = false;
+ wh = -ah;
+ wk = (ak / ah - r) / sqr;
+ gw = 2 * gh;
+ is = -1;
+ }
+
+ do {
+ if (flag) {
+ wh = -ak;
+ wk = (ah / ak - r) / sqr;
+ gw = 2 * gk;
+ is = 1;
+ }
+ flag = true;
+ sgn = -1;
+ t = 0;
+ if (wk != 0) {
+ if (Math.abs (wk) >= 1)
+ if (Math.abs (wk) == 1) {
+ t = wk * gw * (1 - gw) / 2;
+ b += sgn * t;
+ if (is >= 0) // Another Fortran goto
+ break;
+ else
+ continue;
+ } else {
+ sgn = -sgn;
+ wh = wh * wk;
+ if (SINGLE_FLAG)
+ g2 = Gauss(wh);
+ else
+ g2 = NormalDist.cdf01(wh);
+ wk = 1 / wk;
+ if (wk < 0)
+ b = b + .5;
+ b = b - (gw + g2) / 2 + gw * g2;
+ }
+ /* ****
+ // Cette m'ethode de Borth est plus lente que simple Donnelly
+ if (ndig <= 7 && Math.abs (wh) > 1.6 && Math.abs (wk) > 0.3) {
+ b += sgn * BorthT (wh, wk);
+ if (is >= 0)
+ break;
+ else
+ continue;
+ }
+ *****/
+ h2 = wh * wh;
+ a2 = wk * wk;
+ h4 = h2 * .5;
+ ex = 0;
+ if (h4 < 300.0)
+ ex = Math.exp (-h4);
+ w2 = h4 * ex;
+ ap = 1;
+ s2 = ap - ex;
+ sp = ap;
+ s1 = 0;
+ sn = s1;
+ conex = Math.abs (con / wk);
+ do {
+ cn = ap * s2 / (sn + sp);
+ s1 += cn;
+ if (Math.abs (cn) <= conex)
+ break;
+ sn = sp;
+ sp += 1;
+ s2 -= w2;
+ w2 *= h4 / sp;
+ ap = -ap * a2;
+ } while (true);
+ t = (Math.atan (wk) - wk * s1) / TWO_PI;
+ b += sgn * t;
+ }
+ if (is >= 0)
+ break;
+ } while (ak != 0);
+
+ if (b < EPSILON)
+ b = 0;
+ if (b > 1)
+ b = 1;
+ return b;
+}
+
+
+ /**
+ * Computes the <EM>binormal</EM> distribution function with parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>,
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> = <TT>sigma2</TT>,
+ * correlation <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT> and <TT>ndig</TT> decimal digits of accuracy.
+ *
+ */
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho, int ndig) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return cdf (X, Y, rho, ndig);
+ }
+
+
+ /**
+ * Computes the upper <EM>binormal</EM> distribution function with parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>,
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> = <TT>sigma2</TT>,
+ * <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT> and <TT>ndig</TT> decimal digits of accuracy.
+ *
+ */
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho, int ndig) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return barF (X, Y, rho, ndig);
+ }
+
+
+ /**
+ * Computes the upper <EM>standard binormal</EM> distribution function with parameters <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT> and
+ * <TT>ndig</TT> decimal digits of accuracy.
+ *
+ */
+ public static double barF (double x, double y, double rho, int ndig) {
+ return cdf (-x, -y, rho, ndig);
+ }
+
+
+ public double cdf (double x, double y) {
+ return cdf ((x-mu1)/sigma1, (y-mu2)/sigma2, rho, ndigit);
+ }
+
+ public static double cdf (double x, double y, double rho) {
+ return cdf (x, y, rho, 15);
+ }
+
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ return cdf (mu1, sigma1, x, mu2, sigma2, y, rho, 15);
+ }
+
+ public double barF (double x, double y) {
+ return barF ((x-mu1)/sigma1, (y-mu2)/sigma2, rho, ndigit);
+ }
+
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ return barF (mu1, sigma1, x, mu2, sigma2, y, rho, 15);
+ }
+
+ public static double barF (double x, double y, double rho) {
+ return barF (x, y, rho, 15);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDonnellyDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDonnellyDist.tex
new file mode 100644
index 0000000..b7581cc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalDonnellyDist.tex
@@ -0,0 +1,356 @@
+\defmodule {BiNormalDonnellyDist}
+
+ Extends the class \class{BiNormalDist} for the {\em bivariate
+ normal\/} distribution\latex{ \cite[page 84]{tJOH72a}}
+ using a translation of Donnelly's {\sc Fortran} code\latex{
+ in \cite{tDON73a}}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BiNormalDonnellyDist
+ * Description: bivariate normal distribution using Donnelly's code
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class BiNormalDonnellyDist extends BiNormalDist \begin{hide} {
+
+private static final double TWOPI = 2.0*Math.PI;
+private static final double SQRTPI = Math.sqrt(Math.PI);
+private static final int KMAX = 6;
+
+private static double BB[] = new double[KMAX + 1];
+private static final double CB[] = {
+ 0.9999936, -0.9992989, 0.9872976,
+ -0.9109973, 0.6829098, -0.3360210, 0.07612251 };
+
+
+private static double BorthT (double h, double a)
+{
+ int k;
+ final double w = a * h / Num.RAC2;
+ final double w2 = w * w;
+ double z = w * Math.exp(-w2);
+
+ BB[0] = SQRTPI * (Gauss (Num.RAC2 * w) - 0.5);
+ for (k = 1; k <= KMAX; ++k) {
+ BB[k] = ((2 * k - 1) * BB[k - 1] - z) / 2.0;
+ z *= w2;
+ }
+
+ final double h2 = h * h / 2.0;
+ z = h / Num.RAC2;
+ double sum = 0;
+ for (k = 0; k <= KMAX; ++k) {
+ sum += CB[k] * BB[k] / z;
+ z *= h2;
+ }
+ return sum * Math.exp (-h2) / TWOPI;
+}
+
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BiNormalDonnellyDist (double rho, int ndig) \begin{hide} {
+ super (rho);
+ if (ndig > 15)
+ throw new IllegalArgumentException ("ndig > 15");
+ decPrec = ndigit = ndig;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor with default parameters
+ $\mu_1 = \mu_2 = 0$, $\sigma_1 = \sigma_2 = 1$, correlation
+ $\rho = $\texttt{ rho}, and $d = $ \texttt{ndig} digits of accuracy
+ (the absolute error is smaller than $10^{-d}$). Restriction: $d \le 15$.
+ \end{tabb}
+\begin{code}
+
+ public BiNormalDonnellyDist (double rho) \begin{hide} {
+ this (rho, 15);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{BiNormalDonnellyDist}{double,int}~\texttt{(rho, 15)}.
+ \end{tabb}
+\begin{code}
+
+ public BiNormalDonnellyDist (double mu1, double sigma1, double mu2,
+ double sigma2, double rho, int ndig) \begin{hide} {
+ super (mu1, sigma1, mu2, sigma2, rho);
+ if (ndig > 15)
+ throw new IllegalArgumentException ("ndig > 15");
+ decPrec = ndigit = ndig;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor with parameters
+ $\mu_1$ = \texttt{mu1}, $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1},
+ $\sigma_2$ = \texttt{sigma2}, $\rho$ = \texttt{rho}, and $d = $ \texttt{ndig}
+ digits of accuracy. Restriction: $d \le 15$.
+ \end{tabb}
+\begin{code}
+
+ public BiNormalDonnellyDist (double mu1, double sigma1, double mu2,
+ double sigma2, double rho) \begin{hide} {
+ this (mu1, sigma1, mu2, sigma2, rho, 15);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{BiNormalDonnellyDist}{double,double,double,double,double,int}~\texttt{(mu1, sigma1, mu2, sigma2, rho, 15)}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+ The following methods use the parameter \texttt{ndig} for the number of digits of
+ absolute accuracy. If the same methods are called without the \texttt{ndig}
+ parameter, a default value of \texttt{ndig} = 15 will be used.
+
+\begin{code}
+
+ public static double cdf (double x, double y, double rho, int ndig) \begin{hide} {
+ /*
+ * This is a translation of the FORTRAN code published by Thomas G. Donnelly
+ * in the CACM, Vol. 16, Number 10, p. 638, (1973)
+ */
+
+ if (ndig > 15)
+ throw new IllegalArgumentException ("ndig > 15");
+ double b = specialCDF (x, y, rho, 13.0);
+ if (b >= 0.0)
+ return b;
+ b = 0;
+
+ final boolean SINGLE_FLAG = ndig <= 7 ? true : false;
+ final double TWO_PI = 2.0 * Math.PI;
+ final double r = rho;
+ final double ah = -x;
+ final double ak = -y;
+
+ double a2, ap, cn, conex, ex, g2, gh, gk, gw = 0, h2, h4, rr, s1, s2,
+ sgn, sn, sp, sqr, t, temp, w2, wh = 0, wk = 0;
+ int is = -1;
+
+ if (SINGLE_FLAG) {
+ gh = Gauss (x) / 2.0;
+ gk = Gauss (y) / 2.0;
+ } else {
+ gh = NormalDist.cdf01 (x) / 2.0;
+ gk = NormalDist.cdf01 (y) / 2.0;
+ }
+ boolean flag = true; // Easiest way to translate a Fortran goto
+
+ rr = (1 - r) * (1 + r);
+ if (rr < 0)
+ throw new IllegalArgumentException ("|rho| > 1");
+ sqr = Math.sqrt(rr);
+ final double con = Math.PI * Num.TEN_NEG_POW[ndig];
+ final double EPSILON = 0.5*Num.TEN_NEG_POW[ndig];
+
+ if (ah != 0) {
+ b = gh;
+ if (ah * ak < 0)
+ b -= 0.5;
+ else if (ah * ak == 0) {
+ flag = false;
+ }
+ } else if (ak == 0) {
+ return Math.asin (r) / TWO_PI + 0.25;
+ }
+
+ if (flag)
+ b += gk;
+ if (ah != 0) {
+ flag = false;
+ wh = -ah;
+ wk = (ak / ah - r) / sqr;
+ gw = 2 * gh;
+ is = -1;
+ }
+
+ do {
+ if (flag) {
+ wh = -ak;
+ wk = (ah / ak - r) / sqr;
+ gw = 2 * gk;
+ is = 1;
+ }
+ flag = true;
+ sgn = -1;
+ t = 0;
+ if (wk != 0) {
+ if (Math.abs (wk) >= 1)
+ if (Math.abs (wk) == 1) {
+ t = wk * gw * (1 - gw) / 2;
+ b += sgn * t;
+ if (is >= 0) // Another Fortran goto
+ break;
+ else
+ continue;
+ } else {
+ sgn = -sgn;
+ wh = wh * wk;
+ if (SINGLE_FLAG)
+ g2 = Gauss(wh);
+ else
+ g2 = NormalDist.cdf01(wh);
+ wk = 1 / wk;
+ if (wk < 0)
+ b = b + .5;
+ b = b - (gw + g2) / 2 + gw * g2;
+ }
+ /*****
+ // Cette m'ethode de Borth est plus lente que simple Donnelly
+ if (ndig <= 7 && Math.abs (wh) > 1.6 && Math.abs (wk) > 0.3) {
+ b += sgn * BorthT (wh, wk);
+ if (is >= 0)
+ break;
+ else
+ continue;
+ }
+ *****/
+ h2 = wh * wh;
+ a2 = wk * wk;
+ h4 = h2 * .5;
+ ex = 0;
+ if (h4 < 300.0)
+ ex = Math.exp (-h4);
+ w2 = h4 * ex;
+ ap = 1;
+ s2 = ap - ex;
+ sp = ap;
+ s1 = 0;
+ sn = s1;
+ conex = Math.abs (con / wk);
+ do {
+ cn = ap * s2 / (sn + sp);
+ s1 += cn;
+ if (Math.abs (cn) <= conex)
+ break;
+ sn = sp;
+ sp += 1;
+ s2 -= w2;
+ w2 *= h4 / sp;
+ ap = -ap * a2;
+ } while (true);
+ t = (Math.atan (wk) - wk * s1) / TWO_PI;
+ b += sgn * t;
+ }
+ if (is >= 0)
+ break;
+ } while (ak != 0);
+
+ if (b < EPSILON)
+ b = 0;
+ if (b > 1)
+ b = 1;
+ return b;
+}\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the standard {\em binormal\/} distribution (\ref{eq:cdf2binormal})
+ with the method described in \cite{tDON73a}, where \texttt{ndig} is the
+ number of decimal digits of accuracy provided (\texttt{ndig} $\le 15$).
+ The code was translated from the Fortran program written by T. G. Donnelly
+ and copyrighted by the ACM (see
+ \url{http://www.acm.org/pubs/copyright_policy/#Notice}). The absolute error
+ is expected to be smaller than $10^{-d}$, where $d=$ \texttt{ndig}.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho, int ndig) \begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return cdf (X, Y, rho, ndig);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the {\em binormal\/} distribution function
+ (\ref{eq:cdf1binormal}) with parameters $\mu_1$ = \texttt{mu1},
+ $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1}, $\sigma_2$ = \texttt{sigma2},
+ correlation $\rho$ = \texttt{rho} and \texttt{ndig} decimal digits of accuracy.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho, int ndig) \begin{hide} {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double X = (x - mu1)/sigma1;
+ double Y = (y - mu2)/sigma2;
+ return barF (X, Y, rho, ndig);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the upper {\em binormal\/} distribution function
+ (\ref{eq:cdf3binormal}) with parameters $\mu_1$ = \texttt{mu1},
+ $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1}, $\sigma_2$ = \texttt{sigma2},
+ $\rho$ = \texttt{rho} and \texttt{ndig} decimal digits of accuracy.
+\end{tabb}
+\begin{code}
+
+ public static double barF (double x, double y, double rho, int ndig) \begin{hide} {
+ return cdf (-x, -y, rho, ndig);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the upper {\em standard binormal\/} distribution function
+ (\ref{eq:cdf3binormal}) with parameters $\rho$ = \texttt{rho} and
+ \texttt{ndig} decimal digits of accuracy.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ public double cdf (double x, double y) {
+ return cdf ((x-mu1)/sigma1, (y-mu2)/sigma2, rho, ndigit);
+ }
+
+ public static double cdf (double x, double y, double rho) {
+ return cdf (x, y, rho, 15);
+ }
+
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ return cdf (mu1, sigma1, x, mu2, sigma2, y, rho, 15);
+ }
+
+ public double barF (double x, double y) {
+ return barF ((x-mu1)/sigma1, (y-mu2)/sigma2, rho, ndigit);
+ }
+
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ return barF (mu1, sigma1, x, mu2, sigma2, y, rho, 15);
+ }
+
+ public static double barF (double x, double y, double rho) {
+ return barF (x, y, rho, 15);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiNormalGenzDist.java b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalGenzDist.java
new file mode 100644
index 0000000..e19f402
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalGenzDist.java
@@ -0,0 +1,306 @@
+
+
+/*
+ * Class: BiNormalGenzDist
+ * Description: bivariate normal distribution using Genz's algorithm
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+
+
+/**
+ * Extends the class {@link BiNormalDist} for the <EM>bivariate
+ * normal</EM> distribution
+ * using Genz's algorithm as described in.
+ *
+ */
+public class BiNormalGenzDist extends BiNormalDist {
+
+ private static final double[][] W = {
+ // Gauss Legendre points and weights, n = 6
+ { 0.1713244923791705, 0.3607615730481384, 0.4679139345726904},
+
+ // Gauss Legendre points and weights, n = 12
+ { 0.4717533638651177e-1, 0.1069393259953183, 0.1600783285433464,
+ 0.2031674267230659, 0.2334925365383547, 0.2491470458134029 },
+
+ // Gauss Legendre points and weights, n = 20
+ { 0.1761400713915212e-1, 0.4060142980038694e-1, 0.6267204833410906e-1,
+ 0.8327674157670475e-1, 0.1019301198172404, 0.1181945319615184,
+ 0.1316886384491766, 0.1420961093183821, 0.1491729864726037,
+ 0.1527533871307259 }
+ };
+
+ private static final double[][] X = {
+ // Gauss Legendre points and weights, n = 6
+ { 0.9324695142031522, 0.6612093864662647, 0.2386191860831970},
+
+ // Gauss Legendre points and weights, n = 12
+ { 0.9815606342467191, 0.9041172563704750, 0.7699026741943050,
+ 0.5873179542866171, 0.3678314989981802, 0.1252334085114692 },
+
+ // Gauss Legendre points and weights, n = 20
+ { 0.9931285991850949, 0.9639719272779138, 0.9122344282513259,
+ 0.8391169718222188, 0.7463319064601508, 0.6360536807265150,
+ 0.5108670019508271, 0.3737060887154196, 0.2277858511416451,
+ 0.7652652113349733e-1 }
+ };
+
+
+ /**
+ * Constructs a <TT>BiNormalGenzDist</TT> object with default parameters
+ *
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB> = <I>μ</I><SUB>2</SUB> = 0</SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> = <I>σ</I><SUB>2</SUB> = 1</SPAN> and correlation
+ * <SPAN CLASS="MATH"><I>ρ</I> =</SPAN><TT> rho</TT>.
+ *
+ */
+ public BiNormalGenzDist (double rho) {
+ super (rho);
+ }
+
+
+ /**
+ * Constructs a <TT>BiNormalGenzDist</TT> object with parameters <SPAN CLASS="MATH"><I>μ</I><SUB>1</SUB></SPAN> = <TT>mu1</TT>,
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>2</SUB></SPAN> = <TT>mu2</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB></SPAN> = <TT>sigma1</TT>, <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB></SPAN> = <TT>sigma2</TT>
+ * and <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT>.
+ *
+ */
+ public BiNormalGenzDist (double mu1, double sigma1,
+ double mu2, double sigma2, double rho) {
+ super (mu1, sigma1, mu2, sigma2, rho);
+ }
+
+
+ /**
+ * Computes the standard <EM>binormal</EM> distribution
+ * with the method described in. The code for the <TT>cdf</TT>
+ * was translated directly from the Matlab code written by Alan Genz
+ * and available from his web page at
+ * <TT><A NAME="tex2html1"
+ * HREF="http://www.math.wsu.edu/faculty/genz/homepage">http://www.math.wsu.edu/faculty/genz/homepage</A></TT> (the code is copyrighted by Alan Genz
+ * and is included in this package with the kind permission of the author).
+ * The absolute error is expected to be smaller than
+ * <SPAN CLASS="MATH">0.5⋅10<SUP>-15</SUP></SPAN>.
+ *
+ */
+ public static double cdf (double x, double y, double rho) {
+ double bvn = specialCDF (x, y, rho, 40.0);
+ if (bvn >= 0.0)
+ return bvn;
+
+/*
+// Copyright (C) 2005, Alan Genz, All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided 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 contributor name(s) 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.
+//
+// function p = bvnl( dh, dk, r )
+//
+// A function for computing bivariate normal probabilities.
+// bvnl calculates the probability that x < dh and y < dk.
+// parameters
+// dh 1st upper integration limit
+// dk 2nd upper integration limit
+// r correlation coefficient
+//
+// Author
+// Alan Genz
+// Department of Mathematics
+// Washington State University
+// Pullman, Wa 99164-3113
+// Email : alangenz at wsu.edu
+// This function is based on the method described by
+// Drezner, Z and G.O. Wesolowsky, (1989),
+// On the computation of the bivariate normal inegral,
+// Journal of Statist. Comput. Simul. 35, pp. 101-107,
+// with major modifications for double precision, for |r| close to 1,
+// and for matlab by Alan Genz - last modifications 7/98.
+//
+// p = bvnu( -dh, -dk, r );
+// return
+//
+// end bvnl
+//
+// function p = bvnu( dh, dk, r )
+//
+// A function for computing bivariate normal probabilities.
+// bvnu calculates the probability that x > dh and y > dk.
+// parameters
+// dh 1st lower integration limit
+// dk 2nd lower integration limit
+// r correlation coefficient
+//
+// Author
+// Alan Genz
+// Department of Mathematics
+// Washington State University
+// Pullman, Wa 99164-3113
+// Email : alangenz at wsu.edu
+//
+// This function is based on the method described by
+// Drezner, Z and G.O. Wesolowsky, (1989),
+// On the computation of the bivariate normal inegral,
+// Journal of Statist. Comput. Simul. 35, pp. 101-107,
+// with major modifications for double precision, for |r| close to 1,
+// and for matlab by Alan Genz - last modifications 7/98.
+// Note: to compute the probability that x < dh and y < dk, use
+// bvnu( -dh, -dk, r ).
+//
+*/
+
+ final double TWOPI = 2.0 * Math.PI;
+ final double sqrt2pi = 2.50662827463100050241; // sqrt(TWOPI)
+ double h, k, hk, hs, asr, sn, as, a, b, c, d, sp, rs, ep, bs, xs;
+ int i, lg, ng, is;
+
+ if (Math.abs (rho) < 0.3) {
+ ng = 0;
+ lg = 3;
+
+ } else if (Math.abs (rho) < 0.75) {
+ ng = 1;
+ lg = 6;
+
+ } else {
+ ng = 2;
+ lg = 10;
+ }
+
+ h = -x;
+ k = -y;
+ hk = h * k;
+ bvn = 0;
+ if (Math.abs (rho) < 0.925) {
+ hs = (h * h + k * k) / 2.0;
+ asr = Math.asin (rho);
+ for (i = 0; i < lg; ++i) {
+ sn = Math.sin (asr * (1.0 - X[ng][i]) / 2.0);
+ bvn += W[ng][i] * Math.exp ((sn * hk - hs) / (1.0 - sn * sn));
+ sn = Math.sin (asr * (1.0 + X[ng][i]) / 2.0);
+ bvn += W[ng][i] * Math.exp ((sn * hk - hs) / (1.0 - sn * sn));
+ }
+ bvn = bvn * asr /(4.0*Math.PI) +
+ NormalDist.cdf01 (-h) * NormalDist.cdf01 (-k);
+
+ } else {
+ if (rho < 0.0) {
+ k = -k;
+ hk = -hk;
+ }
+ if (Math.abs (rho) < 1.0) {
+ as = (1.0 - rho) * (1.0 + rho);
+ a = Math.sqrt (as);
+ bs = (h - k) * (h - k);
+ c = (4.0 - hk) / 8.0;
+ d = (12.0 - hk) / 16.0;
+ asr = -(bs / as + hk) / 2.0;
+ if (asr > -100.0)
+ bvn = a * Math.exp (asr) * (1.0 - c * (bs - as) * (1.0 -
+ d * bs / 5.0) / 3.0 + c * d * as * as / 5.0);
+
+ if (-hk < 100.0) {
+ b = Math.sqrt (bs);
+ sp = sqrt2pi * NormalDist.cdf01 (-b / a);
+ bvn = bvn - Math.exp (-hk / 2.0) * sp * b * (1.0 - c * bs * (1.0 -
+ d * bs / 5.0) / 3.0);
+ }
+ a = a / 2.0;
+ for (i = 0; i < lg; ++i) {
+ for (is = -1; is <= 1; is += 2) {
+ xs = (a * (is * X[ng][i] + 1.0));
+ xs = xs * xs;
+ rs = Math.sqrt (1.0 - xs);
+ asr = -(bs / xs + hk) / 2.0;
+ if (asr > -100.0) {
+ sp = (1.0 + c * xs * (1.0 + d * xs));
+ ep = Math.exp (-hk * (1.0 - rs) / (2.0 * (1.0 + rs))) / rs;
+ bvn += a * W[ng][i] * Math.exp (asr) * (ep - sp);
+ }
+ }
+ }
+ bvn = -bvn / TWOPI;
+ }
+ if (rho > 0.0) {
+ if (k > h)
+ h = k;
+ bvn += NormalDist.cdf01 (-h);
+ }
+ if (rho < 0.0) {
+ xs = NormalDist.cdf01(-h) - NormalDist.cdf01(-k);
+ if (xs < 0.0)
+ xs = 0.0;
+ bvn = -bvn + xs;
+ }
+ }
+ if (bvn <= 0.0)
+ return 0.0;
+ if (bvn >= 1.0)
+ return 1.0;
+ return bvn;
+
+ }
+
+
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double Z = (x - mu1)/sigma1;
+ double T = (y - mu2)/sigma2;
+ return cdf (Z, T, rho);
+ }
+
+ public double cdf (double x, double y) {
+ return cdf ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }
+
+ public double barF (double x, double y) {
+ return barF ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }
+
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double Z = (x - mu1)/sigma1;
+ double T = (y - mu2)/sigma2;
+ return barF (Z, T, rho);
+ }
+
+ public static double barF (double x, double y, double rho) {
+ return cdf (-x, -y, rho);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiNormalGenzDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalGenzDist.tex
new file mode 100644
index 0000000..5150fd9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiNormalGenzDist.tex
@@ -0,0 +1,317 @@
+\defmodule {BiNormalGenzDist}
+
+Extends the class \class{BiNormalDist} for the {\em bivariate
+normal\/} distribution\latex{ \cite[page 84]{tJOH72a}}
+ using Genz's algorithm as described in \cite{tGEN04a}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BiNormalGenzDist
+ * Description: bivariate normal distribution using Genz's algorithm
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+\end{hide}
+
+public class BiNormalGenzDist extends BiNormalDist \begin{hide} {
+
+ private static final double[][] W = {
+ // Gauss Legendre points and weights, n = 6
+ { 0.1713244923791705, 0.3607615730481384, 0.4679139345726904},
+
+ // Gauss Legendre points and weights, n = 12
+ { 0.4717533638651177e-1, 0.1069393259953183, 0.1600783285433464,
+ 0.2031674267230659, 0.2334925365383547, 0.2491470458134029 },
+
+ // Gauss Legendre points and weights, n = 20
+ { 0.1761400713915212e-1, 0.4060142980038694e-1, 0.6267204833410906e-1,
+ 0.8327674157670475e-1, 0.1019301198172404, 0.1181945319615184,
+ 0.1316886384491766, 0.1420961093183821, 0.1491729864726037,
+ 0.1527533871307259 }
+ };
+
+ private static final double[][] X = {
+ // Gauss Legendre points and weights, n = 6
+ { 0.9324695142031522, 0.6612093864662647, 0.2386191860831970},
+
+ // Gauss Legendre points and weights, n = 12
+ { 0.9815606342467191, 0.9041172563704750, 0.7699026741943050,
+ 0.5873179542866171, 0.3678314989981802, 0.1252334085114692 },
+
+ // Gauss Legendre points and weights, n = 20
+ { 0.9931285991850949, 0.9639719272779138, 0.9122344282513259,
+ 0.8391169718222188, 0.7463319064601508, 0.6360536807265150,
+ 0.5108670019508271, 0.3737060887154196, 0.2277858511416451,
+ 0.7652652113349733e-1 }
+ };\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BiNormalGenzDist (double rho) \begin{hide} {
+ super (rho);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{BiNormalGenzDist} object with default parameters
+ $\mu_1 = \mu_2 = 0$, $\sigma_1 = \sigma_2 = 1$ and correlation
+ $\rho = $\texttt{ rho}.
+ \end{tabb}
+\begin{code}
+
+ public BiNormalGenzDist (double mu1, double sigma1,
+ double mu2, double sigma2, double rho) \begin{hide} {
+ super (mu1, sigma1, mu2, sigma2, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{BiNormalGenzDist} object with parameters $\mu_1$ = \texttt{mu1},
+ $\mu_2$ = \texttt{mu2}, $\sigma_1$ = \texttt{sigma1}, $\sigma_2$ = \texttt{sigma2}
+ and $\rho$ = \texttt{rho}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double cdf (double x, double y, double rho) \begin{hide} {
+ double bvn = specialCDF (x, y, rho, 40.0);
+ if (bvn >= 0.0)
+ return bvn;
+
+/*
+// Copyright (C) 2005, Alan Genz, All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided 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 contributor name(s) 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.
+//
+// function p = bvnl( dh, dk, r )
+//
+// A function for computing bivariate normal probabilities.
+// bvnl calculates the probability that x < dh and y < dk.
+// parameters
+// dh 1st upper integration limit
+// dk 2nd upper integration limit
+// r correlation coefficient
+//
+// Author
+// Alan Genz
+// Department of Mathematics
+// Washington State University
+// Pullman, Wa 99164-3113
+// Email : alangenz at wsu.edu
+// This function is based on the method described by
+// Drezner, Z and G.O. Wesolowsky, (1989),
+// On the computation of the bivariate normal inegral,
+// Journal of Statist. Comput. Simul. 35, pp. 101-107,
+// with major modifications for double precision, for |r| close to 1,
+// and for matlab by Alan Genz - last modifications 7/98.
+//
+// p = bvnu( -dh, -dk, r );
+// return
+//
+// end bvnl
+//
+// function p = bvnu( dh, dk, r )
+//
+// A function for computing bivariate normal probabilities.
+// bvnu calculates the probability that x > dh and y > dk.
+// parameters
+// dh 1st lower integration limit
+// dk 2nd lower integration limit
+// r correlation coefficient
+//
+// Author
+// Alan Genz
+// Department of Mathematics
+// Washington State University
+// Pullman, Wa 99164-3113
+// Email : alangenz at wsu.edu
+//
+// This function is based on the method described by
+// Drezner, Z and G.O. Wesolowsky, (1989),
+// On the computation of the bivariate normal inegral,
+// Journal of Statist. Comput. Simul. 35, pp. 101-107,
+// with major modifications for double precision, for |r| close to 1,
+// and for matlab by Alan Genz - last modifications 7/98.
+// Note: to compute the probability that x < dh and y < dk, use
+// bvnu( -dh, -dk, r ).
+//
+*/
+
+ final double TWOPI = 2.0 * Math.PI;
+ final double sqrt2pi = 2.50662827463100050241; // sqrt(TWOPI)
+ double h, k, hk, hs, asr, sn, as, a, b, c, d, sp, rs, ep, bs, xs;
+ int i, lg, ng, is;
+
+ if (Math.abs (rho) < 0.3) {
+ ng = 0;
+ lg = 3;
+
+ } else if (Math.abs (rho) < 0.75) {
+ ng = 1;
+ lg = 6;
+
+ } else {
+ ng = 2;
+ lg = 10;
+ }
+
+ h = -x;
+ k = -y;
+ hk = h * k;
+ bvn = 0;
+ if (Math.abs (rho) < 0.925) {
+ hs = (h * h + k * k) / 2.0;
+ asr = Math.asin (rho);
+ for (i = 0; i < lg; ++i) {
+ sn = Math.sin (asr * (1.0 - X[ng][i]) / 2.0);
+ bvn += W[ng][i] * Math.exp ((sn * hk - hs) / (1.0 - sn * sn));
+ sn = Math.sin (asr * (1.0 + X[ng][i]) / 2.0);
+ bvn += W[ng][i] * Math.exp ((sn * hk - hs) / (1.0 - sn * sn));
+ }
+ bvn = bvn * asr /(4.0*Math.PI) +
+ NormalDist.cdf01 (-h) * NormalDist.cdf01 (-k);
+
+ } else {
+ if (rho < 0.0) {
+ k = -k;
+ hk = -hk;
+ }
+ if (Math.abs (rho) < 1.0) {
+ as = (1.0 - rho) * (1.0 + rho);
+ a = Math.sqrt (as);
+ bs = (h - k) * (h - k);
+ c = (4.0 - hk) / 8.0;
+ d = (12.0 - hk) / 16.0;
+ asr = -(bs / as + hk) / 2.0;
+ if (asr > -100.0)
+ bvn = a * Math.exp (asr) * (1.0 - c * (bs - as) * (1.0 -
+ d * bs / 5.0) / 3.0 + c * d * as * as / 5.0);
+
+ if (-hk < 100.0) {
+ b = Math.sqrt (bs);
+ sp = sqrt2pi * NormalDist.cdf01 (-b / a);
+ bvn = bvn - Math.exp (-hk / 2.0) * sp * b * (1.0 - c * bs * (1.0 -
+ d * bs / 5.0) / 3.0);
+ }
+ a = a / 2.0;
+ for (i = 0; i < lg; ++i) {
+ for (is = -1; is <= 1; is += 2) {
+ xs = (a * (is * X[ng][i] + 1.0));
+ xs = xs * xs;
+ rs = Math.sqrt (1.0 - xs);
+ asr = -(bs / xs + hk) / 2.0;
+ if (asr > -100.0) {
+ sp = (1.0 + c * xs * (1.0 + d * xs));
+ ep = Math.exp (-hk * (1.0 - rs) / (2.0 * (1.0 + rs))) / rs;
+ bvn += a * W[ng][i] * Math.exp (asr) * (ep - sp);
+ }
+ }
+ }
+ bvn = -bvn / TWOPI;
+ }
+ if (rho > 0.0) {
+ if (k > h)
+ h = k;
+ bvn += NormalDist.cdf01 (-h);
+ }
+ if (rho < 0.0) {
+ xs = NormalDist.cdf01(-h) - NormalDist.cdf01(-k);
+ if (xs < 0.0)
+ xs = 0.0;
+ bvn = -bvn + xs;
+ }
+ }
+ if (bvn <= 0.0)
+ return 0.0;
+ if (bvn >= 1.0)
+ return 1.0;
+ return bvn;
+
+ }\end{hide}
+\end{code}
+\begin{tabb}
+% Same as
+% \method{cdf}{double,double,double,double,double,double,double}~{\tt
+% (0, 1, x, 0, 1, y, rho)}.
+ Computes the standard {\em binormal\/} distribution (\ref{eq:cdf2binormal})
+ with the method described in \cite{tGEN04a}. The code for the \texttt{cdf}
+ was translated directly from the Matlab code written by Alan Genz
+ and available from his web page at
+ \url{http://www.math.wsu.edu/faculty/genz/homepage}
+ (the code is copyrighted by Alan Genz
+ and is included in this package with the kind permission of the author).
+ The absolute error is expected to be smaller than $0.5 \cdot 10^{-15}$.
+ \end{tabb}
+\begin{code} \begin{hide}
+
+ public static double cdf (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double Z = (x - mu1)/sigma1;
+ double T = (y - mu2)/sigma2;
+ return cdf (Z, T, rho);
+ }
+
+ public double cdf (double x, double y) {
+ return cdf ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }
+
+ public double barF (double x, double y) {
+ return barF ((x-mu1)/sigma1, (y-mu2)/sigma2, rho);
+ }
+
+ public static double barF (double mu1, double sigma1, double x,
+ double mu2, double sigma2, double y,
+ double rho) {
+ if (sigma1 <= 0)
+ throw new IllegalArgumentException ("sigma1 <= 0");
+ if (sigma2 <= 0)
+ throw new IllegalArgumentException ("sigma2 <= 0");
+ double Z = (x - mu1)/sigma1;
+ double T = (y - mu2)/sigma2;
+ return barF (Z, T, rho);
+ }
+
+ public static double barF (double x, double y, double rho) {
+ return cdf (-x, -y, rho);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiStudentDist.java b/source/umontreal/iro/lecuyer/probdistmulti/BiStudentDist.java
new file mode 100644
index 0000000..c053b20
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiStudentDist.java
@@ -0,0 +1,327 @@
+
+
+/*
+ * Class: BiStudentDist
+ * Description: standard bivariate Student t-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.probdist.StudentDist;
+
+
+/**
+ * Extends the class {@link ContinuousDistribution2Dim} for the <EM> standard bivariate Student's <SPAN CLASS="MATH"><I>t</I></SPAN></EM> distribution.
+ * The correlation between <SPAN CLASS="MATH"><I>X</I></SPAN> and <SPAN CLASS="MATH"><I>Y</I></SPAN> is <SPAN CLASS="MATH"><I>r</I></SPAN>
+ * and the number of degrees of freedom is <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ * Its probability density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>, <I>y</I>) = (1 + (<I>x</I><SUP>2</SUP> -2<I>ρxy</I> + <I>y</I><SUP>2</SUP>)/(<I>ν</I>(1 - <I>ρ</I><SUP>2</SUP>)))<SUP>-(<I>ν</I>+2)/2</SUP>/(2<I>π</I>(1-r^2)<SUP>1/2</SUP>),
+ * </DIV><P></P>
+ * and the corresponding distribution function (the <TT>cdf</TT>) is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>T</I><SUB><I>ν</I></SUB>(<I>x</I>, <I>y</I>, <I>r</I>) = ∫<SUB>-∞</SUB><SUP>x</SUP><I>dx</I>∫<SUB>-∞</SUB><SUP>y</SUP><I>dy</I> <I>f</I> (<I>x</I>, <I>y</I>)/(2<I>π</I>(1 - r^2)<SUP>1/2</SUP>).
+ * </DIV><P></P>
+ * We also define the upper distribution function called <TT>barF</TT> as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * bar(T)<SUB><I>ν</I></SUB>(<I>x</I>, <I>y</I>, <I>r</I>) = ∫<SUP>∞</SUP><SUB>x</SUB><I>dx</I>∫<SUP>∞</SUP><SUB>y</SUB><I>dy</I> <I>f</I> (<I>x</I>, <I>y</I>)/(2<I>π</I>(1 - r^2)<SUP>1/2</SUP>).
+ * </DIV><P></P>
+ *
+ */
+public class BiStudentDist extends ContinuousDistribution2Dim {
+ protected int nu; // Number of degrees of freedom
+ protected double rho;
+ protected double facRho; // sqrt(1 - rho^2)
+
+
+
+
+ /**
+ * Constructs a <TT>BiStudentDist</TT> object with correlation <SPAN CLASS="MATH"><I>ρ</I> =</SPAN><TT> rho</TT>
+ * and <SPAN CLASS="MATH"><I>ν</I></SPAN> = <TT>nu</TT> degrees of freedom.
+ *
+ */
+ public BiStudentDist (int nu, double rho) {
+ setParams (nu, rho);
+ }
+
+
+ public double density (double x, double y) {
+ if (Math.abs(rho) == 1.)
+ throw new IllegalArgumentException ("|rho| = 1");
+ double T = 1.0 + (x*x - 2.0*rho*x*y + y*y) / (nu*facRho*facRho);
+ return 1.0 / (Math.pow (T, (nu + 2)/2.0) * (2.0*Math.PI*facRho));
+ }
+
+ public double cdf (double x, double y) {
+ return cdf (nu, x, y, rho);
+ }
+
+ public double barF (double x, double y) {
+ return barF (nu, x, y, rho);
+ }
+
+
+ /**
+ * Computes the standard bivariate Student's <SPAN CLASS="MATH"><I>t</I></SPAN> density function with correlation <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT> and
+ * <SPAN CLASS="MATH"><I>ν</I></SPAN> = <TT>nu</TT> degrees of freedom.
+ *
+ */
+ public static double density (int nu, double x, double y, double rho) {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) >= 1.)
+ throw new IllegalArgumentException ("|rho| >= 1");
+ double fRho = (1.0 - rho)*(1.0 + rho);
+ double T = 1.0 + (x*x - 2.0*rho*x*y + y*y) / (nu*fRho);
+ return 1.0 / (Math.pow (T, (nu + 2)/2.0) * (2.0*Math.PI*Math.sqrt(fRho)));
+ }
+
+
+ /**
+ * Computes the standard bivariate Student's <SPAN CLASS="MATH"><I>t</I></SPAN> distribution
+ * using the method described in. The code for the <TT>cdf</TT>
+ * was translated directly from the Matlab code written by Alan Genz
+ * and available from his web page at
+ * <TT><A NAME="tex2html1"
+ * HREF="http://www.math.wsu.edu/faculty/genz/homepage">http://www.math.wsu.edu/faculty/genz/homepage</A></TT> (the code is copyrighted by Alan Genz
+ * and is included in this package with the kind permission of the author).
+ * The correlation is <SPAN CLASS="MATH"><I>ρ</I> =</SPAN><TT> rho</TT> and the number of
+ * degrees of freedom is <SPAN CLASS="MATH"><I>ν</I></SPAN> = <TT>nu</TT>.
+ *
+ */
+ public static double cdf (int nu, double x, double y, double rho) {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+// This function is translated from Alan Genz's Matlab code.
+/*
+% function p = bvtl( nu, dh, dk, r )
+%
+% A function for computing bivariate t probabilities.
+% bvtl calculates the probability that x < dh and y < dk;
+% parameters
+% nu number of degrees of freedom
+% dh 1st upper integration limit
+% dk 2nd upper integration limit
+% r correlation coefficient
+%
+% This function is based on the method described by
+% Dunnett, C.W. and M. Sobel, (1954),
+% A bivariate generalization of Student's t-distribution
+% with tables for certain special cases,
+% Biometrika 41, pp. 153-169,
+%
+% Alan Genz
+% Department of Mathematics
+% Washington State University
+% Pullman, Wa 99164-3113
+% Email : alangenz at wsu.edu
+%
+*/
+ final double dh = x;
+ final double dk = y;
+ final double eps = 1.0e-15;
+ final double tpi = 2.*Math.PI;
+ double hrk, krh, bvt, snu;
+ double gmph, gmpk, xnkh, xnhk, qhrk, hkn, hpk, hkrn;
+ double btnckh, btnchk, btpdkh, btpdhk;
+
+ if (1. - rho <= eps) {
+ x = Math.min (dh, dk);
+ return StudentDist.cdf (nu, x);
+ }
+ if (rho + 1. <= eps) {
+ if (dh > -dk)
+ return StudentDist.cdf(nu, dh) - StudentDist.cdf (nu, -dk);
+ else
+ return 0.;
+ }
+ final double ors = (1. - rho)*(1. + rho);
+ hrk = dh - rho*dk; krh = dk - rho*dh;
+ if (Math.abs(hrk) + ors > 0.) {
+ xnhk = hrk*hrk/( hrk*hrk + ors*( nu + dk*dk));
+ xnkh = krh*krh/( krh*krh + ors*( nu + dh*dh));
+ } else {
+ xnhk = 0.;
+ xnkh = 0.;
+ }
+ int hs, ks, j;
+ if (dh - rho*dk > 0.)
+ hs = 1;
+ else if (dh - rho*dk < 0.)
+ hs = -1;
+ else
+ hs = 0;
+ if (dk - rho*dh > 0.)
+ ks = 1;
+ else if (dk - rho*dh < 0.)
+ ks = -1;
+ else
+ ks = 0;
+ if (nu % 2 == 0) {
+ bvt = Math.atan2 (Math.sqrt(ors), -rho)/tpi;
+ gmph = dh/Math.sqrt (16.*( nu + dh*dh));
+ gmpk = dk/Math.sqrt (16.*( nu + dk*dk));
+ btnckh = 2.*Math.atan2 (Math.sqrt(xnkh), Math.sqrt(1. - xnkh))/Math.PI;
+ btpdkh = 2.*Math.sqrt( xnkh*( 1. - xnkh))/Math.PI;
+ btnchk = 2.*Math.atan2( Math.sqrt(xnhk), Math.sqrt(1. - xnhk))/Math.PI;
+ btpdhk = 2.*Math.sqrt( xnhk*( 1. - xnhk))/Math.PI;
+ for (j = 1; j <= nu/2; ++j) {
+ bvt = bvt + gmph*( 1. + ks*btnckh);
+ bvt = bvt + gmpk*( 1. + hs*btnchk);
+ btnckh = btnckh + btpdkh;
+ btpdkh = 2*j*btpdkh*( 1. - xnkh )/(2*j+1);
+ btnchk = btnchk + btpdhk;
+ btpdhk = 2*j*btpdhk*( 1. - xnhk )/(2*j+1);
+ gmph = gmph*( j - 0.5 )/( j*( 1. + dh*dh/nu));
+ gmpk = gmpk*( j - 0.5 )/( j*( 1. + dk*dk/nu));
+ }
+
+ } else {
+ qhrk = Math.sqrt( dh*dh + dk*dk - 2.*rho*dh*dk + nu*ors);
+ hkrn = dh*dk + rho*nu; hkn = dh*dk - nu; hpk = dh + dk;
+ bvt = Math.atan2( -Math.sqrt(nu)*(hkn*qhrk+hpk*hkrn),
+ hkn*hkrn-nu*hpk*qhrk )/tpi;
+ if (bvt < -10.*eps)
+ bvt = bvt + 1;
+ gmph = dh/( tpi*Math.sqrt(nu)*( 1. + dh*dh/nu));
+ gmpk = dk/( tpi*Math.sqrt(nu)*( 1. + dk*dk/nu));
+ btnckh = Math.sqrt( xnkh ); btpdkh = btnckh;
+ btnchk = Math.sqrt( xnhk ); btpdhk = btnchk;
+ for (j = 1; j <= (nu - 1)/2; ++j) {
+ bvt = bvt + gmph*( 1. + ks*btnckh );
+ bvt = bvt + gmpk*( 1. + hs*btnchk );
+ btpdkh = (2*j-1)*btpdkh*( 1. - xnkh )/(2*j);
+ btnckh = btnckh + btpdkh;
+ btpdhk = (2*j-1)*btpdhk*( 1. - xnhk )/(2*j);
+ btnchk = btnchk + btpdhk;
+ gmph = gmph*j/(( j + 0.5 )*( 1. + dh*dh/nu));
+ gmpk = gmpk*j/(( j + 0.5 )*( 1. + dk*dk/nu));
+ }
+ }
+ return bvt;
+ }
+
+
+ /**
+ * Computes the standard upper bivariate Student's <SPAN CLASS="MATH"><I>t</I></SPAN> distribution.
+ *
+ */
+ public static double barF (int nu, double x, double y, double rho)
+ {
+ double u = 1.0 + cdf (nu, x, y, rho) - cdf (nu, XBIG, y, rho) -
+ cdf (nu, x, XBIG, rho);
+ final double eps = 1.0e-15;
+ if (u < eps) return 0.0;
+ if (u <= 1.0) return u;
+ return 1.0;
+ }
+
+
+ public double[] getMean() {
+ return getMean (nu, rho);
+ }
+
+ /**
+ * Returns the mean vector
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = (0, 0)</SPAN> of the bivariate Student's <SPAN CLASS="MATH"><I>t</I></SPAN> distribution.
+ *
+ */
+ public static double[] getMean (int nu, double rho) {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double mean[] = new double[2];
+
+ mean[0] = 0;
+ mean[1] = 0;
+
+ return mean;
+ }
+
+
+ public double[][] getCovariance() {
+ return getCovariance (nu, rho);
+ }
+
+ /**
+ * Returns the covariance matrix of the bivariate Student's <SPAN CLASS="MATH"><I>t</I></SPAN> distribution.
+ *
+ */
+ public static double[][] getCovariance (int nu, double rho) {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double cov[][] = new double[2][2];
+
+ double coeff = (double) nu / ((double) nu - 2.0);
+
+ cov[0][0] = coeff;
+ cov[0][1] = coeff * rho;
+ cov[1][0] = coeff * rho;
+ cov[1][1] = coeff;
+
+ return cov;
+ }
+
+
+ public double[][] getCorrelation() {
+ return getCovariance (nu, rho);
+ }
+
+ /**
+ * Returns the correlation matrix of the bivariate Student's <SPAN CLASS="MATH"><I>t</I></SPAN> distribution.
+ *
+ */
+ public static double[][] getCorrelation (int nu, double rho) {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double corr[][] = new double[2][2];
+
+ corr[0][0] = 1.0;
+ corr[0][1] = rho;
+ corr[1][0] = rho;
+ corr[1][1] = 1.0;
+
+ return corr;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>ν</I></SPAN> = <TT>nu</TT> and
+ * <SPAN CLASS="MATH"><I>ρ</I></SPAN> = <TT>rho</TT> of this object.
+ *
+ */
+ protected void setParams (int nu, double rho) {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+ this.dimension = 2;
+ this.nu = nu;
+ this.rho = rho;
+ facRho = Math.sqrt((1.0 - rho)*(1.0 + rho));
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/BiStudentDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/BiStudentDist.tex
new file mode 100644
index 0000000..eb0cb8c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/BiStudentDist.tex
@@ -0,0 +1,357 @@
+\defmodule {BiStudentDist}
+
+Extends the class \class{ContinuousDistribution2Dim} for the {\em
+ standard bivariate Student's $t$\/} distribution\latex{ \cite[page 132]{tJOH72a}}.
+ The correlation between $X$ and $Y$ is \latex{$\rho$}\html{$r$}
+ and the number of degrees of freedom is $\nu$.
+Its probability density is
+\eq
+ f (x, y) =
+ \latex{\frac{1}{2\pi\sqrt{1-\rho^2}}\left[1 + \frac{x^2
+ - 2\rho x y + y^2}{\nu(1-\rho^2)}\right]^{-(\nu + 2)/2}}
+ \html{\left(1 + (x^2 - 2\rho x y + y^2) /(\nu(1-\rho^2))\right)^{-(\nu + 2)/2}/
+ (2\pi\sqrt{1-r^2})}, \eqlabel{eq:pdf1bit}
+\endeq
+and the corresponding distribution function (the \texttt{cdf}) is
+\begin{latexonly}
+\eq
+ T_\nu(x, y, \rho) =
+ \frac{1}{2\pi\sqrt{1-\rho^2}} \int_{-\infty}^x dx
+ \int_{-\infty}^y dy\, f (x, y). \eqlabel{eq:cdf1bit}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ T_\nu(x, y, r) =
+ \int_{-\infty}^x dx \int_{-\infty}^y dy\, f(x,y) /(2\pi\sqrt{1 - r^{2}}).
+\endeq
+\end{htmlonly}
+We also define the upper distribution function called \texttt{barF} as
+\begin{latexonly}
+\eq
+ \overline T_\nu(x, y, \rho) =
+ \frac{1}{2\pi\sqrt{1-\rho^2}} \int^{\infty}_x dx
+ \int^{\infty}_y dy\, f(x,y). \eqlabel{eq:cdf2bit}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ \overline T_\nu(x, y, r) =
+ \int^{\infty}_x dx \int^{\infty}_y dy\, f(x,y) /(2\pi\sqrt{1 - r^2}).
+\endeq
+\end{htmlonly}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BiStudentDist
+ * Description: standard bivariate Student t-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.StudentDist;
+\end{hide}
+
+public class BiStudentDist extends ContinuousDistribution2Dim \begin{hide} {
+ protected int nu; // Number of degrees of freedom
+ protected double rho;
+ protected double facRho; // sqrt(1 - rho^2)
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public BiStudentDist (int nu, double rho) \begin{hide} {
+ setParams (nu, rho);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a \texttt{BiStudentDist} object with correlation $\rho = $\texttt{ rho}
+ and $\nu$ = \texttt{nu} degrees of freedom.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double density (double x, double y) {
+ if (Math.abs(rho) == 1.)
+ throw new IllegalArgumentException ("|rho| = 1");
+ double T = 1.0 + (x*x - 2.0*rho*x*y + y*y) / (nu*facRho*facRho);
+ return 1.0 / (Math.pow (T, (nu + 2)/2.0) * (2.0*Math.PI*facRho));
+ }
+
+ public double cdf (double x, double y) {
+ return cdf (nu, x, y, rho);
+ }
+
+ public double barF (double x, double y) {
+ return barF (nu, x, y, rho);
+ }
+\end{hide}
+
+ public static double density (int nu, double x, double y, double rho) \begin{hide} {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) >= 1.)
+ throw new IllegalArgumentException ("|rho| >= 1");
+ double fRho = (1.0 - rho)*(1.0 + rho);
+ double T = 1.0 + (x*x - 2.0*rho*x*y + y*y) / (nu*fRho);
+ return 1.0 / (Math.pow (T, (nu + 2)/2.0) * (2.0*Math.PI*Math.sqrt(fRho)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the standard bivariate Student's $t$ density function
+ (\ref{eq:pdf1bit}) with correlation $\rho$ = \texttt{rho} and
+ $\nu$ = \texttt{nu} degrees of freedom.
+\end{tabb}
+\begin{code}
+
+ public static double cdf (int nu, double x, double y, double rho) \begin{hide} {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+// This function is translated from Alan Genz's Matlab code.
+/*
+% function p = bvtl( nu, dh, dk, r )
+%
+% A function for computing bivariate t probabilities.
+% bvtl calculates the probability that x < dh and y < dk;
+% parameters
+% nu number of degrees of freedom
+% dh 1st upper integration limit
+% dk 2nd upper integration limit
+% r correlation coefficient
+%
+% This function is based on the method described by
+% Dunnett, C.W. and M. Sobel, (1954),
+% A bivariate generalization of Student's t-distribution
+% with tables for certain special cases,
+% Biometrika 41, pp. 153-169,
+%
+% Alan Genz
+% Department of Mathematics
+% Washington State University
+% Pullman, Wa 99164-3113
+% Email : alangenz at wsu.edu
+%
+*/
+ final double dh = x;
+ final double dk = y;
+ final double eps = 1.0e-15;
+ final double tpi = 2.*Math.PI;
+ double hrk, krh, bvt, snu;
+ double gmph, gmpk, xnkh, xnhk, qhrk, hkn, hpk, hkrn;
+ double btnckh, btnchk, btpdkh, btpdhk;
+
+ if (1. - rho <= eps) {
+ x = Math.min (dh, dk);
+ return StudentDist.cdf (nu, x);
+ }
+ if (rho + 1. <= eps) {
+ if (dh > -dk)
+ return StudentDist.cdf(nu, dh) - StudentDist.cdf (nu, -dk);
+ else
+ return 0.;
+ }
+ final double ors = (1. - rho)*(1. + rho);
+ hrk = dh - rho*dk; krh = dk - rho*dh;
+ if (Math.abs(hrk) + ors > 0.) {
+ xnhk = hrk*hrk/( hrk*hrk + ors*( nu + dk*dk));
+ xnkh = krh*krh/( krh*krh + ors*( nu + dh*dh));
+ } else {
+ xnhk = 0.;
+ xnkh = 0.;
+ }
+ int hs, ks, j;
+ if (dh - rho*dk > 0.)
+ hs = 1;
+ else if (dh - rho*dk < 0.)
+ hs = -1;
+ else
+ hs = 0;
+ if (dk - rho*dh > 0.)
+ ks = 1;
+ else if (dk - rho*dh < 0.)
+ ks = -1;
+ else
+ ks = 0;
+ if (nu % 2 == 0) {
+ bvt = Math.atan2 (Math.sqrt(ors), -rho)/tpi;
+ gmph = dh/Math.sqrt (16.*( nu + dh*dh));
+ gmpk = dk/Math.sqrt (16.*( nu + dk*dk));
+ btnckh = 2.*Math.atan2 (Math.sqrt(xnkh), Math.sqrt(1. - xnkh))/Math.PI;
+ btpdkh = 2.*Math.sqrt( xnkh*( 1. - xnkh))/Math.PI;
+ btnchk = 2.*Math.atan2( Math.sqrt(xnhk), Math.sqrt(1. - xnhk))/Math.PI;
+ btpdhk = 2.*Math.sqrt( xnhk*( 1. - xnhk))/Math.PI;
+ for (j = 1; j <= nu/2; ++j) {
+ bvt = bvt + gmph*( 1. + ks*btnckh);
+ bvt = bvt + gmpk*( 1. + hs*btnchk);
+ btnckh = btnckh + btpdkh;
+ btpdkh = 2*j*btpdkh*( 1. - xnkh )/(2*j+1);
+ btnchk = btnchk + btpdhk;
+ btpdhk = 2*j*btpdhk*( 1. - xnhk )/(2*j+1);
+ gmph = gmph*( j - 0.5 )/( j*( 1. + dh*dh/nu));
+ gmpk = gmpk*( j - 0.5 )/( j*( 1. + dk*dk/nu));
+ }
+
+ } else {
+ qhrk = Math.sqrt( dh*dh + dk*dk - 2.*rho*dh*dk + nu*ors);
+ hkrn = dh*dk + rho*nu; hkn = dh*dk - nu; hpk = dh + dk;
+ bvt = Math.atan2( -Math.sqrt(nu)*(hkn*qhrk+hpk*hkrn),
+ hkn*hkrn-nu*hpk*qhrk )/tpi;
+ if (bvt < -10.*eps)
+ bvt = bvt + 1;
+ gmph = dh/( tpi*Math.sqrt(nu)*( 1. + dh*dh/nu));
+ gmpk = dk/( tpi*Math.sqrt(nu)*( 1. + dk*dk/nu));
+ btnckh = Math.sqrt( xnkh ); btpdkh = btnckh;
+ btnchk = Math.sqrt( xnhk ); btpdhk = btnchk;
+ for (j = 1; j <= (nu - 1)/2; ++j) {
+ bvt = bvt + gmph*( 1. + ks*btnckh );
+ bvt = bvt + gmpk*( 1. + hs*btnchk );
+ btpdkh = (2*j-1)*btpdkh*( 1. - xnkh )/(2*j);
+ btnckh = btnckh + btpdkh;
+ btpdhk = (2*j-1)*btpdhk*( 1. - xnhk )/(2*j);
+ btnchk = btnchk + btpdhk;
+ gmph = gmph*j/(( j + 0.5 )*( 1. + dh*dh/nu));
+ gmpk = gmpk*j/(( j + 0.5 )*( 1. + dk*dk/nu));
+ }
+ }
+ return bvt;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the standard bivariate Student's $t$ distribution (\ref{eq:cdf1bit})
+ using the method described in \cite{tGEN04a}. The code for the \texttt{cdf}
+ was translated directly from the Matlab code written by Alan Genz
+ and available from his web page at
+ \url{http://www.math.wsu.edu/faculty/genz/homepage}
+ (the code is copyrighted by Alan Genz
+ and is included in this package with the kind permission of the author).
+ The correlation is $\rho = $\texttt{ rho} and the number of
+ degrees of freedom is $\nu$ = \texttt{nu}.
+ % The absolute error is expected to be smaller than $10^{-15}$.
+\end{tabb}
+\begin{code}
+
+ public static double barF (int nu, double x, double y, double rho) \begin{hide}
+ {
+ double u = 1.0 + cdf (nu, x, y, rho) - cdf (nu, XBIG, y, rho) -
+ cdf (nu, x, XBIG, rho);
+ final double eps = 1.0e-15;
+ if (u < eps) return 0.0;
+ if (u <= 1.0) return u;
+ return 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the standard upper bivariate Student's $t$ distribution
+ (\ref{eq:cdf2bit}).
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ public double[] getMean() {
+ return getMean (nu, rho);
+ }\end{hide}
+
+ public static double[] getMean (int nu, double rho)\begin{hide} {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double mean[] = new double[2];
+
+ mean[0] = 0;
+ mean[1] = 0;
+
+ return mean;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the mean vector $E[X] = (0, 0)$ of the bivariate Student's $t$ distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double[][] getCovariance() {
+ return getCovariance (nu, rho);
+ }\end{hide}
+
+ public static double[][] getCovariance (int nu, double rho)\begin{hide} {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double cov[][] = new double[2][2];
+
+ double coeff = (double) nu / ((double) nu - 2.0);
+
+ cov[0][0] = coeff;
+ cov[0][1] = coeff * rho;
+ cov[1][0] = coeff * rho;
+ cov[1][1] = coeff;
+
+ return cov;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the covariance matrix of the bivariate Student's $t$ distribution.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double[][] getCorrelation() {
+ return getCovariance (nu, rho);
+ }\end{hide}
+
+ public static double[][] getCorrelation (int nu, double rho)\begin{hide} {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+
+ double corr[][] = new double[2][2];
+
+ corr[0][0] = 1.0;
+ corr[0][1] = rho;
+ corr[1][0] = rho;
+ corr[1][1] = 1.0;
+
+ return corr;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the correlation matrix of the bivariate Student's $t$ distribution.
+\end{tabb}
+\begin{code}
+
+ protected void setParams (int nu, double rho) \begin{hide} {
+ if (nu < 1)
+ throw new IllegalArgumentException ("nu < 1");
+ if (Math.abs(rho) > 1.)
+ throw new IllegalArgumentException ("|rho| > 1");
+ this.dimension = 2;
+ this.nu = nu;
+ this.rho = rho;
+ facRho = Math.sqrt((1.0 - rho)*(1.0 + rho));
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\nu$ = \texttt{nu} and
+ $\rho$ = \texttt{rho} of this object.
+ \end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistribution2Dim.java b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistribution2Dim.java
new file mode 100644
index 0000000..32f38c9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistribution2Dim.java
@@ -0,0 +1,175 @@
+
+
+/*
+ * Class: ContinuousDistribution2Dim
+ * Description: Mother class 2-dimensional continuous distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Classes implementing 2-dimensional continuous distributions should inherit
+ * from this class.
+ * Such distributions are characterized by a <SPAN CLASS="textit">density</SPAN> function <SPAN CLASS="MATH"><I>f</I> (<I>x</I>, <I>y</I>)</SPAN>;
+ * thus the signature of a <TT>density</TT> method is supplied here.
+ * This class also provides a default implementation of
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>, <I>y</I>)</SPAN>,
+ * the upper <TT>CDF</TT>. The inverse function <SPAN CLASS="MATH"><I>F</I><SUP>-1</SUP>(<I>u</I>)</SPAN> represents a curve
+ * <SPAN CLASS="MATH"><I>y</I> = <I>h</I>(<I>x</I>)</SPAN> of constant <SPAN CLASS="MATH"><I>u</I></SPAN> and it is not implemented.
+ *
+ */
+public abstract class ContinuousDistribution2Dim
+ extends ContinuousDistributionMulti {
+
+ /**
+ * Defines the target number of decimals of accuracy when
+ * approximating a distribution function, but there is <SPAN CLASS="textit">no guarantee</SPAN> that
+ * this target is always attained.
+ *
+ */
+ public int decPrec = 15;
+
+
+ // x infinity for some distributions
+ protected static final double XINF = Double.MAX_VALUE;
+
+ // x infinity for some distributions
+ protected static final double XBIG = 1000.0;
+
+ // EPSARRAY[j]: Epsilon required for j decimal degits of precision
+ protected static final double[] EPSARRAY = {
+ 0.5, 0.5E-1, 0.5E-2, 0.5E-3, 0.5E-4, 0.5E-5, 0.5E-6, 0.5E-7, 0.5E-8,
+ 0.5E-9, 0.5E-10, 0.5E-11, 0.5E-12, 0.5E-13, 0.5E-14, 0.5E-15, 0.5E-16,
+ 0.5E-17, 0.5E-18, 0.5E-19, 0.5E-20, 0.5E-21, 0.5E-22, 0.5E-23, 0.5E-24,
+ 0.5E-25, 0.5E-26, 0.5E-27, 0.5E-28, 0.5E-29, 0.5E-30, 0.5E-31, 0.5E-32,
+ 0.5E-33, 0.5E-34, 0.5E-35
+ };
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>f</I> (<I>x</I>, <I>y</I>)</SPAN>, the density of <SPAN CLASS="MATH">(<I>X</I>, <I>Y</I>)</SPAN> evaluated at <SPAN CLASS="MATH">(<I>x</I>, <I>y</I>)</SPAN>.
+ *
+ * @param x value <SPAN CLASS="MATH"><I>x</I></SPAN> at which the density is evaluated
+ *
+ * @param y value <SPAN CLASS="MATH"><I>y</I></SPAN> at which the density is evaluated
+ *
+ * @return density function evaluated at <SPAN CLASS="MATH">(<I>x</I>, <I>y</I>)</SPAN>
+ *
+ */
+ public abstract double density (double x, double y);
+
+
+ /**
+ * Simply calls <TT>density (x[0], x[1])</TT>.
+ *
+ * @param x point
+ * <SPAN CLASS="MATH">(<I>x</I>[0], <I>x</I>[1])</SPAN> at which the density is evaluated
+ *
+ * @return density function evaluated at
+ * <SPAN CLASS="MATH">(<I>x</I>[0], <I>x</I>[1])</SPAN>
+ *
+ */
+ public double density (double[] x) {
+ if (x.length != 2)
+ throw new IllegalArgumentException("x must be in dimension 2");
+
+ return density (x[0], x[1]);
+ }
+
+
+ /**
+ * .
+ *
+ * Computes the distribution function <SPAN CLASS="MATH"><I>F</I>(<I>x</I>, <I>y</I>)</SPAN>:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I>, <I>y</I>) = <I>P</I>[<I>X</I> <= <I>x</I>, <I>Y</I> <= <I>y</I>] = ∫<SUB>-∞</SUB><SUP>x</SUP><I>ds</I>∫<SUB>-∞</SUB><SUP>y</SUP><I>dt</I> <I>f</I> (<I>s</I>, <I>t</I>).
+ * </DIV><P></P>
+ *
+ * @param x value <SPAN CLASS="MATH"><I>x</I></SPAN> at which the distribution function is evaluated
+ *
+ * @param y value <SPAN CLASS="MATH"><I>y</I></SPAN> at which the distribution function is evaluated
+ *
+ * @return distribution function evaluated at <SPAN CLASS="MATH">(<I>x</I>, <I>y</I>)</SPAN>
+ *
+ */
+ public abstract double cdf (double x, double y);
+
+ /**
+ * .
+ *
+ * Computes the upper cumulative distribution function
+ *
+ * <SPAN CLASS="MATH">bar(F)(<I>x</I>, <I>y</I>)</SPAN>:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * bar(F)(<I>x</I>, <I>y</I>) = <I>P</I>[<I>X</I> >= <I>x</I>, <I>Y</I> >= <I>y</I>] = ∫<SUP>∞</SUP><SUB>x</SUB><I>ds</I>∫<SUP>∞</SUP><SUB>y</SUB><I>dt</I> <I>f</I> (<I>s</I>, <I>t</I>).
+ * </DIV><P></P>
+ *
+ * @param x value <SPAN CLASS="MATH"><I>x</I></SPAN> at which the upper distribution is evaluated
+ *
+ * @param y value <SPAN CLASS="MATH"><I>y</I></SPAN> at which the upper distribution is evaluated
+ *
+ * @return upper distribution function evaluated at <SPAN CLASS="MATH">(<I>x</I>, <I>y</I>)</SPAN>
+ *
+ */
+ public double barF (double x, double y) {
+ double u = 1.0 + cdf (x, y) - cdf (XINF, y) - cdf (x, XINF);
+ if (u <= 0.0) return 0.0;
+ if (u >= 1.0) return 1.0;
+ return u;
+ }
+
+
+ /**
+ * .
+ *
+ * Computes the cumulative probability in the square region
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>[<I>a</I><SUB>1</SUB> <= <I>X</I> <= <I>b</I><SUB>1</SUB>, <I>a</I><SUB>2</SUB> <= <I>Y</I> <= <I>b</I><SUB>2</SUB>] = ∫<SUB>a<SUB>1</SUB></SUB><SUP>b<SUB>1</SUB></SUP><I>dx</I>∫<SUB>a<SUB>2</SUB></SUB><SUP>b<SUB>2</SUB></SUP><I>dy</I> <I>f</I> (<I>x</I>, <I>y</I>).
+ * </DIV><P></P>
+ *
+ * @param a1 <SPAN CLASS="MATH"><I>x</I></SPAN> lower limit of the square
+ *
+ * @param a2 <SPAN CLASS="MATH"><I>y</I></SPAN> lower limit of the square
+ *
+ * @param b1 <SPAN CLASS="MATH"><I>x</I></SPAN> upper limit of the square
+ *
+ * @param b2 <SPAN CLASS="MATH"><I>y</I></SPAN> upper limit of the square
+ *
+ * @return the cumulative probability in the square region
+ *
+ */
+ public double cdf (double a1, double a2, double b1, double b2) {
+ if (a1 >= b1 || a2 >= b2) return 0.0;
+ return cdf (b1, b2) - cdf (a1, b2) - cdf (b1, a2) + cdf(a1, a2);
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistribution2Dim.tex b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistribution2Dim.tex
new file mode 100644
index 0000000..dd0370a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistribution2Dim.tex
@@ -0,0 +1,155 @@
+\defmodule {ContinuousDistribution2Dim}
+
+Classes implementing 2-dimensional continuous distributions should inherit
+from this class.
+Such distributions are characterized by a \emph{density} function $f(x, y)$;
+thus the signature of a \texttt{density} method is supplied here.
+This class also provides a default implementation of $\overline F(x, y)$,
+the upper \texttt{CDF}. The inverse function $F^{-1}(u)$ represents a curve
+$y = h(x)$ of constant $u$ and it is not implemented.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ContinuousDistribution2Dim
+ * Description: Mother class 2-dimensional continuous distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public abstract class ContinuousDistribution2Dim
+ extends ContinuousDistributionMulti\begin{hide} {\end{hide}
+
+ public int decPrec = 15;
+\end{code}
+\begin{tabb} Defines the target number of decimals of accuracy when
+ approximating a distribution function, but there is \emph{no guarantee} that
+ this target is always attained.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ // x infinity for some distributions
+ protected static final double XINF = Double.MAX_VALUE;
+
+ // x infinity for some distributions
+ protected static final double XBIG = 1000.0;
+
+ // EPSARRAY[j]: Epsilon required for j decimal degits of precision
+ protected static final double[] EPSARRAY = {
+ 0.5, 0.5E-1, 0.5E-2, 0.5E-3, 0.5E-4, 0.5E-5, 0.5E-6, 0.5E-7, 0.5E-8,
+ 0.5E-9, 0.5E-10, 0.5E-11, 0.5E-12, 0.5E-13, 0.5E-14, 0.5E-15, 0.5E-16,
+ 0.5E-17, 0.5E-18, 0.5E-19, 0.5E-20, 0.5E-21, 0.5E-22, 0.5E-23, 0.5E-24,
+ 0.5E-25, 0.5E-26, 0.5E-27, 0.5E-28, 0.5E-29, 0.5E-30, 0.5E-31, 0.5E-32,
+ 0.5E-33, 0.5E-34, 0.5E-35
+ };\end{hide}
+
+ public abstract double density (double x, double y);
+\end{code}
+\begin{tabb} Returns $f(x, y)$, the density of $(X, Y)$ evaluated at $(x, y)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value $x$ at which the density is evaluated}
+ \param{y}{value $y$ at which the density is evaluated}
+ \return{density function evaluated at $(x, y)$
+\end{htmlonly}
+\begin{code}
+
+ public double density (double[] x)\begin{hide} {
+ if (x.length != 2)
+ throw new IllegalArgumentException("x must be in dimension 2");
+
+ return density (x[0], x[1]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Simply calls \texttt{density (x[0], x[1])}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{point $(x[0], x[1])$ at which the density is evaluated}
+ \return{density function evaluated at $(x[0], x[1])$
+\end{htmlonly}
+\begin{code}
+
+ public abstract double cdf (double x, double y); \end{code}
+ \begin{tabb} Computes the distribution function $F(x, y)$:
+ \eq
+ F(x, y) = P[X\le x, Y \le y] =
+ \int_{-\infty}^x ds \int_{-\infty}^y dt\, f(s, t).
+ \endeq
+ \end{tabb}
+ \begin{htmlonly}
+ \param{x}{value $x$ at which the distribution function is evaluated}
+ \param{y}{value $y$ at which the distribution function is evaluated}
+ \return{distribution function evaluated at $(x, y)$
+ \end{htmlonly}
+\begin{code}
+
+ public double barF (double x, double y) \begin{hide} {
+ double u = 1.0 + cdf (x, y) - cdf (XINF, y) - cdf (x, XINF);
+ if (u <= 0.0) return 0.0;
+ if (u >= 1.0) return 1.0;
+ return u;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the upper cumulative distribution function
+ $\overline F(x, y)$:
+ \eq
+ \overline F(x, y) = P[X\ge x, Y \ge y] =
+ \int^{\infty}_x ds \int^{\infty}_y dt\, f(s, t).
+ \endeq
+% The default implementation computes
+% $\overline F(x, y) = 1 - F(\infty, y) - F(x, \infty) + F(x, y)$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x}{value $x$ at which the upper distribution is evaluated}
+ \param{y}{value $y$ at which the upper distribution is evaluated}
+ \return{upper distribution function evaluated at $(x, y)$
+\end{htmlonly}
+\begin{code}
+
+ public double cdf (double a1, double a2, double b1, double b2) \begin{hide} {
+ if (a1 >= b1 || a2 >= b2) return 0.0;
+ return cdf (b1, b2) - cdf (a1, b2) - cdf (b1, a2) + cdf(a1, a2);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes the cumulative probability in the square region
+ \eq
+ P[a_1 \le X \le b_1,\: a_2 \le Y \le b_2] =
+ \int_{a_1}^{b_1} dx \int_{a_2}^{b_2} dy\, f(x, y).
+ \endeq
+ \end{tabb}
+\begin{htmlonly}
+ \param{a1}{$x$ lower limit of the square}
+ \param{a2}{$y$ lower limit of the square}
+ \param{b1}{$x$ upper limit of the square}
+ \param{b2}{$y$ upper limit of the square}
+ \return{the cumulative probability in the square region}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistributionMulti.java b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistributionMulti.java
new file mode 100644
index 0000000..fb96dd0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistributionMulti.java
@@ -0,0 +1,98 @@
+
+
+/*
+ * Class: ContinuousDistributionMulti
+ * Description: mother class for continuous multidimensional distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Classes implementing continuous multi-dimensional distributions should inherit
+ * from this class. Such distributions are characterized by a <SPAN CLASS="textit">density</SPAN>
+ * function
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>d</SUB>)</SPAN>;
+ * thus the signature of a <TT>density</TT> method is supplied here.
+ * All array indices start at 0.
+ *
+ */
+public abstract class ContinuousDistributionMulti {
+ protected int dimension;
+
+
+
+ /**
+ * Returns
+ * <SPAN CLASS="MATH"><I>f</I> (<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>d</SUB>)</SPAN>, the probability density of
+ * <SPAN CLASS="MATH"><I>X</I></SPAN> evaluated at the point
+ * <SPAN CLASS="MATH"><I>x</I></SPAN>, where
+ * <SPAN CLASS="MATH"><I>x</I> = {<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>d</SUB>}</SPAN>. The convention is that
+ *
+ * <SPAN CLASS="MATH"><texttt>x</texttt>[<texttt>i</texttt> - <texttt>1</texttt>] = <I>x</I><SUB>i</SUB></SPAN>.
+ *
+ * @param x value at which the density is evaluated
+ *
+ * @return density function evaluated at <TT>x</TT>
+ *
+ */
+ public abstract double density (double[] x);
+
+
+ /**
+ * Returns the dimension <SPAN CLASS="MATH"><I>d</I></SPAN> of the distribution.
+ *
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+
+ /**
+ * Returns the mean vector of the distribution, defined as
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>i</SUB> = <I>E</I>[<I>X</I><SUB>i</SUB>]</SPAN>.
+ *
+ */
+ public abstract double[] getMean();
+
+
+ /**
+ * Returns the variance-covariance matrix of the distribution, defined as
+ * <BR>
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>ij</SUB> = <I>E</I>[(<I>X</I><SUB>i</SUB> - <I>μ</I><SUB>i</SUB>)(<I>X</I><SUB>j</SUB> - <I>μ</I><SUB>j</SUB>)]</SPAN>.
+ *
+ */
+ public abstract double[][] getCovariance();
+
+
+ /**
+ * Returns the correlation matrix of the distribution, defined as
+ *
+ * <SPAN CLASS="MATH"><I>ρ</I><SUB>ij</SUB> = <I>σ</I><SUB>ij</SUB>/(σ_iiσ_jj)<SUP>1/2</SUP></SPAN>.
+ *
+ */
+ public abstract double[][] getCorrelation();
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistributionMulti.tex b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistributionMulti.tex
new file mode 100644
index 0000000..df0ce50
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/ContinuousDistributionMulti.tex
@@ -0,0 +1,93 @@
+\defmodule {ContinuousDistributionMulti}
+
+Classes implementing continuous multi-dimensional distributions should inherit
+from this class. Such distributions are characterized by a \emph{density}
+ function $f(x_1, x_2, \ldots, x_d)$;
+thus the signature of a \texttt{density} method is supplied here.
+All array indices start at 0.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ContinuousDistributionMulti
+ * Description: mother class for continuous multidimensional distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public abstract class ContinuousDistributionMulti\begin{hide} {
+ protected int dimension;
+\end{hide}
+
+
+ public abstract double density (double[] x);
+\end{code}
+\begin{tabb} Returns $f(x_1, x_2, \ldots, x_d)$, the probability density of
+ $X$ evaluated at the point
+ $x$, where $x = \{x_1, x_2, \ldots, x_d\}$. The convention is that
+ $\texttt{x[i-1]} = x_i$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the density is evaluated}
+ \return{density function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public int getDimension()\begin{hide} {
+ return dimension;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dimension $d$ of the distribution.
+\end{tabb}
+\begin{code}
+
+ public abstract double[] getMean();
+\end{code}
+\begin{tabb}
+ Returns the mean vector of the distribution, defined as $\mu_{i} = E[X_i]$.
+\end{tabb}
+\begin{code}
+
+ public abstract double[][] getCovariance();
+\end{code}
+\begin{tabb}
+ Returns the variance-covariance matrix of the distribution, defined as\\
+ $\sigma_{ij} = E[(X_i - \mu_i)(X_j - \mu_j)]$.
+\end{tabb}
+\begin{code}
+
+ public abstract double[][] getCorrelation();
+\end{code}
+\begin{tabb}
+ Returns the correlation matrix of the distribution, defined as
+ $\rho_{ij} = \sigma_{ij}/\sqrt{\sigma_{ii}\sigma_{jj}}$.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/DirichletDist.java b/source/umontreal/iro/lecuyer/probdistmulti/DirichletDist.java
new file mode 100644
index 0000000..8d54662
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/DirichletDist.java
@@ -0,0 +1,352 @@
+
+
+/*
+ * Class: DirichletDist
+ * Description: Dirichlet distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.util.Num;
+import optimization.*;
+
+
+/**
+ * Implements the abstract class {@link ContinuousDistributionMulti} for the
+ * <EM>Dirichlet</EM> distribution with parameters
+ * (<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>α</I><SUB>d</SUB></SPAN>),
+ * <SPAN CLASS="MATH"><I>α</I><SUB>i</SUB> > 0</SPAN>.
+ * The probability density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>d</SUB>) = <I>Γ</I>(<I>α</I><SUB>0</SUB>)∏<SUB>i=1</SUB><SUP>d</SUP><I>x</I><SUB>i</SUB><SUP><I>α</I><SUB>i</SUB>-1</SUP>/(∏<SUB>i=1</SUB><SUP>d</SUP><I>Γ</I>(<I>α</I><SUB>i</SUB>))
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB> >= 0</SPAN>,
+ * <SPAN CLASS="MATH">∑<SUB>i=1</SUB><SUP>d</SUP><I>x</I><SUB>i</SUB> = 1</SPAN>,
+ * <SPAN CLASS="MATH"><I>α</I><SUB>0</SUB> = ∑<SUB>i=1</SUB><SUP>d</SUP><I>α</I><SUB>i</SUB></SPAN>,
+ * and <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the Gamma function.
+ *
+ */
+public class DirichletDist extends ContinuousDistributionMulti {
+ private static final double LOGMIN = -709.1; // Log(MIN_DOUBLE/2)
+ protected double[] alpha;
+
+ private static class Optim implements Uncmin_methods
+ {
+ double[] logP;
+ int n;
+ int k;
+
+ public Optim (double[] logP, int n) {
+ this.n = n;
+ this.k = logP.length;
+ this.logP = new double[k];
+ System.arraycopy (logP, 0, this.logP, 0, k);
+ }
+
+ public double f_to_minimize (double[] alpha) {
+ double sumAlpha = 0.0;
+ double sumLnGammaAlpha = 0.0;
+ double sumAlphaLnP = 0.0;
+
+ for (int i = 1; i < alpha.length; i++) {
+ if (alpha[i] <= 0.0)
+ return 1.0e200;
+
+ sumAlpha += alpha[i];
+ sumLnGammaAlpha += Num.lnGamma (alpha[i]);
+ sumAlphaLnP += ((alpha[i] - 1.0) * logP[i - 1]);
+ }
+
+ return (- n * (Num.lnGamma (sumAlpha) - sumLnGammaAlpha + sumAlphaLnP));
+ }
+
+ public void gradient (double[] alpha, double[] g)
+ {
+ }
+
+ public void hessian (double[] alpha, double[][] h)
+ {
+ }
+ }
+
+
+
+ public DirichletDist (double[] alpha) {
+ setParams (alpha);
+ }
+
+
+ public double density (double[] x) {
+ return density_ (alpha, x);
+ }
+
+ public double[] getMean() {
+ return getMean_ (alpha);
+ }
+
+ public double[][] getCovariance() {
+ return getCovariance_ (alpha);
+ }
+
+ public double[][] getCorrelation () {
+ return getCorrelation_ (alpha);
+ }
+
+ private static void verifParam (double[] alpha) {
+
+ for (int i = 0; i < alpha.length;i++)
+ {
+ if (alpha[i] <= 0)
+ throw new IllegalArgumentException("alpha[" + i + "] <= 0");
+ }
+ }
+
+ private static double density_ (double[] alpha, double[] x) {
+ double alpha0 = 0.0;
+ double sumLnGamma = 0.0;
+ double sumAlphaLnXi = 0.0;
+
+ if (alpha.length != x.length)
+ throw new IllegalArgumentException ("alpha and x must have the same dimension");
+
+ for (int i = 0; i < alpha.length; i++) {
+ alpha0 += alpha[i];
+ sumLnGamma += Num.lnGamma (alpha[i]);
+ if (x[i] <= 0.0 || x[i] >= 1.0)
+ return 0.0;
+ sumAlphaLnXi += (alpha[i] - 1.0) * Math.log (x[i]);
+ }
+
+ return Math.exp (Num.lnGamma (alpha0) - sumLnGamma + sumAlphaLnXi);
+ }
+
+ /**
+ * Computes the density of the Dirichlet distribution
+ * with parameters (<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>α</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double density (double[] alpha, double[] x) {
+ verifParam (alpha);
+ return density_ (alpha, x);
+ }
+
+
+ private static double[][] getCovariance_ (double[] alpha) {
+ double[][] cov = new double[alpha.length][alpha.length];
+ double alpha0 = 0.0;
+
+ for (int i =0; i < alpha.length; i++)
+ alpha0 += alpha[i];
+
+ for (int i = 0; i < alpha.length; i++) {
+ for (int j = 0; j < alpha.length; j++)
+ cov[i][j] = - (alpha[i] * alpha[j]) / (alpha0 * alpha0 * (alpha0 + 1.0));
+
+ cov[i][i] = (alpha[i] / alpha0) * (1.0 - alpha[i] / alpha0) / (alpha0 + 1.0);
+ }
+
+ return cov;
+ }
+
+ /**
+ * Computes the covariance matrix of the Dirichlet distribution
+ * with parameters (<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>α</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[][] getCovariance (double[] alpha) {
+ verifParam (alpha);
+
+ return getCovariance_ (alpha);
+ }
+
+
+ private static double[][] getCorrelation_ (double[] alpha) {
+ double corr[][] = new double[alpha.length][alpha.length];
+ double alpha0 = 0.0;
+
+ for (int i =0; i < alpha.length; i++)
+ alpha0 += alpha[i];
+
+ for (int i = 0; i < alpha.length; i++) {
+ for (int j = 0; j < alpha.length; j++)
+ corr[i][j] = - Math.sqrt ((alpha[i] * alpha[j]) /
+ ((alpha0 - alpha[i]) * (alpha0 - alpha[j])));
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }
+
+ /**
+ * Computes the correlation matrix of the Dirichlet distribution
+ * with parameters (<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>α</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[][] getCorrelation (double[] alpha) {
+ verifParam (alpha);
+ return getCorrelation_ (alpha);
+ }
+
+
+ /**
+ * Estimates the parameters [
+ * <SPAN CLASS="MATH">hat(α_1),…, hat(α_d)</SPAN>]
+ * of the Dirichlet distribution using the maximum likelihood method. It uses the
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> observations of <SPAN CLASS="MATH"><I>d</I></SPAN> components in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>][<I>j</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…, <I>d</I> - 1</SPAN>.
+ *
+ * @param x the list of observations to use to evaluate parameters
+ *
+ * @param n the number of observations to use to evaluate parameters
+ *
+ * @param d the dimension of each vector
+ *
+ * @return returns the parameter [
+ * <SPAN CLASS="MATH">hat(α_1),…, hat(α_d)</SPAN>]
+ *
+ */
+ public static double[] getMLE (double[][] x, int n, int d) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ double[] logP = new double[d];
+ double mean[] = new double[d];
+ double var[] = new double[d];
+ int i;
+ int j;
+ for (i = 0; i < d; i++) {
+ logP[i] = 0.0;
+ mean[i] = 0.0;
+ }
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < d; j++) {
+ if (x[i][j] > 0.)
+ logP[j] += Math.log (x[i][j]);
+ else
+ logP[j] += LOGMIN;
+ mean[j] += x[i][j];
+ }
+ }
+
+ for (i = 0; i < d; i++) {
+ logP[i] /= (double) n;
+ mean[i] /= (double) n;
+ }
+
+ double sum = 0.0;
+ for (j = 0; j < d; j++) {
+ sum = 0.0;
+ for (i = 0; i < n; i++)
+ sum += (x[i][j] - mean[j]) * (x[i][j] - mean[j]);
+ var[j] = sum / (double) n;
+ }
+
+ double alpha0 = (mean[0] * (1.0 - mean[0])) / var[0] - 1.0;
+ Optim system = new Optim (logP, n);
+
+ double[] parameters = new double[d];
+ double[] xpls = new double[d + 1];
+ double[] alpha = new double[d + 1];
+ double[] fpls = new double[d + 1];
+ double[] gpls = new double[d + 1];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[d + 1][d + 1];
+ double[] udiag = new double[d + 1];
+
+ for (i = 1; i <= d; i++)
+ alpha[i] = mean[i - 1] * alpha0;
+
+ Uncmin_f77.optif0_f77 (d, alpha, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (i = 0; i < d; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }
+
+
+ private static double[] getMean_ (double[] alpha) {
+ double alpha0 = 0.0;
+ double[] mean = new double[alpha.length];
+
+ for (int i = 0; i < alpha.length;i++)
+ alpha0 += alpha[i];
+
+ for (int i = 0; i < alpha.length; i++)
+ mean[i] = alpha[i] / alpha0;
+
+ return mean;
+ }
+
+
+ /**
+ * Computes the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>α</I><SUB>i</SUB>/<I>α</I><SUB>0</SUB></SPAN> of the Dirichlet distribution
+ * with parameters (<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>α</I><SUB>d</SUB></SPAN>), where
+ * <SPAN CLASS="MATH"><I>α</I><SUB>0</SUB> = ∑<SUB>i=1</SUB><SUP>d</SUP><I>α</I><SUB>i</SUB></SPAN>.
+ *
+ */
+ public static double[] getMean (double[] alpha) {
+ verifParam (alpha);
+ return getMean_ (alpha);
+ }
+
+
+ /**
+ * Returns the parameters (<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>α</I><SUB>d</SUB></SPAN>) of this object.
+ *
+ */
+ public double[] getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>i</I></SPAN>th component of the alpha vector.
+ *
+ */
+ public double getAlpha (int i) {
+ return alpha[i];
+ }
+
+
+ /**
+ * Sets the parameters (<SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>α</I><SUB>d</SUB></SPAN>) of this object.
+ *
+ */
+ public void setParams (double[] alpha) {
+ this.dimension = alpha.length;
+ this.alpha = new double[dimension];
+ for (int i = 0; i < dimension; i++) {
+ if (alpha[i] <= 0)
+ throw new IllegalArgumentException("alpha[" + i + "] <= 0");
+ this.alpha[i] = alpha[i];
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/DirichletDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/DirichletDist.tex
new file mode 100644
index 0000000..0a8e5b5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/DirichletDist.tex
@@ -0,0 +1,374 @@
+\defmodule{DirichletDist}
+
+Implements the abstract class \class{ContinuousDistributionMulti} for the
+{\em Dirichlet} distribution with parameters
+($\alpha_1$,\ldots,$\alpha_d$), $\alpha_i > 0$.
+The probability density is
+\begin{htmlonly}
+\eq
+ f(x_1,\ldots, x_d) = {\Gamma(\alpha_0)
+ \prod_{i=1}^{d} x_i^{\alpha_i - 1}}
+ / \left({\prod_{i=1}^{d} \Gamma(\alpha_i)}\right)
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x_1,\ldots, x_d) = \frac{\Gamma(\alpha_0)
+ \prod_{i=1}^{d} x_i^{\alpha_i - 1}}{\prod_{i=1}^{d} \Gamma(\alpha_i)}
+\eqlabel{eq:fDirichlet}
+\endeq
+\end{latexonly}
+where $x_i \ge 0$, $\sum_{i=1}^d x_i = 1$, $\alpha_0 = \sum_{i=1}^{d} \alpha_i$,
+ and $\Gamma$ is the Gamma function.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: DirichletDist
+ * Description: Dirichlet distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import optimization.*;
+\end{hide}
+
+public class DirichletDist extends ContinuousDistributionMulti \begin{hide} {
+ private static final double LOGMIN = -709.1; // Log(MIN_DOUBLE/2)
+ protected double[] alpha;
+
+ private static class Optim implements Uncmin_methods
+ {
+ double[] logP;
+ int n;
+ int k;
+
+ public Optim (double[] logP, int n) {
+ this.n = n;
+ this.k = logP.length;
+ this.logP = new double[k];
+ System.arraycopy (logP, 0, this.logP, 0, k);
+ }
+
+ public double f_to_minimize (double[] alpha) {
+ double sumAlpha = 0.0;
+ double sumLnGammaAlpha = 0.0;
+ double sumAlphaLnP = 0.0;
+
+ for (int i = 1; i < alpha.length; i++) {
+ if (alpha[i] <= 0.0)
+ return 1.0e200;
+
+ sumAlpha += alpha[i];
+ sumLnGammaAlpha += Num.lnGamma (alpha[i]);
+ sumAlphaLnP += ((alpha[i] - 1.0) * logP[i - 1]);
+ }
+
+ return (- n * (Num.lnGamma (sumAlpha) - sumLnGammaAlpha + sumAlphaLnP));
+ }
+
+ public void gradient (double[] alpha, double[] g)
+ {
+ }
+
+ public void hessian (double[] alpha, double[][] h)
+ {
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public DirichletDist (double[] alpha) \begin{hide} {
+ setParams (alpha);
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double[] x) {
+ return density_ (alpha, x);
+ }
+
+ public double[] getMean() {
+ return getMean_ (alpha);
+ }
+
+ public double[][] getCovariance() {
+ return getCovariance_ (alpha);
+ }
+
+ public double[][] getCorrelation () {
+ return getCorrelation_ (alpha);
+ }
+
+ private static void verifParam (double[] alpha) {
+
+ for (int i = 0; i < alpha.length;i++)
+ {
+ if (alpha[i] <= 0)
+ throw new IllegalArgumentException("alpha[" + i + "] <= 0");
+ }
+ }
+
+ private static double density_ (double[] alpha, double[] x) {
+ double alpha0 = 0.0;
+ double sumLnGamma = 0.0;
+ double sumAlphaLnXi = 0.0;
+
+ if (alpha.length != x.length)
+ throw new IllegalArgumentException ("alpha and x must have the same dimension");
+
+ for (int i = 0; i < alpha.length; i++) {
+ alpha0 += alpha[i];
+ sumLnGamma += Num.lnGamma (alpha[i]);
+ if (x[i] <= 0.0 || x[i] >= 1.0)
+ return 0.0;
+ sumAlphaLnXi += (alpha[i] - 1.0) * Math.log (x[i]);
+ }
+
+ return Math.exp (Num.lnGamma (alpha0) - sumLnGamma + sumAlphaLnXi);
+ }\end{hide}
+
+ public static double density (double[] alpha, double[] x)\begin{hide} {
+ verifParam (alpha);
+ return density_ (alpha, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the density (\ref{eq:fDirichlet}) of the Dirichlet distribution
+ with parameters ($\alpha_1$, \ldots, $\alpha_d$).
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[][] getCovariance_ (double[] alpha) {
+ double[][] cov = new double[alpha.length][alpha.length];
+ double alpha0 = 0.0;
+
+ for (int i =0; i < alpha.length; i++)
+ alpha0 += alpha[i];
+
+ for (int i = 0; i < alpha.length; i++) {
+ for (int j = 0; j < alpha.length; j++)
+ cov[i][j] = - (alpha[i] * alpha[j]) / (alpha0 * alpha0 * (alpha0 + 1.0));
+
+ cov[i][i] = (alpha[i] / alpha0) * (1.0 - alpha[i] / alpha0) / (alpha0 + 1.0);
+ }
+
+ return cov;
+ }\end{hide}
+
+ public static double[][] getCovariance (double[] alpha)\begin{hide} {
+ verifParam (alpha);
+
+ return getCovariance_ (alpha);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the covariance matrix of the Dirichlet distribution
+ with parameters ($\alpha_1$, \ldots, $\alpha_d$).
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[][] getCorrelation_ (double[] alpha) {
+ double corr[][] = new double[alpha.length][alpha.length];
+ double alpha0 = 0.0;
+
+ for (int i =0; i < alpha.length; i++)
+ alpha0 += alpha[i];
+
+ for (int i = 0; i < alpha.length; i++) {
+ for (int j = 0; j < alpha.length; j++)
+ corr[i][j] = - Math.sqrt ((alpha[i] * alpha[j]) /
+ ((alpha0 - alpha[i]) * (alpha0 - alpha[j])));
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }\end{hide}
+
+ public static double[][] getCorrelation (double[] alpha)\begin{hide} {
+ verifParam (alpha);
+ return getCorrelation_ (alpha);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the correlation matrix of the Dirichlet distribution
+ with parameters ($\alpha_1$, \ldots, $\alpha_d$).
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (double[][] x, int n, int d)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ double[] logP = new double[d];
+ double mean[] = new double[d];
+ double var[] = new double[d];
+ int i;
+ int j;
+ for (i = 0; i < d; i++) {
+ logP[i] = 0.0;
+ mean[i] = 0.0;
+ }
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < d; j++) {
+ if (x[i][j] > 0.)
+ logP[j] += Math.log (x[i][j]);
+ else
+ logP[j] += LOGMIN;
+ mean[j] += x[i][j];
+ }
+ }
+
+ for (i = 0; i < d; i++) {
+ logP[i] /= (double) n;
+ mean[i] /= (double) n;
+ }
+
+ double sum = 0.0;
+ for (j = 0; j < d; j++) {
+ sum = 0.0;
+ for (i = 0; i < n; i++)
+ sum += (x[i][j] - mean[j]) * (x[i][j] - mean[j]);
+ var[j] = sum / (double) n;
+ }
+
+ double alpha0 = (mean[0] * (1.0 - mean[0])) / var[0] - 1.0;
+ Optim system = new Optim (logP, n);
+
+ double[] parameters = new double[d];
+ double[] xpls = new double[d + 1];
+ double[] alpha = new double[d + 1];
+ double[] fpls = new double[d + 1];
+ double[] gpls = new double[d + 1];
+ int[] itrcmd = new int[2];
+ double[][] a = new double[d + 1][d + 1];
+ double[] udiag = new double[d + 1];
+
+ for (i = 1; i <= d; i++)
+ alpha[i] = mean[i - 1] * alpha0;
+
+ Uncmin_f77.optif0_f77 (d, alpha, system, xpls, fpls, gpls, itrcmd, a, udiag);
+
+ for (i = 0; i < d; i++)
+ parameters[i] = xpls[i+1];
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters [$\hat{\alpha_1},\ldots,\hat{\alpha_d}$]
+ of the Dirichlet distribution using the maximum likelihood method. It uses the
+ $n$ observations of $d$ components in table $x[i][j]$, $i = 0, 1, \ldots, n-1$
+ and $j = 0, 1, \ldots, d-1$.
+ \begin{detailed}
+ The equations of the maximum likelihood are defined in \cite[Technical appendix]{ccAVR04a}
+ \begin{eqnarray*}
+ L(\hat\alpha_1,\hat\alpha_2,\ldots,\hat\alpha_k) & = & n \left( G(\alpha_0) - \sum_{i=1}^{k} G(\hat\alpha_i) \right) + \sum_{i=1}^{k} (\hat\alpha_i - 1) Z_i\\
+ \end{eqnarray*}
+ where $G$ is the logarithm of the gamma function and
+ \begin{eqnarray*}
+ \alpha_0 & = & \sum_{i=1}^{k} \hat\alpha_i\\
+ Z_i & = & \sum_{j=1}^{n} \ln (X_{i,j}) \qquad \mbox{for }i=1,\ldots,k.
+ \end{eqnarray*}
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations to use to evaluate parameters}
+ \param{n}{the number of observations to use to evaluate parameters}
+ \param{d}{the dimension of each vector}
+ \return{returns the parameter [$\hat{\alpha_1},\ldots,\hat{\alpha_d}$]}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private static double[] getMean_ (double[] alpha) {
+ double alpha0 = 0.0;
+ double[] mean = new double[alpha.length];
+
+ for (int i = 0; i < alpha.length;i++)
+ alpha0 += alpha[i];
+
+ for (int i = 0; i < alpha.length; i++)
+ mean[i] = alpha[i] / alpha0;
+
+ return mean;
+ }
+\end{hide}
+
+ public static double[] getMean (double[] alpha)\begin{hide} {
+ verifParam (alpha);
+ return getMean_ (alpha);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the mean $E[X] = \alpha_i / \alpha_0$ of the Dirichlet distribution
+ with parameters ($\alpha_1$, \ldots, $\alpha_d$), where $\alpha_0 = \sum_{i=1}^{d} \alpha_i$.
+\end{tabb}
+\begin{code}
+
+ public double[] getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameters ($\alpha_1$, \ldots, $\alpha_d$) of this object.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha (int i)\begin{hide} {
+ return alpha[i];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $i$th component of the alpha vector.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double[] alpha)\begin{hide} {
+ this.dimension = alpha.length;
+ this.alpha = new double[dimension];
+ for (int i = 0; i < dimension; i++) {
+ if (alpha[i] <= 0)
+ throw new IllegalArgumentException("alpha[" + i + "] <= 0");
+ this.alpha[i] = alpha[i];
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters ($\alpha_1$, \ldots, $\alpha_d$) of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/DiscreteDistributionIntMulti.java b/source/umontreal/iro/lecuyer/probdistmulti/DiscreteDistributionIntMulti.java
new file mode 100644
index 0000000..41d9fad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/DiscreteDistributionIntMulti.java
@@ -0,0 +1,130 @@
+
+
+/*
+ * Class: DiscreteDistributionIntMulti
+ * Description: mother class for discrete distributions over the integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+
+/**
+ * Classes implementing multi-dimensional discrete distributions over the integers
+ * should inherit from this class.
+ * It specifies the signature of methods for computing the mass function
+ * (or probability)
+ * <SPAN CLASS="MATH"><I>p</I>(<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>d</SUB>) = <I>P</I>[<I>X</I><SUB>1</SUB> = <I>x</I><SUB>1</SUB>, <I>X</I><SUB>2</SUB> = <I>x</I><SUB>2</SUB>,…, <I>X</I><SUB>d</SUB> = <I>x</I><SUB>d</SUB>]</SPAN> and the cumulative probabilities
+ * for a random vector <SPAN CLASS="MATH"><I>X</I></SPAN> with a discrete distribution over the integers.
+ *
+ */
+public abstract class DiscreteDistributionIntMulti {
+ protected int dimension;
+
+
+ /**
+ * Returns the probability mass function
+ * <SPAN CLASS="MATH"><I>p</I>(<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>d</SUB>)</SPAN>,
+ * which should be a real number in <SPAN CLASS="MATH">[0, 1]</SPAN>.
+ *
+ * @param x value at which the mass function must be evaluated
+ *
+ * @return the mass function evaluated at <TT>x</TT>
+ *
+ */
+ public abstract double prob (int[] x);
+
+
+ /**
+ * Computes the cumulative probability function <SPAN CLASS="MATH"><I>F</I></SPAN> of the distribution evaluated
+ * at <TT>x</TT>, assuming the lowest values start at 0, i.e. computes
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I>(<I>x</I><SUB>1</SUB>, <I>x</I><SUB>2</SUB>,…, <I>x</I><SUB>d</SUB>) = ∑<SUB>s<SUB>1</SUB>=0</SUB><SUP>x<SUB>1</SUB></SUP>∑<SUB>s<SUB>2</SUB>=0</SUB><SUP>x<SUB>2</SUB> ... </SUP>∑<SUB>s<SUB>d</SUB>=0</SUB><SUP>x<SUB>d</SUB></SUP><I>p</I>(<I>s</I><SUB>1</SUB>, <I>s</I><SUB>2</SUB>,…, <I>s</I><SUB>d</SUB>).
+ * </DIV><P></P>
+ * Uses the naive implementation, is very inefficient and may underflows.
+ *
+ */
+ public double cdf (int x[]) {
+ int is[] = new int[x.length];
+ for (int i = 0; i < is.length; i++)
+ is[i] = 0;
+
+ boolean end = false;
+ double sum = 0.0;
+ int j;
+ while (!end) {
+ sum += prob (is);
+ is[0]++;
+
+ if (is[0] > x[0]) {
+ is[0] = 0;
+ j = 1;
+ while (j < x.length && is[j] == x[j])
+ is[j++] = 0;
+
+ if (j == x.length)
+ end = true;
+ else
+ is[j]++;
+ }
+ }
+
+ return sum;
+ }
+
+
+ /**
+ * Returns the dimension <SPAN CLASS="MATH"><I>d</I></SPAN> of the distribution.
+ *
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+
+ /**
+ * Returns the mean vector of the distribution, defined as
+ * <SPAN CLASS="MATH"><I>μ</I><SUB>i</SUB> = <I>E</I>[<I>X</I><SUB>i</SUB>]</SPAN>.
+ *
+ */
+ public abstract double[] getMean();
+
+
+ /**
+ * Returns the variance-covariance matrix of the distribution, defined as
+ * <BR>
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>ij</SUB> = <I>E</I>[(<I>X</I><SUB>i</SUB> - <I>μ</I><SUB>i</SUB>)(<I>X</I><SUB>j</SUB> - <I>μ</I><SUB>j</SUB>)]</SPAN>.
+ *
+ */
+ public abstract double[][] getCovariance();
+
+
+ /**
+ * Returns the correlation matrix of the distribution, defined as
+ *
+ * <SPAN CLASS="MATH"><I>ρ</I><SUB>ij</SUB> = <I>σ</I><SUB>ij</SUB>/(σ_iiσ_jj)<SUP>1/2</SUP></SPAN>.
+ *
+ */
+ public abstract double[][] getCorrelation();
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/DiscreteDistributionIntMulti.tex b/source/umontreal/iro/lecuyer/probdistmulti/DiscreteDistributionIntMulti.tex
new file mode 100644
index 0000000..7489c61
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/DiscreteDistributionIntMulti.tex
@@ -0,0 +1,129 @@
+\defmodule {DiscreteDistributionIntMulti}
+
+Classes implementing multi-dimensional discrete distributions over the integers
+should inherit from this class.
+It specifies the signature of methods for computing the mass function
+(or probability) $p(x_1, x_2, \ldots, x_d) =
+ P[X_1 = x_1, X_2 = x_2, \ldots, X_d = x_d]$ and the cumulative probabilities
+for a random vector $X$ with a discrete distribution over the integers.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DiscreteDistributionIntMulti
+ * Description: mother class for discrete distributions over the integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+
+
+public abstract class DiscreteDistributionIntMulti\begin{hide} {
+ protected int dimension;
+ \end{hide}
+
+ public abstract double prob (int[] x);
+\end{code}
+\begin{tabb} Returns the probability mass function $p(x_1, x_2, \ldots, x_d)$,
+ which should be a real number in $[0,1]$.
+ % The notation used is $\texttt{x[i-1]} = x_i$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the mass function must be evaluated}
+ \return{the mass function evaluated at \texttt{x}}
+\end{htmlonly}
+\begin{code}
+
+ public double cdf (int x[])\begin{hide} {
+ int is[] = new int[x.length];
+ for (int i = 0; i < is.length; i++)
+ is[i] = 0;
+
+ boolean end = false;
+ double sum = 0.0;
+ int j;
+ while (!end) {
+ sum += prob (is);
+ is[0]++;
+
+ if (is[0] > x[0]) {
+ is[0] = 0;
+ j = 1;
+ while (j < x.length && is[j] == x[j])
+ is[j++] = 0;
+
+ if (j == x.length)
+ end = true;
+ else
+ is[j]++;
+ }
+ }
+
+ return sum;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the cumulative probability function $F$ of the distribution evaluated
+ at \texttt{x}, assuming the lowest values start at 0, i.e. computes
+$$
+ F (x_1, x_2, \ldots, x_d) =
+ \sum_{s_1=0}^{x_1} \sum_{s_2=0}^{x_2} \cdots
+ \sum_{s_d=0}^{x_d} p(s_1, s_2, \ldots, s_d).
+$$
+ Uses the naive implementation, is very inefficient and may underflows.
+\end{tabb}
+\begin{code}
+
+ public int getDimension()\begin{hide} {
+ return dimension;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dimension $d$ of the distribution.
+\end{tabb}
+\begin{code}
+
+ public abstract double[] getMean();
+\end{code}
+\begin{tabb}
+ Returns the mean vector of the distribution, defined as $\mu_{i} = E[X_i]$.
+\end{tabb}
+\begin{code}
+
+ public abstract double[][] getCovariance();
+\end{code}
+\begin{tabb}
+ Returns the variance-covariance matrix of the distribution, defined as\\
+ $\sigma_{ij} = E[(X_i - \mu_i)(X_j - \mu_j)]$.
+\end{tabb}
+\begin{code}
+
+ public abstract double[][] getCorrelation();
+\end{code}
+\begin{tabb}
+ Returns the correlation matrix of the distribution, defined as
+ $\rho_{ij} = \sigma_{ij}/\sqrt{\sigma_{ii}\sigma_{jj}}$.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/MultiNormalDist.java b/source/umontreal/iro/lecuyer/probdistmulti/MultiNormalDist.java
new file mode 100644
index 0000000..1295640
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/MultiNormalDist.java
@@ -0,0 +1,338 @@
+
+
+/*
+ * Class: MultiNormalDist
+ * Description: multinormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.Algebra;
+
+
+
+/**
+ * Implements the abstract class {@link ContinuousDistributionMulti} for the
+ * <EM>multinormal</EM> distribution with mean vector <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> and covariance
+ * matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>.
+ * The probability density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<B>x</B> = <I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>d</SUB>) = exp(- (<B>x</B> - <I><B>μ</B></I>)<SUP>T</SUP><I><B>Σ</B></I><SUP>-1</SUP>(<B>x</B> - <I><B>μ</B></I>)/2)/((2π)^d det())<SUP>1/2</SUP>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH">1#1 = (<I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>d</SUB>)</SPAN>.
+ *
+ */
+public class MultiNormalDist extends ContinuousDistributionMulti {
+ protected int dim;
+ protected double[] mu;
+ protected DoubleMatrix2D sigma;
+ protected DoubleMatrix2D invSigma;
+
+ protected static Algebra algebra = new Algebra();
+
+
+
+ public MultiNormalDist (double[] mu, double[][] sigma) {
+ setParams (mu, sigma);
+ }
+
+
+ public double density (double[] x) {
+ double sum = 0.0;
+
+ if (invSigma == null)
+ invSigma = algebra.inverse(sigma);
+
+ double[] temp = new double[mu.length];
+ for (int i = 0; i < mu.length; i++)
+ {
+ sum = 0.0;
+ for (int j = 0; j < mu.length; j++)
+ sum += ((x[j] - mu[j]) * invSigma.getQuick (j, i));
+ temp[i] = sum;
+ }
+
+ sum = 0.0;
+ for (int i = 0; i < mu.length; i++)
+ sum += temp[i] * (x[i] - mu[i]);
+
+ return (Math.exp(-0.5 * sum) / Math.sqrt (Math.pow (2 * Math.PI, mu.length) * algebra.det (sigma)));
+ }
+
+ public double[] getMean() {
+ return mu;
+ }
+
+ public double[][] getCovariance() {
+ return sigma.toArray();
+ }
+
+ public double[][] getCorrelation () {
+ return getCorrelation_ (mu, sigma.toArray());
+ }
+
+ /**
+ * Computes the density of the multinormal distribution
+ * with parameters <SPAN CLASS="MATH"><I><B>μ</B></I> =</SPAN> <TT>mu</TT> and
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I> =</SPAN> <TT>sigma</TT>,
+ * evaluated at <TT>x</TT>.
+ *
+ */
+ public static double density (double[] mu, double[][] sigma, double[] x) {
+ double sum = 0.0;
+ DoubleMatrix2D sig;
+ DoubleMatrix2D inv;
+
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ sig = new DenseDoubleMatrix2D (sigma);
+ inv = algebra.inverse (sig);
+
+ double[] temp = new double[mu.length];
+ for (int i = 0; i < mu.length; i++)
+ {
+ sum = 0.0;
+ for (int j = 0; j < mu.length; j++)
+ sum += ((x[j] - mu[j]) * inv.getQuick (j, i));
+ temp[i] = sum;
+ }
+
+ sum = 0.0;
+ for (int i = 0; i < mu.length; i++)
+ sum += temp[i] * (x[i] - mu[i]);
+
+ return (Math.exp(-0.5 * sum) / Math.sqrt (Math.pow (2 * Math.PI, mu.length) * algebra.det (sig)));
+ }
+
+
+ /**
+ * Returns the dimension <SPAN CLASS="MATH"><I>d</I></SPAN> of the distribution.
+ *
+ */
+ public int getDimension() {
+ return dim;
+ }
+
+
+ /**
+ * Returns the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I><B>X</B></I>] = <I><B>μ</B></I></SPAN> of the multinormal distribution
+ * with parameters <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> and
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>.
+ *
+ */
+ public static double[] getMean (double[] mu, double[][] sigma) {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ return mu;
+ }
+
+
+ /**
+ * Computes the covariance matrix of the multinormal distribution
+ * with parameters <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> and
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>.
+ *
+ */
+ public static double[][] getCovariance (double[] mu, double[][] sigma) {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ return sigma;
+ }
+
+
+ private static double[][] getCorrelation_ (double[] mu, double[][] sigma) {
+ double corr[][] = new double[mu.length][mu.length];
+
+ for (int i = 0; i < mu.length; i++) {
+ for (int j = 0; j < mu.length; j++)
+ corr[i][j] = - sigma[i][j] / Math.sqrt (sigma[i][i] * sigma[j][j]);
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }
+
+ /**
+ * Computes the correlation matrix of the multinormal distribution
+ * with parameters <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> and
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>).
+ *
+ */
+ public static double[][] getCorrelation (double[] mu, double[][] sigma) {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ return getCorrelation_ (mu, sigma);
+ }
+
+
+ /**
+ * Estimates the parameters <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> of the multinormal distribution using
+ * the maximum likelihood method. It uses the <SPAN CLASS="MATH"><I>n</I></SPAN> observations of <SPAN CLASS="MATH"><I>d</I></SPAN>
+ * components in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>][<I>j</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…, <I>d</I> - 1</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @param d the dimension of each observation
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH"><I><B>μ</B></I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I><B>μ</B></I><SUB>d</SUB></SPAN>]
+ *
+ */
+ public static double[] getMLEMu (double[][] x, int n, int d) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ double[] parameters = new double[d];
+ for (int i = 0; i < parameters.length; i++)
+ parameters[i] = 0.0;
+
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j < d; j++)
+ parameters[j] += x[i][j];
+
+ for (int i = 0; i < parameters.length; i++)
+ parameters[i] = parameters[i] / (double) n;
+
+ return parameters;
+ }
+
+
+ /**
+ * Estimates the parameters
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> of the multinormal distribution using
+ * the maximum likelihood method. It uses the <SPAN CLASS="MATH"><I>n</I></SPAN> observations of <SPAN CLASS="MATH"><I>d</I></SPAN>
+ * components in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>][<I>j</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>n</I> - 1</SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…, <I>d</I> - 1</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param n the number of observations used to evaluate parameters
+ *
+ * @param d the dimension of each observation
+ *
+ * @return returns the covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>
+ *
+ */
+ public static double[][] getMLESigma (double[][] x, int n, int d) {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ double[] mean = getMLEMu (x, n, d);
+ double[][] parameters = new double[d][d];
+ for (int i = 0; i < parameters.length; i++)
+ for (int j = 0; j < parameters.length; j++)
+ parameters[i][j] = 0.0;
+
+ for (int i = 0; i < parameters.length; i++)
+ {
+ for (int j = 0; j < parameters.length; j++)
+ {
+ sum = 0.0;
+ for (int t = 0; t < n; t++)
+ sum += (x[t][i] - mean[i]) * (x[t][j] - mean[j]);
+ parameters[i][j] = sum / (double) n;
+ }
+ }
+
+ return parameters;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> of this object.
+ *
+ */
+ public double[] getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>i</I></SPAN>-th component of the parameter <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> of this object.
+ *
+ */
+ public double getMu (int i) {
+ return mu[i];
+ }
+
+
+ /**
+ * Returns the parameter
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> of this object.
+ *
+ */
+ public double[][] getSigma() {
+ return sigma.toArray();
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I><B>μ</B></I></SPAN> and
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> of this object.
+ *
+ */
+ public void setParams (double[] mu, double[][] sigma) {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ this.mu = new double[mu.length];
+ this.dimension = mu.length;
+ System.arraycopy(mu, 0, this.mu, 0, mu.length);
+ this.sigma = new DenseDoubleMatrix2D (sigma);
+
+ invSigma = null;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/MultiNormalDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/MultiNormalDist.tex
new file mode 100644
index 0000000..1d4ed43
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/MultiNormalDist.tex
@@ -0,0 +1,342 @@
+\defmodule{MultiNormalDist}
+
+Implements the abstract class \class{ContinuousDistributionMulti} for the
+{\em multinormal} distribution with mean vector $\boldmu$ and covariance
+matrix $\boldSigma$.
+The probability density is
+\begin{htmlonly}
+\eq
+ f(\mathbf{x} = x_1,\ldots,x_d) =
+ \exp\left(-(\mathbf{x} - \boldmu)^{T} \boldSigma^{-1}
+ (\mathbf{x} - \boldmu)/2\right)
+ /{\sqrt{(2\pi)^{d} \mbox{det}(\boldSigma)}}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(\boldx) = \frac{1}{\sqrt{(2\pi)^{d} \det\boldSigma}}
+ \exp\left(-\frac{1}{2}(\boldx - \boldmu)^{T} \boldSigma^{-1} (\boldx - \boldmu)\right)
+\eqlabel{eq:fMultinormal}
+\endeq
+\end{latexonly}
+where $\boldx = (x_1,\ldots,x_d)$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: MultiNormalDist
+ * Description: multinormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.Algebra;
+\end{hide}
+
+
+public class MultiNormalDist extends ContinuousDistributionMulti \begin{hide} {
+ protected int dim;
+ protected double[] mu;
+ protected DoubleMatrix2D sigma;
+ protected DoubleMatrix2D invSigma;
+
+ protected static Algebra algebra = new Algebra();
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public MultiNormalDist (double[] mu, double[][] sigma) \begin{hide} {
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double density (double[] x) {
+ double sum = 0.0;
+
+ if (invSigma == null)
+ invSigma = algebra.inverse(sigma);
+
+ double[] temp = new double[mu.length];
+ for (int i = 0; i < mu.length; i++)
+ {
+ sum = 0.0;
+ for (int j = 0; j < mu.length; j++)
+ sum += ((x[j] - mu[j]) * invSigma.getQuick (j, i));
+ temp[i] = sum;
+ }
+
+ sum = 0.0;
+ for (int i = 0; i < mu.length; i++)
+ sum += temp[i] * (x[i] - mu[i]);
+
+ return (Math.exp(-0.5 * sum) / Math.sqrt (Math.pow (2 * Math.PI, mu.length) * algebra.det (sigma)));
+ }
+
+ public double[] getMean() {
+ return mu;
+ }
+
+ public double[][] getCovariance() {
+ return sigma.toArray();
+ }
+
+ public double[][] getCorrelation () {
+ return getCorrelation_ (mu, sigma.toArray());
+ }\end{hide}
+
+ public static double density (double[] mu, double[][] sigma, double[] x)\begin{hide} {
+ double sum = 0.0;
+ DoubleMatrix2D sig;
+ DoubleMatrix2D inv;
+
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ sig = new DenseDoubleMatrix2D (sigma);
+ inv = algebra.inverse (sig);
+
+ double[] temp = new double[mu.length];
+ for (int i = 0; i < mu.length; i++)
+ {
+ sum = 0.0;
+ for (int j = 0; j < mu.length; j++)
+ sum += ((x[j] - mu[j]) * inv.getQuick (j, i));
+ temp[i] = sum;
+ }
+
+ sum = 0.0;
+ for (int i = 0; i < mu.length; i++)
+ sum += temp[i] * (x[i] - mu[i]);
+
+ return (Math.exp(-0.5 * sum) / Math.sqrt (Math.pow (2 * Math.PI, mu.length) * algebra.det (sig)));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the density (\ref{eq:fMultinormal}) of the multinormal distribution
+ with parameters $\boldmu =$ \texttt{mu} and $\boldSigma =$ \texttt{sigma},
+ evaluated at \texttt{x}.
+\end{tabb}
+\begin{code}
+
+ public int getDimension()\begin{hide} {
+ return dim;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dimension $d$ of the distribution.
+\end{tabb}
+\begin{code}
+
+ public static double[] getMean (double[] mu, double[][] sigma)\begin{hide} {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the mean $E[\boldX] = \boldmu$ of the multinormal distribution
+ with parameters $\boldmu$ and $\boldSigma$.
+\end{tabb}
+\begin{code}
+
+ public static double[][] getCovariance (double[] mu, double[][] sigma)\begin{hide} {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ return sigma;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the covariance matrix of the multinormal distribution
+ with parameters $\boldmu$ and $\boldSigma$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[][] getCorrelation_ (double[] mu, double[][] sigma) {
+ double corr[][] = new double[mu.length][mu.length];
+
+ for (int i = 0; i < mu.length; i++) {
+ for (int j = 0; j < mu.length; j++)
+ corr[i][j] = - sigma[i][j] / Math.sqrt (sigma[i][i] * sigma[j][j]);
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }\end{hide}
+
+ public static double[][] getCorrelation (double[] mu, double[][] sigma)\begin{hide} {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ return getCorrelation_ (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the correlation matrix of the multinormal distribution
+ with parameters $\boldmu$ and $\boldSigma$).
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLEMu (double[][] x, int n, int d)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ double[] parameters = new double[d];
+ for (int i = 0; i < parameters.length; i++)
+ parameters[i] = 0.0;
+
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j < d; j++)
+ parameters[j] += x[i][j];
+
+ for (int i = 0; i < parameters.length; i++)
+ parameters[i] = parameters[i] / (double) n;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $\boldmu$ of the multinormal distribution using
+ the maximum likelihood method. It uses the $n$ observations of $d$
+ components in table $x[i][j]$, $i = 0, 1, \ldots, n-1$ and
+ $j = 0, 1, \ldots, d-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \param{d}{the dimension of each observation}
+ \return{returns the parameters [$\boldmu_1$,\ldots,$\boldmu_d$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] getMLESigma (double[][] x, int n, int d)\begin{hide} {
+ double sum = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ double[] mean = getMLEMu (x, n, d);
+ double[][] parameters = new double[d][d];
+ for (int i = 0; i < parameters.length; i++)
+ for (int j = 0; j < parameters.length; j++)
+ parameters[i][j] = 0.0;
+
+ for (int i = 0; i < parameters.length; i++)
+ {
+ for (int j = 0; j < parameters.length; j++)
+ {
+ sum = 0.0;
+ for (int t = 0; t < n; t++)
+ sum += (x[t][i] - mean[i]) * (x[t][j] - mean[j]);
+ parameters[i][j] = sum / (double) n;
+ }
+ }
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates the parameters $\boldSigma$ of the multinormal distribution using
+ the maximum likelihood method. It uses the $n$ observations of $d$
+ components in table $x[i][j]$, $i = 0, 1, \ldots, n-1$ and
+ $j = 0, 1, \ldots, d-1$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{n}{the number of observations used to evaluate parameters}
+ \param{d}{the dimension of each observation}
+ \return{returns the covariance matrix $\boldSigma$}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameter $\boldmu$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getMu (int i)\begin{hide} {
+ return mu[i];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $i$-th component of the parameter $\boldmu$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[][] getSigma()\begin{hide} {
+ return sigma.toArray();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameter $\boldSigma$ of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double[] mu, double[][] sigma)\begin{hide} {
+ if (sigma.length != sigma[0].length)
+ throw new IllegalArgumentException ("sigma must be a square matrix");
+ if (mu.length != sigma.length)
+ throw new IllegalArgumentException ("mu and sigma must have the same dimension");
+
+ this.mu = new double[mu.length];
+ this.dimension = mu.length;
+ System.arraycopy(mu, 0, this.mu, 0, mu.length);
+ this.sigma = new DenseDoubleMatrix2D (sigma);
+
+ invSigma = null;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\boldmu$ and $\boldSigma$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/MultinomialDist.java b/source/umontreal/iro/lecuyer/probdistmulti/MultinomialDist.java
new file mode 100644
index 0000000..494908c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/MultinomialDist.java
@@ -0,0 +1,342 @@
+
+
+/*
+ * Class: MultinomialDist
+ * Description: multinomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * Implements the abstract class {@link DiscreteDistributionIntMulti} for the
+ * <EM>multinomial</EM> distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and
+ * (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>).
+ * The probability mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>[<I>X</I> = (<I>x</I><SUB>1</SUB>,..., <I>x</I><SUB>d</SUB>)] = <I>n</I>!∏<SUB>i=1</SUB><SUP>d</SUP><I>p</I><SUB>i</SUB><SUP>x<SUB>i</SUB></SUP>/<I>x</I><SUB>i</SUB>!,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH">∑<SUB>i=1</SUB><SUP>d</SUP><I>x</I><SUB>i</SUB> = <I>n</I></SPAN> and
+ * <SPAN CLASS="MATH">∑<SUB>i=1</SUB><SUP>d</SUP><I>p</I><SUB>i</SUB> = 1</SPAN>.
+ *
+ */
+public class MultinomialDist extends DiscreteDistributionIntMulti {
+ protected int n;
+ protected double p[];
+
+
+
+
+ /**
+ * Creates a <TT>MultinomialDist</TT> object with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and
+ * (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) such that
+ * <SPAN CLASS="MATH">∑<SUB>i=1</SUB><SUP>d</SUP><I>p</I><SUB>i</SUB> = 1</SPAN>. We have
+ * <SPAN CLASS="MATH"><I>p</I><SUB>i</SUB> =</SPAN> <TT>p[i-1]</TT>.
+ *
+ */
+ public MultinomialDist (int n, double p[]) {
+ setParams (n, p);
+ }
+
+ public double prob (int x[]) {
+ return prob_ (n, p, x);
+ }
+
+ public double cdf (int x[]) {
+ return cdf_ (n, p, x);
+ }
+
+ public double[] getMean() {
+ return getMean_ (n, p);
+ }
+
+ public double[][] getCovariance() {
+ return getCovariance_ (n, p);
+ }
+
+ public double[][] getCorrelation () {
+ return getCorrelation_ (n, p);
+ }
+
+ private static void verifParam(int n, double p[]) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sumPi = 0.0;
+ for (int i = 0; i < p.length; i++) {
+ if ((p[i] < 0) || (p[i] > 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+ sumPi += p[i];
+ }
+
+ if (sumPi != 1.0)
+ throw new IllegalArgumentException ("p is not a probability vector");
+ }
+
+ private static double prob_ (int n, double p[], int x[]) {
+ if (x.length != p.length)
+ throw new IllegalArgumentException ("x and p must have the same dimension");
+
+ double sumXFact = 0.0;
+ int sumX = 0;
+ double sumPX = 0.0;
+
+ for (int i = 0; i < p.length; i++) {
+ sumX += x[i];
+ sumXFact += Num.lnFactorial (x[i]);
+ sumPX += (x[i] * Math.log (p[i]));
+ }
+
+ if (sumX != n)
+ return 0.0;
+ else {
+ return Math.exp (Num.lnFactorial (n) - sumXFact + sumPX);
+ }
+ }
+
+
+ /**
+ * Computes the probability mass function
+ * of the multinomial distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and
+ * (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double prob (int n, double p[], int x[]) {
+ verifParam (n, p);
+ return prob_ (n, p, x);
+ }
+
+
+ private static double cdf_ (int n, double p[], int x[]) {
+ boolean end = false;
+ double sum = 0.0;
+ int j;
+
+ if (x.length != p.length)
+ throw new IllegalArgumentException ("x and p must have the same dimension");
+
+ int is[] = new int[x.length];
+ for (int i = 0; i < is.length; i++)
+ is[i] = 0;
+
+ sum = 0.0;
+ while (! end) {
+ sum += prob (n, p, is);
+ is[0]++;
+
+ if (is[0] > x[0]) {
+ is[0] = 0;
+ j = 1;
+ while (j < x.length && is[j] == x[j])
+ is[j++] = 0;
+
+ if (j == x.length)
+ end = true;
+ else
+ is[j]++;
+ }
+ }
+
+ return sum;
+ }
+
+ /**
+ * Computes the function <SPAN CLASS="MATH"><I>F</I></SPAN> of the multinomial distribution with
+ * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double cdf (int n, double p[], int x[]) {
+ verifParam (n, p);
+ return cdf_ (n, p, x);
+ }
+
+
+ private static double[] getMean_ (int n, double[] p) {
+ double mean[] = new double[p.length];
+
+ for (int i = 0; i < p.length; i++)
+ mean[i] = n * p[i];
+
+ return mean;
+ }
+
+ /**
+ * Computes the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I><SUB>i</SUB>] = <I>np</I><SUB>i</SUB></SPAN> of the multinomial distribution
+ * with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[] getMean (int n, double[] p) {
+ verifParam (n, p);
+
+ return getMean_ (n, p);
+ }
+
+
+ private static double[][] getCovariance_ (int n, double[] p) {
+ double cov[][] = new double[p.length][p.length];
+
+ for (int i = 0; i < p.length; i++) {
+ for (int j = 0; j < p.length; j++)
+ cov[i][j] = -n * p[i] * p[j];
+
+ cov[i][i] = n * p[i] * (1.0 - p[i]);
+ }
+ return cov;
+ }
+
+ /**
+ * Computes the covariance matrix of the multinomial distribution
+ * with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[][] getCovariance (int n, double[] p) {
+ verifParam (n, p);
+ return getCovariance_ (n, p);
+ }
+
+
+ private static double[][] getCorrelation_ (int n, double[] p) {
+ double corr[][] = new double[p.length][p.length];
+
+ for (int i = 0; i < p.length; i++) {
+ for (int j = 0; j < p.length; j++)
+ corr[i][j] = -Math.sqrt(p[i] * p[j] / ((1.0 - p[i]) * (1.0 - p[j])));
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }
+
+ /**
+ * Computes the correlation matrix of the multinomial distribution
+ * with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[][] getCorrelation (int n, double[] p) {
+ verifParam (n, p);
+ return getCorrelation_ (n, p);
+ }
+
+
+ /**
+ * Estimates and returns the parameters [<SPAN CLASS="MATH">hat(p_i)</SPAN>,...,<SPAN CLASS="MATH">hat(p_d)</SPAN>] of the
+ * multinomial distribution using the maximum likelihood method. It uses the <SPAN CLASS="MATH"><I>m</I></SPAN>
+ * observations of <SPAN CLASS="MATH"><I>d</I></SPAN> components in table <SPAN CLASS="MATH"><I>x</I>[<I>i</I>][<I>j</I>]</SPAN>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…, <I>d</I> - 1</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @param d the dimension of each observation
+ *
+ * @param n the number of independant trials for each series
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(p_i)</SPAN>,...,<SPAN CLASS="MATH">hat(p_d)</SPAN>]
+ *
+ */
+ public static double[] getMLE (int x[][], int m, int d, int n) {
+ double parameters[] = new double[d];
+ double xBar[] = new double[d];
+ double N = 0.0;
+
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ for (int i = 0; i < d; i++)
+ xBar[i] = 0;
+
+ for (int v = 0; v < m; v++)
+ for (int c = 0; c < d; c++)
+ xBar[c] += x[v][c];
+
+ for (int i = 0; i < d; i++)
+ {
+ xBar[i] = xBar[i] / (double) n;
+ N += xBar[i];
+ }
+ if (N != (double) n)
+ throw new IllegalArgumentException("n is not correct");
+
+ for (int i = 0; i < d; i++)
+ parameters[i] = xBar[i] / (double) n;
+
+ return parameters;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+ /**
+ * Returns the parameters (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) of this object.
+ *
+ */
+ public double[] getP() {
+ return p;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>,...,<SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) of this object.
+ *
+ */
+ public void setParams (int n, double p[]) {
+ double sumP = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p.length < 2)
+ throw new IllegalArgumentException ("p.length < 2");
+
+ this.n = n;
+ this.dimension = p.length;
+ this.p = new double[dimension];
+ for (int i = 0; i < dimension; i++)
+ {
+ if ((p[i] < 0) || (p[i] > 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+
+ this.p[i] = p[i];
+ sumP += p[i];
+ }
+
+ if (sumP != 1.0)
+ throw new IllegalArgumentException ("p is not a probability vector");
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/MultinomialDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/MultinomialDist.tex
new file mode 100644
index 0000000..d963bf5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/MultinomialDist.tex
@@ -0,0 +1,358 @@
+\defmodule{MultinomialDist}
+
+Implements the abstract class \class{DiscreteDistributionIntMulti} for the
+{\em multinomial} distribution with parameters $n$ and
+($p_1$, \ldots,$p_d$).
+The probability mass function is \cite{tJOH69a}
+\begin{htmlonly}
+\eq
+ P[X = (x_1,...,x_d)] = {n!} \prod_{i=1}^{d}p_i^{x_i}/x_i!,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ P[X = (x_1,\ldots,x_d)] = {n!} \prod_{i=1}^{d}\frac{p_i^{x_i}}{x_i!},
+\eqlabel{eq:fMultinomial}
+\endeq
+\end{latexonly}
+where $\sum_{i=1}^{d} x_i = n$ and $\sum_{i=1}^{d} p_i = 1$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: MultinomialDist
+ * Description: multinomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class MultinomialDist extends DiscreteDistributionIntMulti \begin{hide} {
+ protected int n;
+ protected double p[];
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public MultinomialDist (int n, double p[]) \begin{hide} {
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a \texttt{MultinomialDist} object with parameters $n$ and
+ ($p_1$,\ldots,$p_d$) such that $\sum_{i=1}^{d} p_i = 1$. We have
+ $p_i = $ \texttt{p[i-1]}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ public double prob (int x[]) {
+ return prob_ (n, p, x);
+ }
+
+ public double cdf (int x[]) {
+ return cdf_ (n, p, x);
+ }
+
+ public double[] getMean() {
+ return getMean_ (n, p);
+ }
+
+ public double[][] getCovariance() {
+ return getCovariance_ (n, p);
+ }
+
+ public double[][] getCorrelation () {
+ return getCorrelation_ (n, p);
+ }
+
+ private static void verifParam(int n, double p[]) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+
+ double sumPi = 0.0;
+ for (int i = 0; i < p.length; i++) {
+ if ((p[i] < 0) || (p[i] > 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+ sumPi += p[i];
+ }
+
+ if (sumPi != 1.0)
+ throw new IllegalArgumentException ("p is not a probability vector");
+ }
+
+ private static double prob_ (int n, double p[], int x[]) {
+ if (x.length != p.length)
+ throw new IllegalArgumentException ("x and p must have the same dimension");
+
+ double sumXFact = 0.0;
+ int sumX = 0;
+ double sumPX = 0.0;
+
+ for (int i = 0; i < p.length; i++) {
+ sumX += x[i];
+ sumXFact += Num.lnFactorial (x[i]);
+ sumPX += (x[i] * Math.log (p[i]));
+ }
+
+ if (sumX != n)
+ return 0.0;
+ else {
+ return Math.exp (Num.lnFactorial (n) - sumXFact + sumPX);
+ }
+ }
+\end{hide}
+
+ public static double prob (int n, double p[], int x[])\begin{hide} {
+ verifParam (n, p);
+ return prob_ (n, p, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the probability mass function (\ref{eq:fMultinomial})
+ of the multinomial distribution with parameters $n$ and
+ ($p_1$,\ldots,$p_d$) evaluated at $x$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double cdf_ (int n, double p[], int x[]) {
+ boolean end = false;
+ double sum = 0.0;
+ int j;
+
+ if (x.length != p.length)
+ throw new IllegalArgumentException ("x and p must have the same dimension");
+
+ int is[] = new int[x.length];
+ for (int i = 0; i < is.length; i++)
+ is[i] = 0;
+
+ sum = 0.0;
+ while (! end) {
+ sum += prob (n, p, is);
+ is[0]++;
+
+ if (is[0] > x[0]) {
+ is[0] = 0;
+ j = 1;
+ while (j < x.length && is[j] == x[j])
+ is[j++] = 0;
+
+ if (j == x.length)
+ end = true;
+ else
+ is[j]++;
+ }
+ }
+
+ return sum;
+ }\end{hide}
+
+ public static double cdf (int n, double p[], int x[])\begin{hide} {
+ verifParam (n, p);
+ return cdf_ (n, p, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the function $F$ of the multinomial distribution with
+ parameters $n$ and ($p_1$,\ldots,$p_d$) evaluated at $x$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[] getMean_ (int n, double[] p) {
+ double mean[] = new double[p.length];
+
+ for (int i = 0; i < p.length; i++)
+ mean[i] = n * p[i];
+
+ return mean;
+ }\end{hide}
+
+ public static double[] getMean (int n, double[] p)\begin{hide} {
+ verifParam (n, p);
+
+ return getMean_ (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the mean $E[X_i] = np_i$ of the multinomial distribution
+ with parameters $n$ and ($p_1$,\ldots,$p_d$).
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[][] getCovariance_ (int n, double[] p) {
+ double cov[][] = new double[p.length][p.length];
+
+ for (int i = 0; i < p.length; i++) {
+ for (int j = 0; j < p.length; j++)
+ cov[i][j] = -n * p[i] * p[j];
+
+ cov[i][i] = n * p[i] * (1.0 - p[i]);
+ }
+ return cov;
+ }\end{hide}
+
+ public static double[][] getCovariance (int n, double[] p)\begin{hide} {
+ verifParam (n, p);
+ return getCovariance_ (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the covariance matrix of the multinomial distribution
+ with parameters $n$ and ($p_1$,\ldots,$p_d$).
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[][] getCorrelation_ (int n, double[] p) {
+ double corr[][] = new double[p.length][p.length];
+
+ for (int i = 0; i < p.length; i++) {
+ for (int j = 0; j < p.length; j++)
+ corr[i][j] = -Math.sqrt(p[i] * p[j] / ((1.0 - p[i]) * (1.0 - p[j])));
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }\end{hide}
+
+ public static double[][] getCorrelation (int n, double[] p)\begin{hide} {
+ verifParam (n, p);
+ return getCorrelation_ (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the correlation matrix of the multinomial distribution
+ with parameters $n$ and ($p_1$,\ldots,$p_d$).
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int x[][], int m, int d, int n)\begin{hide} {
+ double parameters[] = new double[d];
+ double xBar[] = new double[d];
+ double N = 0.0;
+
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ if (d <= 0)
+ throw new IllegalArgumentException ("d <= 0");
+
+ for (int i = 0; i < d; i++)
+ xBar[i] = 0;
+
+ for (int v = 0; v < m; v++)
+ for (int c = 0; c < d; c++)
+ xBar[c] += x[v][c];
+
+ for (int i = 0; i < d; i++)
+ {
+ xBar[i] = xBar[i] / (double) n;
+ N += xBar[i];
+ }
+ if (N != (double) n)
+ throw new IllegalArgumentException("n is not correct");
+
+ for (int i = 0; i < d; i++)
+ parameters[i] = xBar[i] / (double) n;
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates and returns the parameters [$\hat{p_i}$,\ldots,$\hat{p_d}$] of the
+ multinomial distribution using the maximum likelihood method. It uses the $m$
+ observations of $d$ components in table $x[i][j]$, $i = 0, 1, \ldots, m-1$
+ and $j = 0, 1, \ldots, d-1$.
+ \begin{detailed}%
+ The equations of the maximum likelihood are defined as %in \cite{xxxxx}:
+ \begin{eqnarray*}
+ \hat p_i = \frac{\bar{X_i}}{N}.
+ \end{eqnarray*}
+ \end{detailed}%
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \param{d}{the dimension of each observation}
+ \param{n}{the number of independant trials for each series}
+ \return{returns the parameters [$\hat{p_i}$,\ldots,$\hat{p_d}$]}
+\end{htmlonly}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameters ($p_1$,\ldots,$p_d$) of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParams (int n, double p[])\begin{hide} {
+ double sumP = 0.0;
+
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p.length < 2)
+ throw new IllegalArgumentException ("p.length < 2");
+
+ this.n = n;
+ this.dimension = p.length;
+ this.p = new double[dimension];
+ for (int i = 0; i < dimension; i++)
+ {
+ if ((p[i] < 0) || (p[i] > 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+
+ this.p[i] = p[i];
+ sumP += p[i];
+ }
+
+ if (sumP != 1.0)
+ throw new IllegalArgumentException ("p is not a probability vector");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $n$ and ($p_1$,\ldots,$p_d$) of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/NegativeMultinomialDist.java b/source/umontreal/iro/lecuyer/probdistmulti/NegativeMultinomialDist.java
new file mode 100644
index 0000000..fe2cb62
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/NegativeMultinomialDist.java
@@ -0,0 +1,509 @@
+
+
+/*
+ * Class: NegativeMultinomialDist
+ * Description: negative multinomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti;
+
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * Implements the class {@link DiscreteDistributionIntMulti} for the
+ * <EM>negative multinomial</EM> distribution with parameters <SPAN CLASS="MATH"><I>n</I> > 0</SPAN> and
+ * (
+ * <SPAN CLASS="MATH"><I>p</I><SUB>1</SUB>,…, <I>p</I><SUB>d</SUB></SPAN>) such that all <SPAN CLASS="MATH">0 < <I>p</I><SUB>i</SUB> < 1</SPAN> and
+ * <SPAN CLASS="MATH">∑<SUB>i=1</SUB><SUP>d</SUP><I>p</I><SUB>i</SUB> < 1</SPAN>.
+ * The probability mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>P</I>[<I>X</I> = (<I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>d</SUB>)] = (<I>Γ</I>(<I>n</I>+∑<SUB>i=1</SUB><SUP>d</SUP><I>x</I><SUB>i</SUB>)/<I>Γ</I>(<I>n</I>))<I>p</I><SUB>0</SUB><SUP>n</SUP>∏<SUB>i=1</SUB><SUP>d</SUP><I>p</I><SUB>i</SUB><SUP>x<SUB>i</SUB></SUP>/<I>x</I><SUB>i</SUB>!
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>p</I><SUB>0</SUB> = 1 - ∑<SUB>i=1</SUB><SUP>d</SUP><I>p</I><SUB>i</SUB></SPAN>.
+ *
+ */
+public class NegativeMultinomialDist extends DiscreteDistributionIntMulti {
+ protected double n;
+ protected double p[];
+
+ private static class Function implements MathFunction {
+ protected double Fl[];
+ protected int ups[];
+ protected int k;
+ protected int M;
+ protected int sumUps;
+
+ public Function (int k, int m, int ups[], double Fl[]) {
+ this.k = k;
+ this.M = m;
+
+ this.Fl = new double[Fl.length];
+ System.arraycopy (Fl, 0, this.Fl, 0, Fl.length);
+ this.ups = new int[ups.length];
+ System.arraycopy (ups, 0, this.ups, 0, ups.length);
+
+ sumUps = 0;
+ for (int i = 0; i < ups.length; i++)
+ sumUps += ups[i];
+ }
+
+ public double evaluate (double gamma) {
+ double sum = 0.0;
+ for (int l = 0; l < M; l++)
+ sum += (Fl[l] / (gamma + (double) l));
+ return (sum - Math.log1p (sumUps / (k * gamma)));
+ }
+ }
+
+
+ private static class FuncInv extends Function implements MathFunction {
+
+ public FuncInv (int k, int m, int ups[], double Fl[]) {
+ super (k, m, ups, Fl);
+ }
+
+ public double evaluate (double nu) {
+ double sum = 0.0;
+ for (int l = 0; l < M; l++)
+ sum += Fl[l] / (1.0 + nu * l);
+ return (sum *nu - Math.log1p (sumUps * nu / k));
+ }
+ }
+
+
+
+ /**
+ * Creates a <TT>NegativeMultinomialDist</TT> object with parameters <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) such that
+ * <SPAN CLASS="MATH">∑<SUB>i=1</SUB><SUP>d</SUP><I>p</I><SUB>i</SUB> < 1</SPAN>,
+ * as described above. We have <SPAN CLASS="MATH"><I>p</I><SUB>i</SUB> =</SPAN> <TT>p[i-1]</TT>.
+ *
+ */
+ public NegativeMultinomialDist (double n, double p[]) {
+ setParams (n, p);
+ }
+
+
+ public double prob (int x[]) {
+ return prob_ (n, p, x);
+ }
+/*
+ public double cdf (int x[]) {
+ throw new UnsupportedOperationException ("cdf not implemented");
+ }
+*/
+ public double[] getMean() {
+ return getMean_ (n, p);
+ }
+
+ public double[][] getCovariance() {
+ return getCovariance_ (n, p);
+ }
+
+ public double[][] getCorrelation() {
+ return getCorrelation_ (n, p);
+ }
+
+ private static void verifParam (double n, double p[]) {
+ double sumPi = 0.0;
+
+ if (n <= 0.0)
+ throw new IllegalArgumentException("n <= 0");
+
+ for (int i = 0; i < p.length;i++) {
+ if ((p[i] < 0) || (p[i] >= 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+
+ sumPi += p[i];
+ }
+ if (sumPi >= 1.0)
+ throw new IllegalArgumentException("p is not a probability vector");
+ }
+
+ private static double prob_ (double n, double p[], int x[]) {
+ double p0 = 0.0;
+ double sumPi= 0.0;
+ double sumXi= 0.0;
+ double sumLnXiFact = 0.0;
+ double sumXiLnPi = 0.0;
+
+ if (x.length != p.length)
+ throw new IllegalArgumentException ("x and p must have the same size");
+
+ for (int i = 0; i < p.length;i++)
+ {
+ sumPi += p[i];
+ sumXi += x[i];
+ sumLnXiFact += Num.lnFactorial (x[i]);
+ sumXiLnPi += x[i] * Math.log (p[i]);
+ }
+ p0 = 1.0 - sumPi;
+
+ return Math.exp (Num.lnGamma (n + sumXi) - (Num.lnGamma (n) +
+ sumLnXiFact) + n * Math.log (p0) + sumXiLnPi);
+ }
+
+ /**
+ * Computes the probability mass function
+ * of the negative multinomial distribution with parameters
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>), evaluated at
+ * <SPAN CLASS="MATH"><B>x</B></SPAN>.
+ *
+ */
+ public static double prob (double n, double p[], int x[]) {
+ verifParam (n, p);
+ return prob_ (n, p, x);
+ }
+
+
+ private static double cdf_ (double n, double p[], int x[]) {
+ throw new UnsupportedOperationException ("cdf not implemented");
+ }
+
+ /**
+ * Computes the cumulative probability function <SPAN CLASS="MATH"><I>F</I></SPAN> of the
+ * negative multinomial distribution with parameters <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>k</SUB></SPAN>), evaluated at
+ * <SPAN CLASS="MATH"><B>x</B></SPAN>.
+ *
+ */
+ public static double cdf (double n, double p[], int x[]) {
+ verifParam (n, p);
+
+ return cdf_ (n, p, x);
+ }
+
+
+ private static double[] getMean_ (double n, double p[]) {
+ double p0 = 0.0;
+ double sumPi= 0.0;
+ double mean[] = new double[p.length];
+
+ for (int i = 0; i < p.length;i++)
+ sumPi += p[i];
+ p0 = 1.0 - sumPi;
+
+ for (int i = 0; i < p.length; i++)
+ mean[i] = n * p[i] / p0;
+
+ return mean;
+ }
+
+ /**
+ * Computes the mean
+ * <SPAN CLASS="MATH"><I>E</I>[<I>X</I>] = <I>np</I><SUB>i</SUB>/<I>p</I><SUB>0</SUB></SPAN> of the negative multinomial distribution
+ * with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[] getMean (double n, double p[]) {
+ verifParam (n, p);
+
+ return getMean_ (n, p);
+ }
+
+
+ private static double[][] getCovariance_ (double n, double p[]) {
+ double p0 = 0.0;
+ double sumPi= 0.0;
+ double cov[][] = new double[p.length][p.length];
+
+ for (int i = 0; i < p.length;i++)
+ sumPi += p[i];
+ p0 = 1.0 - sumPi;
+
+ for (int i = 0; i < p.length; i++)
+ {
+ for (int j = 0; j < p.length; j++)
+ cov[i][j] = n * p[i] * p[j] / (p0 * p0);
+
+ cov[i][i] = n * p[i] * (p[i] + p0) / (p0 * p0);
+ }
+
+ return cov;
+ }
+
+ /**
+ * Computes the covariance matrix of the negative multinomial distribution
+ * with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[][] getCovariance (double n, double p[]) {
+ verifParam (n, p);
+
+ return getCovariance_ (n, p);
+ }
+
+
+ private static double[][] getCorrelation_ (double n, double[] p) {
+ double corr[][] = new double[p.length][p.length];
+ double sumPi= 0.0;
+ double p0;
+
+ for (int i = 0; i < p.length;i++)
+ sumPi += p[i];
+ p0 = 1.0 - sumPi;
+
+ for (int i = 0; i < p.length; i++) {
+ for (int j = 0; j < p.length; j++)
+ corr[i][j] = Math.sqrt(p[i] * p[j] /((p0 + p[i]) * (p0 + p[j])));
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }
+
+ /**
+ * Computes the correlation matrix of the negative multinomial distribution
+ * with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>).
+ *
+ */
+ public static double[][] getCorrelation (double n, double[] p) {
+ verifParam (n, p);
+ return getCorrelation_ (n, p);
+ }
+
+
+ /**
+ * Estimates and returns the parameters [<SPAN CLASS="MATH">hat(n)</SPAN>, <SPAN CLASS="MATH">hat(p)<SUB>1</SUB></SPAN>, ...,
+ * <SPAN CLASS="MATH">hat(p)<SUB>d</SUB></SPAN>]
+ * of the negative multinomial distribution using the maximum likelihood method.
+ * It uses the <SPAN CLASS="MATH"><I>m</I></SPAN> observations of <SPAN CLASS="MATH"><I>d</I></SPAN> components in table
+ * <TT>x[<SPAN CLASS="MATH"><I>i</I></SPAN>][<SPAN CLASS="MATH"><I>j</I></SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN> and
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…, <I>d</I> - 1</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @param d the dimension of each vector
+ *
+ * @return returns the parameters [<SPAN CLASS="MATH">hat(n)</SPAN>, <SPAN CLASS="MATH">hat(p)<SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH">hat(p)<SUB>d</SUB></SPAN>]
+ *
+ */
+ public static double[] getMLE (int x[][], int m, int d) {
+ int ups[] = new int[m];
+ double mean[] = new double[d];
+
+ int i, j, l;
+ int M;
+ int prop;
+
+ // Initialization
+ for (i = 0; i < d; i++)
+ mean[i] = 0;
+
+ // Ups_j = Sum_k x_ji
+ // mean_i = Sum_m x_ji / m
+ for (j = 0; j < m; j++) {
+ ups[j] = 0;
+ for (i = 0; i < d; i++) {
+ ups[j] += x[j][i];
+ mean[i] += x[j][i];
+ }
+ }
+ for (i = 0; i < d; i++)
+ mean[i] /= m;
+
+/*
+ double var = 0.0;
+ if (d > 1) {
+ // Calcule la covariance 0,1
+ for (j = 0; j < m; j++)
+ var += (x[j][0] - mean[0])*(x[j][1] - mean[1]);
+ var /= m;
+ } else {
+ // Calcule la variance 0
+ for (j = 0; j < m; j++)
+ var += (x[j][0] - mean[0])*(x[j][0] - mean[0]);
+ var /= m;
+ }
+*/
+
+ // M = Max(Ups_j)
+ M = ups[0];
+ for (j = 1; j < m; j++)
+ if (ups[j] > M)
+ M = ups[j];
+
+ if (M >= Integer.MAX_VALUE)
+ throw new IllegalArgumentException("n/p_i too large");
+
+ double Fl[] = new double[M];
+ for (l = 0; l < M; l++) {
+ prop = 0;
+ for (j = 0; j < m; j++)
+ if (ups[j] > l)
+ prop++;
+
+ Fl[l] = (double) prop / (double) m;
+ }
+
+/*
+ // Estime la valeur initiale de n pour brentDekker: pourrait
+ // accélérer brentDekker (gam0/1000, gam0*1000, f, 1e-5).
+ // Reste à bien tester.
+ if (d > 1) {
+ double gam0 = mean[0] * mean[1] / var;
+ System.out.println ("gam0 = " + gam0);
+ } else {
+ double t = var/mean[0] - 1.0;
+ double gam0 = mean[0] / t;
+ System.out.println ("gam0 = " + gam0);
+ }
+*/
+ double parameters[] = new double[d + 1];
+ Function f = new Function (m, (int)M, ups, Fl);
+ parameters[0] = RootFinder.brentDekker (1e-9, 1e9, f, 1e-5);
+
+ double lambda[] = new double[d];
+ double sumLambda = 0.0;
+ for (i = 0; i < d; i++) {
+ lambda[i] = mean[i] / parameters[0];
+ sumLambda += lambda[i];
+ }
+
+ for (i = 0; i < d; i++) {
+ parameters[i + 1] = lambda[i] / (1.0 + sumLambda);
+ if (parameters[i + 1] > 1.0)
+ throw new IllegalArgumentException("p_i > 1");
+ }
+
+ return parameters;
+ }
+
+
+ /**
+ * Estimates and returns the parameter
+ * <SPAN CLASS="MATH"><I>ν</I> = 1/hat(n)</SPAN>
+ * of the negative multinomial distribution using the maximum likelihood method.
+ * It uses the <SPAN CLASS="MATH"><I>m</I></SPAN> observations of <SPAN CLASS="MATH"><I>d</I></SPAN> components in table
+ * <TT>x[<SPAN CLASS="MATH"><I>i</I></SPAN>][<SPAN CLASS="MATH"><I>j</I></SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 0, 1,…, <I>m</I> - 1</SPAN> and
+ * <SPAN CLASS="MATH"><I>j</I> = 0, 1,…, <I>d</I> - 1</SPAN>.
+ *
+ * @param x the list of observations used to evaluate parameters
+ *
+ * @param m the number of observations used to evaluate parameters
+ *
+ * @param d the dimension of each vector
+ *
+ * @return returns the parameter <SPAN CLASS="MATH">1/hat(n)</SPAN>
+ *
+ */
+ public static double getMLEninv (int x[][], int m, int d) {
+ int ups[] = new int[m];
+ double mean[] = new double[d];
+ int i, j, l;
+ int M;
+ int prop;
+
+ // Initialization
+ for (i = 0; i < d; i++)
+ mean[i] = 0;
+
+ // Ups_j = Sum_k x_ji
+ // mean_i = Sum_m x_ji / m
+ for (j = 0; j < m; j++) {
+ ups[j] = 0;
+ for (i = 0; i < d; i++) {
+ ups[j] += x[j][i];
+ mean[i] += x[j][i];
+ }
+ }
+ for (i = 0; i < d; i++)
+ mean[i] /= m;
+
+ // M = Max(Ups_j)
+ M = ups[0];
+ for (j = 1; j < m; j++)
+ if (ups[j] > M)
+ M = ups[j];
+
+ if (M >= Integer.MAX_VALUE)
+ throw new IllegalArgumentException("n/p_i too large");
+
+ double Fl[] = new double[M];
+ for (l = 0; l < M; l++) {
+ prop = 0;
+ for (j = 0; j < m; j++)
+ if (ups[j] > l)
+ prop++;
+
+ Fl[l] = (double) prop / (double) m;
+ }
+
+ FuncInv f = new FuncInv (m, M, ups, Fl);
+ double nu = RootFinder.brentDekker (1.0e-8, 1.0e8, f, 1e-5);
+ return nu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public double getGamma() {
+ return n;
+ }
+
+
+ /**
+ * Returns the parameters (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) of this object.
+ *
+ */
+ public double[] getP() {
+ return p;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and (<SPAN CLASS="MATH"><I>p</I><SUB>1</SUB></SPAN>, ..., <SPAN CLASS="MATH"><I>p</I><SUB>d</SUB></SPAN>) of this object.
+ *
+ */
+ public void setParams (double n, double p[]) {
+ if (n <= 0.0)
+ throw new IllegalArgumentException("n <= 0");
+
+ this.n = n;
+ this.dimension = p.length;
+ this.p = new double[dimension];
+
+ double sumPi = 0.0;
+ for (int i = 0; i < dimension; i++) {
+ if ((p[i] < 0) || (p[i] >= 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+
+ sumPi += p[i];
+ this.p[i] = p[i];
+ }
+
+ if (sumPi >= 1.0)
+ throw new IllegalArgumentException("p is not a probability vector");
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/NegativeMultinomialDist.tex b/source/umontreal/iro/lecuyer/probdistmulti/NegativeMultinomialDist.tex
new file mode 100644
index 0000000..58533cf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/NegativeMultinomialDist.tex
@@ -0,0 +1,542 @@
+\defmodule{NegativeMultinomialDist}
+
+Implements the class \class{DiscreteDistributionIntMulti} for the
+{\em negative multinomial} distribution with parameters $n > 0$ and
+($p_1, \ldots, p_d$) such that all $0<p_i<1$ and $\sum_{i=1}^{d} p_i < 1$.
+The probability mass function is \cite{tJOH69a}
+\begin{htmlonly}
+\eq
+ P[X = (x_1, \ldots ,x_d)] = \left({\Gamma\left(n + \sum_{i=1}^{d} x_i\right)}/
+ {\Gamma(n)}\right)
+ p_0^{n}\prod_{i=1}^{d} p_i^{x_i}/x_i!
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ P[X = (x_1, \ldots,x_d)] = \frac{\Gamma\left(n +
+ \sum_{i=1}^{d} x_i\right) p_0^{n}}
+ {\Gamma(n)}
+ \prod_{i=1}^{d} \frac{p_i^{x_i}}{x_i!}
+\eqlabel{eq:fNegativeMultinomial}
+\endeq
+\end{latexonly}
+where $p_0 = 1 - \sum_{i=1}^{d} p_i$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NegativeMultinomialDist
+ * Description: negative multinomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.util.RootFinder;
+import umontreal.iro.lecuyer.functions.MathFunction;
+\end{hide}
+
+public class NegativeMultinomialDist extends DiscreteDistributionIntMulti \begin{hide} {
+ protected double n;
+ protected double p[];
+
+ private static class Function implements MathFunction {
+ protected double Fl[];
+ protected int ups[];
+ protected int k;
+ protected int M;
+ protected int sumUps;
+
+ public Function (int k, int m, int ups[], double Fl[]) {
+ this.k = k;
+ this.M = m;
+
+ this.Fl = new double[Fl.length];
+ System.arraycopy (Fl, 0, this.Fl, 0, Fl.length);
+ this.ups = new int[ups.length];
+ System.arraycopy (ups, 0, this.ups, 0, ups.length);
+
+ sumUps = 0;
+ for (int i = 0; i < ups.length; i++)
+ sumUps += ups[i];
+ }
+
+ public double evaluate (double gamma) {
+ double sum = 0.0;
+ for (int l = 0; l < M; l++)
+ sum += (Fl[l] / (gamma + (double) l));
+ return (sum - Math.log1p (sumUps / (k * gamma)));
+ }
+ }
+
+
+ private static class FuncInv extends Function implements MathFunction {
+
+ public FuncInv (int k, int m, int ups[], double Fl[]) {
+ super (k, m, ups, Fl);
+ }
+
+ public double evaluate (double nu) {
+ double sum = 0.0;
+ for (int l = 0; l < M; l++)
+ sum += Fl[l] / (1.0 + nu * l);
+ return (sum *nu - Math.log1p (sumUps * nu / k));
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NegativeMultinomialDist (double n, double p[]) \begin{hide} {
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a \texttt{NegativeMultinomialDist} object with parameters $n$
+ and ($p_1$, \ldots, $p_d$) such that $\sum_{i=1}^{d} p_i < 1$,
+ as described above. We have $p_i = $ \texttt{p[i-1]}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public double prob (int x[]) {
+ return prob_ (n, p, x);
+ }
+/*
+ public double cdf (int x[]) {
+ throw new UnsupportedOperationException ("cdf not implemented");
+ }
+*/
+ public double[] getMean() {
+ return getMean_ (n, p);
+ }
+
+ public double[][] getCovariance() {
+ return getCovariance_ (n, p);
+ }
+
+ public double[][] getCorrelation() {
+ return getCorrelation_ (n, p);
+ }
+
+ private static void verifParam (double n, double p[]) {
+ double sumPi = 0.0;
+
+ if (n <= 0.0)
+ throw new IllegalArgumentException("n <= 0");
+
+ for (int i = 0; i < p.length;i++) {
+ if ((p[i] < 0) || (p[i] >= 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+
+ sumPi += p[i];
+ }
+ if (sumPi >= 1.0)
+ throw new IllegalArgumentException("p is not a probability vector");
+ }
+
+ private static double prob_ (double n, double p[], int x[]) {
+ double p0 = 0.0;
+ double sumPi= 0.0;
+ double sumXi= 0.0;
+ double sumLnXiFact = 0.0;
+ double sumXiLnPi = 0.0;
+
+ if (x.length != p.length)
+ throw new IllegalArgumentException ("x and p must have the same size");
+
+ for (int i = 0; i < p.length;i++)
+ {
+ sumPi += p[i];
+ sumXi += x[i];
+ sumLnXiFact += Num.lnFactorial (x[i]);
+ sumXiLnPi += x[i] * Math.log (p[i]);
+ }
+ p0 = 1.0 - sumPi;
+
+ return Math.exp (Num.lnGamma (n + sumXi) - (Num.lnGamma (n) +
+ sumLnXiFact) + n * Math.log (p0) + sumXiLnPi);
+ }\end{hide}
+
+ public static double prob (double n, double p[], int x[])\begin{hide} {
+ verifParam (n, p);
+ return prob_ (n, p, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the probability mass function (\ref{eq:fNegativeMultinomial})
+ of the negative multinomial distribution with parameters
+ $n$ and ($p_1$, \ldots, $p_d$), evaluated at $\mathbf{x}$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double cdf_ (double n, double p[], int x[]) {
+ throw new UnsupportedOperationException ("cdf not implemented");
+ }\end{hide}
+
+ public static double cdf (double n, double p[], int x[])\begin{hide} {
+ verifParam (n, p);
+
+ return cdf_ (n, p, x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the cumulative probability function $F$ of the
+ negative multinomial distribution with parameters $n$
+ and ($p_1$, \ldots, $p_k$), evaluated at $\mathbf{x}$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[] getMean_ (double n, double p[]) {
+ double p0 = 0.0;
+ double sumPi= 0.0;
+ double mean[] = new double[p.length];
+
+ for (int i = 0; i < p.length;i++)
+ sumPi += p[i];
+ p0 = 1.0 - sumPi;
+
+ for (int i = 0; i < p.length; i++)
+ mean[i] = n * p[i] / p0;
+
+ return mean;
+ }\end{hide}
+
+ public static double[] getMean (double n, double p[])\begin{hide} {
+ verifParam (n, p);
+
+ return getMean_ (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the mean $E[X] = n p_i / p_0$ of the negative multinomial distribution
+ with parameters $n$ and ($p_1$, \ldots, $p_d$).
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[][] getCovariance_ (double n, double p[]) {
+ double p0 = 0.0;
+ double sumPi= 0.0;
+ double cov[][] = new double[p.length][p.length];
+
+ for (int i = 0; i < p.length;i++)
+ sumPi += p[i];
+ p0 = 1.0 - sumPi;
+
+ for (int i = 0; i < p.length; i++)
+ {
+ for (int j = 0; j < p.length; j++)
+ cov[i][j] = n * p[i] * p[j] / (p0 * p0);
+
+ cov[i][i] = n * p[i] * (p[i] + p0) / (p0 * p0);
+ }
+
+ return cov;
+ }\end{hide}
+
+ public static double[][] getCovariance (double n, double p[])\begin{hide} {
+ verifParam (n, p);
+
+ return getCovariance_ (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the covariance matrix of the negative multinomial distribution
+ with parameters $n$ and ($p_1$, \ldots, $p_d$).
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private static double[][] getCorrelation_ (double n, double[] p) {
+ double corr[][] = new double[p.length][p.length];
+ double sumPi= 0.0;
+ double p0;
+
+ for (int i = 0; i < p.length;i++)
+ sumPi += p[i];
+ p0 = 1.0 - sumPi;
+
+ for (int i = 0; i < p.length; i++) {
+ for (int j = 0; j < p.length; j++)
+ corr[i][j] = Math.sqrt(p[i] * p[j] /((p0 + p[i]) * (p0 + p[j])));
+ corr[i][i] = 1.0;
+ }
+ return corr;
+ }\end{hide}
+
+ public static double[][] getCorrelation (double n, double[] p)\begin{hide} {
+ verifParam (n, p);
+ return getCorrelation_ (n, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes the correlation matrix of the negative multinomial distribution
+ with parameters $n$ and ($p_1$, \ldots, $p_d$).
+\end{tabb}
+\begin{code}
+
+ public static double[] getMLE (int x[][], int m, int d)\begin{hide} {
+ int ups[] = new int[m];
+ double mean[] = new double[d];
+
+ int i, j, l;
+ int M;
+ int prop;
+
+ // Initialization
+ for (i = 0; i < d; i++)
+ mean[i] = 0;
+
+ // Ups_j = Sum_k x_ji
+ // mean_i = Sum_m x_ji / m
+ for (j = 0; j < m; j++) {
+ ups[j] = 0;
+ for (i = 0; i < d; i++) {
+ ups[j] += x[j][i];
+ mean[i] += x[j][i];
+ }
+ }
+ for (i = 0; i < d; i++)
+ mean[i] /= m;
+
+/*
+ double var = 0.0;
+ if (d > 1) {
+ // Calcule la covariance 0,1
+ for (j = 0; j < m; j++)
+ var += (x[j][0] - mean[0])*(x[j][1] - mean[1]);
+ var /= m;
+ } else {
+ // Calcule la variance 0
+ for (j = 0; j < m; j++)
+ var += (x[j][0] - mean[0])*(x[j][0] - mean[0]);
+ var /= m;
+ }
+*/
+
+ // M = Max(Ups_j)
+ M = ups[0];
+ for (j = 1; j < m; j++)
+ if (ups[j] > M)
+ M = ups[j];
+
+ if (M >= Integer.MAX_VALUE)
+ throw new IllegalArgumentException("n/p_i too large");
+
+ double Fl[] = new double[M];
+ for (l = 0; l < M; l++) {
+ prop = 0;
+ for (j = 0; j < m; j++)
+ if (ups[j] > l)
+ prop++;
+
+ Fl[l] = (double) prop / (double) m;
+ }
+
+/*
+ // Estime la valeur initiale de n pour brentDekker: pourrait
+ // accélérer brentDekker (gam0/1000, gam0*1000, f, 1e-5).
+ // Reste à bien tester.
+ if (d > 1) {
+ double gam0 = mean[0] * mean[1] / var;
+ System.out.println ("gam0 = " + gam0);
+ } else {
+ double t = var/mean[0] - 1.0;
+ double gam0 = mean[0] / t;
+ System.out.println ("gam0 = " + gam0);
+ }
+*/
+ double parameters[] = new double[d + 1];
+ Function f = new Function (m, (int)M, ups, Fl);
+ parameters[0] = RootFinder.brentDekker (1e-9, 1e9, f, 1e-5);
+
+ double lambda[] = new double[d];
+ double sumLambda = 0.0;
+ for (i = 0; i < d; i++) {
+ lambda[i] = mean[i] / parameters[0];
+ sumLambda += lambda[i];
+ }
+
+ for (i = 0; i < d; i++) {
+ parameters[i + 1] = lambda[i] / (1.0 + sumLambda);
+ if (parameters[i + 1] > 1.0)
+ throw new IllegalArgumentException("p_i > 1");
+ }
+
+ return parameters;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates and returns the parameters [$\hat{n}$, $\hat{p}_1$, \ldots,
+ $\hat{p}_d$]
+ of the negative multinomial distribution using the maximum likelihood method.
+ It uses the $m$ observations of $d$ components in table
+ \texttt{x[$i$][$j$]}, $i = 0, 1, \ldots, m-1$ and $j = 0, 1, \ldots, d-1$.
+ \begin{detailed}
+ The equations of the maximum likelihood are defined in \cite{tJOH69a}:
+ \begin{eqnarray*}
+ \sum_{s=1}^{M} \frac{F_s}{(\hat{n} + s - 1)} & = & \ln \left(1 +
+ \frac{1}{\hat{n} m} \sum_{j=1}^{m} \Upsilon_j \right)\\[8pt]
+ p_i & = & \frac{\lambda_i}{1 + \sum_{j=1}^{d} \lambda_j}
+ \qquad \mbox{for } i=1, \ldots,d
+ \end{eqnarray*}
+ where
+ \begin{eqnarray*}
+ \lambda_i & = & \frac{\sum_{j=1}^{m} X_{i,j}}{\hat{n} m}
+ \qquad \mbox{for } i=1, \ldots,d\\
+ \Upsilon_j & = & \sum_{i=1}^{d} X_{i,j} \qquad \mbox{for } j=1, \ldots,m\\
+ F_s & = & \frac{1}{m} \sum_{j=1}^{m} \mbox{\textbf{1}}\{\Upsilon_j \ge s\}
+ \qquad \mbox{for } s=1, \ldots,M\\
+ M & = & \max_j \{\Upsilon_j\}
+ \end{eqnarray*}
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \param{d}{the dimension of each vector}
+ \return{returns the parameters [$\hat{n}$, $\hat{p}_1$, \ldots, $\hat{p}_d$]}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMLEninv (int x[][], int m, int d)\begin{hide} {
+ int ups[] = new int[m];
+ double mean[] = new double[d];
+ int i, j, l;
+ int M;
+ int prop;
+
+ // Initialization
+ for (i = 0; i < d; i++)
+ mean[i] = 0;
+
+ // Ups_j = Sum_k x_ji
+ // mean_i = Sum_m x_ji / m
+ for (j = 0; j < m; j++) {
+ ups[j] = 0;
+ for (i = 0; i < d; i++) {
+ ups[j] += x[j][i];
+ mean[i] += x[j][i];
+ }
+ }
+ for (i = 0; i < d; i++)
+ mean[i] /= m;
+
+ // M = Max(Ups_j)
+ M = ups[0];
+ for (j = 1; j < m; j++)
+ if (ups[j] > M)
+ M = ups[j];
+
+ if (M >= Integer.MAX_VALUE)
+ throw new IllegalArgumentException("n/p_i too large");
+
+ double Fl[] = new double[M];
+ for (l = 0; l < M; l++) {
+ prop = 0;
+ for (j = 0; j < m; j++)
+ if (ups[j] > l)
+ prop++;
+
+ Fl[l] = (double) prop / (double) m;
+ }
+
+ FuncInv f = new FuncInv (m, M, ups, Fl);
+ double nu = RootFinder.brentDekker (1.0e-8, 1.0e8, f, 1e-5);
+ return nu;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Estimates and returns the parameter $\nu = 1/\hat{n}$
+ of the negative multinomial distribution using the maximum likelihood method.
+ It uses the $m$ observations of $d$ components in table
+ \texttt{x[$i$][$j$]}, $i = 0, 1, \ldots, m-1$ and $j = 0, 1, \ldots, d-1$.
+ \begin{detailed}
+ The equation of maximum likelihood is defined as:
+ \[
+ \sum_{s=1}^{M} \frac{\nu F_s}{(1 + \nu(s - 1))} = \ln \left(1 +
+ \frac{\nu}{m} \sum_{j=1}^{m} \Upsilon_j \right)\\[8pt]
+ \]
+ where the symbols are defined as in \method{getMLE}{int[][], int, int}{}.
+ \end{detailed}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the list of observations used to evaluate parameters}
+ \param{m}{the number of observations used to evaluate parameters}
+ \param{d}{the dimension of each vector}
+ \return{returns the parameter $1/\hat{n}$}
+\end{htmlonly}
+\begin{code}
+
+ public double getGamma()\begin{hide} {
+ return n;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double[] getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the parameters ($p_1$, \ldots, $p_d$) of this object.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double n, double p[])\begin{hide} {
+ if (n <= 0.0)
+ throw new IllegalArgumentException("n <= 0");
+
+ this.n = n;
+ this.dimension = p.length;
+ this.p = new double[dimension];
+
+ double sumPi = 0.0;
+ for (int i = 0; i < dimension; i++) {
+ if ((p[i] < 0) || (p[i] >= 1))
+ throw new IllegalArgumentException("p is not a probability vector");
+
+ sumPi += p[i];
+ this.p[i] = p[i];
+ }
+
+ if (sumPi >= 1.0)
+ throw new IllegalArgumentException("p is not a probability vector");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $n$ and ($p_1$, \ldots, $p_d$) of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/continuous.tex b/source/umontreal/iro/lecuyer/probdistmulti/continuous.tex
new file mode 100644
index 0000000..c94c136
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/continuous.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Continuous Distributions}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/continuous2dim.tex b/source/umontreal/iro/lecuyer/probdistmulti/continuous2dim.tex
new file mode 100644
index 0000000..b41316b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/continuous2dim.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{2-Dimensional Continuous Distributions}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/discrete.tex b/source/umontreal/iro/lecuyer/probdistmulti/discrete.tex
new file mode 100644
index 0000000..1cfdf97
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/discrete.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Discrete Distributions over Integers}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/general.tex b/source/umontreal/iro/lecuyer/probdistmulti/general.tex
new file mode 100644
index 0000000..df08c3a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/general.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{General Classes}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/guideprobdistmulti.bbl b/source/umontreal/iro/lecuyer/probdistmulti/guideprobdistmulti.bbl
new file mode 100644
index 0000000..c38a881
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/guideprobdistmulti.bbl
@@ -0,0 +1,37 @@
+\begin{thebibliography}{1}
+
+\bibitem{ccAVR04a}
+A.~N. Avramidis, A.~Deslauriers, and P.~L'Ecuyer.
+\newblock Modeling daily arrivals to a telephone call center.
+\newblock {\em Management Science}, 50(7):896--908, 2004.
+
+\bibitem{tDON73a}
+T.~G. Donnelly.
+\newblock Algorithm 462: Bivariate normal distribution.
+\newblock {\em Communications of the ACM}, 16(10):638, 1973.
+
+\bibitem{tDRE90a}
+Z.~Drezner and G.~O. Wesolowsky.
+\newblock On the computation of the bivariate normal integral.
+\newblock {\em Journal of Statistical Computation and Simulation}, 35:101--107,
+ 1990.
+
+\bibitem{tGEN04a}
+A.~Genz.
+\newblock Numerical computation of rectangular bivariate and trivariate normal
+ and $t$ probabilities.
+\newblock {\em Statistics and Computing}, 14:151--160, 2004.
+\newblock See \url{http://www.math.wsu.edu/faculty/genz/homepage}.
+
+\bibitem{tJOH69a}
+N.~L. Johnson and S.~Kotz.
+\newblock {\em Distributions in Statistics: Discrete Distributions}.
+\newblock Houghton Mifflin, Boston, 1969.
+
+\bibitem{tJOH72a}
+N.~L. Johnson and S.~Kotz.
+\newblock {\em Distributions in Statistics: Continuous Multivariate
+ Distributions}.
+\newblock John Wiley, New York, NY, 1972.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/guideprobdistmulti.tex b/source/umontreal/iro/lecuyer/probdistmulti/guideprobdistmulti.tex
new file mode 100644
index 0000000..9ac80f1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/guideprobdistmulti.tex
@@ -0,0 +1,74 @@
+\documentclass[12pt]{article}
+
+\usepackage{amssymb}
+\usepackage{url}
+\usepackage{alltt}
+\usepackage{html}
+% \usepackage{tcode}
+\usepackage{ssj}
+
+\newcommand{\cH}{\latex{\mathcal{H}}\html{H}}
+
+\mytwoheads\dateheadtrue
+\detailedfalse
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+
+\title{probdistmulti}{Multivariate Probability Distributions}
+
+%\footnote{\normalsize
+% Mathieu Bague and Richard Simard
+% have participated in the design and implementation of ProbDistMulti}
+
+This package provides tools to compute densities, mass functions,
+distribution functions
+for various continuous and discrete multivariate probability distributions.
+% It also offers facilities for performing and
+% reporting different types of univariate goodness-of-fit tests.
+
+\vfill
+\end{titlepage}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+
+\include{overview}
+
+%% General
+\include{general}
+\include{DiscreteDistributionIntMulti}
+\include{ContinuousDistributionMulti}
+\include{ContinuousDistribution2Dim}
+
+\include{discrete}
+\include{MultinomialDist}
+\include{NegativeMultinomialDist}
+
+\include{continuous2dim}
+%% Continuous 2-Dimensionnal distributions
+\include{BiNormalDist}
+\include{BiNormalGenzDist}
+\include{BiNormalDonnellyDist}
+\include{BiStudentDist}
+
+\include{continuous}
+\include{MultiNormalDist}
+\include{DirichletDist}
+
+\include{norta/overview}
+%\include{norta/NortaInitDisc}
+%\include{norta/NI1}
+%\include{norta/NI2a}
+%\include{norta/NI2b}
+%\include{norta/NI3}
+
+\bibliographystyle{plain}
+\bibliography{stat,random,simul,math,ift,callc}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI1.java b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI1.java
new file mode 100644
index 0000000..83b5db0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI1.java
@@ -0,0 +1,192 @@
+
+
+/*
+ * Class: NI1
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti.norta;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements the algorithm NI1 based on Brent method for
+ * root-finding. The root is found with accuracy = tolerance.
+ *
+ */
+public class NI1 extends NortaInitDisc
+{
+ private double tolerance; /* Desired accuracy for the root-finder
+ algorithm (epsilon in paragraph
+ "Method NI1" of section 3 in the paper).*/
+
+
+
+ /**
+ * Constructor of the class NI1 with the target rank correlation rX,
+ * the two discrete marginals dist1 and dist2,
+ * the parameter for truncation tr and the accuracy tolerance,
+ * which corresponds to epsilon in the paper
+ * (paragraph "Method NI1" of section 3).
+ *
+ */
+ public NI1 (double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, double tolerance)
+ {
+ super(rX, dist1, dist2, tr);
+ this.tolerance = tolerance;
+ computeParams();
+ }
+
+
+ /**
+ * Computes and returns the correlation <SPAN CLASS="MATH"><I>ρ</I><SUB>Z</SUB></SPAN> using the algorithm NI1.
+ *
+ */
+ public double computeCorr()
+ {
+ final double ITMAX = 100; // Maximum number of iterations
+ final double EPS = 1.0e-15; // Machine accuracy
+ double b; /* Latest iterate and closest approximation of the root
+ (the returned solution at the end). */
+ double a; // Previous iterate (previous value of b).
+ double c; /* Previous or older iterate so that f(b) and f(c)
+ have opposite signs (may coincide with a). */
+ double fa, fb, fc; /* Evaluations of the function f at points
+ a, b and c. */
+ double tolerance1; /* Final tolerance wich involves the machine
+ accuracy and the initial desired tolerance
+ (epsilon). */
+ /* * a, b, c, fa, fb, fc, and tolerance correspond to a, b, c, f(a)
+ f(b), f(c) and epsilon, respectively, in the paper
+ (paragraph "Method NI1" of section 3). */
+ double x1, x2; // Left and right endpoints of initial search interval
+ double pp, q, rrr, s; /* Parameters to compute inverse quadratic
+ interpolation. */
+ double xm; // Bisection point.
+ double min1, min2; /* Criterias to check whether inverse quadratic
+ interpolation can be performed or not. */
+ double e = 0.0, d = 0.0; /* Parameters to specify bounding interval
+ and whether bisection or inverse
+ quadratic interpolation was performed
+ at one iteration before the last one. */
+ // Precompute constants.
+ double cc = rX * sd1 * sd2;
+ double ccc = cc + mu1 * mu2;
+
+ if (rX == 0)
+ return 0.0;
+ if (rX > 0) { // Orient the search and initialize a, b, c
+ x1 = 0.0;
+ x2 = 1.0;
+ a = x1;
+ b = x2;
+ c = x2;
+ fa = - cc;
+ fb = integ (b) - ccc;
+ } else {
+ x1 = -1.0;
+ x2 = 0.0;
+ a = x1;
+ b = x2;
+ c = x2;
+ fa = integ (a) - ccc;
+ fb = - cc;
+ }
+ fc = fb;
+
+ for (int i = 1; i <= ITMAX; i++) { // Begin the search
+ if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
+ // Rename a, b, c and adjust bounding interval d
+ c = a;
+ fc = fa;
+ e = d = b - a;
+ }
+ if (Math.abs (fc) < Math.abs (fb)) {
+ a = b;
+ b = c;
+ c = a;
+ fa = fb;
+ fb = fc;
+ fc = fa;
+ }
+
+ // Convergence check
+ tolerance1 = 2.0 * EPS * Math.abs (b) + 0.5 * tolerance;
+ xm = 0.5 * (c - b);
+ if (Math.abs (xm) <= tolerance1 || fb == 0.0)
+ return b;
+
+ if (Math.abs (e) >= tolerance1 && Math.abs (fa) > Math.abs (fb)) {
+ s = fb / fa; // Attempt inverse quadratic interpolation
+ if (a == c) {
+ pp = 2.0 * xm * s;
+ q = 1.0 - s;
+ } else {
+ q = fa / fc;
+ rrr = fb / fc;
+ pp = s * (2.0 * xm * q * (q - rrr) - (b - a) * (rrr - 1.0));
+ q = (q - 1.0) * (rrr - 1.0) * (s - 1.0);
+ }
+ if (pp > 0.0)
+ q = -q; // Check whether in bounds
+ pp = Math.abs (pp);
+ min1 = 3.0 * xm * q - Math.abs (tolerance1 * q);
+ min2 = Math.abs (e * q);
+ if (2.0 * pp < (min1 < min2 ? min1 : min2)) {
+ e = d; // Accept interpolation
+ d = pp / q;
+ } else { // Interpolation failed, use bisection
+ d = xm;
+ e = d;
+ }
+ } else { // Bounds decreasing too slowly, use bisection
+ d = xm;
+ e = d;
+ }
+ a = b; // a becomes the best trial
+ fa = fb;
+ if (Math.abs (d) > tolerance1)
+ b += d; // Evaluate the new trial root
+ else {
+ if (xm > 0)
+ b += Math.abs (tolerance1);
+ else
+ b += -Math.abs (tolerance1);
+ }
+ fb = integ (b) - ccc;
+ }
+
+ return b;
+ }
+
+
+ public String toString()
+ {
+ String desc = super.toString();
+ desc += "tolerance : " + tolerance + "\n";
+ return desc;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI1.tex b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI1.tex
new file mode 100644
index 0000000..42fdede
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI1.tex
@@ -0,0 +1,202 @@
+\defmodule {NI1}
+ This class implements the algorithm NI1 based on Brent method for
+root-finding. The root is found with accuracy = tolerance.
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NI1
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti.norta;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NI1 extends NortaInitDisc\begin{hide}
+{
+ private double tolerance; /* Desired accuracy for the root-finder
+ algorithm (epsilon in paragraph
+ "Method NI1" of section 3 in the paper).*/
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NI1 (double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, double tolerance)\begin{hide}
+ {
+ super(rX, dist1, dist2, tr);
+ this.tolerance = tolerance;
+ computeParams();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructor of the class NI1 with the target rank correlation rX,
+ the two discrete marginals dist1 and dist2,
+ the parameter for truncation tr and the accuracy tolerance,
+ which corresponds to epsilon in the paper
+ (paragraph "Method NI1" of section 3).
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double computeCorr()\begin{hide}
+ {
+ final double ITMAX = 100; // Maximum number of iterations
+ final double EPS = 1.0e-15; // Machine accuracy
+ double b; /* Latest iterate and closest approximation of the root
+ (the returned solution at the end). */
+ double a; // Previous iterate (previous value of b).
+ double c; /* Previous or older iterate so that f(b) and f(c)
+ have opposite signs (may coincide with a). */
+ double fa, fb, fc; /* Evaluations of the function f at points
+ a, b and c. */
+ double tolerance1; /* Final tolerance wich involves the machine
+ accuracy and the initial desired tolerance
+ (epsilon). */
+ /** a, b, c, fa, fb, fc, and tolerance correspond to a, b, c, f(a)
+ f(b), f(c) and epsilon, respectively, in the paper
+ (paragraph "Method NI1" of section 3). */
+ double x1, x2; // Left and right endpoints of initial search interval
+ double pp, q, rrr, s; /* Parameters to compute inverse quadratic
+ interpolation. */
+ double xm; // Bisection point.
+ double min1, min2; /* Criterias to check whether inverse quadratic
+ interpolation can be performed or not. */
+ double e = 0.0, d = 0.0; /* Parameters to specify bounding interval
+ and whether bisection or inverse
+ quadratic interpolation was performed
+ at one iteration before the last one. */
+ // Precompute constants.
+ double cc = rX * sd1 * sd2;
+ double ccc = cc + mu1 * mu2;
+
+ if (rX == 0)
+ return 0.0;
+ if (rX > 0) { // Orient the search and initialize a, b, c
+ x1 = 0.0;
+ x2 = 1.0;
+ a = x1;
+ b = x2;
+ c = x2;
+ fa = - cc;
+ fb = integ (b) - ccc;
+ } else {
+ x1 = -1.0;
+ x2 = 0.0;
+ a = x1;
+ b = x2;
+ c = x2;
+ fa = integ (a) - ccc;
+ fb = - cc;
+ }
+ fc = fb;
+
+ for (int i = 1; i <= ITMAX; i++) { // Begin the search
+ if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
+ // Rename a, b, c and adjust bounding interval d
+ c = a;
+ fc = fa;
+ e = d = b - a;
+ }
+ if (Math.abs (fc) < Math.abs (fb)) {
+ a = b;
+ b = c;
+ c = a;
+ fa = fb;
+ fb = fc;
+ fc = fa;
+ }
+
+ // Convergence check
+ tolerance1 = 2.0 * EPS * Math.abs (b) + 0.5 * tolerance;
+ xm = 0.5 * (c - b);
+ if (Math.abs (xm) <= tolerance1 || fb == 0.0)
+ return b;
+
+ if (Math.abs (e) >= tolerance1 && Math.abs (fa) > Math.abs (fb)) {
+ s = fb / fa; // Attempt inverse quadratic interpolation
+ if (a == c) {
+ pp = 2.0 * xm * s;
+ q = 1.0 - s;
+ } else {
+ q = fa / fc;
+ rrr = fb / fc;
+ pp = s * (2.0 * xm * q * (q - rrr) - (b - a) * (rrr - 1.0));
+ q = (q - 1.0) * (rrr - 1.0) * (s - 1.0);
+ }
+ if (pp > 0.0)
+ q = -q; // Check whether in bounds
+ pp = Math.abs (pp);
+ min1 = 3.0 * xm * q - Math.abs (tolerance1 * q);
+ min2 = Math.abs (e * q);
+ if (2.0 * pp < (min1 < min2 ? min1 : min2)) {
+ e = d; // Accept interpolation
+ d = pp / q;
+ } else { // Interpolation failed, use bisection
+ d = xm;
+ e = d;
+ }
+ } else { // Bounds decreasing too slowly, use bisection
+ d = xm;
+ e = d;
+ }
+ a = b; // a becomes the best trial
+ fa = fb;
+ if (Math.abs (d) > tolerance1)
+ b += d; // Evaluate the new trial root
+ else {
+ if (xm > 0)
+ b += Math.abs (tolerance1);
+ else
+ b += -Math.abs (tolerance1);
+ }
+ fb = integ (b) - ccc;
+ }
+
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the correlation $\rho_Z$ using the algorithm NI1.
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide}
+ {
+ String desc = super.toString();
+ desc += "tolerance : " + tolerance + "\n";
+ return desc;
+ }\end{hide}
+\end{code}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2a.java b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2a.java
new file mode 100644
index 0000000..740fe6f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2a.java
@@ -0,0 +1,206 @@
+
+
+/*
+ * Class: NI2a
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti.norta;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements the algorithm NI2a wich uses the derivative and
+ * simple integration.
+ *
+ */
+public class NI2a extends NortaInitDisc
+{
+ private double h; /* Predefined step size for the integration-grid
+ spacing (also named h in the paper, paragraph
+ "Method NI2" of section 3). */
+ private double delta; /* Small positive parameter to make sure that
+ rho_m is not too close to 1 or -1;
+ (also named delta in the paper, paragraph "
+ Method NI2" of section 3). */
+
+
+
+ /**
+ * Constructor of the class NI2a with the target rank correlation rX,
+ * the two discrete marginals dist1 and dist2,
+ * the parameter for truncation tr, and the specific
+ * parameters h and delta, which correspond to h and delta in the paper
+ * (paragraph "Method NI2" of section 3).
+ *
+ */
+ public NI2a(double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, double h,
+ double delta)
+ {
+ super(rX, dist1, dist2, tr);
+ this.h = h;
+ this. delta = delta;
+ computeParams();
+ }
+
+
+ /**
+ * Computes and returns the correlation <SPAN CLASS="MATH"><I>ρ</I><SUB>Z</SUB></SPAN> using the algorithm NI2a.
+ *
+ */
+ public double computeCorr()
+ {
+ // x, y and c coefficients used for quadratic interpolation.
+ double[] x = new double[3];
+ double[] y = new double[3];
+ double[] c = new double[3];
+ double xtemp = 0.0, temp = 0.0; /* Values of rho and the recursive
+ quantity I_k, given in paragraph
+ "Method NI2" of section 3 in the
+ paper, 2 iterations before the
+ last one. */
+ double xold, iold; /* Values of rho and I_k at one iteration before
+ the last one.*/
+ double xnew, inew; // Values of rho and I_k at the last iteration.
+ double dold, dmid, dnew; /* Values of the derivative function g'
+ at points xold, xmid (xold+h) and xnew.
+ They correspond to g'(rho_0+2kh-2h),
+ g'(rho_0+2kh-h) and g'(rho_0+2kh) in the
+ formula of I_k in the paper
+ (paragraph "Method NI2" of section 3). */
+
+ double d = 0.0; // Integration distance.
+ int m; // Number of iterations needed.
+ /* * d and m correspond to d and m given in the third paragraph of
+ section 4 in the paper. */
+ double b = 0.0; // The returned solution.
+ double h2 = 0.0, hd3 = 0.0; // Precompute constants.
+ double lrx = (integ ( -1) - mu1 * mu2) / sd1 * sd2; // Min.correlation.
+ double urx = (integ (1) - mu1 * mu2) / sd1 * sd2; // Max.correlation.
+ double rho1 = 2 * Math.sin (Math.PI * rX / 6); // The initial guess.
+ double intg1 = integ (rho1); // Computes g_r(rho1).
+ double gr = rX * sd1 * sd2 + mu1 * mu2; /* Target value; integ(\rho)
+ = gr is equivalent to
+ \rho = the solution. */
+
+ if (intg1 == gr)
+ return rho1;
+
+ if (intg1 < gr) { // Orient the search from left to right
+ if (0 < rX && rX < 1) // Do search between rh_0 and rho_m=1 - delta
+ d = 1 - delta - rho1;
+ if ( -1 < rX && rX < 0) // Do search between rh_0 and 0
+ d = -rho1;
+ m = (int) Math.ceil(d / (2 * h));
+ h = d / (2 * m); // Readjust h
+ hd3 = h / 3;
+ h2 = 2 * h;
+ xold = rho1;
+ dold = deriv (xold);
+ iold = intg1;
+
+ for (int i = 1; i <= m; i++) { // Begin the search
+ dmid = deriv (xold + h);
+ xnew = xold + h2;
+ dnew = deriv (xnew);
+ inew = iold + hd3 * (dold + 4 * dmid + dnew);
+
+ if (inew >= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xtemp;
+ x[1] = xold;
+ x[2] = xnew;
+ y[0] = temp;
+ y[1] = iold;
+ y[2] = inew;
+ Misc.interpol (2, x, y, c);
+ b = (c[2] * (xtemp + xold) - c[1] + Math.sqrt ((c[1]
+ - c[2] * (xtemp + xold)) * (c[1] - c[2]
+ * (xtemp + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xtemp + c[2] * xtemp * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew;
+ iold = inew;
+ }
+ b = 1.0 - delta / 2; // The root is at the right of rho_m
+ }
+
+ if (intg1 > gr) { // Orient the search from right to left
+ if (0 < rX && rX < 1) // Do search between 0 and rh_0
+ d = rho1;
+ if ( -1 < rX && rX < 0) // Do search between rho_m=-1+delta and rho_0
+ d = rho1 + 1 - delta;
+ m = (int) Math.ceil (d / (2 * h));
+ h = d / (2 * m); // Readjust h
+ hd3 = h / 3; // Pre-compute constant
+ h2 = 2 * h; // Pre-compute constant
+ xold = rho1;
+ dold = deriv (xold);
+ iold = intg1;
+ for (int i = 1; i <= m; i++) { // Begin the search
+ dmid = deriv (xold - h);
+ xnew = xold - h2;
+ dnew = deriv (xnew);
+ inew = iold - hd3 * (dold + 4 * dmid + dnew);
+ if (inew <= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xnew;
+ x[1] = xold;
+ x[2] = xtemp;
+ y[0] = inew;
+ y[1] = iold;
+ y[2] = temp;
+ Misc.interpol (2, x, y, c);
+ b = (c[2] * (xnew + xold) - c[1] + Math.sqrt ((c[1]
+ - c[2] * (xnew + xold)) * (c[1] - c[2]
+ * (xnew + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xnew + c[2] * xnew * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew;
+ iold = inew;
+ }
+ b = -1 + delta / 2; // The root is at the left of rho_m
+ }
+ return b;
+ }
+
+
+ public String toString()
+ {
+ String desc = super.toString();
+ desc += "h : " + h + "\n";
+ desc += " delta : " + delta + "\n";
+ return desc;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2a.tex b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2a.tex
new file mode 100644
index 0000000..c6a67ce
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2a.tex
@@ -0,0 +1,216 @@
+\defmodule {NI2a}
+This class implements the algorithm NI2a wich uses the derivative and
+ simple integration.
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NI2a
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti.norta;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NI2a extends NortaInitDisc \begin{hide}
+{
+ private double h; /* Predefined step size for the integration-grid
+ spacing (also named h in the paper, paragraph
+ "Method NI2" of section 3). */
+ private double delta; /* Small positive parameter to make sure that
+ rho_m is not too close to 1 or -1;
+ (also named delta in the paper, paragraph "
+ Method NI2" of section 3). */
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NI2a(double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, double h,
+ double delta)\begin{hide}
+ {
+ super(rX, dist1, dist2, tr);
+ this.h = h;
+ this. delta = delta;
+ computeParams();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor of the class NI2a with the target rank correlation rX,
+ the two discrete marginals dist1 and dist2,
+ the parameter for truncation tr, and the specific
+ parameters h and delta, which correspond to h and delta in the paper
+ (paragraph "Method NI2" of section 3).
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double computeCorr()\begin{hide}
+ {
+ // x, y and c coefficients used for quadratic interpolation.
+ double[] x = new double[3];
+ double[] y = new double[3];
+ double[] c = new double[3];
+ double xtemp = 0.0, temp = 0.0; /* Values of rho and the recursive
+ quantity I_k, given in paragraph
+ "Method NI2" of section 3 in the
+ paper, 2 iterations before the
+ last one. */
+ double xold, iold; /* Values of rho and I_k at one iteration before
+ the last one.*/
+ double xnew, inew; // Values of rho and I_k at the last iteration.
+ double dold, dmid, dnew; /* Values of the derivative function g'
+ at points xold, xmid (xold+h) and xnew.
+ They correspond to g'(rho_0+2kh-2h),
+ g'(rho_0+2kh-h) and g'(rho_0+2kh) in the
+ formula of I_k in the paper
+ (paragraph "Method NI2" of section 3). */
+
+ double d = 0.0; // Integration distance.
+ int m; // Number of iterations needed.
+ /** d and m correspond to d and m given in the third paragraph of
+ section 4 in the paper. */
+ double b = 0.0; // The returned solution.
+ double h2 = 0.0, hd3 = 0.0; // Precompute constants.
+ double lrx = (integ ( -1) - mu1 * mu2) / sd1 * sd2; // Min.correlation.
+ double urx = (integ (1) - mu1 * mu2) / sd1 * sd2; // Max.correlation.
+ double rho1 = 2 * Math.sin (Math.PI * rX / 6); // The initial guess.
+ double intg1 = integ (rho1); // Computes g_r(rho1).
+ double gr = rX * sd1 * sd2 + mu1 * mu2; /* Target value; integ(\rho)
+ = gr is equivalent to
+ \rho = the solution. */
+
+ if (intg1 == gr)
+ return rho1;
+
+ if (intg1 < gr) { // Orient the search from left to right
+ if (0 < rX && rX < 1) // Do search between rh_0 and rho_m=1 - delta
+ d = 1 - delta - rho1;
+ if ( -1 < rX && rX < 0) // Do search between rh_0 and 0
+ d = -rho1;
+ m = (int) Math.ceil(d / (2 * h));
+ h = d / (2 * m); // Readjust h
+ hd3 = h / 3;
+ h2 = 2 * h;
+ xold = rho1;
+ dold = deriv (xold);
+ iold = intg1;
+
+ for (int i = 1; i <= m; i++) { // Begin the search
+ dmid = deriv (xold + h);
+ xnew = xold + h2;
+ dnew = deriv (xnew);
+ inew = iold + hd3 * (dold + 4 * dmid + dnew);
+
+ if (inew >= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xtemp;
+ x[1] = xold;
+ x[2] = xnew;
+ y[0] = temp;
+ y[1] = iold;
+ y[2] = inew;
+ Misc.interpol (2, x, y, c);
+ b = (c[2] * (xtemp + xold) - c[1] + Math.sqrt ((c[1]
+ - c[2] * (xtemp + xold)) * (c[1] - c[2]
+ * (xtemp + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xtemp + c[2] * xtemp * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew;
+ iold = inew;
+ }
+ b = 1.0 - delta / 2; // The root is at the right of rho_m
+ }
+
+ if (intg1 > gr) { // Orient the search from right to left
+ if (0 < rX && rX < 1) // Do search between 0 and rh_0
+ d = rho1;
+ if ( -1 < rX && rX < 0) // Do search between rho_m=-1+delta and rho_0
+ d = rho1 + 1 - delta;
+ m = (int) Math.ceil (d / (2 * h));
+ h = d / (2 * m); // Readjust h
+ hd3 = h / 3; // Pre-compute constant
+ h2 = 2 * h; // Pre-compute constant
+ xold = rho1;
+ dold = deriv (xold);
+ iold = intg1;
+ for (int i = 1; i <= m; i++) { // Begin the search
+ dmid = deriv (xold - h);
+ xnew = xold - h2;
+ dnew = deriv (xnew);
+ inew = iold - hd3 * (dold + 4 * dmid + dnew);
+ if (inew <= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xnew;
+ x[1] = xold;
+ x[2] = xtemp;
+ y[0] = inew;
+ y[1] = iold;
+ y[2] = temp;
+ Misc.interpol (2, x, y, c);
+ b = (c[2] * (xnew + xold) - c[1] + Math.sqrt ((c[1]
+ - c[2] * (xnew + xold)) * (c[1] - c[2]
+ * (xnew + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xnew + c[2] * xnew * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew;
+ iold = inew;
+ }
+ b = -1 + delta / 2; // The root is at the left of rho_m
+ }
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the correlation $\rho_Z$ using the algorithm NI2a.
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide}
+ {
+ String desc = super.toString();
+ desc += "h : " + h + "\n";
+ desc += " delta : " + delta + "\n";
+ return desc;
+ }\end{hide}
+\end{code}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2b.java b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2b.java
new file mode 100644
index 0000000..5cde263
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2b.java
@@ -0,0 +1,194 @@
+
+
+/*
+ * Class: NI2b
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti.norta;
+
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements the algorithm NI2b wich uses the derivative
+ * and simple integration.
+ *
+ */
+public class NI2b extends NortaInitDisc
+{
+ private int m; /* Number of subintervals for the integration = max.
+ number of iterations (also named m in the paper,
+ paragraph "Method NI2" of section 3).*/
+ private double delta; /* Small positive parameter to make sure that
+ rho_m is not too close to 1 or -1;
+ (also named delta in the paper, paragraph
+ "Method NI2" of section 3)*/
+
+
+
+ /**
+ * Constructor of the class NI2b with the target rank correlation rX,
+ * the two discrete marginals dist1 and dist2,
+ * the parameter for truncation tr, the specific parameters m and
+ * delta which correspond to m and delta in the paper
+ * (paragraph "Method NI2" of section 3).
+ *
+ */
+ public NI2b(double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, int m,
+ double delta)
+ {
+ super(rX, dist1, dist2, tr);
+ this.m = m;
+ this.delta = delta;
+ computeParams();
+ }
+
+
+ /**
+ * Computes and returns the correlation <SPAN CLASS="MATH"><I>ρ</I><SUB>Z</SUB></SPAN> using the algorithm NI2b.
+ *
+ */
+ public double computeCorr()
+ {
+ // x, y and c oefficients used for quadratic interpolation
+ double[] x = new double[3];
+ double[] y = new double[3];
+ double[] c = new double[3];
+ double xtemp = 0.0, temp = 0.0; /* Values of rho and the recursive
+ quantity I_k, given in paragraph
+ "Method NI2" of section 3 in
+ the paper,2 iterations before the
+ last one. */
+ double xold, iold; /* Values of rho and I_k at one iteration before
+ the last one.*/
+ double xnew, inew; // rho and I_k at the last iteration.
+ double dold, dmid, dnew; /* Values of the derivative function g'
+ at points xold, xmid (xold+h) and xnew.
+ They correspond to g'(rho_0+2kh-2h),
+ g'(rho_0+2kh-h) and g'(rho_0+2kh) in the
+ formula of I_k in the paper
+ (paragraph "Method NI2" of section 3). */
+ double h = (1 - delta) / (2 * m); /* Step size for the
+ integration-grid spacing (2*h).
+ It corresponds to h given in
+ the third paragraph of
+ section 4 in the paper */
+ double b = 0.0; // The returned solution.
+ double rho1 = 0.0; // The initial guess.
+ double intg1 = mu1 * mu2; // Computes g_r(rho1).
+ double gr = rX * sd1 * sd2 + mu1 * mu2; /* Target value; integ(rho)
+ = gr is equivalent to
+ rho = the solution. */
+ // Pre-compute constants
+ double hd3 = h / 3;
+ double h2 = 2 * h;
+
+ if (intg1 == gr)
+ return rho1;
+
+ if (0 < rX && rX < 1) { // Do search between 0 and rho_m=1-delta
+ xold = rho1;
+ dold = deriv(xold);
+ iold = intg1;
+
+ for (int i = 1; i <= m ;i++) { // Begin the search
+ dmid = deriv(xold + h);
+ xnew = xold + h2;
+ dnew = deriv(xnew);
+ inew = iold + hd3 * (dold + 4 * dmid + dnew);
+ if (inew >= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xtemp;
+ x[1] = xold;
+ x[2] = xnew;
+ y[0] = temp;
+ y[1] = iold;
+ y[2] = inew;
+ Misc.interpol(2, x, y, c);
+ b = (c[2] * (xtemp + xold) - c[1] + Math.sqrt((c[1]
+ - c[2] * (xtemp + xold)) * (c[1] - c[2]
+ * (xtemp + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xtemp + c[2] * xtemp * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew ;
+ iold = inew;
+ }
+ // Integration up to 1-delta did not bracket root
+ // return 1-delta/2 ( = midpoint of current bracketing interval)
+ b = 1 - delta / 2;
+ }
+
+ if ( -1 < rX && rX < 0) { // Do search between rho_m=-1+delta and 0
+ xold = rho1;
+ dold = deriv(xold);
+ iold = intg1;
+
+ for (int i = 1; i <= m ;i++) { // Begin the search
+ dmid = deriv(xold - h);
+ xnew = xold - h2;
+ dnew = deriv(xnew);
+ inew = iold - hd3 * (dold + 4 * dmid + dnew);
+ if (inew <= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xnew;
+ x[1] = xold;
+ x[2] = xtemp;
+ y[0] = inew;
+ y[1] = iold;
+ y[2] = temp;
+ Misc.interpol(2, x, y, c);
+ b = (c[2] * (xnew + xold) - c[1] + Math.sqrt((c[1]
+ - c[2] * (xnew + xold)) * (c[1] - c[2]
+ * (xnew + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xnew + c[2] * xnew * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew ;
+ iold = inew;
+ }
+ // Integration up to -1+delta did not bracket root
+ b = -1 + delta / 2; // return 1-delta/2
+ // ( = midpoint of current bracketing interval )
+ }
+ return b;
+ }
+
+
+ public String toString()
+ {
+ String desc = super.toString();
+ desc += "m : " + m + "\n";
+ desc += "delta : " + delta + "\n";
+ return desc;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2b.tex b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2b.tex
new file mode 100644
index 0000000..b3bd0de
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI2b.tex
@@ -0,0 +1,204 @@
+\defmodule {NI2b}
+This class implements the algorithm NI2b wich uses the derivative
+ and simple integration.
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NI2b
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti.norta;
+\begin{hide}
+import umontreal.iro.lecuyer.util.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NI2b extends NortaInitDisc \begin{hide}
+{
+ private int m; /* Number of subintervals for the integration = max.
+ number of iterations (also named m in the paper,
+ paragraph "Method NI2" of section 3).*/
+ private double delta; /* Small positive parameter to make sure that
+ rho_m is not too close to 1 or -1;
+ (also named delta in the paper, paragraph
+ "Method NI2" of section 3)*/
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NI2b(double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, int m,
+ double delta)\begin{hide}
+ {
+ super(rX, dist1, dist2, tr);
+ this.m = m;
+ this.delta = delta;
+ computeParams();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor of the class NI2b with the target rank correlation rX,
+ the two discrete marginals dist1 and dist2,
+ the parameter for truncation tr, the specific parameters m and
+ delta which correspond to m and delta in the paper
+ (paragraph "Method NI2" of section 3).
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double computeCorr()\begin{hide}
+ {
+ // x, y and c oefficients used for quadratic interpolation
+ double[] x = new double[3];
+ double[] y = new double[3];
+ double[] c = new double[3];
+ double xtemp = 0.0, temp = 0.0; /* Values of rho and the recursive
+ quantity I_k, given in paragraph
+ "Method NI2" of section 3 in
+ the paper,2 iterations before the
+ last one. */
+ double xold, iold; /* Values of rho and I_k at one iteration before
+ the last one.*/
+ double xnew, inew; // rho and I_k at the last iteration.
+ double dold, dmid, dnew; /* Values of the derivative function g'
+ at points xold, xmid (xold+h) and xnew.
+ They correspond to g'(rho_0+2kh-2h),
+ g'(rho_0+2kh-h) and g'(rho_0+2kh) in the
+ formula of I_k in the paper
+ (paragraph "Method NI2" of section 3). */
+ double h = (1 - delta) / (2 * m); /* Step size for the
+ integration-grid spacing (2*h).
+ It corresponds to h given in
+ the third paragraph of
+ section 4 in the paper */
+ double b = 0.0; // The returned solution.
+ double rho1 = 0.0; // The initial guess.
+ double intg1 = mu1 * mu2; // Computes g_r(rho1).
+ double gr = rX * sd1 * sd2 + mu1 * mu2; /* Target value; integ(rho)
+ = gr is equivalent to
+ rho = the solution. */
+ // Pre-compute constants
+ double hd3 = h / 3;
+ double h2 = 2 * h;
+
+ if (intg1 == gr)
+ return rho1;
+
+ if (0 < rX && rX < 1) { // Do search between 0 and rho_m=1-delta
+ xold = rho1;
+ dold = deriv(xold);
+ iold = intg1;
+
+ for (int i = 1; i <= m ;i++) { // Begin the search
+ dmid = deriv(xold + h);
+ xnew = xold + h2;
+ dnew = deriv(xnew);
+ inew = iold + hd3 * (dold + 4 * dmid + dnew);
+ if (inew >= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xtemp;
+ x[1] = xold;
+ x[2] = xnew;
+ y[0] = temp;
+ y[1] = iold;
+ y[2] = inew;
+ Misc.interpol(2, x, y, c);
+ b = (c[2] * (xtemp + xold) - c[1] + Math.sqrt((c[1]
+ - c[2] * (xtemp + xold)) * (c[1] - c[2]
+ * (xtemp + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xtemp + c[2] * xtemp * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew ;
+ iold = inew;
+ }
+ // Integration up to 1-delta did not bracket root
+ // return 1-delta/2 ( = midpoint of current bracketing interval)
+ b = 1 - delta / 2;
+ }
+
+ if ( -1 < rX && rX < 0) { // Do search between rho_m=-1+delta and 0
+ xold = rho1;
+ dold = deriv(xold);
+ iold = intg1;
+
+ for (int i = 1; i <= m ;i++) { // Begin the search
+ dmid = deriv(xold - h);
+ xnew = xold - h2;
+ dnew = deriv(xnew);
+ inew = iold - hd3 * (dold + 4 * dmid + dnew);
+ if (inew <= gr) { // The root is in current bracketing interval
+ // Compute the parameters of quadratic interpolation
+ x[0] = xnew;
+ x[1] = xold;
+ x[2] = xtemp;
+ y[0] = inew;
+ y[1] = iold;
+ y[2] = temp;
+ Misc.interpol(2, x, y, c);
+ b = (c[2] * (xnew + xold) - c[1] + Math.sqrt((c[1]
+ - c[2] * (xnew + xold)) * (c[1] - c[2]
+ * (xnew + xold)) - 4 * c[2] * (c[0] - c[1]
+ * xnew + c[2] * xnew * xold - gr))) / (2 * c[2]);
+ return b;
+ }
+ xtemp = xold;
+ temp = iold;
+ xold = xnew;
+ dold = dnew ;
+ iold = inew;
+ }
+ // Integration up to -1+delta did not bracket root
+ b = -1 + delta / 2; // return 1-delta/2
+ // ( = midpoint of current bracketing interval )
+ }
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the correlation $\rho_Z$ using the algorithm NI2b.
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide}
+ {
+ String desc = super.toString();
+ desc += "m : " + m + "\n";
+ desc += "delta : " + delta + "\n";
+ return desc;
+ }\end{hide}
+\end{code}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI3.java b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI3.java
new file mode 100644
index 0000000..f837625
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI3.java
@@ -0,0 +1,135 @@
+
+
+/*
+ * Class: NI3
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti.norta;
+
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements the algorithm NI3 (protected Newton-Raphson method).
+ * The root is found with accuracy tolerance.
+ *
+ */
+public class NI3 extends NortaInitDisc
+{
+ private double tolerance; /* Desired accuracy for the root-finder
+ algorithm (epsilon in paragraph
+ "Method NI3" of section 3 in paper).*/
+
+
+
+ /**
+ * Constructor of the class NI3 with the target rank correlation rX,
+ * the two discrete marginals dist1 and dist2,
+ * the parameter for truncation tr and the specific parameter
+ * tolerance which corresponds to epsilon in the paper
+ * (paragraph "Method NI3" of section 3).
+ *
+ */
+ public NI3 (double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, double tolerance)
+ {
+ super(rX, dist1, dist2, tr);
+ this.tolerance = tolerance;
+ computeParams();
+ }
+
+
+ /**
+ * Computes and returns the correlation <SPAN CLASS="MATH"><I>ρ</I><SUB>Z</SUB></SPAN> using the algorithm NI3.
+ *
+ */
+ public double computeCorr()
+ {
+ final double ITMAX = 100; // Maximum number of iterations.
+ double xl, xh; /* Left and right endpoints of the root bracket at
+ all iterations. */
+ double b = 0.0; // The returned solution.
+ double f, df; // Function and its derivative evaluations.
+ double dx; // Correction term.
+ /* * f, df, dx correspond to f(rho_k), f'(rho_k) and f(rho_k)/f'(rho_k),
+ respectively, in the paper (paragraph "Method NI3"of section 3). */
+ double dxold; // The root correction at one iteration before the last one.
+ double temp;// The root at one iteration before the last one.
+ double ccc = rX * sd1 * sd2 + mu1 * mu2; // Precompute constant.
+
+ if (rX == 0.0)
+ return 0.0;
+ if (rX > 0.0) { // Orient the search
+ xl = 0.0;
+ xh = 1.0;
+ } else {
+ xl = -1.0;
+ xh = 0.0;
+ }
+
+ b = 2 * Math.sin (Math.PI * rX / 6); // Initial guess
+ dxold = xh - xl;
+ dx = dxold;
+ f = integ (b) - ccc;
+ df = deriv (b);
+
+ for (int i = 1; i <= ITMAX; i++) { // Begin the search
+ if ((((b - xh) * df - f) * ((b - xl) * df - f) > 0.0)
+ || (Math.abs (2.0 * f) > Math.abs (dxold * df))) {
+ // Do bisection if solution is out of range
+ // or not decreasing fast enough
+ dxold = dx;
+ dx = 0.5 * (xh - xl);
+ b = xl + dx;
+ if (xl == b) // Change in root is negligible.
+ return b; // Accept this root
+ } else {
+ dxold = dx;
+ dx = f / df;
+ temp = b;
+ b -= dx;
+ if (temp == b)
+ return b;
+ }
+ if (Math.abs (dx) < tolerance)
+ return b; // Convergence check
+ f = integ (b) - ccc;
+ df = deriv (b);
+ if (f < 0.0)
+ xl = b; // Maintain the brackets on the root
+ else
+ xh = b;
+ }
+ return b;
+ }
+
+
+ public String toString()
+ {
+ // To display the inputs.
+ String desc = super.toString();
+ desc += "tolerance : " + tolerance + "\n";
+ return desc;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NI3.tex b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI3.tex
new file mode 100644
index 0000000..84f42bc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NI3.tex
@@ -0,0 +1,146 @@
+\defmodule {NI3}
+This class implements the algorithm NI3 (protected Newton-Raphson method).
+ The root is found with accuracy tolerance.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NI3
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti.norta;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NI3 extends NortaInitDisc \begin{hide}
+{
+ private double tolerance; /* Desired accuracy for the root-finder
+ algorithm (epsilon in paragraph
+ "Method NI3" of section 3 in paper).*/
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NI3 (double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr, double tolerance)\begin{hide}
+ {
+ super(rX, dist1, dist2, tr);
+ this.tolerance = tolerance;
+ computeParams();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor of the class NI3 with the target rank correlation rX,
+ the two discrete marginals dist1 and dist2,
+ the parameter for truncation tr and the specific parameter
+ tolerance which corresponds to epsilon in the paper
+ (paragraph "Method NI3" of section 3).
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double computeCorr() \begin{hide}
+ {
+ final double ITMAX = 100; // Maximum number of iterations.
+ double xl, xh; /* Left and right endpoints of the root bracket at
+ all iterations. */
+ double b = 0.0; // The returned solution.
+ double f, df; // Function and its derivative evaluations.
+ double dx; // Correction term.
+ /** f, df, dx correspond to f(rho_k), f'(rho_k) and f(rho_k)/f'(rho_k),
+ respectively, in the paper (paragraph "Method NI3"of section 3). */
+ double dxold; // The root correction at one iteration before the last one.
+ double temp;// The root at one iteration before the last one.
+ double ccc = rX * sd1 * sd2 + mu1 * mu2; // Precompute constant.
+
+ if (rX == 0.0)
+ return 0.0;
+ if (rX > 0.0) { // Orient the search
+ xl = 0.0;
+ xh = 1.0;
+ } else {
+ xl = -1.0;
+ xh = 0.0;
+ }
+
+ b = 2 * Math.sin (Math.PI * rX / 6); // Initial guess
+ dxold = xh - xl;
+ dx = dxold;
+ f = integ (b) - ccc;
+ df = deriv (b);
+
+ for (int i = 1; i <= ITMAX; i++) { // Begin the search
+ if ((((b - xh) * df - f) * ((b - xl) * df - f) > 0.0)
+ || (Math.abs (2.0 * f) > Math.abs (dxold * df))) {
+ // Do bisection if solution is out of range
+ // or not decreasing fast enough
+ dxold = dx;
+ dx = 0.5 * (xh - xl);
+ b = xl + dx;
+ if (xl == b) // Change in root is negligible.
+ return b; // Accept this root
+ } else {
+ dxold = dx;
+ dx = f / df;
+ temp = b;
+ b -= dx;
+ if (temp == b)
+ return b;
+ }
+ if (Math.abs (dx) < tolerance)
+ return b; // Convergence check
+ f = integ (b) - ccc;
+ df = deriv (b);
+ if (f < 0.0)
+ xl = b; // Maintain the brackets on the root
+ else
+ xh = b;
+ }
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the correlation $\rho_Z$ using the algorithm NI3.
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide}
+ {
+ // To display the inputs.
+ String desc = super.toString();
+ desc += "tolerance : " + tolerance + "\n";
+ return desc;
+ }\end{hide}
+\end{code}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NortaInitDisc.java b/source/umontreal/iro/lecuyer/probdistmulti/norta/NortaInitDisc.java
new file mode 100644
index 0000000..2567184
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NortaInitDisc.java
@@ -0,0 +1,203 @@
+
+
+/*
+ * Class: NortaInitDisc
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.probdistmulti.norta;
+
+import umontreal.iro.lecuyer.probdistmulti.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+public abstract class NortaInitDisc {
+ protected double rX; // Target rank correlation
+ // Marginal disttributions of r.variables X_1 and X_2
+ protected DiscreteDistributionInt dist1;
+ protected DiscreteDistributionInt dist2;
+ protected double tr; /* Parameter for the quantile upper limit;
+ used for truncation in the unbounded case. */
+ protected double mu1, mu2, sd1, sd2; /* Means and standard deviations
+ of F_1(X_1) and F_2(X_2). */
+
+ private int m1, m2; // Number of support points of the 2 marginals.
+ private double[] p1; // Probability masses of the marginal 1.
+ private double[] p2; // Probability masses of the marginal 2.
+ /* Quantiles of the cumulative probability masses by the standard
+ normal C.D.F for the 2 marginals */
+ private double[] z1;
+ private double[] z2;
+
+
+ private String tabToString(double[] tab , String message)
+ {
+ String desc = message + "\n [";
+ for (int i = 0; i < tab.length; i++)
+ if (i == tab.length - 1)
+ desc += "]\n";
+ else
+ desc += tab[i] + ",";
+ return desc;
+ }
+
+
+
+ /**
+ * Constructor of the abstract class. It can be called only by
+ * the constructors of subclasses of <TT>NortaInitDisc</TT>.
+ *
+ */
+ public NortaInitDisc (double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr) {
+ this.rX = rX;
+ this.dist1 = dist1;
+ this.dist2 = dist2;
+ this.tr = tr;
+ }
+
+
+ /**
+ * This method computes and returns the correlation <SPAN CLASS="MATH"><I>ρ</I><SUB>Z</SUB></SPAN>.
+ * Every subclass of <TT>NortaInitDisc</TT> must implement it.
+ *
+ */
+ public abstract double computeCorr();
+
+
+ /**
+ * This method computes the following inputs of the two
+ * marginal distributions: <SPAN CLASS="MATH"><I>m</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>m</I><SUB>2</SUB></SPAN>, mu1, mu2, sd1, sd2, and the vectors
+ * p1, p2, z1 and z2. Every subclass of <TT>NortaInitDisc</TT> must call it.
+ *
+ */
+ public void computeParams()
+ {
+ m1 = dist1.inverseFInt (tr) + 1;
+ m2 = dist2.inverseFInt (tr) + 1;
+ // Support points of X_1 and X_2
+ int[] y1 = new int[m1];
+ int[] y2 = new int[m2];
+ p1 = new double[m1];
+ p2 = new double[m2];
+ // Cumulative probability masses of X_1 and X_2
+ double[] f1 = new double[m1];
+ double[] f2 = new double[m2];
+ z1 = new double[m1];
+ z2 = new double[m2];
+ double u11 = 0.0, u22 = 0.0;
+
+ // Compute mu1, sd1, p1 and z1 of X_1
+ for (int i = 0; i < m1; i++) {
+ y1[i] = i;
+ p1[i] = dist1.prob (y1[i]);
+ f1[i] = dist1.cdf (y1[i]);
+ z1[i] = NormalDist.inverseF01 (f1[i]);
+ if (z1[i] == Double.NEGATIVE_INFINITY)
+ z1[i] = NormalDist.inverseF01 (2.2e-308);
+ if (z1[i] == Double.POSITIVE_INFINITY)
+ z1[i] = NormalDist.inverseF01 (1.0 - Math.ulp (1.0));
+ mu1 += f1[i] * p1[i];
+ u11 += f1[i] * f1[i] * p1[i];
+ }
+ sd1 = Math.sqrt (u11 - mu1 * mu1);
+
+ // Compute mu2, sd2, p2 and z2 of X_2
+ for (int i = 0; i < m2; i++) {
+ y2[i] = i;
+ p2[i] = dist2.prob (y2[i]);
+ f2[i] = dist2.cdf (y2[i]);
+ z2[i] = NormalDist.inverseF01 (f2[i]);
+ if (z2[i] == Double.NEGATIVE_INFINITY)
+ z2[i] = NormalDist.inverseF01 (2.2e-308);
+ if (z2[i] == Double.POSITIVE_INFINITY)
+ z2[i] = NormalDist.inverseF01 (1.0 - Math.ulp (1.0));
+ mu2 += f2[i] * p2[i];
+ u22 += f2[i] * f2[i] * p2[i];
+ }
+ sd2 = Math.sqrt (u22 - mu2 * mu2);
+ }
+
+
+ /**
+ * Computes the function value <SPAN CLASS="MATH"><I>g</I><SUB>r</SUB></SPAN> for each correlation.
+ *
+ */
+ public double integ (double r)
+ {
+ double gr = 0.0; // The returned value.
+ for (int i = 0; i < m1 - 1; i++) {
+ double sum = 0.0;
+ for (int j = 0; j < m2 - 1; j++) {
+ sum += p2[j + 1]
+ * BiNormalDonnellyDist.barF (z1[i], z2[j], r);
+ }
+ gr += p1[i + 1] * sum;
+ }
+ return gr;
+ }
+
+
+ /**
+ * Computes the first derivative of function <SPAN CLASS="MATH"><I>g</I><SUB>r</SUB></SPAN> for each correlation.
+ *
+ */
+ public double deriv (double r)
+ {
+ double c = Math.sqrt (1.0 - r * r);
+ double c1 = 2 * c * c;
+ double gp = 0.0; // The returned value
+
+ for (int i = 0; i < m1 - 1; i++) {
+ double z1sq = z1[i] * z1[i];
+ double t1 = 2 * r * z1[i];
+ double sum = 0.0;
+ for (int j = 0; j < m2 - 1; j++) {
+ sum += p2[j + 1]
+ * Math.exp ((t1 * z2[j] - z1sq - z2[j] * z2[j]) / c1);
+ }
+ gp += p1[i + 1] * sum;
+ }
+ gp = gp / (2.0 * Math.PI * c);
+ return gp;
+ }
+
+
+ public String toString()
+ {
+ String desc = "";
+ desc += "rX = " + rX + "\n";
+ desc += "tr = " + tr + "\n";
+ desc += "m1 = " + m1 + "\n";
+ desc += "m2 = " + m2 + "\n";
+ desc += "mu1 = " + mu1 + "\n";
+ desc += "mu2 = " + mu2 + "\n";
+ desc += "sd1 = " + sd1 + "\n";
+ desc += "sd2 = " + sd2 + "\n";
+ desc += tabToString(p1 , "Table p1 : ");
+ desc += tabToString(z1 , "Table z1 : ");
+ desc += tabToString(p2 , "Table p2 : ");
+ desc += tabToString( z2, "Table z2 : ");
+ return desc;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/NortaInitDisc.tex b/source/umontreal/iro/lecuyer/probdistmulti/norta/NortaInitDisc.tex
new file mode 100644
index 0000000..f6c64d3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/NortaInitDisc.tex
@@ -0,0 +1,214 @@
+\defmodule {NortaInitDisc}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NortaInitDisc
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
+ * Organization: DIRO, Université de Montréal
+ * @author Nabil Channouf
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.probdistmulti.norta;
+\begin{hide}
+import umontreal.iro.lecuyer.probdistmulti.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public abstract class NortaInitDisc \begin{hide} {
+ protected double rX; // Target rank correlation
+ // Marginal disttributions of r.variables X_1 and X_2
+ protected DiscreteDistributionInt dist1;
+ protected DiscreteDistributionInt dist2;
+ protected double tr; /* Parameter for the quantile upper limit;
+ used for truncation in the unbounded case. */
+ protected double mu1, mu2, sd1, sd2; /* Means and standard deviations
+ of F_1(X_1) and F_2(X_2). */
+
+ private int m1, m2; // Number of support points of the 2 marginals.
+ private double[] p1; // Probability masses of the marginal 1.
+ private double[] p2; // Probability masses of the marginal 2.
+ /* Quantiles of the cumulative probability masses by the standard
+ normal C.D.F for the 2 marginals */
+ private double[] z1;
+ private double[] z2;
+
+
+ private String tabToString(double[] tab , String message)
+ {
+ String desc = message + "\n [";
+ for (int i = 0; i < tab.length; i++)
+ if (i == tab.length - 1)
+ desc += "]\n";
+ else
+ desc += tab[i] + ",";
+ return desc;
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NortaInitDisc (double rX, DiscreteDistributionInt dist1,
+ DiscreteDistributionInt dist2, double tr)\begin{hide} {
+ this.rX = rX;
+ this.dist1 = dist1;
+ this.dist2 = dist2;
+ this.tr = tr;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructor of the abstract class. It can be called only by
+ the constructors of subclasses of \texttt{NortaInitDisc}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public abstract double computeCorr();
+\end{code}
+\begin{tabb} This method computes and returns the correlation $\rho_Z$.
+ Every subclass of \texttt{NortaInitDisc} must implement it.
+\end{tabb}
+\begin{code}
+
+ public void computeParams() \begin{hide}
+ {
+ m1 = dist1.inverseFInt (tr) + 1;
+ m2 = dist2.inverseFInt (tr) + 1;
+ // Support points of X_1 and X_2
+ int[] y1 = new int[m1];
+ int[] y2 = new int[m2];
+ p1 = new double[m1];
+ p2 = new double[m2];
+ // Cumulative probability masses of X_1 and X_2
+ double[] f1 = new double[m1];
+ double[] f2 = new double[m2];
+ z1 = new double[m1];
+ z2 = new double[m2];
+ double u11 = 0.0, u22 = 0.0;
+
+ // Compute mu1, sd1, p1 and z1 of X_1
+ for (int i = 0; i < m1; i++) {
+ y1[i] = i;
+ p1[i] = dist1.prob (y1[i]);
+ f1[i] = dist1.cdf (y1[i]);
+ z1[i] = NormalDist.inverseF01 (f1[i]);
+ if (z1[i] == Double.NEGATIVE_INFINITY)
+ z1[i] = NormalDist.inverseF01 (2.2e-308);
+ if (z1[i] == Double.POSITIVE_INFINITY)
+ z1[i] = NormalDist.inverseF01 (1.0 - Math.ulp (1.0));
+ mu1 += f1[i] * p1[i];
+ u11 += f1[i] * f1[i] * p1[i];
+ }
+ sd1 = Math.sqrt (u11 - mu1 * mu1);
+
+ // Compute mu2, sd2, p2 and z2 of X_2
+ for (int i = 0; i < m2; i++) {
+ y2[i] = i;
+ p2[i] = dist2.prob (y2[i]);
+ f2[i] = dist2.cdf (y2[i]);
+ z2[i] = NormalDist.inverseF01 (f2[i]);
+ if (z2[i] == Double.NEGATIVE_INFINITY)
+ z2[i] = NormalDist.inverseF01 (2.2e-308);
+ if (z2[i] == Double.POSITIVE_INFINITY)
+ z2[i] = NormalDist.inverseF01 (1.0 - Math.ulp (1.0));
+ mu2 += f2[i] * p2[i];
+ u22 += f2[i] * f2[i] * p2[i];
+ }
+ sd2 = Math.sqrt (u22 - mu2 * mu2);
+ }\end{hide}
+\end{code}
+\begin{tabb} This method computes the following inputs of the two
+ marginal distributions: $m_1$ and $m_2$, mu1, mu2, sd1, sd2, and the vectors
+ p1, p2, z1 and z2. Every subclass of \texttt{NortaInitDisc} must call it.
+\end{tabb}
+\begin{code}
+
+ public double integ (double r) \begin{hide}
+ {
+ double gr = 0.0; // The returned value.
+ for (int i = 0; i < m1 - 1; i++) {
+ double sum = 0.0;
+ for (int j = 0; j < m2 - 1; j++) {
+ sum += p2[j + 1]
+ * BiNormalDonnellyDist.barF (z1[i], z2[j], r);
+ }
+ gr += p1[i + 1] * sum;
+ }
+ return gr;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the function value $g_r$ for each correlation.
+\end{tabb}
+\begin{code}
+
+ public double deriv (double r)\begin{hide}
+ {
+ double c = Math.sqrt (1.0 - r * r);
+ double c1 = 2 * c * c;
+ double gp = 0.0; // The returned value
+
+ for (int i = 0; i < m1 - 1; i++) {
+ double z1sq = z1[i] * z1[i];
+ double t1 = 2 * r * z1[i];
+ double sum = 0.0;
+ for (int j = 0; j < m2 - 1; j++) {
+ sum += p2[j + 1]
+ * Math.exp ((t1 * z2[j] - z1sq - z2[j] * z2[j]) / c1);
+ }
+ gp += p1[i + 1] * sum;
+ }
+ gp = gp / (2.0 * Math.PI * c);
+ return gp;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the first derivative of function $g_r$ for each correlation.
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide}
+ {
+ String desc = "";
+ desc += "rX = " + rX + "\n";
+ desc += "tr = " + tr + "\n";
+ desc += "m1 = " + m1 + "\n";
+ desc += "m2 = " + m2 + "\n";
+ desc += "mu1 = " + mu1 + "\n";
+ desc += "mu2 = " + mu2 + "\n";
+ desc += "sd1 = " + sd1 + "\n";
+ desc += "sd2 = " + sd2 + "\n";
+ desc += tabToString(p1 , "Table p1 : ");
+ desc += tabToString(z1 , "Table z1 : ");
+ desc += tabToString(p2 , "Table p2 : ");
+ desc += tabToString( z2, "Table z2 : ");
+ return desc;
+ }\end{hide}
+\end{code}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/norta/overview.tex b/source/umontreal/iro/lecuyer/probdistmulti/norta/overview.tex
new file mode 100644
index 0000000..c9cb880
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/norta/overview.tex
@@ -0,0 +1,7 @@
+
+
+\latex{\section*{Overview of package \texttt{probdistmulti.norta}}\addcontentsline{toc}{section}{Overview of package \texttt{probdistmulti.norta}}}
+
+
+See the doc \texttt{guideNortaInitDisc.pdf} in subpackage
+\texttt{norta}.
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/overview.tex b/source/umontreal/iro/lecuyer/probdistmulti/overview.tex
new file mode 100644
index 0000000..6662720
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/overview.tex
@@ -0,0 +1,45 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+
+
+This package contains Java classes providing methods to
+compute mass, density, distribution and complementary
+distribution functions for some multi-dimensional discrete
+and continuous probability distributions.
+% and to perform goodness-of-fit tests. % and collect statistics.
+It does not generate random numbers for multivariate distributions;
+for that, see the package \externalclass{umontreal.iro.lecuyer}{randvarmulti}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection* {Distributions}
+
+We recall that the {\em distribution function\/} of a {\em continuous\/} random
+vector $X= \{x_1, x_2, \ldots, x_d\}$ with {\em density\/} $f(x_1, x_2, \ldots,
+x_d)$ over the
+ $d$-dimensional space $R^d$ is
+\begin{eqnarray}
+ F(x_1, x_2, \ldots, x_d) &=& P[X_1\le x_1, X_2\le x_2, \ldots, X_d\le x_d] \\[6pt]
+ &=&
+ \int_{-\infty}^{x_1}\int_{-\infty}^{x_2} \cdots \int_{-\infty}^{x_d} f(s_1, s_2,
+ \ldots, s_d)\; ds_1 ds_2 \ldots ds_d \label{eq:FDist}
+\end{eqnarray}
+while that of a {\em discrete\/} random vector $X$ with {\em mass function\/}
+ $\{p_1, p_2, \ldots, p_d\}$ over a fixed set of real numbers is
+\begin{eqnarray}
+ F(x_1, x_2, \ldots, x_d) &=& P[X_1\le x_1, X_2\le x_2, \ldots, X_d\le x_d] \\[6pt]
+ &=& \sum_{i_1\le x_1}\sum_{i_2\le x_2} \cdots
+ \sum_{i_d\le x_d} p(x_1, x_2, \ldots, x_d), \label{eq:FDistDisc}
+\end{eqnarray}
+where $p(x_1, x_2, \ldots, x_d) = P[X_1 = x_1, X_2 = x_2, \ldots, X_d = x_d]$.
+For a discrete distribution over the set of integers, one has
+\begin{eqnarray}
+ F (x_1, x_2, \ldots, x_d) &=& P[X_1\le x_1, X_2\le x_2, \ldots, X_d\le x_d] \\[6pt]
+ &=& \sum_{s_1=-\infty}^{x_1} \sum_{s_2=-\infty}^{x_2} \cdots
+ \sum_{s_d=-\infty}^{x_d} p(s_1, s_2, \ldots, s_d), \label{eq:FDistDiscInt}
+\end{eqnarray}
+where $p(s_1, s_2, \ldots, s_d) = P[X_1=s_1, X_2=s_2, \ldots, X_d=s_d]$.
+
+We define $\bar{F}$, the {\em complementary distribution function\/}
+of $X$, as
+\eq
+ \bar{F} (x_1, x_2, \ldots, x_d) = P[X_1\ge x_1, X_2\ge x_2, \ldots, X_d\ge x_d].
+\endeq
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/rvclasses.tex b/source/umontreal/iro/lecuyer/probdistmulti/rvclasses.tex
new file mode 100644
index 0000000..3dffd69
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/rvclasses.tex
@@ -0,0 +1,7 @@
+\thispagestyle {nomark}
+\addcontentsline{toc}{section}{\protect\numberline{3.}
+ {The Random Variables Classes}}
+\centerline {\Large\bf The Distribution Classes}
+\vfill\vfill
+%\eject\null\vfill % Page blanche.
+
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/scclasses.tex b/source/umontreal/iro/lecuyer/probdistmulti/scclasses.tex
new file mode 100644
index 0000000..3a64c69
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/scclasses.tex
@@ -0,0 +1,7 @@
+\thispagestyle {nomark}
+\addcontentsline{toc}{section}{\protect\numberline{5.}
+ {The Statistic Collection Classes}}
+\centerline {\Large\bf The Statistical Collector's Classes}
+\vfill\vfill
+%\eject\null\vfill % Page blanche.
+
diff --git a/source/umontreal/iro/lecuyer/probdistmulti/staticclasses.tex b/source/umontreal/iro/lecuyer/probdistmulti/staticclasses.tex
new file mode 100644
index 0000000..df3eeeb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/probdistmulti/staticclasses.tex
@@ -0,0 +1,7 @@
+\thispagestyle {nomark}
+\addcontentsline{toc}{section}{\protect\numberline{2.}
+ {The Static Classes}}
+\centerline {\Large\bf The Static Distribution Classes}
+\vfill\vfill
+%\eject\null\vfill % Page blanche.
+
diff --git a/source/umontreal/iro/lecuyer/randvar/BernoulliGen.java b/source/umontreal/iro/lecuyer/randvar/BernoulliGen.java
new file mode 100644
index 0000000..34ad861
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BernoulliGen.java
@@ -0,0 +1,96 @@
+
+
+/*
+ * Class: BernoulliGen
+ * Description: random variate generators for the Bernoulli distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.BernoulliDist;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+/**
+ * This class implements random variate generators for the
+ * <EM>Bernoulli</EM> distribution (see class
+ * {@link umontreal.iro.lecuyer.probdist.BernoulliDist BernoulliDist}).
+ *
+ */
+public class BernoulliGen extends RandomVariateGenInt {
+ protected double p;
+
+
+
+ /**
+ * Creates a Bernoulli random variate generator with parameter <SPAN CLASS="MATH"><I>p</I></SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public BernoulliGen (RandomStream s, double p) {
+ super (s, new BernoulliDist (p));
+ setParams (p);
+ }
+
+
+ /**
+ * Creates a random variate generator for the <EM>Bernoulli</EM>
+ * distribution <TT>dist</TT> and the random stream <TT>s</TT>.
+ *
+ */
+ public BernoulliGen (RandomStream s, BernoulliDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getP());
+ }
+
+
+ /**
+ * Generates a new integer from the <EM>Bernoulli</EM> distribution with
+ * parameter <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>p</TT>, using the given stream <TT>s</TT>.
+ *
+ */
+ public static int nextInt (RandomStream s, double p) {
+ return BernoulliDist.inverseF (p, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range [0, 1]");
+ this.p = p;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/BernoulliGen.tex b/source/umontreal/iro/lecuyer/randvar/BernoulliGen.tex
new file mode 100644
index 0000000..4fb8e72
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BernoulliGen.tex
@@ -0,0 +1,105 @@
+\defmodule {BernoulliGen}
+
+This class implements random variate generators for the
+{\em Bernoulli\/} distribution (see class
+ \externalclass{umontreal.iro.lecuyer.probdist}{BernoulliDist}).
+
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BernoulliGen
+ * Description: random variate generators for the Bernoulli distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.BernoulliDist;
+import umontreal.iro.lecuyer.rng.RandomStream;\end{hide}
+
+public class BernoulliGen extends RandomVariateGenInt \begin{hide} {
+ protected double p;
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BernoulliGen (RandomStream s, double p)\begin{hide} {
+ super (s, new BernoulliDist (p));
+ setParams (p);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates a Bernoulli random variate generator with parameter $p$,
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public BernoulliGen (RandomStream s, BernoulliDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getP());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a random variate generator for the {\em Bernoulli\/}
+ distribution \texttt{dist} and the random stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static int nextInt (RandomStream s, double p)\begin{hide} {
+ return BernoulliDist.inverseF (p, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new integer from the {\em Bernoulli\/} distribution with
+ parameter $p = $~\texttt{p}, using the given stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range [0, 1]");
+ this.p = p;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $p$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaGen.java b/source/umontreal/iro/lecuyer/randvar/BetaGen.java
new file mode 100644
index 0000000..0e1ea97
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaGen.java
@@ -0,0 +1,161 @@
+
+
+/*
+ * Class: BetaGen
+ * Description: random variate generators with the beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators with the
+ * <EM>beta</EM> distribution with shape parameters
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>, over the interval <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>)</SPAN>, where <SPAN CLASS="MATH"><I>a</I> < <I>b</I></SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = [<I>Γ</I>(<I>α</I> + <I>β</I>)/(<I>Γ</I>(<I>α</I>)<I>Γ</I>(<I>β</I>)(<I>b</I> - <I>a</I>)<SUP><I>α</I>+<I>β</I>-1</SUP>)](<I>x</I> - <I>a</I>)<SUP><I>α</I>-1</SUP>(<I>b</I> - <I>x</I>)<SUP><I>β</I>-1</SUP> for <I>a</I> < <I>x</I> < <I>b</I>,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> elsewhere,
+ * where
+ * <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined
+ * in
+ * {@link GammaGen}.
+ * Local copies of the parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>a</I></SPAN>, and <SPAN CLASS="MATH"><I>b</I></SPAN>
+ * are maintained in this class.
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class BetaGen extends RandomVariateGen {
+
+ // Distribution parameters
+ protected double p;
+ protected double q;
+ protected double a;
+ protected double b;
+ protected int gen;
+
+
+
+ /**
+ * Creates a new beta generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval
+ * <SPAN CLASS="MATH">(</SPAN><TT>a</TT><SPAN CLASS="MATH">,</SPAN> <TT>b</TT><SPAN CLASS="MATH">)</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public BetaGen (RandomStream s, double alpha, double beta,
+ double a, double b) {
+ super (s, new BetaDist (alpha, beta, a, b));
+ setParams (alpha, beta, a, b);
+ }
+
+
+ /**
+ * Creates a new beta generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public BetaGen (RandomStream s, double alpha, double beta) {
+ this (s, alpha, beta, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public BetaGen (RandomStream s, BetaDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getA(), dist.getB());
+ }
+
+
+ /**
+ * Generates a variate from the <EM>beta</EM> distribution with
+ * parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the
+ * interval <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>)</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha,
+ double beta, double a, double b) {
+ return BetaDist.inverseF (alpha, beta, a, b, 15, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return p;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return q;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>a</I></SPAN> of this object.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>b</I></SPAN> of this object.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ protected void setParams (double alpha, double beta, double aa, double bb) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (aa >= bb)
+ throw new IllegalArgumentException ("a >= b");
+ p = alpha;
+ q = beta;
+ a = aa;
+ b = bb;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaGen.tex b/source/umontreal/iro/lecuyer/randvar/BetaGen.tex
new file mode 100644
index 0000000..d7de683
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaGen.tex
@@ -0,0 +1,183 @@
+\defmodule {BetaGen}
+
+This class implements random variate generators with the
+{\em beta\/} distribution with shape parameters $\alpha > 0$ and
+$\beta > 0$, over the interval $(a,b)$, where $a < b$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+f(x) = [\Gamma (\alpha+\beta)/(\Gamma(\alpha)
+\Gamma(\beta)(b - a)^{\alpha+\beta-1})]
+ (x - a)^{\alpha - 1}(b - x)^{\beta-1}
+ \mbox{ for } a < x < b,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac {\Gamma (\alpha+\beta)}{
+ \Gamma(\alpha)\Gamma(\beta)(b - a)^{\alpha+\beta-1}}
+ (x - a)^{\alpha - 1}(b - x)^{\beta-1}
+ \qquad \mbox{ for } a < x < b,
+\endeq
+\end{latexonly}
+and $f(x)=0$ elsewhere,
+where $\Gamma (x)$ is the gamma function defined
+in
+\begin{latexonly}
+(\ref{eq:Gamma}).
+\end{latexonly}
+\begin{htmlonly}
+\class{GammaGen}.
+\end{htmlonly}
+
+Local copies of the parameters $\alpha$, $\beta$, $a$, and $b$
+are maintained in this class.
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+%Available generation methods: inversion performed with a binary search,
+%rejection with log-logistic envelopes, and stratified rejection/patchwork
+%rejection.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BetaGen
+ * Description: random variate generators with the beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class BetaGen extends RandomVariateGen \begin{hide} {
+
+ // Distribution parameters
+ protected double p;
+ protected double q;
+ protected double a;
+ protected double b;
+ protected int gen;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BetaGen (RandomStream s, double alpha, double beta,
+ double a, double b) \begin{hide} {
+ super (s, new BetaDist (alpha, beta, a, b));
+ setParams (alpha, beta, a, b);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new beta generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval
+ $($\texttt{a}$,$~\texttt{b}$)$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public BetaGen (RandomStream s, double alpha, double beta) \begin{hide} {
+ this (s, alpha, beta, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new beta generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval $(0,1)$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public BetaGen (RandomStream s, BetaDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getA(), dist.getB());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha,
+ double beta, double a, double b)\begin{hide} {
+ return BetaDist.inverseF (alpha, beta, a, b, 15, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the {\em beta\/} distribution with
+ parameters $\alpha = $~\texttt{alpha}, $\beta = $~\texttt{beta}, over the
+ interval $(a, b)$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return q;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $a$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $b$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void setParams (double alpha, double beta, double aa, double bb) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (aa >= bb)
+ throw new IllegalArgumentException ("a >= b");
+ p = alpha;
+ q = beta;
+ a = aa;
+ b = bb;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaRejectionLoglogisticGen.java b/source/umontreal/iro/lecuyer/randvar/BetaRejectionLoglogisticGen.java
new file mode 100644
index 0000000..2389c91
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaRejectionLoglogisticGen.java
@@ -0,0 +1,307 @@
+
+
+/*
+ * Class: BetaRejectionLoglogisticGen
+ * Description: beta random variate generators using the rejection method
+ with log-logistic envelopes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * Implements <EM>Beta</EM> random variate generators using
+ * the rejection method with log-logistic envelopes.
+ * The method draws the first two uniforms from the main stream
+ * and uses the auxiliary stream for the remaining uniforms,
+ * when more than two are needed (i.e., when rejection occurs).
+ *
+ */
+public class BetaRejectionLoglogisticGen extends BetaGen {
+
+ private RandomStream auxStream;
+ // Parameters for rejection with log-logistic envelopes
+ private static final int bb = 0;
+ private static final int bc = 1;
+ private double am;
+ private double bm;
+ private double al;
+ private double alnam;
+ private double be;
+ private double ga;
+ private double si;
+ private double rk1;
+ private double rk2;
+
+
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT> over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ * using main stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public BetaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ double alpha, double beta) {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, 0.0, 1.0);
+ init();
+ }
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public BetaRejectionLoglogisticGen (RandomStream s,
+ double alpha, double beta) {
+ this (s, s, alpha, beta);
+ }
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT> over the interval
+ * (<TT>a</TT>, <TT>b</TT>),
+ * using main stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public BetaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ double alpha, double beta, double a, double b) {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, a, b);
+ init();
+ }
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval
+ * (<TT>a</TT>, <TT>b</TT>), using stream <TT>s</TT>.
+ *
+ */
+ public BetaRejectionLoglogisticGen (RandomStream s,
+ double alpha, double beta, double a, double b) {
+ this (s, s, alpha, beta, a, b);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ * The main stream is used for the first uniforms (before a rejection occurs)
+ * and the auxiliary stream is used afterwards (after the first rejection).
+ *
+ */
+ public BetaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ BetaDist dist) {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getA(), dist.getB());
+ init();
+ }
+
+
+ /**
+ * Same as {@link #BetaRejectionLoglogisticGen BetaRejectionLoglogisticGen} <TT>(s, s, dist)</TT>.
+ * The auxiliary stream used will be the same as the main stream.
+ *
+ */
+ public BetaRejectionLoglogisticGen (RandomStream s, BetaDist dist) {
+ this (s, s, dist);
+ }
+
+
+ /**
+ * Returns the auxiliary stream associated with that object.
+ *
+ */
+ public RandomStream getAuxStream() {
+ return auxStream;
+ }
+
+
+ private void init() {
+ // Code taken from UNURAN
+ if (p > 1.0 && q > 1.0) {
+ gen = bb;
+ am = (p < q) ? p : q;
+ bm = (p > q) ? p : q;
+ al = am + bm;
+ be = Math.sqrt ((al - 2.0)/(2.0 * p * q - al));
+ ga = am + 1.0 / be;
+ }
+ else {
+ gen = bc;
+ am = (p > q) ? p : q;
+ bm = (p < q) ? p : q;
+ al = am + bm;
+ alnam = al * Math.log (al/am) - 1.386294361;
+ be = 1.0 / bm;
+ si = 1.0 + am - bm;
+ rk1 = si * (0.013888889 + 0.041666667 * bm) / (am * be - 0.77777778);
+ rk2 = 0.25 + (0.5 + 0.25 / si) * bm;
+ }
+ }
+
+
+ public double nextDouble() {
+ /* ***********************************
+ Previously: return rejectionLogLogistic();
+ Now executes code directly (without calling anything)
+ ***********************************/
+
+ // The code was taken from UNURAN
+ double X = 0.0;
+ double u1,u2,v,w,y,z,r,s,t;
+ RandomStream stream = this.stream;
+ switch (gen) {
+ case bb:
+ /* -X- generator code -X- */
+ while (true) {
+ /* Step 1 */
+ u1 = stream.nextDouble();
+ u2 = stream.nextDouble();
+ stream = auxStream;
+ v = be*Math.log (u1/(1.0 - u1));
+ w = am*Math.exp (v);
+ z = u1*u1* u2;
+ r = ga*v - 1.386294361;
+ s = am + r - w;
+
+ /* Step 2 */
+ if (s + 2.609437912 < 5.0*z) {
+ /* Step 3 */
+ t = Math.log (z);
+ if (s < t)
+ /* Step 4 */
+ if (r + al*Math.log (al/(bm + w)) < t)
+ continue;
+ }
+
+ /* Step 5 */
+ X = equalsDouble (am, p) ? w/(bm + w) : bm/(bm + w);
+ break;
+ }
+ /* -X- end of generator code -X- */
+ break;
+ case bc:
+ while (true) {
+ /* Step 1 */
+ u1 = stream.nextDouble();
+ u2 = stream.nextDouble();
+ stream = auxStream;
+
+ if (u1 < 0.5) {
+ /* Step 2 */
+ y = u1*u2;
+ z = u1*y;
+
+ if ((0.25*u2 - y + z) >= rk1)
+ continue; /* goto 1 */
+
+ /* Step 5 */
+ v = be*Math.log (u1/(1.0 - u1));
+ if (v > 80.0) {
+ if (alnam < Math.log (z))
+ continue;
+ X = equalsDouble (am, p) ? 1.0 : 0.0;
+ break;
+ }
+ else {
+ w = am*Math.exp (v);
+ if ((al*(Math.log (al/(bm + w)) + v) - 1.386294361) <
+ Math.log (z))
+ continue; /* goto 1 */
+
+ /* Step 6_a */
+ X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+ break;
+ }
+ }
+ else {
+ /* Step 3 */
+ z = u1*u1*u2;
+ if (z < 0.25) {
+ /* Step 5 */
+ v = be*Math.log (u1/(1.0 - u1));
+ if (v > 80.0) {
+ X = equalsDouble (am, p) ? 1.0 : 0.0;
+ break;
+ }
+
+ w = am*Math.exp (v);
+ X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+ break;
+ }
+ else {
+ if (z >= rk2)
+ continue;
+ v = be*Math.log (u1/(1.0 - u1));
+ if ( v > 80.0) {
+ if (alnam < Math.log (z))
+ continue;
+ X = equalsDouble (am, p) ? 1.0 : 0.0;
+ break;
+ }
+ w = am*Math.exp (v);
+ if ((al*(Math.log (al/(bm + w)) + v) - 1.386294361) <
+ Math.log (z))
+ continue; /* goto 1 */
+
+ /* Step 6_b */
+ X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+
+ return a + (b-a)*X;
+ }
+
+ private static boolean equalsDouble (double a, double b) {
+ if (a == b)
+ return true;
+ double absa = Math.abs (a);
+ double absb = Math.abs (b);
+ return Math.abs (a - b) <= Math.min (absa, absb)*Num.DBL_EPSILON;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaRejectionLoglogisticGen.tex b/source/umontreal/iro/lecuyer/randvar/BetaRejectionLoglogisticGen.tex
new file mode 100644
index 0000000..7bd51eb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaRejectionLoglogisticGen.tex
@@ -0,0 +1,431 @@
+\defmodule {BetaRejectionLoglogisticGen}
+
+Implements {\em Beta\/} random variate generators using
+the rejection method with log-logistic envelopes\latex{ from \cite{rCHE78a}}.
+The method draws the first two uniforms from the main stream
+and uses the auxiliary stream for the remaining uniforms,
+when more than two are needed (i.e., when rejection occurs).
+\begin{detailed}
+The current implementation is adapted from UNURAN.
+\end{detailed}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BetaRejectionLoglogisticGen
+ * Description: beta random variate generators using the rejection method
+ with log-logistic envelopes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class BetaRejectionLoglogisticGen extends BetaGen \begin{hide} {
+
+ private RandomStream auxStream;
+ // Parameters for rejection with log-logistic envelopes
+ private static final int bb = 0;
+ private static final int bc = 1;
+ private double am;
+ private double bm;
+ private double al;
+ private double alnam;
+ private double be;
+ private double ga;
+ private double si;
+ private double rk1;
+ private double rk2;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BetaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ double alpha, double beta) \begin{hide} {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, 0.0, 1.0);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta} over the interval $(0,1)$,
+ using main stream \texttt{s} and auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+\end{tabb}
+\begin{code}
+
+ public BetaRejectionLoglogisticGen (RandomStream s,
+ double alpha, double beta) \begin{hide} {
+ this (s, s, alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval $(0,1)$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public BetaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ double alpha, double beta, double a, double b) \begin{hide} {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, a, b);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta} over the interval
+ (\texttt{a}, \texttt{b}),
+ using main stream \texttt{s} and auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+\end{tabb}
+\begin{code}
+
+ public BetaRejectionLoglogisticGen (RandomStream s,
+ double alpha, double beta, double a, double b) \begin{hide} {
+ this (s, s, alpha, beta, a, b);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval
+ (\texttt{a}, \texttt{b}), using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public BetaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ BetaDist dist) \begin{hide} {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getA(), dist.getB());
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s} and auxiliary stream \texttt{aux}.
+ The main stream is used for the first uniforms (before a rejection occurs)
+ and the auxiliary stream is used afterwards (after the first rejection).
+\end{tabb}
+\begin{code}
+
+ public BetaRejectionLoglogisticGen (RandomStream s, BetaDist dist)\begin{hide} {
+ this (s, s, dist);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{BetaRejectionLoglogisticGen}{}~\texttt{(s, s, dist)}.
+ The auxiliary stream used will be the same as the main stream.
+\end{tabb}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public RandomStream getAuxStream()\begin{hide} {
+ return auxStream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the auxiliary stream associated with that object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private void init() {
+ // Code taken from UNURAN
+ if (p > 1.0 && q > 1.0) {
+ gen = bb;
+ am = (p < q) ? p : q;
+ bm = (p > q) ? p : q;
+ al = am + bm;
+ be = Math.sqrt ((al - 2.0)/(2.0 * p * q - al));
+ ga = am + 1.0 / be;
+ }
+ else {
+ gen = bc;
+ am = (p > q) ? p : q;
+ bm = (p < q) ? p : q;
+ al = am + bm;
+ alnam = al * Math.log (al/am) - 1.386294361;
+ be = 1.0 / bm;
+ si = 1.0 + am - bm;
+ rk1 = si * (0.013888889 + 0.041666667 * bm) / (am * be - 0.77777778);
+ rk2 = 0.25 + (0.5 + 0.25 / si) * bm;
+ }
+ }\end{hide}
+\end{code}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ /************************************
+ Previously: return rejectionLogLogistic();
+ Now executes code directly (without calling anything)
+ ***********************************/
+
+ // The code was taken from UNURAN
+ double X = 0.0;
+ double u1,u2,v,w,y,z,r,s,t;
+ RandomStream stream = this.stream;
+ switch (gen) {
+ case bb:
+ /* -X- generator code -X- */
+ while (true) {
+ /* Step 1 */
+ u1 = stream.nextDouble();
+ u2 = stream.nextDouble();
+ stream = auxStream;
+ v = be*Math.log (u1/(1.0 - u1));
+ w = am*Math.exp (v);
+ z = u1*u1* u2;
+ r = ga*v - 1.386294361;
+ s = am + r - w;
+
+ /* Step 2 */
+ if (s + 2.609437912 < 5.0*z) {
+ /* Step 3 */
+ t = Math.log (z);
+ if (s < t)
+ /* Step 4 */
+ if (r + al*Math.log (al/(bm + w)) < t)
+ continue;
+ }
+
+ /* Step 5 */
+ X = equalsDouble (am, p) ? w/(bm + w) : bm/(bm + w);
+ break;
+ }
+ /* -X- end of generator code -X- */
+ break;
+ case bc:
+ while (true) {
+ /* Step 1 */
+ u1 = stream.nextDouble();
+ u2 = stream.nextDouble();
+ stream = auxStream;
+
+ if (u1 < 0.5) {
+ /* Step 2 */
+ y = u1*u2;
+ z = u1*y;
+
+ if ((0.25*u2 - y + z) >= rk1)
+ continue; /* goto 1 */
+
+ /* Step 5 */
+ v = be*Math.log (u1/(1.0 - u1));
+ if (v > 80.0) {
+ if (alnam < Math.log (z))
+ continue;
+ X = equalsDouble (am, p) ? 1.0 : 0.0;
+ break;
+ }
+ else {
+ w = am*Math.exp (v);
+ if ((al*(Math.log (al/(bm + w)) + v) - 1.386294361) <
+ Math.log (z))
+ continue; /* goto 1 */
+
+ /* Step 6_a */
+ X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+ break;
+ }
+ }
+ else {
+ /* Step 3 */
+ z = u1*u1*u2;
+ if (z < 0.25) {
+ /* Step 5 */
+ v = be*Math.log (u1/(1.0 - u1));
+ if (v > 80.0) {
+ X = equalsDouble (am, p) ? 1.0 : 0.0;
+ break;
+ }
+
+ w = am*Math.exp (v);
+ X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+ break;
+ }
+ else {
+ if (z >= rk2)
+ continue;
+ v = be*Math.log (u1/(1.0 - u1));
+ if ( v > 80.0) {
+ if (alnam < Math.log (z))
+ continue;
+ X = equalsDouble (am, p) ? 1.0 : 0.0;
+ break;
+ }
+ w = am*Math.exp (v);
+ if ((al*(Math.log (al/(bm + w)) + v) - 1.386294361) <
+ Math.log (z))
+ continue; /* goto 1 */
+
+ /* Step 6_b */
+ X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+
+ return a + (b-a)*X;
+ }
+
+ private static boolean equalsDouble (double a, double b) {
+ if (a == b)
+ return true;
+ double absa = Math.abs (a);
+ double absb = Math.abs (b);
+ return Math.abs (a - b) <= Math.min (absa, absb)*Num.DBL_EPSILON;
+ }
+
+\end{hide}\end{code}
+%% private double rejectionLogLogistic() {
+%% // The code was taken from UNURAN
+%% double X = 0.0;
+%% double u1,u2,v,w,y,z,r,s,t;
+%% RandomStream stream = this.stream;
+%% switch (gen) {
+%% case bb:
+%% /* -X- generator code -X- */
+%% while (true) {
+%% /* Step 1 */
+%% u1 = stream.nextDouble();
+%% u2 = stream.nextDouble();
+%% stream = auxStream;
+%% v = be*Math.log (u1/(1.0 - u1));
+%% w = am*Math.exp (v);
+%% z = u1*u1* u2;
+%% r = ga*v - 1.386294361;
+%% s = am + r - w;
+
+%% /* Step 2 */
+%% if (s + 2.609437912 < 5.0*z) {
+%% /* Step 3 */
+%% t = Math.log (z);
+%% if (s < t)
+%% /* Step 4 */
+%% if (r + al*Math.log (al/(bm + w)) < t)
+%% continue;
+%% }
+
+%% /* Step 5 */
+%% X = equalsDouble (am, p) ? w/(bm + w) : bm/(bm + w);
+%% break;
+%% }
+%% /* -X- end of generator code -X- */
+%% break;
+%% case bc:
+%% while (true) {
+%% /* Step 1 */
+%% u1 = stream.nextDouble();
+%% u2 = stream.nextDouble();
+%% stream = auxStream;
+
+%% if (u1 < 0.5) {
+%% /* Step 2 */
+%% y = u1*u2;
+%% z = u1*y;
+
+%% if ((0.25*u2 - y + z) >= rk1)
+%% continue; /* goto 1 */
+
+%% /* Step 5 */
+%% v = be*Math.log (u1/(1.0 - u1));
+%% if (v > 80.0) {
+%% if (alnam < Math.log (z))
+%% continue;
+%% X = equalsDouble (am, p) ? 1.0 : 0.0;
+%% break;
+%% }
+%% else {
+%% w = am*Math.exp (v);
+%% if ((al*(Math.log (al/(bm + w)) + v) - 1.386294361) <
+%% Math.log (z))
+%% continue; /* goto 1 */
+
+%% /* Step 6_a */
+%% X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+%% break;
+%% }
+%% }
+%% else {
+%% /* Step 3 */
+%% z = u1*u1*u2;
+%% if (z < 0.25) {
+%% /* Step 5 */
+%% v = be*Math.log (u1/(1.0 - u1));
+%% if (v > 80.0) {
+%% X = equalsDouble (am, p) ? 1.0 : 0.0;
+%% break;
+%% }
+
+%% w = am*Math.exp (v);
+%% X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+%% break;
+%% }
+%% else {
+%% if (z >= rk2)
+%% continue;
+%% v = be*Math.log (u1/(1.0 - u1));
+%% if ( v > 80.0) {
+%% if (alnam < Math.log (z))
+%% continue;
+%% X = equalsDouble (am, p) ? 1.0 : 0.0;
+%% break;
+%% }
+%% w = am*Math.exp (v);
+%% if ((al*(Math.log (al/(bm + w)) + v) - 1.386294361) <
+%% Math.log (z))
+%% continue; /* goto 1 */
+
+%% /* Step 6_b */
+%% X = !equalsDouble (am, p) ? bm/(bm + w) : w/(bm + w);
+%% break;
+%% }
+%% }
+%% }
+%% break;
+%% default:
+%% throw new IllegalStateException();
+%% }
+
+%% return a + (b-a)*X;
+%% }
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaStratifiedRejectionGen.java b/source/umontreal/iro/lecuyer/randvar/BetaStratifiedRejectionGen.java
new file mode 100644
index 0000000..6efe55f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaStratifiedRejectionGen.java
@@ -0,0 +1,526 @@
+
+
+/*
+ * Class: BetaStratifiedRejectionGen
+ * Description: beta random variate generators using the stratified
+ rejection/patchwork rejection method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>Beta</EM> random variate generators using
+ * the stratified rejection/patchwork rejection
+ * method.
+ * This method draws one uniform from the main stream and uses the
+ * auxiliary stream for any additional uniform variates that might be needed.
+ *
+ */
+public class BetaStratifiedRejectionGen extends BetaGen {
+
+ private RandomStream auxStream;
+ private int gen;
+
+ // Parameters for stratified rejection/patchwork rejection
+ private static final int b00 = 2;
+ private static final int b01 = 3;
+ private static final int b01inv = 4;
+ private static final int b1prs = 5;
+ private double pint;
+ private double qint;
+ private double p_;
+ private double q_;
+ private double c;
+ private double t;
+ private double fp;
+ private double fq;
+ private double ml;
+ private double mu;
+ private double p1;
+ private double p2;
+ private double s;
+ private double m;
+ private double D;
+ private double Dl;
+ private double x1;
+ private double x2;
+ private double x4;
+ private double x5;
+ private double f1;
+ private double f2;
+ private double f4;
+ private double f5;
+ private double ll;
+ private double lr;
+ private double z2;
+ private double z4;
+ private double p3;
+ private double p4;
+
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ * using main stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public BetaStratifiedRejectionGen (RandomStream s, RandomStream aux,
+ double alpha, double beta) {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, 0.0, 1.0);
+ init();
+ }
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public BetaStratifiedRejectionGen (RandomStream s,
+ double alpha, double beta) {
+ this (s, s, alpha, beta);
+ }
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval
+ * (<TT>a</TT>, <TT>b</TT>),
+ * using main stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public BetaStratifiedRejectionGen (RandomStream s, RandomStream aux,
+ double alpha, double beta, double a, double b) {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, a, b);
+ init();
+ }
+
+
+ /**
+ * Creates a beta random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, over the interval
+ * (<TT>a</TT>, <TT>b</TT>), using stream <TT>s</TT>.
+ *
+ */
+ public BetaStratifiedRejectionGen (RandomStream s,
+ double alpha, double beta, double a, double b) {
+ this (s, s, alpha, beta, a, b);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using the given stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number
+ * of variates must be drawn from the main stream.
+ *
+ */
+ public BetaStratifiedRejectionGen (RandomStream s, RandomStream aux,
+ BetaDist dist) {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getA(), dist.getB());
+ init();
+ }
+
+
+ /**
+ * Same as {@link #BetaStratifiedRejectionGen BetaStratifiedRejectionGen}<TT>(s, s, dist)</TT>.
+ * The auxiliary stream used will be the same as the main stream.
+ *
+ */
+ public BetaStratifiedRejectionGen (RandomStream s, BetaDist dist) {
+ this (s, s, dist);
+ }
+
+
+ /**
+ * Returns the auxiliary stream associated with this object.
+ *
+ */
+ public RandomStream getAuxStream() {
+ return auxStream;
+ }
+
+
+ public double nextDouble() {
+ /* ***********************************
+ Previously: return stratifiedRejection();
+ Now executes code directly (without calling anything)
+ ***********************************/
+
+ // The code was taken from UNURAN
+ double X = 0.0;
+ double U, V, Z, W, Y;
+ RandomStream stream = this.stream;
+ switch (gen) {
+ case b00:
+ /* -X- generator code -X- */
+ while (true) {
+ U = stream.nextDouble()*p2;
+ stream = auxStream;
+ if (U <= p1) { /* X < t */
+ Z = Math.exp (Math.log (U/p1)/p);
+ X = t*Z;
+ /* squeeze accept: L(x) = 1 + (1 - q)x */
+ V = stream.nextDouble()*fq;
+ if (V <= 1. - q_*X)
+ break;
+ /* squeeze reject: U(x) = 1 + ((1 - t)^(q-1) - 1)/t * x */
+ if (V <= 1. + (fq - 1.) * Z) {
+ /* quotient accept: quot(x) = (1 - x)^(q-1) / fq */
+ if (Math.log (V) <= q_*Math.log (1. - X))
+ break;
+ }
+ }
+ else { /* X > t */
+ Z = Math.exp (Math.log ((U-p1)/(p2-p1) )/q);
+ X = 1. - (1. - t)*Z;
+ /* squeeze accept: L(x) = 1 + (1 - p)(1 - x) */
+ V = stream.nextDouble()*fp;
+ if (V <= 1.0 - p_*(1. - X))
+ break;
+ /* squeeze reject: U(x) = 1 + (t^(p-1) - 1)/(1 - t) * (1 - x) */
+ if (V <= 1.0 + (fp - 1.) * Z) {
+ /* quotient accept: quot(x) = x^(p-1) / fp */
+ if (Math.log (V) <= p_*Math.log (X))
+ break;
+ }
+ }
+ }
+ /* -X- end of generator code -X- */
+ break;
+ case b01:
+ case b01inv:
+ /* -X- generator code -X- */
+ while (true) {
+ U = stream.nextDouble()*p2;
+ stream = auxStream;
+ if (U <= p1) { /* X < t */
+ Z = Math.exp (Math.log (U/p1)/pint);
+ X = t*Z;
+ /* squeeze accept: L(x) = 1 + m1*x, ml = -m1 */
+ V = stream.nextDouble();
+ if (V <= 1. - ml * X)
+ break;
+ /* squeeze reject: U(x) = 1 + m2*x, mu = -m2 * t */
+ if (V <= 1. - mu * Z)
+ /* quotient accept: quot(x) = (1 - x)^(q-1) */
+ if (Math.log (V) <= q_*Math.log (1. - X))
+ break;
+ }
+ else { /* X > t */
+ Z = Math.exp (Math.log ((U-p1)/(p2-p1)) / qint);
+ X = 1. - (1. - t)*Z;
+ /* squeeze accept: L(x) = 1 + (1 - p)(1 - x) */
+ V = stream.nextDouble()*fp;
+ if (V <= 1. - p_ * (1. - X))
+ break;
+ /* squeeze reject: U(x) = 1 + (t^(p-1) - 1)/(1 - t) * (1 - x) */
+ if (V <= 1. + (fp - 1.) * Z)
+ /* quotient accept: quot(x) = (x)^(p-1) / fp */
+ if (Math.log (V) <= p_*Math.log (X))
+ break;
+ }
+ }
+ if (p>q)
+ /* p and q has been swapped */
+ X = 1. - X;
+ /* -X- end of generator code -X- */
+ break;
+ case b1prs:
+ while (true) {
+ U = stream.nextDouble()*p4;
+ stream = auxStream;
+ if (U <= p1) {
+ /* immediate accept: x2 < X < m, - f(x2) < W < 0 */
+ W = U/Dl - f2;
+ if (W <= 0.0) {
+ X = m - U/f2;
+ break;
+ }
+ /* immediate accept: x1 < X < x2, 0 < W < f(x1) */
+ if (W <= f1) {
+ X = x2 - W/f1*Dl;
+ break;
+ }
+ /* candidates for acceptance-rejection-test */
+ U = stream.nextDouble();
+ V = Dl*U;
+ X = x2 - V;
+ Y = x2 + V;
+ /* squeeze accept: L(x) = f(x2) (x - z2) / (x2 - z2) */
+ if (W*(x2 - z2) <= f2*(X - z2))
+ break;
+ V = f2 + f2 - W;
+ if (V < 1.0) {
+ /* squeeze accept: L(x) = f(x2) + (1 - f(x2))(x - x2)/(m - x2) */
+ if (V <= f2 + (1. - f2)*U) {
+ X = Y;
+ break;
+ }
+ /* quotient accept: x2 < Y < m, W >= 2f2 - f(Y) */
+ if (V <= Math.exp ( p_*Math.log (Y/m)
+ + q_*Math.log ((10. - Y)/(1.0 - m)) ) ) {
+ X = Y;
+ break;
+ }
+ }
+ }
+ else if (U <= p2) {
+ U -= p1;
+ /* immediate accept: m < X < x4, - f(x4) < W < 0 */
+ W = U/D - f4;
+ if (W <= 0.) {
+ X = m + U/f4;
+ break;
+ }
+ /* immediate accept: x4 < X < x5, 0 < W < f(x5) */
+ if (W <= f5) {
+ X = x4 + W/f5 * D;
+ break;
+ }
+ /* candidates for acceptance-rejection-test */
+ U = stream.nextDouble();
+ V = D*U;
+ X = x4 + V;
+ Y = x4 - V;
+ /* squeeze accept: L(x) = f(x4) (z4 - x) / (z4 - x4) */
+ if (W*(z4 - x4) <= f4*(z4 - X))
+ break;
+ V = f4 + f4 - W;
+ if (V < 1.0) {
+ /* squeeze accept: L(x) = f(x4) + (1 - f(x4))(x4 - x)/(x4 - m) */
+ if (V <= f4 + (1.0 - f4)*U) {
+ X = Y;
+ break;
+ }
+ /* quotient accept: m < Y < x4, W >= 2f4 - f(Y) */
+ if (V <= Math.exp ( p_*Math.log (Y/m)
+ + q_*Math.log ((1.0 - Y)/(1.0 - m)))) {
+ X = Y;
+ break;
+ }
+ }
+ }
+ else if (U <= p3) { /* X < x1 */
+ U = (U - p2)/(p3 - p2);
+ Y = Math.log (U);
+ X = x1 + ll*Y;
+ if (X <= 0.0) /* X > 0!! */
+ continue;
+ W = U*stream.nextDouble();
+ /* squeeze accept: L(x) = f(x1) (x - z1) / (x1 - z1) */
+ /* z1 = x1 - ll, W <= 1 + (X - x1)/ll */
+ if (W <= 1.0 + Y)
+ break;
+ W *= f1;
+ }
+ else { /* x5 < X */
+ U = (U - p3)/(p4 - p3);
+ Y = Math.log (U);
+ X = x5 - lr*Y;
+ if (X >= 1.0) /* X < 1!! */
+ continue;
+ W = U*stream.nextDouble();
+ /* squeeze accept: L(x) = f(x5) (z5 - x) / (z5 - x5) */
+ /* z5 = x5 + lr, W <= 1 + (x5 - X)/lr */
+ if (W <= 1.0 + Y)
+ break;
+ W *= f5;
+ }
+ /* density accept: f(x) = (x/m)^(p_) ((1 - x)/(1 - m))^(q_) */
+ if (Math.log (W) <= p_*Math.log (X/m)
+ + q_*Math.log ((1.0 - X)/(1.0 - m)))
+ break;
+ }
+ /* -X- end of generator code -X- */
+ break;
+ default: throw new IllegalStateException();
+ }
+
+ return gen == b01inv ? a + (b-a)*(1.0 - X) : a + (b-a)*X;
+ }
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta,
+ double a, double b) {
+ return BetaDist.inverseF (alpha, beta, a, b, 15, s.nextDouble());
+ }
+
+
+ private void init() {
+ // Code taken from UNURAN
+ if (p > 1.) {
+ if (q>1.) /* p > 1 && q > 1 */
+ gen = b1prs;
+ else { /* p > 1 && q <= 1 */
+ gen = b01inv;
+ double temp = p;
+ p = q;
+ q = temp;
+ }
+ }
+ else {
+ if (q>1.) /* p <= 1 && q > 1 */
+ gen = b01;
+ else /* p <= 1 && q <= 1 */
+ gen = b00;
+ }
+
+ switch (gen) {
+ case b00:
+ /* -X- setup code -X- */
+ p_ = p - 1.;
+ q_ = q - 1.;
+ c = (q*q_)/(p*p_); /* q(1-q) / p(1-p) */
+ t = (c == 1.) ? 0.5 : (1. - Math.sqrt (c))/(1. - c); /* t = t_opt */
+ fp = Math.exp (p_*Math.log (t));
+ fq = Math.exp (q_*Math.log (1. - t)); /* f(t) = fa * fb */
+
+ p1 = t/p; /* 0 < X < t */
+ p2 = (1. - t)/q + p1; /* t < X < 1 */
+ /* -X- end of setup code -X- */
+ break;
+ case b01:
+ case b01inv:
+ /* -X- setup code -X- */
+ /* internal use of p and q */
+ if (p > q) {
+ /* swap p and q */
+ pint = q;
+ qint = p;
+ }
+ else {
+ pint = p;
+ qint = q;
+ }
+
+ p_ = pint - 1.;
+ q_ = qint - 1.;
+ t = p_/(pint - qint); /* one step Newton * start value t */
+ fq = Math.exp ((q_ - 1.)*Math.log (1. - t));
+ fp = pint - (pint + q_)*t;
+ t -= (t - (1. - fp)*(1. - t)*fq/qint)/(1. - fp*fq);
+ fp = Math.exp (p_*Math.log (t));
+ fq = Math.exp (q_*Math.log (1. - t)); /* f(t) = fa * fb */
+ if (q_ <= 1.0) {
+ ml = (1. - fq)/t; /* ml = -m1 */
+ mu = q_ * t; /* mu = -m2 * t */
+ }
+ else {
+ ml = q_;
+ mu = 1. - fq;
+ }
+ p1 = t/pint; /* 0 < X < t */
+ p2 = fq*(1. - t)/qint + p1; /* t < X < 1 */
+ /* -X- end of setup code -X- */
+ break;
+ case b1prs:
+ /* -X- setup code -X- */
+ p_ = p - 1.0;
+ q_ = q - 1.0;
+ s = p_ + q_;
+ m = p_/s;
+
+ if (p_ > 1.0 || q_ > 1.0)
+ D = Math.sqrt (m * (1. - m)/(s - 1.0));
+
+ if (p_ <= 1.0) {
+ x2 = (Dl = m * 0.5);
+ x1 = z2 = f1 = ll = 0.0;
+ }
+ else {
+ x2 = m - D;
+ x1 = x2 - D;
+ z2 = x2*(1.0 - (1.0 - x2)/(s*D));
+ if (x1 <= 0.0 || (s - 6.0)*x2 - p_ + 3.0 > 0.0) {
+ x1 = z2; x2 = (x1 + m)*0.5;
+ Dl = m - x2;
+ }
+ else {
+ Dl = D;
+ }
+ f1 = Math.exp ( p_*Math.log (x1/m)
+ + q_*Math.log ((1.0 - x1)/(1.0 - m)) );
+ ll = x1*(1.0 - x1)/(s*(m - x1)); /* z1 = x1 - ll */
+ }
+ f2 = Math.exp ( p_*Math.log (x2/m)
+ + q_*Math.log ((1.0 - x2)/(1.0 - m)) );
+
+ if (q_ <= 1.) {
+ D = (1.0 - m)*0.5;
+ x4 = 1.0 - D;
+ x5 = z4 = 1.0;
+ f5 = lr = 0.0;
+ }
+ else {
+ x4 = m + D;
+ x5 = x4 + D;
+ z4 = x4*(1.0 + (1.0 - x4)/(s*D));
+ if (x5 >= 1.0 || (s - 6.0)*x4 - p_ + 3.0 < 0.0) {
+ x5 = z4;
+ x4 = (m + x5)*0.5;
+ D = x4 - m;
+ }
+ f5 = Math.exp ( p_*Math.log (x5/m)
+ + q_*Math.log ((1.0 - x5)/(1. - m)) );
+ lr = x5*(1.0 - x5)/(s*(x5 - m)); /* z5 = x5 + lr */
+ }
+ f4 = Math.exp ( p_*Math.log (x4/m)
+ + q_*Math.log ((1.0 - x4)/(1.0 - m)) );
+
+ p1 = f2*(Dl + Dl); /* x1 < X < m */
+ p2 = f4*(D + D) + p1; /* m < X < x5 */
+ p3 = f1*ll + p2; /* X < x1 */
+ p4 = f5*lr + p3; /* x5 < X */
+ /* -X- end of setup code -X- */
+ break;
+ default: throw new IllegalStateException();
+ }
+ }
+
+ private static boolean equalsDouble (double a, double b) {
+ if (a == b)
+ return true;
+ double absa = Math.abs (a);
+ double absb = Math.abs (b);
+ return Math.abs (a - b) <= Math.min (absa, absb)*Num.DBL_EPSILON;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaStratifiedRejectionGen.tex b/source/umontreal/iro/lecuyer/randvar/BetaStratifiedRejectionGen.tex
new file mode 100644
index 0000000..cdc0d1e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaStratifiedRejectionGen.tex
@@ -0,0 +1,726 @@
+\defmodule {BetaStratifiedRejectionGen}
+
+This class implements {\em Beta\/} random variate generators using
+ the stratified rejection/patch\-work rejection
+ method\latex{ from \cite{rSAK83a,rSTA93a}}.
+ This method draws one uniform from the main stream and uses the
+auxiliary stream for any additional uniform variates that might be needed.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BetaStratifiedRejectionGen
+ * Description: beta random variate generators using the stratified
+ rejection/patchwork rejection method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.util.Num;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class BetaStratifiedRejectionGen extends BetaGen \begin{hide} {
+
+ private RandomStream auxStream;
+ private int gen;
+
+ // Parameters for stratified rejection/patchwork rejection
+ private static final int b00 = 2;
+ private static final int b01 = 3;
+ private static final int b01inv = 4;
+ private static final int b1prs = 5;
+ private double pint;
+ private double qint;
+ private double p_;
+ private double q_;
+ private double c;
+ private double t;
+ private double fp;
+ private double fq;
+ private double ml;
+ private double mu;
+ private double p1;
+ private double p2;
+ private double s;
+ private double m;
+ private double D;
+ private double Dl;
+ private double x1;
+ private double x2;
+ private double x4;
+ private double x5;
+ private double f1;
+ private double f2;
+ private double f4;
+ private double f5;
+ private double ll;
+ private double lr;
+ private double z2;
+ private double z4;
+ private double p3;
+ private double p4;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BetaStratifiedRejectionGen (RandomStream s, RandomStream aux,
+ double alpha, double beta) \begin{hide} {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, 0.0, 1.0);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval $(0,1)$,
+ using main stream \texttt{s} and auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+\end{tabb}
+\begin{code}
+
+ public BetaStratifiedRejectionGen (RandomStream s,
+ double alpha, double beta) \begin{hide} {
+ this (s, s, alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval $(0,1)$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public BetaStratifiedRejectionGen (RandomStream s, RandomStream aux,
+ double alpha, double beta, double a, double b) \begin{hide} {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, beta, a, b);
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval
+ (\texttt{a}, \texttt{b}),
+ using main stream \texttt{s} and auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+\end{tabb}
+\begin{code}
+
+ public BetaStratifiedRejectionGen (RandomStream s,
+ double alpha, double beta, double a, double b) \begin{hide} {
+ this (s, s, alpha, beta, a, b);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a beta random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta =$ \texttt{beta}, over the interval
+ (\texttt{a}, \texttt{b}), using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public BetaStratifiedRejectionGen (RandomStream s, RandomStream aux,
+ BetaDist dist) \begin{hide} {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getA(), dist.getB());
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using the given stream \texttt{s} and auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number
+ of variates must be drawn from the main stream.
+ \end{tabb}
+\begin{code}
+
+ public BetaStratifiedRejectionGen (RandomStream s, BetaDist dist)\begin{hide} {
+ this (s, s, dist);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Same as \method{BetaStratifiedRejectionGen}{}\texttt{(s, s, dist)}.
+ The auxiliary stream used will be the same as the main stream.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public RandomStream getAuxStream()\begin{hide} {
+ return auxStream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the auxiliary stream associated with this object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ /************************************
+ Previously: return stratifiedRejection();
+ Now executes code directly (without calling anything)
+ ***********************************/
+
+ // The code was taken from UNURAN
+ double X = 0.0;
+ double U, V, Z, W, Y;
+ RandomStream stream = this.stream;
+ switch (gen) {
+ case b00:
+ /* -X- generator code -X- */
+ while (true) {
+ U = stream.nextDouble()*p2;
+ stream = auxStream;
+ if (U <= p1) { /* X < t */
+ Z = Math.exp (Math.log (U/p1)/p);
+ X = t*Z;
+ /* squeeze accept: L(x) = 1 + (1 - q)x */
+ V = stream.nextDouble()*fq;
+ if (V <= 1. - q_*X)
+ break;
+ /* squeeze reject: U(x) = 1 + ((1 - t)^(q-1) - 1)/t * x */
+ if (V <= 1. + (fq - 1.) * Z) {
+ /* quotient accept: quot(x) = (1 - x)^(q-1) / fq */
+ if (Math.log (V) <= q_*Math.log (1. - X))
+ break;
+ }
+ }
+ else { /* X > t */
+ Z = Math.exp (Math.log ((U-p1)/(p2-p1) )/q);
+ X = 1. - (1. - t)*Z;
+ /* squeeze accept: L(x) = 1 + (1 - p)(1 - x) */
+ V = stream.nextDouble()*fp;
+ if (V <= 1.0 - p_*(1. - X))
+ break;
+ /* squeeze reject: U(x) = 1 + (t^(p-1) - 1)/(1 - t) * (1 - x) */
+ if (V <= 1.0 + (fp - 1.) * Z) {
+ /* quotient accept: quot(x) = x^(p-1) / fp */
+ if (Math.log (V) <= p_*Math.log (X))
+ break;
+ }
+ }
+ }
+ /* -X- end of generator code -X- */
+ break;
+ case b01:
+ case b01inv:
+ /* -X- generator code -X- */
+ while (true) {
+ U = stream.nextDouble()*p2;
+ stream = auxStream;
+ if (U <= p1) { /* X < t */
+ Z = Math.exp (Math.log (U/p1)/pint);
+ X = t*Z;
+ /* squeeze accept: L(x) = 1 + m1*x, ml = -m1 */
+ V = stream.nextDouble();
+ if (V <= 1. - ml * X)
+ break;
+ /* squeeze reject: U(x) = 1 + m2*x, mu = -m2 * t */
+ if (V <= 1. - mu * Z)
+ /* quotient accept: quot(x) = (1 - x)^(q-1) */
+ if (Math.log (V) <= q_*Math.log (1. - X))
+ break;
+ }
+ else { /* X > t */
+ Z = Math.exp (Math.log ((U-p1)/(p2-p1)) / qint);
+ X = 1. - (1. - t)*Z;
+ /* squeeze accept: L(x) = 1 + (1 - p)(1 - x) */
+ V = stream.nextDouble()*fp;
+ if (V <= 1. - p_ * (1. - X))
+ break;
+ /* squeeze reject: U(x) = 1 + (t^(p-1) - 1)/(1 - t) * (1 - x) */
+ if (V <= 1. + (fp - 1.) * Z)
+ /* quotient accept: quot(x) = (x)^(p-1) / fp */
+ if (Math.log (V) <= p_*Math.log (X))
+ break;
+ }
+ }
+ if (p>q)
+ /* p and q has been swapped */
+ X = 1. - X;
+ /* -X- end of generator code -X- */
+ break;
+ case b1prs:
+ while (true) {
+ U = stream.nextDouble()*p4;
+ stream = auxStream;
+ if (U <= p1) {
+ /* immediate accept: x2 < X < m, - f(x2) < W < 0 */
+ W = U/Dl - f2;
+ if (W <= 0.0) {
+ X = m - U/f2;
+ break;
+ }
+ /* immediate accept: x1 < X < x2, 0 < W < f(x1) */
+ if (W <= f1) {
+ X = x2 - W/f1*Dl;
+ break;
+ }
+ /* candidates for acceptance-rejection-test */
+ U = stream.nextDouble();
+ V = Dl*U;
+ X = x2 - V;
+ Y = x2 + V;
+ /* squeeze accept: L(x) = f(x2) (x - z2) / (x2 - z2) */
+ if (W*(x2 - z2) <= f2*(X - z2))
+ break;
+ V = f2 + f2 - W;
+ if (V < 1.0) {
+ /* squeeze accept: L(x) = f(x2) + (1 - f(x2))(x - x2)/(m - x2) */
+ if (V <= f2 + (1. - f2)*U) {
+ X = Y;
+ break;
+ }
+ /* quotient accept: x2 < Y < m, W >= 2f2 - f(Y) */
+ if (V <= Math.exp ( p_*Math.log (Y/m)
+ + q_*Math.log ((10. - Y)/(1.0 - m)) ) ) {
+ X = Y;
+ break;
+ }
+ }
+ }
+ else if (U <= p2) {
+ U -= p1;
+ /* immediate accept: m < X < x4, - f(x4) < W < 0 */
+ W = U/D - f4;
+ if (W <= 0.) {
+ X = m + U/f4;
+ break;
+ }
+ /* immediate accept: x4 < X < x5, 0 < W < f(x5) */
+ if (W <= f5) {
+ X = x4 + W/f5 * D;
+ break;
+ }
+ /* candidates for acceptance-rejection-test */
+ U = stream.nextDouble();
+ V = D*U;
+ X = x4 + V;
+ Y = x4 - V;
+ /* squeeze accept: L(x) = f(x4) (z4 - x) / (z4 - x4) */
+ if (W*(z4 - x4) <= f4*(z4 - X))
+ break;
+ V = f4 + f4 - W;
+ if (V < 1.0) {
+ /* squeeze accept: L(x) = f(x4) + (1 - f(x4))(x4 - x)/(x4 - m) */
+ if (V <= f4 + (1.0 - f4)*U) {
+ X = Y;
+ break;
+ }
+ /* quotient accept: m < Y < x4, W >= 2f4 - f(Y) */
+ if (V <= Math.exp ( p_*Math.log (Y/m)
+ + q_*Math.log ((1.0 - Y)/(1.0 - m)))) {
+ X = Y;
+ break;
+ }
+ }
+ }
+ else if (U <= p3) { /* X < x1 */
+ U = (U - p2)/(p3 - p2);
+ Y = Math.log (U);
+ X = x1 + ll*Y;
+ if (X <= 0.0) /* X > 0!! */
+ continue;
+ W = U*stream.nextDouble();
+ /* squeeze accept: L(x) = f(x1) (x - z1) / (x1 - z1) */
+ /* z1 = x1 - ll, W <= 1 + (X - x1)/ll */
+ if (W <= 1.0 + Y)
+ break;
+ W *= f1;
+ }
+ else { /* x5 < X */
+ U = (U - p3)/(p4 - p3);
+ Y = Math.log (U);
+ X = x5 - lr*Y;
+ if (X >= 1.0) /* X < 1!! */
+ continue;
+ W = U*stream.nextDouble();
+ /* squeeze accept: L(x) = f(x5) (z5 - x) / (z5 - x5) */
+ /* z5 = x5 + lr, W <= 1 + (x5 - X)/lr */
+ if (W <= 1.0 + Y)
+ break;
+ W *= f5;
+ }
+ /* density accept: f(x) = (x/m)^(p_) ((1 - x)/(1 - m))^(q_) */
+ if (Math.log (W) <= p_*Math.log (X/m)
+ + q_*Math.log ((1.0 - X)/(1.0 - m)))
+ break;
+ }
+ /* -X- end of generator code -X- */
+ break;
+ default: throw new IllegalStateException();
+ }
+
+ return gen == b01inv ? a + (b-a)*(1.0 - X) : a + (b-a)*X;
+ }
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta,
+ double a, double b) {
+ return BetaDist.inverseF (alpha, beta, a, b, 15, s.nextDouble());
+ }
+
+
+ private void init() {
+ // Code taken from UNURAN
+ if (p > 1.) {
+ if (q>1.) /* p > 1 && q > 1 */
+ gen = b1prs;
+ else { /* p > 1 && q <= 1 */
+ gen = b01inv;
+ double temp = p;
+ p = q;
+ q = temp;
+ }
+ }
+ else {
+ if (q>1.) /* p <= 1 && q > 1 */
+ gen = b01;
+ else /* p <= 1 && q <= 1 */
+ gen = b00;
+ }
+
+ switch (gen) {
+ case b00:
+ /* -X- setup code -X- */
+ p_ = p - 1.;
+ q_ = q - 1.;
+ c = (q*q_)/(p*p_); /* q(1-q) / p(1-p) */
+ t = (c == 1.) ? 0.5 : (1. - Math.sqrt (c))/(1. - c); /* t = t_opt */
+ fp = Math.exp (p_*Math.log (t));
+ fq = Math.exp (q_*Math.log (1. - t)); /* f(t) = fa * fb */
+
+ p1 = t/p; /* 0 < X < t */
+ p2 = (1. - t)/q + p1; /* t < X < 1 */
+ /* -X- end of setup code -X- */
+ break;
+ case b01:
+ case b01inv:
+ /* -X- setup code -X- */
+ /* internal use of p and q */
+ if (p > q) {
+ /* swap p and q */
+ pint = q;
+ qint = p;
+ }
+ else {
+ pint = p;
+ qint = q;
+ }
+
+ p_ = pint - 1.;
+ q_ = qint - 1.;
+ t = p_/(pint - qint); /* one step Newton * start value t */
+ fq = Math.exp ((q_ - 1.)*Math.log (1. - t));
+ fp = pint - (pint + q_)*t;
+ t -= (t - (1. - fp)*(1. - t)*fq/qint)/(1. - fp*fq);
+ fp = Math.exp (p_*Math.log (t));
+ fq = Math.exp (q_*Math.log (1. - t)); /* f(t) = fa * fb */
+ if (q_ <= 1.0) {
+ ml = (1. - fq)/t; /* ml = -m1 */
+ mu = q_ * t; /* mu = -m2 * t */
+ }
+ else {
+ ml = q_;
+ mu = 1. - fq;
+ }
+ p1 = t/pint; /* 0 < X < t */
+ p2 = fq*(1. - t)/qint + p1; /* t < X < 1 */
+ /* -X- end of setup code -X- */
+ break;
+ case b1prs:
+ /* -X- setup code -X- */
+ p_ = p - 1.0;
+ q_ = q - 1.0;
+ s = p_ + q_;
+ m = p_/s;
+
+ if (p_ > 1.0 || q_ > 1.0)
+ D = Math.sqrt (m * (1. - m)/(s - 1.0));
+
+ if (p_ <= 1.0) {
+ x2 = (Dl = m * 0.5);
+ x1 = z2 = f1 = ll = 0.0;
+ }
+ else {
+ x2 = m - D;
+ x1 = x2 - D;
+ z2 = x2*(1.0 - (1.0 - x2)/(s*D));
+ if (x1 <= 0.0 || (s - 6.0)*x2 - p_ + 3.0 > 0.0) {
+ x1 = z2; x2 = (x1 + m)*0.5;
+ Dl = m - x2;
+ }
+ else {
+ Dl = D;
+ }
+ f1 = Math.exp ( p_*Math.log (x1/m)
+ + q_*Math.log ((1.0 - x1)/(1.0 - m)) );
+ ll = x1*(1.0 - x1)/(s*(m - x1)); /* z1 = x1 - ll */
+ }
+ f2 = Math.exp ( p_*Math.log (x2/m)
+ + q_*Math.log ((1.0 - x2)/(1.0 - m)) );
+
+ if (q_ <= 1.) {
+ D = (1.0 - m)*0.5;
+ x4 = 1.0 - D;
+ x5 = z4 = 1.0;
+ f5 = lr = 0.0;
+ }
+ else {
+ x4 = m + D;
+ x5 = x4 + D;
+ z4 = x4*(1.0 + (1.0 - x4)/(s*D));
+ if (x5 >= 1.0 || (s - 6.0)*x4 - p_ + 3.0 < 0.0) {
+ x5 = z4;
+ x4 = (m + x5)*0.5;
+ D = x4 - m;
+ }
+ f5 = Math.exp ( p_*Math.log (x5/m)
+ + q_*Math.log ((1.0 - x5)/(1. - m)) );
+ lr = x5*(1.0 - x5)/(s*(x5 - m)); /* z5 = x5 + lr */
+ }
+ f4 = Math.exp ( p_*Math.log (x4/m)
+ + q_*Math.log ((1.0 - x4)/(1.0 - m)) );
+
+ p1 = f2*(Dl + Dl); /* x1 < X < m */
+ p2 = f4*(D + D) + p1; /* m < X < x5 */
+ p3 = f1*ll + p2; /* X < x1 */
+ p4 = f5*lr + p3; /* x5 < X */
+ /* -X- end of setup code -X- */
+ break;
+ default: throw new IllegalStateException();
+ }
+ }
+
+ private static boolean equalsDouble (double a, double b) {
+ if (a == b)
+ return true;
+ double absa = Math.abs (a);
+ double absb = Math.abs (b);
+ return Math.abs (a - b) <= Math.min (absa, absb)*Num.DBL_EPSILON;
+ }
+
+\end{hide}\end{code}
+%% private double stratifiedRejection() {
+%% // The code was taken from UNURAN
+%% double X = 0.0;
+%% double U, V, Z, W, Y;
+%% RandomStream stream = this.stream;
+%% switch (gen) {
+%% case b00:
+%% /* -X- generator code -X- */
+%% while (true) {
+%% U = stream.nextDouble()*p2;
+%% stream = auxStream;
+%% if (U <= p1) { /* X < t */
+%% Z = Math.exp (Math.log (U/p1)/p);
+%% X = t*Z;
+%% /* squeeze accept: L(x) = 1 + (1 - q)x */
+%% V = stream.nextDouble()*fq;
+%% if (V <= 1. - q_*X)
+%% break;
+%% /* squeeze reject: U(x) = 1 + ((1 - t)^(q-1) - 1)/t * x */
+%% if (V <= 1. + (fq - 1.) * Z) {
+%% /* quotient accept: quot(x) = (1 - x)^(q-1) / fq */
+%% if (Math.log (V) <= q_*Math.log (1. - X))
+%% break;
+%% }
+%% }
+%% else { /* X > t */
+%% Z = Math.exp (Math.log ((U-p1)/(p2-p1) )/q);
+%% X = 1. - (1. - t)*Z;
+%% /* squeeze accept: L(x) = 1 + (1 - p)(1 - x) */
+%% V = stream.nextDouble()*fp;
+%% if (V <= 1.0 - p_*(1. - X))
+%% break;
+%% /* squeeze reject: U(x) = 1 + (t^(p-1) - 1)/(1 - t) * (1 - x) */
+%% if (V <= 1.0 + (fp - 1.) * Z) {
+%% /* quotient accept: quot(x) = x^(p-1) / fp */
+%% if (Math.log (V) <= p_*Math.log (X))
+%% break;
+%% }
+%% }
+%% }
+%% /* -X- end of generator code -X- */
+%% break;
+%% case b01:
+%% case b01inv:
+%% /* -X- generator code -X- */
+%% while (true) {
+%% U = stream.nextDouble()*p2;
+%% stream = auxStream;
+%% if (U <= p1) { /* X < t */
+%% Z = Math.exp (Math.log (U/p1)/pint);
+%% X = t*Z;
+%% /* squeeze accept: L(x) = 1 + m1*x, ml = -m1 */
+%% V = stream.nextDouble();
+%% if (V <= 1. - ml * X)
+%% break;
+%% /* squeeze reject: U(x) = 1 + m2*x, mu = -m2 * t */
+%% if (V <= 1. - mu * Z)
+%% /* quotient accept: quot(x) = (1 - x)^(q-1) */
+%% if (Math.log (V) <= q_*Math.log (1. - X))
+%% break;
+%% }
+%% else { /* X > t */
+%% Z = Math.exp (Math.log ((U-p1)/(p2-p1)) / qint);
+%% X = 1. - (1. - t)*Z;
+%% /* squeeze accept: L(x) = 1 + (1 - p)(1 - x) */
+%% V = stream.nextDouble()*fp;
+%% if (V <= 1. - p_ * (1. - X))
+%% break;
+%% /* squeeze reject: U(x) = 1 + (t^(p-1) - 1)/(1 - t) * (1 - x) */
+%% if (V <= 1. + (fp - 1.) * Z)
+%% /* quotient accept: quot(x) = (x)^(p-1) / fp */
+%% if (Math.log (V) <= p_*Math.log (X))
+%% break;
+%% }
+%% }
+%% if (p>q)
+%% /* p and q has been swapped */
+%% X = 1. - X;
+%% /* -X- end of generator code -X- */
+%% break;
+%% case b1prs:
+%% while (true) {
+%% U = stream.nextDouble()*p4;
+%% stream = auxStream;
+%% if (U <= p1) {
+%% /* immediate accept: x2 < X < m, - f(x2) < W < 0 */
+%% W = U/Dl - f2;
+%% if (W <= 0.0) {
+%% X = m - U/f2;
+%% break;
+%% }
+%% /* immediate accept: x1 < X < x2, 0 < W < f(x1) */
+%% if (W <= f1) {
+%% X = x2 - W/f1*Dl;
+%% break;
+%% }
+%% /* candidates for acceptance-rejection-test */
+%% U = stream.nextDouble();
+%% V = Dl*U;
+%% X = x2 - V;
+%% Y = x2 + V;
+%% /* squeeze accept: L(x) = f(x2) (x - z2) / (x2 - z2) */
+%% if (W*(x2 - z2) <= f2*(X - z2))
+%% break;
+%% V = f2 + f2 - W;
+%% if (V < 1.0) {
+%% /* squeeze accept: L(x) = f(x2) + (1 - f(x2))(x - x2)/(m - x2) */
+%% if (V <= f2 + (1. - f2)*U) {
+%% X = Y;
+%% break;
+%% }
+%% /* quotient accept: x2 < Y < m, W >= 2f2 - f(Y) */
+%% if (V <= Math.exp ( p_*Math.log (Y/m)
+%% + q_*Math.log ((10. - Y)/(1.0 - m)) ) ) {
+%% X = Y;
+%% break;
+%% }
+%% }
+%% }
+%% else if (U <= p2) {
+%% U -= p1;
+%% /* immediate accept: m < X < x4, - f(x4) < W < 0 */
+%% W = U/D - f4;
+%% if (W <= 0.) {
+%% X = m + U/f4;
+%% break;
+%% }
+%% /* immediate accept: x4 < X < x5, 0 < W < f(x5) */
+%% if (W <= f5) {
+%% X = x4 + W/f5 * D;
+%% break;
+%% }
+%% /* candidates for acceptance-rejection-test */
+%% U = stream.nextDouble();
+%% V = D*U;
+%% X = x4 + V;
+%% Y = x4 - V;
+%% /* squeeze accept: L(x) = f(x4) (z4 - x) / (z4 - x4) */
+%% if (W*(z4 - x4) <= f4*(z4 - X))
+%% break;
+%% V = f4 + f4 - W;
+%% if (V < 1.0) {
+%% /* squeeze accept: L(x) = f(x4) + (1 - f(x4))(x4 - x)/(x4 - m) */
+%% if (V <= f4 + (1.0 - f4)*U) {
+%% X = Y;
+%% break;
+%% }
+%% /* quotient accept: m < Y < x4, W >= 2f4 - f(Y) */
+%% if (V <= Math.exp ( p_*Math.log (Y/m)
+%% + q_*Math.log ((1.0 - Y)/(1.0 - m)))) {
+%% X = Y;
+%% break;
+%% }
+%% }
+%% }
+%% else if (U <= p3) { /* X < x1 */
+%% U = (U - p2)/(p3 - p2);
+%% Y = Math.log (U);
+%% X = x1 + ll*Y;
+%% if (X <= 0.0) /* X > 0!! */
+%% continue;
+%% W = U*stream.nextDouble();
+%% /* squeeze accept: L(x) = f(x1) (x - z1) / (x1 - z1) */
+%% /* z1 = x1 - ll, W <= 1 + (X - x1)/ll */
+%% if (W <= 1.0 + Y)
+%% break;
+%% W *= f1;
+%% }
+%% else { /* x5 < X */
+%% U = (U - p3)/(p4 - p3);
+%% Y = Math.log (U);
+%% X = x5 - lr*Y;
+%% if (X >= 1.0) /* X < 1!! */
+%% continue;
+%% W = U*stream.nextDouble();
+%% /* squeeze accept: L(x) = f(x5) (z5 - x) / (z5 - x5) */
+%% /* z5 = x5 + lr, W <= 1 + (x5 - X)/lr */
+%% if (W <= 1.0 + Y)
+%% break;
+%% W *= f5;
+%% }
+%% /* density accept: f(x) = (x/m)^(p_) ((1 - x)/(1 - m))^(q_) */
+%% if (Math.log (W) <= p_*Math.log (X/m)
+%% + q_*Math.log ((1.0 - X)/(1.0 - m)))
+%% break;
+%% }
+%% /* -X- end of generator code -X- */
+%% break;
+%% default: throw new IllegalStateException();
+%% }
+
+%% return gen == b01inv ? a + (b-a)*(1.0 - X) : a + (b-a)*X;
+%% }
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalBestGen.java b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalBestGen.java
new file mode 100644
index 0000000..cb84d26
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalBestGen.java
@@ -0,0 +1,180 @@
+
+
+/*
+ * Class: BetaSymmetricalBestGen
+ * Description: symmetrical beta random variate generators using
+ Devroye's one-liner method.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.BetaSymmetricalDist;
+
+
+/**
+ * This class implements <EM>symmetrical beta</EM> random variate generators using
+ * Devroye's one-liner method. It is based on Best's relation
+ * between a Student-<SPAN CLASS="MATH"><I>t</I></SPAN> variate and a symmetrical beta variate:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>B</I><SUB><I>α</I>, <I>α</I></SUB>1#12#2(1 + 3#3).
+ * </DIV><P></P>
+ * If <SPAN CLASS="MATH"><I>S</I></SPAN> is a random sign and <SPAN CLASS="MATH"><I>U</I><SUB>1</SUB></SPAN>, <SPAN CLASS="MATH"><I>U</I><SUB>2</SUB></SPAN> are two independent uniform <SPAN CLASS="MATH">[0, 1]</SPAN>
+ * random variates, then the following gives a symmetrical
+ * beta variate:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq.beta.best"></A>
+ * <I>B</I><SUB><I>α</I>, <I>α</I></SUB>[tex2html_wrap_indisplay202][tex2html_wrap_indisplay203] + 4#4
+ * </DIV><P></P>
+ * valid for any shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>.
+ *
+ */
+public class BetaSymmetricalBestGen extends BetaSymmetricalGen {
+ private RandomStream stream2;
+ private RandomStream stream3;
+ private double afactor; // = 1/alpha
+ private static final double TWOPI = 2.0*Math.PI; // = 2 Pi
+
+
+
+ /**
+ * Creates a symmetrical beta random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, using stream <TT>s1</TT> to generate
+ * <SPAN CLASS="MATH"><I>U</I><SUB>1</SUB></SPAN>, stream <TT>s2</TT> to generate <SPAN CLASS="MATH"><I>U</I><SUB>2</SUB></SPAN> and stream <TT>s3</TT> to
+ * generate <SPAN CLASS="MATH"><I>S</I></SPAN>, as given in equation.
+ *
+ */
+ public BetaSymmetricalBestGen (RandomStream s1, RandomStream s2,
+ RandomStream s3, double alpha) {
+ super (s1, null);
+ stream2 = s2;
+ stream3 = s3;
+ afactor = 1.0/alpha;
+ setParams (alpha, alpha, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a symmetrical beta random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, using only one stream <TT>s1</TT>
+ * to generate <SPAN CLASS="MATH"><I>U</I><SUB>1</SUB></SPAN>, <SPAN CLASS="MATH"><I>U</I><SUB>2</SUB></SPAN>, and <SPAN CLASS="MATH"><I>S</I></SPAN> as given in equation.
+ *
+ */
+ public BetaSymmetricalBestGen (RandomStream s1, double alpha) {
+ this (s1, s1, s1, alpha);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s1</TT> to generate <SPAN CLASS="MATH"><I>U</I><SUB>1</SUB></SPAN>, stream <TT>s2</TT> to generate <SPAN CLASS="MATH"><I>U</I><SUB>2</SUB></SPAN>
+ * and stream <TT>s3</TT> to generate <SPAN CLASS="MATH"><I>S</I></SPAN> as given in equation.
+ *
+ */
+ public BetaSymmetricalBestGen (RandomStream s1, RandomStream s2,
+ RandomStream s3, BetaSymmetricalDist dist) {
+ super (s1, dist);
+ stream2 = s2;
+ stream3 = s3;
+ afactor = 1.0/dist.getAlpha();
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getAlpha(), dist.getA(), dist.getB());
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using only one stream <TT>s1</TT>.
+ *
+ */
+ public BetaSymmetricalBestGen (RandomStream s1, BetaSymmetricalDist dist) {
+ this (s1, s1, s1, dist);
+ }
+
+
+ /**
+ * Generates a random number using Devroye's one-liner method.
+ * Restriction:
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s1, RandomStream s2,
+ RandomStream s3, double alpha) {
+ double cos, temp, v, S;
+ cos = Math.cos (TWOPI * s2.nextDouble());
+ temp = 1.0/Math.pow(s1.nextDouble(), 1.0/alpha) - 1.0;
+ v = Math.sqrt(1.0 + 1.0 / (temp*cos*cos));
+ S = s3.nextDouble();
+ if (S < 0.5)
+ return 0.5 - 0.5/v;
+ else
+ return 0.5 + 0.5/v;
+ }
+
+
+ /**
+ * Generates a random number using Devroye's one-liner method with
+ * only one stream <TT>s</TT>.
+ * Restriction:
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha) {
+ return nextDouble (s, s, s, alpha);
+ }
+
+
+ public double nextDouble() {
+ // Generates a random number using Devroye's one liner method
+ double cos, temp, v, S;
+ cos = Math.cos (TWOPI * stream2.nextDouble());
+ temp = 1.0/Math.pow(stream.nextDouble(), afactor) - 1.0;
+ v = Math.sqrt(1.0 + 1.0 / (temp*cos*cos));
+ S = stream3.nextDouble();
+ if (S < 0.5)
+ return 0.5 - 0.5/v;
+ else
+ return 0.5 + 0.5/v;
+ }
+
+ /**
+ * Returns stream <TT>s2</TT> associated with this object.
+ *
+ */
+ public RandomStream getStream2() {
+ return stream2;
+ }
+
+
+ /**
+ * Returns stream <TT>s3</TT> associated with this object.
+ *
+ */
+ public RandomStream getStream3() {
+ return stream3;
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalBestGen.tex b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalBestGen.tex
new file mode 100644
index 0000000..3d65e41
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalBestGen.tex
@@ -0,0 +1,183 @@
+\defmodule {BetaSymmetricalBestGen}
+
+This class implements {\em symmetrical beta\/} random variate generators using
+Devroye's one-liner method. It is based on Best's relation \cite{rBES78a}
+between a Student-$t$ variate and a symmetrical beta variate:
+\[
+ B_{\alpha, \alpha} \stackrel{\mathcal L}{=} \frac 1 2 \left(
+ 1 + \frac{T_{2\alpha}}{\sqrt{2\alpha + T_{2\alpha}^2}}\right).
+\]
+If $S$ is a random sign and $U_1$, $U_2$ are two independent uniform $[0,1]$
+random variates, then the following gives a symmetrical
+ beta variate \cite{rDEV96a}:
+\begin{equation}
+ B_{\alpha, \alpha} \stackrel{\mathcal L}{=} \frac 1 2 + \frac{S}{2
+ \sqrt{1 + \frac{1}{\left(U_1^{-1/\alpha} - 1\right)\cos^2(2\pi U_2)}}}
+\label{eq.beta.best}
+\end{equation}
+valid for any shape parameter $\alpha > 0$.
+
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BetaSymmetricalBestGen
+ * Description: symmetrical beta random variate generators using
+ Devroye's one-liner method.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.BetaSymmetricalDist;
+\end{hide}
+
+public class BetaSymmetricalBestGen extends BetaSymmetricalGen \begin{hide} {
+ private RandomStream stream2;
+ private RandomStream stream3;
+ private double afactor; // = 1/alpha
+ private static final double TWOPI = 2.0*Math.PI; // = 2 Pi
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BetaSymmetricalBestGen (RandomStream s1, RandomStream s2,
+ RandomStream s3, double alpha) \begin{hide} {
+ super (s1, null);
+ stream2 = s2;
+ stream3 = s3;
+ afactor = 1.0/alpha;
+ setParams (alpha, alpha, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a symmetrical beta random variate generator with
+ parameter $\alpha =$ \texttt{alpha}, using stream \texttt{s1} to generate
+ $U_1$, stream \texttt{s2} to generate $U_2$ and stream \texttt{s3} to
+ generate $S$, as given in equation (\ref{eq.beta.best}).
+ \end{tabb}
+\begin{code}
+
+ public BetaSymmetricalBestGen (RandomStream s1, double alpha) \begin{hide} {
+ this (s1, s1, s1, alpha);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a symmetrical beta random variate generator with
+ parameter $\alpha =$ \texttt{alpha}, using only one stream \texttt{s1}
+ to generate $U_1$, $U_2$, and $S$ as given in equation (\ref{eq.beta.best}).
+ \end{tabb}
+\begin{code}
+
+ public BetaSymmetricalBestGen (RandomStream s1, RandomStream s2,
+ RandomStream s3, BetaSymmetricalDist dist) \begin{hide} {
+ super (s1, dist);
+ stream2 = s2;
+ stream3 = s3;
+ afactor = 1.0/dist.getAlpha();
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getAlpha(), dist.getA(), dist.getB());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s1} to generate $U_1$, stream \texttt{s2} to generate $U_2$
+ and stream \texttt{s3} to generate $S$ as given in equation (\ref{eq.beta.best}).
+ \end{tabb}
+\begin{code}
+
+ public BetaSymmetricalBestGen (RandomStream s1, BetaSymmetricalDist dist) \begin{hide} {
+ this (s1, s1, s1, dist);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using only one stream \texttt{s1}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static double nextDouble (RandomStream s1, RandomStream s2,
+ RandomStream s3, double alpha) \begin{hide} {
+ double cos, temp, v, S;
+ cos = Math.cos (TWOPI * s2.nextDouble());
+ temp = 1.0/Math.pow(s1.nextDouble(), 1.0/alpha) - 1.0;
+ v = Math.sqrt(1.0 + 1.0 / (temp*cos*cos));
+ S = s3.nextDouble();
+ if (S < 0.5)
+ return 0.5 - 0.5/v;
+ else
+ return 0.5 + 0.5/v;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a random number using Devroye's one-liner method.
+ Restriction: $\alpha > 0$.
+ \end{tabb}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha) \begin{hide} {
+ return nextDouble (s, s, s, alpha);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a random number using Devroye's one-liner method with
+ only one stream \texttt{s}.
+ Restriction: $\alpha > 0$.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ // Generates a random number using Devroye's one liner method
+ double cos, temp, v, S;
+ cos = Math.cos (TWOPI * stream2.nextDouble());
+ temp = 1.0/Math.pow(stream.nextDouble(), afactor) - 1.0;
+ v = Math.sqrt(1.0 + 1.0 / (temp*cos*cos));
+ S = stream3.nextDouble();
+ if (S < 0.5)
+ return 0.5 - 0.5/v;
+ else
+ return 0.5 + 0.5/v;
+ }\end{hide}
+
+ public RandomStream getStream2()\begin{hide} {
+ return stream2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns stream \texttt{s2} associated with this object.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream3()\begin{hide} {
+ return stream3;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns stream \texttt{s3} associated with this object.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalGen.java b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalGen.java
new file mode 100644
index 0000000..55886d6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalGen.java
@@ -0,0 +1,67 @@
+
+
+/*
+ * Class: BetaSymmetricalGen
+ * Description: random variate generators for the symmetrical beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators with the
+ * <SPAN CLASS="textit">symmetrical beta</SPAN> distribution with shape parameters
+ * <SPAN CLASS="MATH"><I>α</I> = <I>β</I></SPAN>,
+ * over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>.
+ *
+ */
+public class BetaSymmetricalGen extends BetaGen {
+
+
+
+ /**
+ * Creates a new symmetrical beta generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT>, over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public BetaSymmetricalGen (RandomStream s, double alpha) {
+ this (s, new BetaSymmetricalDist (alpha));
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public BetaSymmetricalGen (RandomStream s, BetaSymmetricalDist dist) {
+ super (s, dist);
+ }
+
+
+ public static double nextDouble (RandomStream s, double alpha) {
+ return BetaSymmetricalDist.inverseF (alpha, s.nextDouble());
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalGen.tex b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalGen.tex
new file mode 100644
index 0000000..e60a494
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalGen.tex
@@ -0,0 +1,79 @@
+\defmodule {BetaSymmetricalGen}
+
+This class implements random variate generators with the
+\emph{symmetrical beta} distribution with shape parameters $\alpha = \beta$,
+over the interval $(0,1)$.
+% It uses inversion
+% by expanding the distribution in a series around 0 and inverts using
+% Newton's method for the solution of non-linear equations.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BetaSymmetricalGen
+ * Description: random variate generators for the symmetrical beta distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class BetaSymmetricalGen extends BetaGen \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BetaSymmetricalGen (RandomStream s, double alpha) \begin{hide} {
+ this (s, new BetaSymmetricalDist (alpha));
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new symmetrical beta generator with parameters $\alpha =$
+ \texttt{alpha}, over the interval $(0,1)$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public BetaSymmetricalGen (RandomStream s, BetaSymmetricalDist dist) \begin{hide} {
+ super (s, dist);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha)\begin{hide} {
+ return BetaSymmetricalDist.inverseF (alpha, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalPolarGen.java b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalPolarGen.java
new file mode 100644
index 0000000..78865f8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalPolarGen.java
@@ -0,0 +1,172 @@
+
+
+/*
+ * Class: BetaSymmetricalPolarGen
+ * Description: symmetrical beta random variate generators using
+ Ulrich's polar method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.BetaSymmetricalDist;
+
+
+/**
+ * This class implements <EM>symmetrical beta</EM> random variate generators using
+ * Ulrich's polar method. The method generates two uniform
+ * random variables
+ * <SPAN CLASS="MATH"><I>x</I>∈[0, 1]</SPAN> and
+ * <SPAN CLASS="MATH"><I>y</I>∈[- 1, 1]</SPAN> until
+ *
+ * <SPAN CLASS="MATH"><I>x</I><SUP>2</SUP> + <I>y</I><SUP>2</SUP> <= 1</SPAN>. Then it returns
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq.beta.ulrich"></A>
+ * (1/2) + (<I>xy</I>(1 - S^2/(2α- 1))<SUP>1/2</SUP>)/<I>S</I>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>S</I> = <I>x</I><SUP>2</SUP> + <I>y</I><SUP>2</SUP></SPAN>, and <SPAN CLASS="MATH"><I>α</I></SPAN> is the shape parameter of the beta
+ * distribution. The method is valid only when
+ * <SPAN CLASS="MATH"><I>α</I> > 1/2</SPAN>.
+ *
+ */
+public class BetaSymmetricalPolarGen extends BetaSymmetricalGen {
+ private double afactor; // = 2/(2*alpha - 1)
+ private RandomStream stream2;
+
+
+
+ /**
+ * Creates a symmetrical beta random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, using stream <TT>s1</TT> to generate <SPAN CLASS="MATH"><I>x</I></SPAN>
+ * and stream <TT>s2</TT> to generate <SPAN CLASS="MATH"><I>y</I></SPAN>, as in above. Restriction:
+ * <SPAN CLASS="MATH"><I>α</I> > 1/2</SPAN>.
+ *
+ */
+ public BetaSymmetricalPolarGen (RandomStream s1, RandomStream s2,
+ double alpha) {
+ super (s1, null);
+ stream2 = s2;
+ if (alpha <= 0.5)
+ throw new IllegalArgumentException (" must have alpha > 1/2");
+ afactor = 2.0/(2.0*alpha - 1.0);
+ setParams (alpha, alpha, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a symmetrical beta random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, using stream <TT>s1</TT> to generate <SPAN CLASS="MATH"><I>x</I></SPAN>
+ * and <SPAN CLASS="MATH"><I>y</I></SPAN>, as in above. Restriction:
+ * <SPAN CLASS="MATH"><I>α</I> > 1/2</SPAN>.
+ *
+ */
+ public BetaSymmetricalPolarGen (RandomStream s1, double alpha) {
+ this (s1, s1, alpha);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>, using
+ * stream <TT>s1</TT> to generate <SPAN CLASS="MATH"><I>x</I></SPAN> and stream <TT>s2</TT> to generate <SPAN CLASS="MATH"><I>y</I></SPAN>,
+ * as in above.
+ * Restriction: <TT>dist</TT> must have
+ * <SPAN CLASS="MATH"><I>α</I> > 1/2</SPAN>.
+ *
+ */
+ public BetaSymmetricalPolarGen (RandomStream s1, RandomStream s2,
+ BetaSymmetricalDist dist) {
+ super (s1, dist);
+ stream2 = s2;
+ double alp = dist.getAlpha();
+ if (alp <= 0.5)
+ throw new IllegalArgumentException (" must have alpha > 1/2");
+ afactor = 2.0/(2.0*dist.getAlpha() - 1.0);
+ setParams (alp, alp, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using only one stream <TT>s1</TT>.
+ * Restriction: <TT>dist</TT> must have
+ * <SPAN CLASS="MATH"><I>α</I> > 1/2</SPAN>.
+ *
+ */
+ public BetaSymmetricalPolarGen (RandomStream s1,
+ BetaSymmetricalDist dist) {
+ this (s1, s1, dist);
+ }
+
+
+ /**
+ * Generates a random number using Ulrich's polar method. Stream
+ * <TT>s1</TT> generates <SPAN CLASS="MATH"><I>x</I></SPAN> and stream <TT>s2</TT> generates <SPAN CLASS="MATH"><I>y</I></SPAN>
+ * [see eq.].
+ * Restriction:
+ * <SPAN CLASS="MATH"><I>α</I> > 1/2</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s1, RandomStream s2,
+ double alpha) {
+ double u, v, S;
+ do {
+ u = s1.nextDouble();
+ v = -1.0 + 2.0*s2.nextDouble();
+ S = u*u + v*v;
+ } while (S > 1.0);
+ return 0.5 + u*v/S* Math.sqrt(1.0 - Math.pow(S, 2.0/(2.0*alpha - 1.0)));
+ }
+
+
+ /**
+ * Generates a random number by Ulrich's polar method using
+ * stream <TT>s</TT>. Restriction:
+ * <SPAN CLASS="MATH"><I>α</I> > 1/2</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha) {
+ return nextDouble (s, s, alpha);
+ }
+
+
+ public double nextDouble() {
+ // Generates a random number using Ulrich's polar method.
+ double u, v, S;
+ do {
+ u = stream.nextDouble();
+ v = -1.0 + 2.0*stream2.nextDouble();
+ S = u*u + v*v;
+ } while (S > 1.0);
+ return 0.5 + u*v/S* Math.sqrt(1.0 - Math.pow(S, afactor));
+ }
+
+ /**
+ * Returns stream <TT>s2</TT> associated with this object.
+ *
+ */
+ public RandomStream getStream2() {
+ return stream2;
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalPolarGen.tex b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalPolarGen.tex
new file mode 100644
index 0000000..03040ae
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BetaSymmetricalPolarGen.tex
@@ -0,0 +1,172 @@
+\defmodule {BetaSymmetricalPolarGen}
+
+This class implements {\em symmetrical beta\/} random variate generators using
+Ulrich's polar method \cite{rULR84a}. The method generates two uniform
+random variables $x \in [0, 1]$ and $y \in [-1, 1]$ until
+ $x^2 + y^2 \le 1$. Then it returns
+\begin{equation}
+\begin{latexonly}
+ \frac12 + \frac{xy}{S}\sqrt{1 - S^{2/(2\alpha - 1)}} \label{eq.beta.ulrich}
+\end{latexonly}
+\begin{htmlonly}
+ (1 / 2) + ({xy}\sqrt{1 - S^{2/(2\alpha - 1)}}) / S \label{eq.beta.ulrich}
+\end{htmlonly}
+\end{equation}
+where $S = x^2 + y^2$, and $\alpha$ is the shape parameter of the beta
+distribution. The method is valid only when $\alpha > 1/2$.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BetaSymmetricalPolarGen
+ * Description: symmetrical beta random variate generators using
+ Ulrich's polar method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.BetaSymmetricalDist;
+\end{hide}
+
+public class BetaSymmetricalPolarGen extends BetaSymmetricalGen \begin{hide} {
+ private double afactor; // = 2/(2*alpha - 1)
+ private RandomStream stream2;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BetaSymmetricalPolarGen (RandomStream s1, RandomStream s2,
+ double alpha) \begin{hide} {
+ super (s1, null);
+ stream2 = s2;
+ if (alpha <= 0.5)
+ throw new IllegalArgumentException (" must have alpha > 1/2");
+ afactor = 2.0/(2.0*alpha - 1.0);
+ setParams (alpha, alpha, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a symmetrical beta random variate generator with
+ parameter $\alpha =$ \texttt{alpha}, using stream \texttt{s1} to generate $x$
+ and stream \texttt{s2} to generate $y$, as in
+(\ref{eq.beta.ulrich}) above. Restriction: $\alpha > 1/2$.
+\end{tabb}
+\begin{code}
+
+ public BetaSymmetricalPolarGen (RandomStream s1, double alpha) \begin{hide} {
+ this (s1, s1, alpha);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a symmetrical beta random variate generator with
+ parameter $\alpha =$ \texttt{alpha}, using stream \texttt{s1} to generate $x$
+ and $y$, as in (\ref{eq.beta.ulrich}) above. Restriction: $\alpha > 1/2$.
+\end{tabb}
+\begin{code}
+
+ public BetaSymmetricalPolarGen (RandomStream s1, RandomStream s2,
+ BetaSymmetricalDist dist) \begin{hide} {
+ super (s1, dist);
+ stream2 = s2;
+ double alp = dist.getAlpha();
+ if (alp <= 0.5)
+ throw new IllegalArgumentException (" must have alpha > 1/2");
+ afactor = 2.0/(2.0*dist.getAlpha() - 1.0);
+ setParams (alp, alp, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist}, using
+ stream \texttt{s1} to generate $x$ and stream \texttt{s2} to generate $y$,
+ as in (\ref{eq.beta.ulrich}) above.
+ Restriction: \texttt{dist} must have $\alpha > 1/2$.
+ \end{tabb}
+\begin{code}
+
+ public BetaSymmetricalPolarGen (RandomStream s1,
+ BetaSymmetricalDist dist) \begin{hide} {
+ this (s1, s1, dist);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using only one stream \texttt{s1}.
+ Restriction: \texttt{dist} must have $\alpha > 1/2$.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static double nextDouble (RandomStream s1, RandomStream s2,
+ double alpha) \begin{hide} {
+ double u, v, S;
+ do {
+ u = s1.nextDouble();
+ v = -1.0 + 2.0*s2.nextDouble();
+ S = u*u + v*v;
+ } while (S > 1.0);
+ return 0.5 + u*v/S* Math.sqrt(1.0 - Math.pow(S, 2.0/(2.0*alpha - 1.0)));
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a random number using Ulrich's polar method. Stream
+ \texttt{s1} generates $x$ and stream \texttt{s2} generates $y$
+ [see eq. (\ref{eq.beta.ulrich})].
+ Restriction: $\alpha > 1/2$.
+ \end{tabb}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha) \begin{hide} {
+ return nextDouble (s, s, alpha);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a random number by Ulrich's polar method using
+ stream \texttt{s}. Restriction: $\alpha > 1/2$.
+ \end{tabb}
+\begin{code} \begin{hide}
+
+ public double nextDouble() {
+ // Generates a random number using Ulrich's polar method.
+ double u, v, S;
+ do {
+ u = stream.nextDouble();
+ v = -1.0 + 2.0*stream2.nextDouble();
+ S = u*u + v*v;
+ } while (S > 1.0);
+ return 0.5 + u*v/S* Math.sqrt(1.0 - Math.pow(S, afactor));
+ }\end{hide}
+
+ public RandomStream getStream2()\begin{hide} {
+ return stream2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns stream \texttt{s2} associated with this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/BinomialConvolutionGen.java b/source/umontreal/iro/lecuyer/randvar/BinomialConvolutionGen.java
new file mode 100644
index 0000000..5664737
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BinomialConvolutionGen.java
@@ -0,0 +1,100 @@
+
+
+/*
+ * Class: BinomialConvolutionGen
+ * Description: binomial random variate generators using the convolution method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;
+
+/**
+ * Implements binomial random variate generators using the
+ * convolution method.
+ * This method generates <SPAN CLASS="MATH"><I>n</I></SPAN> Bernouilli random variates with
+ * parameter <SPAN CLASS="MATH"><I>p</I></SPAN> and adds them up.
+ * Its advantages are that it requires
+ * little computer memory and no setup time.
+ * Its disadvantage is that it is very slow for large <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * It makes sense only when <SPAN CLASS="MATH"><I>n</I></SPAN> is small.
+ *
+ */
+public class BinomialConvolutionGen extends BinomialGen {
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">binomial</SPAN> random variate generator with
+ * parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public BinomialConvolutionGen (RandomStream s, int n, double p) {
+ super (s, null);
+ setParams (n, p);
+ }
+
+
+ /**
+ * Creates a random variate generator for the <SPAN CLASS="textit">binomial</SPAN>
+ * distribution <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public BinomialConvolutionGen (RandomStream s, BinomialDist dist) {
+ super (s, dist);
+ }
+
+
+ public int nextInt() {
+ int x = 0;
+ for (int i = 0; i < n; i++) {
+ double unif = stream.nextDouble();
+ if (unif <= p)
+ x++;
+ }
+ return x;
+ }
+
+ public static int nextInt (RandomStream s, int n, double p) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p < 0 || p > 1)
+ throw new IllegalArgumentException ("p must be in [0,1]");
+ return convolution (s, n, p);
+ }
+ /**
+ * Generates a new integer from the binomial distribution with parameters
+ * <SPAN CLASS="MATH"><I>n</I> =</SPAN> <TT>n</TT> and <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>p</TT>, using the given stream <TT>s</TT>.
+ *
+ */
+
+
+ private static int convolution (RandomStream stream, int n, double p) {
+ int x = 0;
+ for (int i = 0; i < n; i++) {
+ double unif = stream.nextDouble();
+ if (unif <= p)
+ x++;
+ }
+ return x;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/BinomialConvolutionGen.tex b/source/umontreal/iro/lecuyer/randvar/BinomialConvolutionGen.tex
new file mode 100644
index 0000000..25cb6ac
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BinomialConvolutionGen.tex
@@ -0,0 +1,110 @@
+\defmodule {BinomialConvolutionGen}
+
+Implements binomial random variate generators using the
+convolution method.
+This method generates $n$ Bernouilli random variates with
+parameter $p$ and adds them up.
+Its advantages are that it requires
+little computer memory and no setup time.
+Its disadvantage is that it is very slow for large $n$.
+It makes sense only when $n$ is small.
+
+% A local copy of the parameters $n$ and $p$ is maintained in this class.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BinomialConvolutionGen
+ * Description: binomial random variate generators using the convolution method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;\end{hide}
+
+public class BinomialConvolutionGen extends BinomialGen \begin{hide} {
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BinomialConvolutionGen (RandomStream s, int n, double p)\begin{hide} {
+ super (s, null);
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a \emph{binomial} random variate generator with
+ parameters $n$ and $p$, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public BinomialConvolutionGen (RandomStream s, BinomialDist dist) \begin{hide} {
+ super (s, dist);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a random variate generator for the \emph{binomial}
+ distribution \texttt{dist} and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public int nextInt() {
+ int x = 0;
+ for (int i = 0; i < n; i++) {
+ double unif = stream.nextDouble();
+ if (unif <= p)
+ x++;
+ }
+ return x;
+ }
+
+ public static int nextInt (RandomStream s, int n, double p) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (p < 0 || p > 1)
+ throw new IllegalArgumentException ("p must be in [0,1]");
+ return convolution (s, n, p);
+ }
+\end{code}
+\begin{tabb}
+ Generates a new integer from the binomial distribution with parameters
+ $n = $~\texttt{n} and $p = $~\texttt{p}, using the given stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ private static int convolution (RandomStream stream, int n, double p) {
+ int x = 0;
+ for (int i = 0; i < n; i++) {
+ double unif = stream.nextDouble();
+ if (unif <= p)
+ x++;
+ }
+ return x;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/BinomialGen.java b/source/umontreal/iro/lecuyer/randvar/BinomialGen.java
new file mode 100644
index 0000000..9b686ea
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BinomialGen.java
@@ -0,0 +1,124 @@
+
+
+/*
+ * Class: BinomialGen
+ * Description: random variate generators for the binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;
+
+/**
+ * This class implements random variate generators for the
+ * <EM>binomial</EM> distribution. It has parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> with
+ * mass function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = nCr(<I>n</I>, <I>x</I>)<I>p</I><SUP>x</SUP>(1 - <I>p</I>)<SUP>n-x</SUP> = <I>n</I>!/(<I>x</I>!(<I>n</I> - <I>x</I>)!) <I>p</I><SUP>x</SUP>(1 - <I>p</I>)<SUP>n-x</SUP> for <I>x</I> = 0, 1, 2,..., <I>n</I>
+ * </DIV><P></P>
+ * where nCr<SPAN CLASS="MATH">(<I>n</I>, <I>x</I>)</SPAN> is the number of combinations of <SPAN CLASS="MATH"><I>x</I></SPAN> objects
+ * among <SPAN CLASS="MATH"><I>n</I></SPAN>,
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> is a positive integer, and
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextInt</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class BinomialGen extends RandomVariateGenInt {
+ protected int n = -1;
+ protected double p = -1.0;
+
+
+
+ /**
+ * Creates a binomial random variate generator with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public BinomialGen (RandomStream s, int n, double p) {
+ super (s, new BinomialDist (n, p));
+ setParams (n, p);
+ }
+
+
+ /**
+ * Creates a random variate generator for the <EM>binomial</EM>
+ * distribution <TT>dist</TT> and the random stream <TT>s</TT>.
+ *
+ */
+ public BinomialGen (RandomStream s, BinomialDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN(), dist.getP());
+ }
+
+
+ /**
+ * Generates a new integer from the <EM>binomial</EM> distribution with
+ * parameters
+ * <SPAN CLASS="MATH"><I>n</I> =</SPAN> <TT>n</TT> and <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>p</TT>, using the given stream <TT>s</TT>.
+ *
+ */
+ public static int nextInt (RandomStream s, int n, double p) {
+ return BinomialDist.inverseF (n, p, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (int n, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range [0, 1]");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.p = p;
+ this.n = n;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/BinomialGen.tex b/source/umontreal/iro/lecuyer/randvar/BinomialGen.tex
new file mode 100644
index 0000000..77c382d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/BinomialGen.tex
@@ -0,0 +1,139 @@
+\defmodule {BinomialGen}
+
+This class implements random variate generators for the
+{\em binomial\/} distribution. It has parameters $n$ and $p$ with
+ mass function
+\begin{htmlonly}
+\eq
+ p(x) = \mbox{nCr}(n, x) p^x (1-p)^{n-x} =
+ n!/(x!(n-x)!)\; p^x (1-p)^{n-x} \qquad \mbox {for }
+ x=0,1,2,\dots, n
+\endeq
+where nCr$(n,x)$ is the number of combinations of $x$ objects
+among $n$,
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ p(x) = {n \choose x} p^x (1-p)^{n-x} =
+ \frac {n!}{x!(n-x)!}\; p^x (1-p)^{n-x} \qquad \mbox {for }
+ x=0,1,2,\dots, n \label{eq:fmass-binomial}
+\endeq
+where
+\end{latexonly}
+$n$ is a positive integer, and $0\le p\le 1$.
+
+% No local copy of the parameters $n$ and $p$ is maintained in this class.
+The (non-static) \texttt{nextInt} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BinomialGen
+ * Description: random variate generators for the binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;\end{hide}
+
+public class BinomialGen extends RandomVariateGenInt \begin{hide} {
+ protected int n = -1;
+ protected double p = -1.0;
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BinomialGen (RandomStream s, int n, double p)\begin{hide} {
+ super (s, new BinomialDist (n, p));
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates a binomial random variate generator with parameters $n$ and $p$,
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public BinomialGen (RandomStream s, BinomialDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN(), dist.getP());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a random variate generator for the {\em binomial\/}
+ distribution \texttt{dist} and the random stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static int nextInt (RandomStream s, int n, double p)\begin{hide} {
+ return BinomialDist.inverseF (n, p, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new integer from the {\em binomial\/} distribution with
+ parameters
+ $n = $~\texttt{n} and $p = $~\texttt{p}, using the given stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int n, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range [0, 1]");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.p = p;
+ this.n = n;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $n$ and $p$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/CauchyGen.java b/source/umontreal/iro/lecuyer/randvar/CauchyGen.java
new file mode 100644
index 0000000..f92cd6c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/CauchyGen.java
@@ -0,0 +1,122 @@
+
+
+/*
+ * Class: CauchyGen
+ * Description: random variate generators for the Cauchy distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the <EM>Cauchy</EM>
+ * distribution. The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>β</I>/(<I>π</I>[(<I>x</I> - <I>α</I>)<SUP>2</SUP> + <I>β</I><SUP>2</SUP>]) for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls
+ * <TT>inverseF</TT> on the distribution.
+ *
+ */
+public class CauchyGen extends RandomVariateGen {
+ protected double alpha;
+ protected double beta;
+
+
+
+ /**
+ * Creates a Cauchy random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public CauchyGen (RandomStream s, double alpha, double beta) {
+ super (s, new CauchyDist(alpha, beta));
+ setParams(alpha, beta);
+ }
+
+
+ /**
+ * Creates a Cauchy random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public CauchyGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Create a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public CauchyGen (RandomStream s, CauchyDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }
+
+
+ /**
+ * Generates a new variate from the <EM>Cauchy</EM> distribution with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta) {
+ return CauchyDist.inverseF (alpha, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ protected void setParams (double alpha, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/CauchyGen.tex b/source/umontreal/iro/lecuyer/randvar/CauchyGen.tex
new file mode 100644
index 0000000..df98885
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/CauchyGen.tex
@@ -0,0 +1,134 @@
+\defmodule {CauchyGen}
+
+This class implements random variate generators for the {\em Cauchy\/}
+distribution. The density is
+\begin{htmlonly}%
+\eq
+ f(x) = \beta/(\pi[(x - \alpha)^2 + \beta^2])
+\mbox{ for }-\infty < x < \infty,
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+(see, e.g., \cite {tJOH95a} p.\ 299):
+\eq
+ f (x) = \frac{\beta}{\pi[(x-\alpha)^2 + \beta^2]},
+ \qquad \mbox{for } -\infty < x < \infty, \eqlabel{eq:fcauchy}
+\endeq
+\end{latexonly}
+where $\beta > 0$.
+
+The (non-static) \texttt{nextDouble} method simply calls
+\texttt{inverseF} on the distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: CauchyGen
+ * Description: random variate generators for the Cauchy distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class CauchyGen extends RandomVariateGen \begin{hide} {
+ protected double alpha;
+ protected double beta;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public CauchyGen (RandomStream s, double alpha, double beta) \begin{hide} {
+ super (s, new CauchyDist(alpha, beta));
+ setParams(alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Cauchy random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\beta$ = \texttt{beta},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public CauchyGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Cauchy random variate generator with parameters
+ $\alpha =0 $ and $\beta = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public CauchyGen (RandomStream s, CauchyDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }\end{hide}
+\end{code}
+\begin{tabb} Create a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta)\begin{hide} {
+ return CauchyDist.inverseF (alpha, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the {\em Cauchy\/} distribution with parameters
+ $\alpha = $~\texttt{alpha} and $\beta = $~\texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\beta$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void setParams (double alpha, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiGen.java b/source/umontreal/iro/lecuyer/randvar/ChiGen.java
new file mode 100644
index 0000000..b31f873
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiGen.java
@@ -0,0 +1,106 @@
+
+
+/*
+ * Class: ChiGen
+ * Description: random variate generators for the chi distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>chi</EM> distribution. It has <SPAN CLASS="MATH"><I>ν</I> > 0</SPAN> degrees of freedom and
+ * its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>e</I><SUP>-x<SUP>2</SUP>/2</SUP><I>x</I><SUP><I>ν</I>-1</SUP>/(2<SUP>(<I>ν</I>/2)-1</SUP><I>Γ</I>(<I>ν</I>/2)) for <I>x</I> > 0
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined
+ * in {@link GammaGen}.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution (slow).
+ *
+ */
+public class ChiGen extends RandomVariateGen {
+ protected int nu = -1;
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">chi</SPAN> random variate generator with
+ * <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public ChiGen (RandomStream s, int nu) {
+ super (s, new ChiDist(nu));
+ setParams (nu);
+ }
+
+
+ /**
+ * Create a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public ChiGen (RandomStream s, ChiDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getNu ());
+ }
+
+
+ /**
+ * Generates a random variate from the chi distribution with <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT>
+ * degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ return ChiDist.inverseF (nu, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>ν</I></SPAN> for this object.
+ *
+ *
+ */
+ public int getNu() {
+ return nu;
+ }
+
+
+
+ protected void setParams (int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ this.nu = nu;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiGen.tex b/source/umontreal/iro/lecuyer/randvar/ChiGen.tex
new file mode 100644
index 0000000..5c000d2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiGen.tex
@@ -0,0 +1,121 @@
+\defmodule {ChiGen}
+
+This class implements random variate generators for the
+{\em chi\/} distribution. It has $\nu>0$ degrees of freedom and
+its density function is
+\begin{htmlonly}%
+\eq
+ f (x) = e^{-x^2 /2} x^{\nu-1} / (2^{(\nu /2) - 1}\Gamma (\nu /2))
+ \qquad \mbox {for } x > 0
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+(see \cite{tJOH95a}, page 417)
+\eq f (x) = \frac {e^{-x^2 /2} x^{\nu-1}}{2^{(\nu /2) - 1}\Gamma (\nu /2)}
+ \qquad \mbox {for } x > 0,
+ \eqlabel{eq:Fchi}
+\endeq
+\end{latexonly}
+where $\Gamma (x)$ is the gamma function defined
+in\latex{ (\ref{eq:Gamma})}\html{ \class{GammaGen}}.
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution (slow).
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiGen
+ * Description: random variate generators for the chi distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ChiGen extends RandomVariateGen \begin{hide} {
+ protected int nu = -1;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public ChiGen (RandomStream s, int nu) \begin{hide} {
+ super (s, new ChiDist(nu));
+ setParams (nu);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{chi} random variate generator with
+ $\nu =$ \texttt{nu} degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ChiGen (RandomStream s, ChiDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getNu ());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Create a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, int nu)\begin{hide} {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ return ChiDist.inverseF (nu, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a random variate from the chi distribution with $\nu = $~\texttt{nu}
+ degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public int getNu()\begin{hide} {
+ return nu;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the value of $\nu$ for this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ this.nu = nu;
+ }
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiRatioOfUniformsGen.java b/source/umontreal/iro/lecuyer/randvar/ChiRatioOfUniformsGen.java
new file mode 100644
index 0000000..4de4871
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiRatioOfUniformsGen.java
@@ -0,0 +1,137 @@
+
+
+/*
+ * Class: ChiRatioOfUniformsGen
+ * Description: Chi random variate generators using the ratio of uniforms
+ method with shift
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>Chi</EM> random variate generators using
+ * the ratio of uniforms method with shift.
+ *
+ */
+public class ChiRatioOfUniformsGen extends ChiGen {
+
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">chi</SPAN> random variate generator with
+ * <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public ChiRatioOfUniformsGen (RandomStream s, int nu) {
+ super (s, null);
+ setParams (nu);
+ }
+
+
+ /**
+ * Create a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public ChiRatioOfUniformsGen (RandomStream s, ChiDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getNu ());
+ }
+
+
+ public double nextDouble() {
+ return ratioOfUniforms (stream, nu);
+ }
+
+ public static double nextDouble (RandomStream s, int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ return ratioOfUniforms (s, nu);
+ }
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ //================================================================
+ // Method ratio of uniforms with shift.
+ // code taken and adapted from unuran
+ // file : /distributions/c_chi_gen.c
+ //===============================================================
+
+ private static double ratioOfUniforms (RandomStream stream, int nu) {
+ double u,v,z,zz,r;
+ if (nu == 1) {
+ while (true) {
+ u = stream.nextDouble();
+ v = stream.nextDouble() * 0.857763884960707;
+ z = v / u;
+ if (z < 0) continue;
+ zz = z * z;
+ r = 2.5 - zz;
+ if (z < 0.)
+ r = r + zz * z / (3. * z);
+ if (u < r * 0.3894003915)
+ break;
+ if (zz > (1.036961043 / u + 1.4))
+ continue;
+ if (2 * Math.log(u) < (- zz * 0.5 ))
+ break;
+ }
+ }
+
+ else { // nu > 1
+ final double b = Math.sqrt(nu - 1.);
+ final double vm1 = - 0.6065306597 * (1. - 0.25 / (b * b + 1.));
+ final double vm = (-b > vm1) ? -b : vm1;
+ final double vp = 0.6065306597 * (0.7071067812 + b) / (0.5 + b);
+ final double vd = vp - vm;
+ while (true) {
+ u = stream.nextDouble();
+ v = stream.nextDouble() * vd + vm;
+ z = v / u;
+ if (z < -b)
+ continue;
+ zz = z * z;
+ r = 2.5 - zz;
+ if (z < 0.0)
+ r = r + zz * z / (3.0 * (z + b));
+ if (u < r * 0.3894003915) {
+ z += b;
+ break;
+ }
+ if (zz > (1.036961043 / u + 1.4))
+ continue;
+ if (2. * Math.log(u) <
+ (Math.log(1.0 + z / b) * b * b - zz * 0.5 - z * b)) {
+ z += b;
+ break;
+ }
+ }
+ }
+ return z;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiRatioOfUniformsGen.tex b/source/umontreal/iro/lecuyer/randvar/ChiRatioOfUniformsGen.tex
new file mode 100644
index 0000000..63d174f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiRatioOfUniformsGen.tex
@@ -0,0 +1,149 @@
+\defmodule {ChiRatioOfUniformsGen}
+
+This class implements {\em Chi\/} random variate generators using
+ the ratio of uniforms method with shift.
+
+% A local copy of the parameter $\nu$ is maintained in this class.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiRatioOfUniformsGen
+ * Description: Chi random variate generators using the ratio of uniforms
+ method with shift
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ChiRatioOfUniformsGen extends ChiGen \begin{hide} {
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public ChiRatioOfUniformsGen (RandomStream s, int nu) \begin{hide} {
+ super (s, null);
+ setParams (nu);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a \emph{chi} random variate generator with
+ $\nu =$ \texttt{nu} degrees of freedom, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public ChiRatioOfUniformsGen (RandomStream s, ChiDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getNu ());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Create a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return ratioOfUniforms (stream, nu);
+ }
+
+ public static double nextDouble (RandomStream s, int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("nu <= 0");
+ return ratioOfUniforms (s, nu);
+ }
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ //================================================================
+ // Method ratio of uniforms with shift.
+ // code taken and adapted from unuran
+ // file : /distributions/c_chi_gen.c
+ //===============================================================
+
+ private static double ratioOfUniforms (RandomStream stream, int nu) {
+ double u,v,z,zz,r;
+ if (nu == 1) {
+ while (true) {
+ u = stream.nextDouble();
+ v = stream.nextDouble() * 0.857763884960707;
+ z = v / u;
+ if (z < 0) continue;
+ zz = z * z;
+ r = 2.5 - zz;
+ if (z < 0.)
+ r = r + zz * z / (3. * z);
+ if (u < r * 0.3894003915)
+ break;
+ if (zz > (1.036961043 / u + 1.4))
+ continue;
+ if (2 * Math.log(u) < (- zz * 0.5 ))
+ break;
+ }
+ }
+
+ else { // nu > 1
+ final double b = Math.sqrt(nu - 1.);
+ final double vm1 = - 0.6065306597 * (1. - 0.25 / (b * b + 1.));
+ final double vm = (-b > vm1) ? -b : vm1;
+ final double vp = 0.6065306597 * (0.7071067812 + b) / (0.5 + b);
+ final double vd = vp - vm;
+ while (true) {
+ u = stream.nextDouble();
+ v = stream.nextDouble() * vd + vm;
+ z = v / u;
+ if (z < -b)
+ continue;
+ zz = z * z;
+ r = 2.5 - zz;
+ if (z < 0.0)
+ r = r + zz * z / (3.0 * (z + b));
+ if (u < r * 0.3894003915) {
+ z += b;
+ break;
+ }
+ if (zz > (1.036961043 / u + 1.4))
+ continue;
+ if (2. * Math.log(u) <
+ (Math.log(1.0 + z / b) * b * b - zz * 0.5 - z * b)) {
+ z += b;
+ break;
+ }
+ }
+ }
+ return z;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareGen.java b/source/umontreal/iro/lecuyer/randvar/ChiSquareGen.java
new file mode 100644
index 0000000..1dc44cc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareGen.java
@@ -0,0 +1,106 @@
+
+
+/*
+ * Class: ChiSquareGen
+ * Description: random variate generators with the chi square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators with the
+ * <EM>chi square</EM> distribution with <SPAN CLASS="MATH"><I>n</I> > 0</SPAN> degrees of freedom.
+ * Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>x</I><SUP>n/2-1</SUP><I>e</I><SUP>-x/2</SUP>/(2<SUP>n/2</SUP><I>Γ</I>(<I>n</I>/2)) for <I>x</I> > 0, 0 elsewhere
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined
+ * in {@link GammaGen}.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class ChiSquareGen extends RandomVariateGen {
+ protected int n = -1;
+
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">chi square</SPAN> random variate generator with
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public ChiSquareGen (RandomStream s, int n) {
+ super (s, new ChiSquareDist(n));
+ setParams (n);
+ }
+
+
+ /**
+ * Create a new generator for the distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public ChiSquareGen (RandomStream s, ChiSquareDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN ());
+ }
+
+
+ /**
+ * Generates a new variate from the chi square distribution
+ * with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, int n) {
+ return ChiSquareDist.inverseF (n, s.nextDouble());
+ }
+
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>n</I></SPAN> for this object.
+ *
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+
+ protected void setParams (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareGen.tex b/source/umontreal/iro/lecuyer/randvar/ChiSquareGen.tex
new file mode 100644
index 0000000..a14b577
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareGen.tex
@@ -0,0 +1,119 @@
+\defmodule {ChiSquareGen}
+
+This class implements random variate generators with the
+{\em chi square\/} distribution with $n>0$ degrees of freedom.
+Its density function is
+\begin{htmlonly}
+\eq
+ f(x) = x^{n/2-1}e^{-x/2}/(2^{n/2}\Gamma(n/2))\mbox{ for } x > 0, 0\mbox { elsewhere}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) =
+ \frac{e^{-x/2}x^{n/2-1}}{2^{n/2}\Gamma(n/2)}
+ \qquad\mbox{ for } x > 0, \eqlabel{eq:Fchi2}
+\endeq
+\end{latexonly}
+where $\Gamma (x)$ is the gamma function defined
+in\latex{ (\ref{eq:Gamma})}\html{ \class{GammaGen}}.
+
+% No local copy of the parameter $n$ is maintained in this class.
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiSquareGen
+ * Description: random variate generators with the chi square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ChiSquareGen extends RandomVariateGen \begin{hide} {
+ protected int n = -1;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ChiSquareGen (RandomStream s, int n) \begin{hide} {
+ super (s, new ChiSquareDist(n));
+ setParams (n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{chi square} random variate generator with
+ $n$ degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ChiSquareGen (RandomStream s, ChiSquareDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN ());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Create a new generator for the distribution \texttt{dist}
+ and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, int n) \begin{hide} {
+ return ChiSquareDist.inverseF (n, s.nextDouble());
+ }
+\end{hide}
+\end{code}
+ \begin{tabb} Generates a new variate from the chi square distribution
+ with $n$ degrees of freedom, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the value of $n$ for this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = n;
+ }
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGamGen.java b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGamGen.java
new file mode 100644
index 0000000..67d75a2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGamGen.java
@@ -0,0 +1,103 @@
+
+
+/*
+ * Class: ChiSquareNoncentralGamGen
+ * Description: noncentral chi-square random variate generators using the
+ additive property of the noncentral chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>noncentral chi square</EM> random variate generators
+ * using the additive property of the noncentral chi square distribution. It uses the following algorithm: generate a real
+ *
+ * <SPAN CLASS="MATH"><I>X</I>∼<I>N</I>((λ)<SUP>1/2</SUP>, 1)</SPAN> from a normal distribution with variance 1,
+ * generate a real
+ * <SPAN CLASS="MATH"><I>Y</I>∼<I>Γ</I>((<I>ν</I> - 1)/2, 1/2)</SPAN> from a gamma distribution,
+ * then return <SPAN CLASS="MATH"><I>X</I><SUP>2</SUP> + <I>Y</I></SPAN>. Here <SPAN CLASS="MATH"><I>ν</I></SPAN> is the number of degrees of freedom and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> is the noncentrality parameter.
+ *
+ * <P>
+ * To generate the normal variates, one uses the fast
+ * <SPAN CLASS="textit">acceptance-complement ratio</SPAN> method in
+ * (see class {@link umontreal.iro.lecuyer.randvar.NormalACRGen NormalACRGen}).
+ * To generate the gamma variates, one uses acceptance-rejection for <SPAN CLASS="MATH"><I>α</I> < 1</SPAN>,
+ * and acceptance-complement for
+ * <SPAN CLASS="MATH"><I>α</I> >= 1</SPAN>, as proposed in
+ * (see class {@link umontreal.iro.lecuyer.randvar.GammaAcceptanceRejectionGen GammaAcceptanceRejectionGen}).
+ *
+ * <P>
+ * This noncentral chi square generator is faster than the generator
+ * {@link umontreal.iro.lecuyer.randvar.ChiSquareNoncentralPoisGen ChiSquareNoncentralPoisGen} .
+ * For small <SPAN CLASS="MATH"><I>λ</I></SPAN>, it is nearly twice as fast. As <SPAN CLASS="MATH"><I>λ</I></SPAN> increases,
+ * it is still faster but not as much.
+ *
+ */
+public class ChiSquareNoncentralGamGen extends ChiSquareNoncentralGen {
+ private double racLam = -1.0;
+
+
+
+ /**
+ * Creates a noncentral chi square random variate generator with
+ * with <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> degrees of freedom and noncentrality parameter
+ * <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT> using stream <TT>stream</TT>, as described above.
+ *
+ */
+ public ChiSquareNoncentralGamGen (RandomStream stream,
+ double nu, double lambda) {
+ super (stream, null);
+ setParams (nu, lambda);
+ racLam = Math.sqrt(lambda);
+ }
+
+
+ public double nextDouble() {
+ return gamGen (stream, nu, racLam);
+ }
+
+ /**
+ * Generates a variate from the noncentral chi square
+ * distribution with parameters <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT> using stream <TT>stream</TT>, as described above.
+ *
+ */
+ public static double nextDouble (RandomStream stream,
+ double nu, double lambda) {
+ double racLam = Math.sqrt(lambda);
+ return gamGen (stream, nu, racLam);
+ }
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double gamGen (RandomStream s, double nu, double racLam) {
+ // racLam = sqrt(lambda)
+ double x = NormalACRGen.nextDouble (s, racLam, 1.0);
+ double y = GammaAcceptanceRejectionGen.nextDouble(s, 0.5*(nu - 1.0), 0.5);
+ return x*x + y;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGamGen.tex b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGamGen.tex
new file mode 100644
index 0000000..337042c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGamGen.tex
@@ -0,0 +1,112 @@
+\defmodule {ChiSquareNoncentralGamGen}
+
+This class implements {\em noncentral chi square\/} random variate generators
+using the additive property of the noncentral chi square distribution
+\cite{tKRI06a}. It uses the following algorithm: generate a real
+ $X \sim N(\sqrt\lambda, 1)$ from a normal distribution with variance 1,
+generate a real $Y \sim \Gamma((\nu - 1)/2, 1/2)$ from a gamma distribution,
+then return $X^2 + Y$. Here $\nu$ is the number of degrees of freedom and
+$\lambda$ is the noncentrality parameter.
+
+To generate the normal variates, one uses the fast
+\emph{acceptance-complement ratio} method in \cite{rHOR90a}
+(see class \externalclass{umontreal.iro.lecuyer.randvar}{NormalACRGen}).
+To generate the gamma variates, one uses acceptance-rejection for $\alpha<1$,
+and acceptance-complement for $\alpha\ge 1$, as proposed in \cite{rAHR72b,rAHR82a}
+(see class \externalclass{umontreal.iro.lecuyer.randvar}
+{GammaAcceptanceRejectionGen}).
+
+This noncentral chi square generator is faster than the generator
+\externalclass{umontreal.iro.lecuyer.randvar}{ChiSquareNoncentral\-PoisGen}
+\begin{latexonly}on the next page of this guide\end{latexonly}.
+For small $\lambda$, it is nearly twice as fast. As $\lambda$ increases,
+it is still faster but not as much.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiSquareNoncentralGamGen
+ * Description: noncentral chi-square random variate generators using the
+ additive property of the noncentral chi-square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ChiSquareNoncentralGamGen extends ChiSquareNoncentralGen \begin{hide} {
+ private double racLam = -1.0;
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ChiSquareNoncentralGamGen (RandomStream stream,
+ double nu, double lambda) \begin{hide} {
+ super (stream, null);
+ setParams (nu, lambda);
+ racLam = Math.sqrt(lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a noncentral chi square random variate generator with
+ with $\nu = $ \texttt{nu} degrees of freedom and noncentrality parameter
+$\lambda = $ \texttt{lambda} using stream \texttt{stream}, as described above.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return gamGen (stream, nu, racLam);
+ }\end{hide}
+
+ public static double nextDouble (RandomStream stream,
+ double nu, double lambda) \begin{hide} {
+ double racLam = Math.sqrt(lambda);
+ return gamGen (stream, nu, racLam);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a variate from the noncentral chi square
+ distribution with parameters $\nu = $~\texttt{nu} and $\lambda =
+ $~\texttt{lambda} using stream \texttt{stream}, as described above.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double gamGen (RandomStream s, double nu, double racLam) {
+ // racLam = sqrt(lambda)
+ double x = NormalACRGen.nextDouble (s, racLam, 1.0);
+ double y = GammaAcceptanceRejectionGen.nextDouble(s, 0.5*(nu - 1.0), 0.5);
+ return x*x + y;
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGen.java b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGen.java
new file mode 100644
index 0000000..78bbc50
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGen.java
@@ -0,0 +1,121 @@
+
+
+/*
+ * Class: ChiSquareNoncentralGen
+ * Description: random variate generators for the noncentral chi square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>noncentral chi square</EM> distribution with <SPAN CLASS="MATH"><I>ν</I> > 0</SPAN> degrees of freedom
+ * and noncentrality parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>. See the definition in
+ * {@link umontreal.iro.lecuyer.probdist.ChiSquareNoncentralDist ChiSquareNoncentralDist}.
+ *
+ */
+public class ChiSquareNoncentralGen extends RandomVariateGen {
+ protected double nu = -1.0;
+ protected double lambda = -1.0;
+
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">noncentral chi square</SPAN> random variate generator
+ * with <TT>nu</TT> <SPAN CLASS="MATH">= <I>ν</I> > 0</SPAN> degrees of freedom and noncentrality parameter
+ * <TT>lambda</TT>
+ * <SPAN CLASS="MATH">= <I>λ</I> > 0</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public ChiSquareNoncentralGen (RandomStream s, double nu, double lambda) {
+ super (s, new ChiSquareNoncentralDist(nu, lambda));
+ setParams (nu, lambda);
+ }
+
+
+ /**
+ * Create a new generator for the distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public ChiSquareNoncentralGen (RandomStream s,
+ ChiSquareNoncentralDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getNu (), dist.getLambda());
+ }
+
+
+ /**
+ * Generates a new variate from the noncentral chi square
+ * distribution with <TT>nu</TT> = <SPAN CLASS="MATH"><I>ν</I></SPAN> degrees of freedom and noncentrality
+ * parameter <TT>lambda</TT> <SPAN CLASS="MATH">= <I>λ</I></SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double nu, double lambda) {
+ return ChiSquareNoncentralDist.inverseF (nu, lambda, s.nextDouble());
+ }
+
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>ν</I></SPAN> of this object.
+ *
+ */
+ public double getNu() {
+ return nu;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN>
+ * <TT>lambda</TT> of this object.
+ *
+ */
+ protected void setParams (double nu, double lambda) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ this.nu = nu;
+ this.lambda = lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGen.tex b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGen.tex
new file mode 100644
index 0000000..773549d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralGen.tex
@@ -0,0 +1,124 @@
+\defmodule {ChiSquareNoncentralGen}
+
+This class implements random variate generators for the
+{\em noncentral chi square\/} distribution with $\nu > 0$ degrees of freedom
+and noncentrality parameter $\lambda > 0$. See the definition in
+ \externalclass{umontreal.iro.lecuyer.probdist}{ChiSquareNoncentralDist}.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiSquareNoncentralGen
+ * Description: random variate generators for the noncentral chi square distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ChiSquareNoncentralGen extends RandomVariateGen \begin{hide} {
+ protected double nu = -1.0;
+ protected double lambda = -1.0;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ChiSquareNoncentralGen (RandomStream s, double nu, double lambda) \begin{hide} {
+ super (s, new ChiSquareNoncentralDist(nu, lambda));
+ setParams (nu, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{noncentral chi square} random variate generator
+ with \texttt{nu} $=\nu>0$ degrees of freedom and noncentrality parameter
+\texttt{lambda} $= \lambda>0$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ChiSquareNoncentralGen (RandomStream s,
+ ChiSquareNoncentralDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getNu (), dist.getLambda());
+ }\end{hide}
+\end{code}
+\begin{tabb} Create a new generator for the distribution \texttt{dist}
+ and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double nu, double lambda) \begin{hide} {
+ return ChiSquareNoncentralDist.inverseF (nu, lambda, s.nextDouble());
+ }
+\end{hide}
+\end{code}
+\begin{tabb} Generates a new variate from the noncentral chi square
+distribution with \texttt{nu} = $\nu$ degrees of freedom and noncentrality
+parameter \texttt{lambda} $=\lambda$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getNu()\begin{hide} {
+ return nu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the value of $\nu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the value of $\lambda$ for this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double nu, double lambda) {
+ if (nu <= 0.0)
+ throw new IllegalArgumentException ("nu <= 0");
+ if (lambda < 0.0)
+ throw new IllegalArgumentException ("lambda < 0");
+ this.nu = nu;
+ this.lambda = lambda;
+ }
+\end{code}
+\begin{tabb} Sets the parameters $\nu =$ \texttt{nu} and $\lambda = $
+ \texttt{lambda} of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralPoisGen.java b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralPoisGen.java
new file mode 100644
index 0000000..e672355
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralPoisGen.java
@@ -0,0 +1,104 @@
+
+
+/*
+ * Class: ChiSquareNoncentralPoisGen
+ * Description: noncentral chi square random variate generators using Poisson
+ and central chi square generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>noncentral chi square</EM> random variate generators
+ * using Poisson and central chi square generators. It uses the following algorithm:
+ * generate a random integer
+ * <SPAN CLASS="MATH"><I>J</I>∼Poisson(<I>λ</I>/2)</SPAN> from a Poisson
+ * distribution, generate a random real
+ * <SPAN CLASS="MATH"><I>X</I>∼<I>Γ</I>(<I>j</I> + <I>ν</I>/2, 1/2)</SPAN> from a
+ * gamma distribution, then return <SPAN CLASS="MATH"><I>X</I></SPAN>.
+ * Here <SPAN CLASS="MATH"><I>ν</I></SPAN> is the number of degrees of freedom and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> is the noncentrality parameter.
+ *
+ * <P>
+ * To generate the Poisson variates, one
+ * uses tabulated inversion for
+ * <SPAN CLASS="MATH"><I>λ</I> < 10</SPAN>, and the acceptance complement
+ * method for
+ * <SPAN CLASS="MATH"><I>λ</I> >= 10</SPAN>, as in
+ * (see class {@link umontreal.iro.lecuyer.randvar.PoissonTIACGen PoissonTIACGen}).
+ * To generate the gamma variates, one
+ * uses acceptance-rejection for <SPAN CLASS="MATH"><I>α</I> < 1</SPAN>, and acceptance-complement
+ * for
+ * <SPAN CLASS="MATH"><I>α</I> >= 1</SPAN>, as proposed in
+ * (see class {@link umontreal.iro.lecuyer.randvar.GammaAcceptanceRejectionGen GammaAcceptanceRejectionGen}).
+ *
+ */
+public class ChiSquareNoncentralPoisGen extends ChiSquareNoncentralGen {
+
+
+ /**
+ * Creates a noncentral chi square random variate generator
+ * with <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> degrees of freedom, and noncentrality parameter
+ * <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using stream <TT>stream</TT> as described above.
+ * Restriction:
+ * <SPAN CLASS="MATH"><I>λ</I> <= 4.29×10<SUP>9</SUP></SPAN>.
+ *
+ */
+ public ChiSquareNoncentralPoisGen (RandomStream stream,
+ double nu, double lambda) {
+ super (stream, null);
+ setParams (nu, lambda);
+ if (lambda > 4290000000.0)
+ throw new UnsupportedOperationException(" lambda too large");
+ }
+
+
+ public double nextDouble() {
+ return poisGenere (stream, nu, lambda);
+ }
+
+ /**
+ * Generates a variate from the noncentral chi square
+ * distribution with
+ * parameters <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT>, and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using
+ * stream <TT>stream</TT> as described above.
+ * Restriction:
+ * <SPAN CLASS="MATH"><I>λ</I> <= 4.29×10<SUP>9</SUP></SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream stream,
+ double nu, double lambda) {
+ return poisGenere (stream, nu, lambda);
+ }
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double poisGenere (RandomStream s, double nu, double lambda) {
+ int j = PoissonTIACGen.nextInt (s, 0.5*lambda);
+ return GammaAcceptanceRejectionGen.nextDouble (s, 0.5*nu + j, 0.5);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralPoisGen.tex b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralPoisGen.tex
new file mode 100644
index 0000000..2782cb9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ChiSquareNoncentralPoisGen.tex
@@ -0,0 +1,108 @@
+\defmodule {ChiSquareNoncentralPoisGen}
+
+This class implements {\em noncentral chi square\/} random variate generators
+using Poisson and central chi square generators. It uses the following algorithm:
+generate a random integer $J \sim \mbox{Poisson}(\lambda/2)$ from a Poisson
+distribution, generate a random real $X \sim \Gamma(j + \nu/2, 1/2)$ from a
+gamma distribution, then return $X$.
+Here $\nu$ is the number of degrees of freedom and
+$\lambda$ is the noncentrality parameter.
+
+To generate the Poisson variates, one
+uses tabulated inversion for $\lambda<10$, and the acceptance complement
+method for $\lambda \ge 10$, as in \cite{rAHR82b}
+(see class \externalclass{umontreal.iro.lecuyer.randvar}{PoissonTIACGen}).
+To generate the gamma variates, one
+uses acceptance-rejection for $\alpha<1$, and acceptance-complement
+for $\alpha\ge 1$, as proposed in \cite{rAHR72b,rAHR82a}
+(see class \externalclass{umontreal.iro.lecuyer.randvar}{GammaAcceptanceRejectionGen}).
+% The first stream $s_1$ is used to generate the Poisson variates,
+% while the second stream $s_2$ is used to generate gamma variates.
+% One may also use the same stream for both Poisson and gamma variates.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChiSquareNoncentralPoisGen
+ * Description: noncentral chi square random variate generators using Poisson
+ and central chi square generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ChiSquareNoncentralPoisGen extends ChiSquareNoncentralGen \begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ public ChiSquareNoncentralPoisGen (RandomStream stream,
+ double nu, double lambda) \begin{hide} {
+ super (stream, null);
+ setParams (nu, lambda);
+ if (lambda > 4290000000.0)
+ throw new UnsupportedOperationException(" lambda too large");
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a noncentral chi square random variate generator
+with $\nu = $ \texttt{nu} degrees of freedom, and noncentrality parameter
+$\lambda = $ \texttt{lambda}, using stream \texttt{stream} as described above.
+Restriction: $\lambda \le 4.29\times 10^9$.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return poisGenere (stream, nu, lambda);
+ }\end{hide}
+
+ public static double nextDouble (RandomStream stream,
+ double nu, double lambda) \begin{hide} {
+ return poisGenere (stream, nu, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the noncentral chi square
+ distribution with
+ parameters $\nu = $~\texttt{nu}, and $\lambda = $~\texttt{lambda}, using
+ stream \texttt{stream} as described above.
+ Restriction: $\lambda \le 4.29\times 10^9$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double poisGenere (RandomStream s, double nu, double lambda) {
+ int j = PoissonTIACGen.nextInt (s, 0.5*lambda);
+ return GammaAcceptanceRejectionGen.nextDouble (s, 0.5*nu + j, 0.5);
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/ConstantGen.java b/source/umontreal/iro/lecuyer/randvar/ConstantGen.java
new file mode 100644
index 0000000..6dcb1f0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ConstantGen.java
@@ -0,0 +1,67 @@
+
+
+/*
+ * Class: ConstantGen
+ * Description: random variate generator for a constant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+
+
+/**
+ * This class implements a random variate generator
+ * that returns a constant value.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = 1, for <I>x</I> = <I>c</I>,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>p</I>(<I>x</I>) = 0</SPAN> elsewhere.
+ *
+ */
+public class ConstantGen extends RandomVariateGen {
+ private double val;
+
+
+
+ /**
+ * Constructs a new constant generator returning the given value
+ * <TT>val</TT>.
+ *
+ */
+ public ConstantGen (double val) {
+ this.val = val;
+ }
+
+ @Override
+ public double nextDouble() {
+ return val;
+ }
+
+ @Override
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ for (int i = 0; i < n; i++)
+ v[start + i] = val;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/ConstantGen.tex b/source/umontreal/iro/lecuyer/randvar/ConstantGen.tex
new file mode 100644
index 0000000..f09ed0b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ConstantGen.tex
@@ -0,0 +1,88 @@
+\defmodule {ConstantGen}
+
+This class implements a random variate generator
+that returns a constant value.
+Its mass function is
+\begin{latexonly}
+\eq
+ p(x) = \left\{\begin{array}{ll}
+ 1, & \qquad \mbox {for } x = c,\\[5pt]
+ 0, & \qquad\mbox {elsewhere. }
+ \end{array}\right. \eqlabel{eq:randcons}
+\endeq
+\end{latexonly}
+\begin{htmlonly}
+\eq
+ p(x) = 1, \qquad \mbox {for } x = c,
+\endeq
+and $p(x) = 0$ elsewhere.
+\end{htmlonly}%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ConstantGen
+ * Description: random variate generator for a constant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+\end{hide}
+
+public class ConstantGen extends RandomVariateGen \begin{hide} {
+ private double val;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public ConstantGen (double val)\begin{hide} {
+ this.val = val;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new constant generator returning the given value
+\texttt{val}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ @Override
+ public double nextDouble() {
+ return val;
+ }
+
+ @Override
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ for (int i = 0; i < n; i++)
+ v[start + i] = val;
+ }\end{hide}
+\end{code}
+
+\begin{hide}\begin{code}
+}\end{code}
+\end{hide}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/ErlangConvolutionGen.java b/source/umontreal/iro/lecuyer/randvar/ErlangConvolutionGen.java
new file mode 100644
index 0000000..560caf4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ErlangConvolutionGen.java
@@ -0,0 +1,91 @@
+
+
+/*
+ * Class: ErlangConvolutionGen
+ * Description: Erlang random variate generators using the convolution method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>Erlang</EM> random variate generators using
+ * the <EM>convolution</EM> method. This method uses inversion to
+ * generate <SPAN CLASS="MATH"><I>k</I></SPAN> exponential variates with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> and returns
+ * their sum.
+ *
+ */
+public class ErlangConvolutionGen extends ErlangGen {
+
+
+ /**
+ * Creates an Erlang random variate generator with parameters
+ * <TT>k</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public ErlangConvolutionGen (RandomStream s, int k, double lambda) {
+ super (s, null);
+ setParams (k, lambda);
+ }
+
+
+ /**
+ * Creates an Erlang random variate generator with parameters
+ * <TT>k</TT> and
+ * <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public ErlangConvolutionGen (RandomStream s, int k) {
+ this (s, k, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public ErlangConvolutionGen (RandomStream s, ErlangDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getK(), dist.getLambda());
+ }
+
+
+ public double nextDouble() {
+ return convolution (stream, k, lambda);
+ }
+
+ public static double nextDouble (RandomStream s, int k, double lambda) {
+ return convolution (s, k, lambda);
+ }
+
+ private static double convolution (RandomStream s, int k, double lambda) {
+ double x = 0.0;
+ for (int i=0; i<k; i++)
+ x += ExponentialDist.inverseF (lambda, s.nextDouble());
+ return x;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ErlangConvolutionGen.tex b/source/umontreal/iro/lecuyer/randvar/ErlangConvolutionGen.tex
new file mode 100644
index 0000000..f043429
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ErlangConvolutionGen.tex
@@ -0,0 +1,98 @@
+\defmodule {ErlangConvolutionGen}
+
+This class implements {\em Erlang\/} random variate generators using
+the {\em convolution\/} method. This method uses inversion to
+generate $k$ exponential variates with parameter $\lambda$ and returns
+their sum.
+
+% A local copy of the parameters $k$ and $\lambda$ is maintained in this class.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ErlangConvolutionGen
+ * Description: Erlang random variate generators using the convolution method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ErlangConvolutionGen extends ErlangGen \begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ErlangConvolutionGen (RandomStream s, int k, double lambda) \begin{hide} {
+ super (s, null);
+ setParams (k, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an Erlang random variate generator with parameters
+ \texttt{k} and $\lambda $ = \texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ErlangConvolutionGen (RandomStream s, int k) \begin{hide} {
+ this (s, k, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an Erlang random variate generator with parameters
+ \texttt{k} and $\lambda = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ErlangConvolutionGen (RandomStream s, ErlangDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getK(), dist.getLambda());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist}
+ and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+%%%\subsubsection* {Methods}
+\begin{code} \begin{hide}
+
+ public double nextDouble() {
+ return convolution (stream, k, lambda);
+ }
+
+ public static double nextDouble (RandomStream s, int k, double lambda) {
+ return convolution (s, k, lambda);
+ }
+
+ private static double convolution (RandomStream s, int k, double lambda) {
+ double x = 0.0;
+ for (int i=0; i<k; i++)
+ x += ExponentialDist.inverseF (lambda, s.nextDouble());
+ return x;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/ErlangGen.java b/source/umontreal/iro/lecuyer/randvar/ErlangGen.java
new file mode 100644
index 0000000..57ce9e9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ErlangGen.java
@@ -0,0 +1,118 @@
+
+
+/*
+ * Class: ErlangGen
+ * Description: random variate generators for the Erlang distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the <EM>Erlang</EM>
+ * distribution with parameters <SPAN CLASS="MATH"><I>k</I> > 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * This Erlang random variable is the sum of <SPAN CLASS="MATH"><I>k</I></SPAN> exponentials with
+ * parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> and has mean <SPAN CLASS="MATH"><I>k</I>/<I>λ</I></SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class ErlangGen extends GammaGen {
+ protected int k = -1;
+
+
+
+ /**
+ * Creates an Erlang random variate generator with parameters
+ * <TT>k</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public ErlangGen (RandomStream s, int k, double lambda) {
+ super (s, new ErlangDist(k, lambda));
+ setParams (k, lambda);
+ }
+
+
+ /**
+ * Creates an Erlang random variate generator with parameters
+ * <TT>k</TT> and
+ * <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public ErlangGen (RandomStream s, int k) {
+ this (s, k, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public ErlangGen (RandomStream s, ErlangDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getK(), dist.getLambda());
+ }
+
+
+ /**
+ * Generates a new variate from the Erlang distribution with
+ * parameters <SPAN CLASS="MATH"><I>k</I> =</SPAN> <TT>k</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, int k, double lambda) {
+ return ErlangDist.inverseF (k, lambda, 15, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>k</I></SPAN> of this object.
+ *
+ *
+ */
+ public int getK() {
+ return k;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>k</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (int k, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ this.lambda = lambda;
+ this.k = k;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/ErlangGen.tex b/source/umontreal/iro/lecuyer/randvar/ErlangGen.tex
new file mode 100644
index 0000000..1991e11
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ErlangGen.tex
@@ -0,0 +1,121 @@
+\defmodule {ErlangGen}
+
+This class implements random variate generators for the {\em Erlang\/}
+distribution with parameters $k > 0$ and $\lambda > 0$.
+This Erlang random variable is the sum of $k$ exponentials with
+parameter $\lambda$ and has mean $k/\lambda$.
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+% (Describe other options: using GammaGen, AcceptanceRejection,
+% RejectionLoglogistic. Also GammaConvolutionGen)
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ErlangGen
+ * Description: random variate generators for the Erlang distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ErlangGen extends GammaGen \begin{hide} {
+ protected int k = -1;
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ErlangGen (RandomStream s, int k, double lambda) \begin{hide} {
+ super (s, new ErlangDist(k, lambda));
+ setParams (k, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an Erlang random variate generator with parameters
+ \texttt{k} and $\lambda $ = \texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ErlangGen (RandomStream s, int k) \begin{hide} {
+ this (s, k, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an Erlang random variate generator with parameters
+ \texttt{k} and $\lambda = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ErlangGen (RandomStream s, ErlangDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getK(), dist.getLambda());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist}
+ and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, int k, double lambda) \begin{hide} {
+ return ErlangDist.inverseF (k, lambda, 15, s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Generates a new variate from the Erlang distribution with
+ parameters $k = $~\texttt{k} and $\lambda = $~\texttt{lambda},
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public int getK()\begin{hide} {
+ return k;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $k$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int k, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (k <= 0)
+ throw new IllegalArgumentException ("k <= 0");
+ this.lambda = lambda;
+ this.k = k;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $k$ and $\lambda$ of this object.
+\end{tabb}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/ExponentialGen.java b/source/umontreal/iro/lecuyer/randvar/ExponentialGen.java
new file mode 100644
index 0000000..514a117
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ExponentialGen.java
@@ -0,0 +1,107 @@
+
+
+/*
+ * Class: ExponentialGen
+ * Description: random variate generators for the exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>exponential</EM> distribution. The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>λe</I><SUP>-<I>λ</I>x</SUP> for <I>x</I> >= 0,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class ExponentialGen extends RandomVariateGen {
+ protected double lambda;
+
+
+
+ /**
+ * Creates an exponential random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public ExponentialGen (RandomStream s, double lambda) {
+ super (s, new ExponentialDist(lambda));
+ setParams (lambda);
+ }
+
+
+ /**
+ * Creates a new generator for the exponential
+ * distribution <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public ExponentialGen (RandomStream s, ExponentialDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getLambda());
+ }
+
+
+ /**
+ * Uses inversion to generate a new exponential variate
+ * with parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double lambda) {
+ return ExponentialDist.inverseF (lambda, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>λ</I></SPAN> associated with this object.
+ *
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lam</TT> of this object.
+ *
+ */
+ protected void setParams (double lam) {
+ if (lam <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lam;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/ExponentialGen.tex b/source/umontreal/iro/lecuyer/randvar/ExponentialGen.tex
new file mode 100644
index 0000000..dd1e074
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ExponentialGen.tex
@@ -0,0 +1,107 @@
+\defmodule {ExponentialGen}
+
+This class implements random variate generators for the
+{\em exponential\/} distribution. The density is
+\eq
+ f(x) = \lambda e^{-\lambda x} \qquad\mbox{ for }x\ge 0,
+\endeq
+where $\lambda > 0$.
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ExponentialGen
+ * Description: random variate generators for the exponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ExponentialGen extends RandomVariateGen \begin{hide} {
+ protected double lambda;
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ExponentialGen (RandomStream s, double lambda) \begin{hide} {
+ super (s, new ExponentialDist(lambda));
+ setParams (lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an exponential random variate generator with
+ parameter $\lambda $ = \texttt{lambda}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ExponentialGen (RandomStream s, ExponentialDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getLambda());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the exponential
+ distribution \texttt{dist} and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double lambda) \begin{hide} {
+ return ExponentialDist.inverseF (lambda, s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Uses inversion to generate a new exponential variate
+ with parameter $\lambda = $~\texttt{lambda}, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\lambda$ associated with this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double lam) {
+ if (lam <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lam;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $\lambda = $ \texttt{lam} of this object.
+\end{tabb}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/ExponentialInverseFromDensityGen.java b/source/umontreal/iro/lecuyer/randvar/ExponentialInverseFromDensityGen.java
new file mode 100644
index 0000000..aaa45b7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ExponentialInverseFromDensityGen.java
@@ -0,0 +1,130 @@
+
+
+/*
+ * Class: ExponentialInverseFromDensityGen
+ * Description: exponential random variate generators using numerical
+ inversion of the exponential density
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <SPAN CLASS="textit">exponential</SPAN> random variate generators
+ * using numerical inversion of the <SPAN CLASS="textit">exponential</SPAN> density
+ * as described in. It makes use of the class
+ * {@link umontreal.iro.lecuyer.probdist.InverseDistFromDensity InverseDistFromDensity}.
+ * Generating exponential random variables by inversion usually requires
+ * the computation of a logarithm for each generated random number.
+ * Numerical inversion precomputes a set of tables that will speed up the
+ * generation of random variables. This is useful if one
+ * wants to generate a large number of random variables.
+ *
+ */
+public class ExponentialInverseFromDensityGen extends ExponentialGen {
+
+
+
+ /**
+ * Creates an exponential random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>, using stream <TT>stream</TT>.
+ * It uses numerical inversion with precomputed tables.
+ * The <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>ueps</TT> is the desired absolute error in the
+ * <TT>cdf</TT>, and <TT>order</TT> is the degree of the Newton interpolating
+ * polynomial over each interval.
+ *
+ */
+ public ExponentialInverseFromDensityGen (RandomStream stream,
+ double lambda,
+ double ueps, int order) {
+ // dist is the exponential distribution
+ super (stream, lambda);
+ double xc = Math.min(1.0, 0.5/lambda);
+
+ // Member (ExponentialDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ dist = new InverseDistFromDensity ((ContinuousDistribution) dist,
+ xc, ueps, order);
+ }
+
+
+ /**
+ * Similar to the above constructor, with the exponential
+ * distribution <TT>dist</TT>.
+ *
+ */
+ public ExponentialInverseFromDensityGen (RandomStream stream,
+ ExponentialDist dist,
+ double ueps, int order) {
+ super (stream, dist);
+ double xc = Math.min(1.0, 0.5/lambda);
+
+ // Member (ExponentialDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ this.dist = new InverseDistFromDensity (dist, xc, ueps, order);
+ }
+
+
+ /**
+ * Creates a new exponential generator using the <SPAN CLASS="textit">exponential</SPAN>
+ * distribution <TT>dist</TT> and stream <TT>stream</TT>. <TT>dist</TT>
+ * may be obtained by calling method {@link #getDistribution(()) getDistribution},
+ * after using one of the other constructors to create the
+ * precomputed tables. This is useful when one needs many generators
+ * using the same exponential distribution
+ * (same <SPAN CLASS="MATH"><I>λ</I></SPAN>). Precomputing tables for numerical inversion is
+ * costly; thus using only one set of tables for many generators
+ * is more efficient. The first {@link ExponentialInverseFromDensityGen} generator
+ * using the other constructors creates the precomputed tables.
+ * Then all other streams use this constructor with the same set of tables.
+ *
+ */
+ public ExponentialInverseFromDensityGen (RandomStream stream,
+ InverseDistFromDensity dist) {
+ super (stream, null);
+ lambda = -1; // don't know its explicit value; it is inside dist
+ this.dist = dist;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>ueps</TT>.
+ *
+ */
+ public double getUepsilon() {
+ return ((InverseDistFromDensity)dist).getEpsilon();
+ }
+
+
+
+ /**
+ * Returns the order of the interpolating polynomial.
+ *
+ */
+ public int getOrder() {
+ return ((InverseDistFromDensity)dist).getOrder();
+ }
+
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/ExponentialInverseFromDensityGen.tex b/source/umontreal/iro/lecuyer/randvar/ExponentialInverseFromDensityGen.tex
new file mode 100644
index 0000000..323bfae
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ExponentialInverseFromDensityGen.tex
@@ -0,0 +1,140 @@
+\defmodule {ExponentialInverseFromDensityGen}
+
+This class implements \emph{exponential} random variate generators
+ using numerical inversion of the \emph{exponential} density
+as described in \cite{rDER10a}. It makes use of the class
+\externalclass{umontreal.iro.lecuyer.probdist}{InverseDistFromDensity}.
+Generating exponential random variables by inversion usually requires
+the computation of a logarithm for each generated random number.
+Numerical inversion precomputes a set of tables that will speed up the
+generation of random variables. This is useful if one
+wants to generate a large number of random variables.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ExponentialInverseFromDensityGen
+ * Description: exponential random variate generators using numerical
+ inversion of the exponential density
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ExponentialInverseFromDensityGen extends ExponentialGen \begin{hide} {
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ExponentialInverseFromDensityGen (RandomStream stream,
+ double lambda,
+ double ueps, int order) \begin{hide} {
+ // dist is the exponential distribution
+ super (stream, lambda);
+ double xc = Math.min(1.0, 0.5/lambda);
+
+ // Member (ExponentialDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ dist = new InverseDistFromDensity ((ContinuousDistribution) dist,
+ xc, ueps, order);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an exponential random variate generator with
+ parameter $\lambda $ = \texttt{lambda}, using stream \texttt{stream}.
+It uses numerical inversion with precomputed tables.
+The $u$-resolution \texttt{ueps} is the desired absolute error in the
+\texttt{cdf}, and \texttt{order} is the degree of the Newton interpolating
+polynomial over each interval.
+\end{tabb}
+\begin{code}
+
+ public ExponentialInverseFromDensityGen (RandomStream stream,
+ ExponentialDist dist,
+ double ueps, int order) \begin{hide} {
+ super (stream, dist);
+ double xc = Math.min(1.0, 0.5/lambda);
+
+ // Member (ExponentialDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ this.dist = new InverseDistFromDensity (dist, xc, ueps, order);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Similar to the above constructor, with the exponential
+ distribution \texttt{dist}.
+ \end{tabb}
+\begin{code}
+
+ public ExponentialInverseFromDensityGen (RandomStream stream,
+ InverseDistFromDensity dist) \begin{hide} {
+ super (stream, null);
+ lambda = -1; // don't know its explicit value; it is inside dist
+ this.dist = dist;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new exponential generator using the \emph{exponential}
+ distribution \texttt{dist} and stream \texttt{stream}. \texttt{dist}
+ may be obtained by calling method \method{getDistribution}{()},
+ after using one of the other constructors to create the
+ precomputed tables. This is useful when one needs many generators
+ using the same exponential distribution
+(same $\lambda$). Precomputing tables for numerical inversion is
+ costly; thus using only one set of tables for many generators
+is more efficient. The first \class{ExponentialInverseFromDensityGen} generator
+ using the other constructors creates the precomputed tables.
+Then all other streams use this constructor with the same set of tables.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public double getUepsilon()\begin{hide} {
+ return ((InverseDistFromDensity)dist).getEpsilon();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $u$-resolution \texttt{ueps}.
+\end{tabb}
+\begin{code}
+
+ public int getOrder()\begin{hide} {
+ return ((InverseDistFromDensity)dist).getOrder();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the order of the interpolating polynomial.
+\end{tabb}
+
+\begin{hide}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/ExtremeValueGen.java b/source/umontreal/iro/lecuyer/randvar/ExtremeValueGen.java
new file mode 100644
index 0000000..3e46cd3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ExtremeValueGen.java
@@ -0,0 +1,135 @@
+
+
+/*
+ * Class: ExtremeValueGen
+ * Description: random variate generators for the Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+ at Deprecated
+/**
+ * <SPAN CLASS="textbf">This class has been replaced by {@link GumbelGen}</SPAN>.
+ *
+ * <P>
+ * This class implements random variate generators for the <SPAN CLASS="textit">Gumbel</SPAN>
+ * (or <EM>extreme value</EM>) distribution. Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>λe</I><SUP>-e<SUP>-<I>λ</I>(x-<I>α</I>)</SUP>-<I>λ</I>(x-<I>α</I>)</SUP>
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class ExtremeValueGen extends RandomVariateGen {
+ protected double alpha = -1.0;
+ protected double lambda = -1.0;
+
+
+
+
+ /**
+ * Creates an <SPAN CLASS="textit">extreme value</SPAN> random variate generator with
+ * parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public ExtremeValueGen (RandomStream s, double alpha, double lambda) {
+ super (s, new ExtremeValueDist(alpha, lambda));
+ setParams (alpha, lambda);
+ }
+
+
+ /**
+ * Creates an <SPAN CLASS="textit">extreme value</SPAN> random variate generator with
+ * parameters
+ * <SPAN CLASS="MATH"><I>α</I> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public ExtremeValueGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator object for distribution <TT>dist</TT> and
+ * stream <TT>s</TT>.
+ *
+ */
+ public ExtremeValueGen (RandomStream s, ExtremeValueDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ }
+
+
+ /**
+ * Uses inversion to generate a new variate from the extreme value
+ * distribution with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda) {
+ return ExtremeValueDist.inverseF (alpha, lambda, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lambda;
+ this.alpha = alpha;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ExtremeValueGen.tex b/source/umontreal/iro/lecuyer/randvar/ExtremeValueGen.tex
new file mode 100644
index 0000000..b3d7331
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ExtremeValueGen.tex
@@ -0,0 +1,135 @@
+\defmodule {ExtremeValueGen}
+
+\textbf{This class has been replaced by \class{GumbelGen}}.
+
+This class implements random variate generators for the \emph{Gumbel}
+(or {\em extreme value\/}) distribution. Its density is
+\eq
+ f(x) = \lambda e^{-e^{-\lambda(x - \alpha)}-\lambda(x-\alpha)}
+ \eqlabel{eq:fextremevalue}
+\endeq
+where $\lambda>0$.
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ExtremeValueGen
+ * Description: random variate generators for the Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+ at Deprecated
+public class ExtremeValueGen extends RandomVariateGen \begin{hide} {
+ protected double alpha = -1.0;
+ protected double lambda = -1.0;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ExtremeValueGen (RandomStream s, double alpha, double lambda) \begin{hide} {
+ super (s, new ExtremeValueDist(alpha, lambda));
+ setParams (alpha, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an \emph{extreme value} random variate generator with
+ parameters $\alpha =$ \texttt{alpha} and $\lambda $ = \texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ExtremeValueGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an \emph{extreme value} random variate generator with
+ parameters $\alpha = 0$ and $\lambda =1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ExtremeValueGen (RandomStream s, ExtremeValueDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new generator object for distribution \texttt{dist} and
+ stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda) \begin{hide} {
+ return ExtremeValueDist.inverseF (alpha, lambda, s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Uses inversion to generate a new variate from the extreme value
+ distribution with parameters $\alpha = $~\texttt{alpha} and $\lambda =
+ $~\texttt{lambda}, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\lambda$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lambda;
+ this.alpha = alpha;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $\alpha$ and $\lambda$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/FNoncentralGen.java b/source/umontreal/iro/lecuyer/randvar/FNoncentralGen.java
new file mode 100644
index 0000000..0a36bd5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FNoncentralGen.java
@@ -0,0 +1,96 @@
+
+
+/*
+ * Class: FNoncentralGen
+ * Description: random variate generators for the noncentral F-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <SPAN CLASS="textit">noncentral F</SPAN>-distribution.
+ * If <SPAN CLASS="MATH"><I>X</I></SPAN> is a noncentral chi-square random variable with <SPAN CLASS="MATH"><I>ν</I><SUB>1</SUB> > 0</SPAN> degrees of
+ * freedom and noncentrality parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>, and <SPAN CLASS="MATH"><I>Y</I></SPAN> is a chi-square
+ * random variable (statistically independent of <SPAN CLASS="MATH"><I>X</I></SPAN>) with <SPAN CLASS="MATH"><I>ν</I><SUB>2</SUB> > 0</SPAN> degrees
+ * of freedom, then
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>F</I> ' = (<I>X</I>/<I>ν</I><SUB>1</SUB>)/(<I>Y</I>/<I>ν</I><SUB>2</SUB>)
+ * </DIV><P></P>
+ * has a noncentral <SPAN CLASS="MATH"><I>F</I></SPAN>-distribution.
+ *
+ */
+public class FNoncentralGen extends RandomVariateGen {
+ private ChiSquareNoncentralGen noncenchigen;
+ private ChiSquareGen chigen;
+ private double nu1; // degrees of freedom of noncenchigen
+ private int nu2; // degrees of freedom of chigen
+
+ public double nextDouble() {
+ double x = noncenchigen.nextDouble();
+ double y = chigen.nextDouble();
+ return (x * nu2) / (y * nu1);
+ }
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">noncentral-F</SPAN> random variate generator
+ * using noncentral chi-square generator <TT>ncgen</TT> and
+ * chi-square generator <TT>cgen</TT>.
+ *
+ */
+ public FNoncentralGen (ChiSquareNoncentralGen ncgen, ChiSquareGen cgen) {
+ super (null, null);
+ setChiSquareNoncentralGen (ncgen);
+ setChiSquareGen (cgen);
+ }
+
+
+ /**
+ * Sets the noncentral chi-square generator to <TT>ncgen</TT>.
+ *
+ */
+ public void setChiSquareNoncentralGen (ChiSquareNoncentralGen ncgen) {
+ nu1 = ncgen.getNu();
+ noncenchigen = ncgen;
+ }
+
+
+ /**
+ * Sets the chi-square generator to <TT>cgen</TT>.
+ *
+ */
+ public void setChiSquareGen (ChiSquareGen cgen) {
+ nu2 = cgen.getN();
+ chigen = cgen;
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/FNoncentralGen.tex b/source/umontreal/iro/lecuyer/randvar/FNoncentralGen.tex
new file mode 100644
index 0000000..80c9fa2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FNoncentralGen.tex
@@ -0,0 +1,109 @@
+\defmodule {FNoncentralGen}
+
+This class implements random variate generators for the
+\emph{noncentral F}-distribution.
+If $X$ is a noncentral chi-square random variable with $\nu_1 > 0$ degrees of
+freedom and noncentrality parameter $\lambda > 0$, and $Y$ is a chi-square
+random variable (statistically independent of $X$) with $\nu_2>0$ degrees
+ of freedom, then
+\begin{latexonly}%
+\[
+ F' = \frac{X/\nu_1}{Y/\nu_2}
+\]
+\end{latexonly}%
+\begin{htmlonly}%
+\[
+ F\,' = {(X/\nu_1)} / {(Y/\nu_2)}
+\]
+\end{htmlonly}%
+has a noncentral $F$-distribution.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FNoncentralGen
+ * Description: random variate generators for the noncentral F-distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class FNoncentralGen extends RandomVariateGen \begin{hide} {
+ private ChiSquareNoncentralGen noncenchigen;
+ private ChiSquareGen chigen;
+ private double nu1; // degrees of freedom of noncenchigen
+ private int nu2; // degrees of freedom of chigen
+
+ public double nextDouble() {
+ double x = noncenchigen.nextDouble();
+ double y = chigen.nextDouble();
+ return (x * nu2) / (y * nu1);
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public FNoncentralGen (ChiSquareNoncentralGen ncgen, ChiSquareGen cgen) \begin{hide} {
+ super (null, null);
+ setChiSquareNoncentralGen (ncgen);
+ setChiSquareGen (cgen);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{noncentral-F} random variate generator
+ using noncentral chi-square generator \texttt{ncgen} and
+ chi-square generator \texttt{cgen}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public void setChiSquareNoncentralGen (ChiSquareNoncentralGen ncgen)\begin{hide} {
+ nu1 = ncgen.getNu();
+ noncenchigen = ncgen;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the noncentral chi-square generator to \texttt{ncgen}.
+ \end{tabb}
+\begin{code}
+
+ public void setChiSquareGen (ChiSquareGen cgen)\begin{hide} {
+ nu2 = cgen.getN();
+ chigen = cgen;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the chi-square generator to \texttt{cgen}.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/FatigueLifeGen.java b/source/umontreal/iro/lecuyer/randvar/FatigueLifeGen.java
new file mode 100644
index 0000000..ce1f44d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FatigueLifeGen.java
@@ -0,0 +1,134 @@
+
+
+/*
+ * Class: FatigueLifeGen
+ * Description: random variate generators for the fatigue life distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for
+ * the <SPAN CLASS="textit">fatigue life</SPAN> distribution with location
+ * parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>, scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN> and shape
+ * parameter <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = [(((<I>x</I> - <I>μ</I>)/<I>β</I>)<SUP>1/2</SUP> + (<I>β</I>/(<I>x</I> - <I>μ</I>))<SUP>1/2</SUP>)/(2<I>γ</I>(<I>x</I> - <I>μ</I>))]<I>φ</I>((((<I>x</I> - <I>μ</I>)/<I>β</I>)<SUP>1/2</SUP> - (<I>β</I>/(<I>x</I> - <I>μ</I>))<SUP>1/2</SUP>)/<I>γ</I>), <I>x</I> > <I>μ</I>
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>φ</I></SPAN> is the probability density of the standard normal distribution.
+ *
+ */
+public class FatigueLifeGen extends RandomVariateGen {
+ protected double mu;
+ protected double beta;
+ protected double gamma;
+
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">fatigue life</SPAN> random variate generator with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT>, <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT> and <SPAN CLASS="MATH"><I>γ</I></SPAN> =
+ * <TT>gamma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public FatigueLifeGen (RandomStream s, double mu, double beta,
+ double gamma) {
+ super (s, new FatigueLifeDist(mu, beta, gamma));
+ setParams (mu, beta, gamma);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public FatigueLifeGen (RandomStream s, FatigueLifeDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getBeta(), dist.getGamma());
+ }
+
+
+ /**
+ * Generates a variate from the <EM>fatigue life</EM> distribution
+ * with location parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>, scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN> and shape parameter
+ * <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double mu, double beta,
+ double gamma) {
+ return FatigueLifeDist.inverseF (mu, beta, gamma, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>γ</I></SPAN> of this object.
+ *
+ */
+ public double getGamma() {
+ return gamma;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double mu, double beta, double gamma) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ this.mu = mu;
+ this.beta = beta;
+ this.gamma = gamma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/FatigueLifeGen.tex b/source/umontreal/iro/lecuyer/randvar/FatigueLifeGen.tex
new file mode 100644
index 0000000..20fd6b2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FatigueLifeGen.tex
@@ -0,0 +1,147 @@
+\defmodule{FatigueLifeGen}
+
+This class implements random variate generators for
+the \emph{fatigue life} distribution \cite{tBIR69a} with location
+parameter $\mu$, scale parameter $\beta$ and shape
+parameter $\gamma$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = [(((x - \mu) / \beta)^{1/2} + (\beta / (x - \mu))^{1/2}) / (2\gamma(x - \mu))]
+ \phi((((x - \mu) / \beta)^{1/2} - (\beta / (x - \mu))^{1/2}) / \gamma),
+ \qquad x > \mu
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \left[\frac{\sqrt{(x - \mu)/{\beta}} + \sqrt{{\beta}/{(x - \mu)}}}{2\gamma(x - \mu)}\right]
+ \phi\left(\frac{\sqrt{{(x - \mu)}/{\beta}} - \sqrt{{\beta}/{(x - \mu)}}}{\gamma}\right), \qquad x > \mu
+\eqlabel{eq:fFatigueLife}
+\endeq
+\end{latexonly}%
+where $\phi$ is the probability density of the standard normal distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FatigueLifeGen
+ * Description: random variate generators for the fatigue life distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class FatigueLifeGen extends RandomVariateGen \begin{hide} {
+ protected double mu;
+ protected double beta;
+ protected double gamma;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public FatigueLifeGen (RandomStream s, double mu, double beta,
+ double gamma) \begin{hide} {
+ super (s, new FatigueLifeDist(mu, beta, gamma));
+ setParams (mu, beta, gamma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{fatigue life} random variate generator with
+ parameters $\mu =$ \texttt{mu}, $\beta $ = \texttt{beta} and $\gamma$ =
+ \texttt{gamma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public FatigueLifeGen (RandomStream s, FatigueLifeDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getBeta(), dist.getGamma());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double mu, double beta,
+ double gamma)\begin{hide} {
+ return FatigueLifeDist.inverseF (mu, beta, gamma, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the {\em fatigue life\/} distribution
+ with location parameter $\mu$, scale parameter $\beta$ and shape parameter
+ $\gamma$.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\beta$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getGamma()\begin{hide} {
+ return gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\gamma$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\mu$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double mu, double beta, double gamma) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+
+ this.mu = mu;
+ this.beta = beta;
+ this.gamma = gamma;
+ }
+\end{code}
+\begin{tabb}
+ Sets the parameters $\mu$, $\beta$ and $\gamma$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/FisherFGen.java b/source/umontreal/iro/lecuyer/randvar/FisherFGen.java
new file mode 100644
index 0000000..7c8a356
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FisherFGen.java
@@ -0,0 +1,118 @@
+
+
+/*
+ * Class: FisherFGen
+ * Description: random variate generators for the Fisher F distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <SPAN CLASS="textit">Fisher F</SPAN> distribution with <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN>
+ * degrees of freedom, where <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> are positive integers.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>Γ</I>((<I>n</I> + <I>m</I>)/2)<I>n</I><SUP>n/2</SUP><I>m</I><SUP>m/2</SUP>/[<I>Γ</I>(<I>n</I>/2)<I>Γ</I>(<I>m</I>/2)]<I>x</I><SUP>(n-2)/2</SUP>/(<I>m</I> + <I>nx</I>)<SUP>(n+m)/2</SUP>, for <I>x</I> > 0.
+ * </DIV><P></P>
+ *
+ */
+public class FisherFGen extends RandomVariateGen {
+ protected int n = -1;
+ protected int m = -1;
+
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">Fisher F</SPAN> random variate generator with
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public FisherFGen (RandomStream s, int n, int m) {
+ super (s, new FisherFDist(n, m));
+ setParams (n, m);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public FisherFGen (RandomStream s, FisherFDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN(), dist.getM());
+ }
+
+
+ /**
+ * Generates a variate from the <SPAN CLASS="textit">Fisher F</SPAN> distribution with
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, int n, int m) {
+ return FisherFDist.inverseF (n, m, 15, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ *
+ */
+ public int getM() {
+ return m;
+ }
+
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (int n, int m) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.m = m;
+ this.n = n;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/FisherFGen.tex b/source/umontreal/iro/lecuyer/randvar/FisherFGen.tex
new file mode 100644
index 0000000..ecdb59d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FisherFGen.tex
@@ -0,0 +1,131 @@
+\defmodule{FisherFGen}
+
+This class implements random variate generators for the
+\emph{Fisher F} distribution with $n$ and $m$
+degrees of freedom, where $n$ and $m$ are positive integers.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = \Gamma((n + m) / 2)n^{n/2}m^{m/2} / [\Gamma(n/2)\Gamma(m/2)]
+ x^{(n - 2) / 2} / (m + nx)^{(n + m)/2},
+\qquad\mbox{ for } x > 0.
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \frac{\Gamma({(n + m)}/{2})n^{{n}/{2}}m^{{m}/{2}}}{\Gamma({n}/{2})\Gamma({m}/{2})}
+ \frac{x^{{(n - 2)}/{2}}}{(m + nx)^{{(n + m)}/{2}}},
+\qquad\mbox {for } x > 0
+\eqlabel{eq:FisherF}
+\endeq
+\end{latexonly}%
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FisherFGen
+ * Description: random variate generators for the Fisher F distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class FisherFGen extends RandomVariateGen \begin{hide} {
+ protected int n = -1;
+ protected int m = -1;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public FisherFGen (RandomStream s, int n, int m) \begin{hide} {
+ super (s, new FisherFDist(n, m));
+ setParams (n, m);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{Fisher F} random variate generator with
+ $n$ and $m$ degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public FisherFGen (RandomStream s, FisherFDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN(), dist.getM());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, int n, int m)\begin{hide} {
+ return FisherFDist.inverseF (n, m, 15, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the \emph{Fisher F} distribution with
+ $n$ and $m$ degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public int getM()\begin{hide} {
+ return m;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int n, int m) {
+ if (m <= 0)
+ throw new IllegalArgumentException ("m <= 0");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.m = m;
+ this.n = n;
+ }
+\end{code}
+\begin{tabb} Sets the parameters $n$ and $m$ of this object.
+\end{tabb}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/FoldedNormalGen.java b/source/umontreal/iro/lecuyer/randvar/FoldedNormalGen.java
new file mode 100644
index 0000000..96d9390
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FoldedNormalGen.java
@@ -0,0 +1,126 @@
+
+
+/*
+ * Class: FoldedNormalGen
+ * Description: generator of random variates from the folded normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <EM>folded normal</EM> distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> >= 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>φ</I>((<I>x</I> - <I>μ</I>/)<I>σ</I>) + <I>φ</I>((- <I>x</I> - <I>μ</I>)/<I>σ</I>) for <I>x</I> >= 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>φ</I></SPAN> denotes the density function of a standard normal distribution.
+ *
+ */
+public class FoldedNormalGen extends RandomVariateGen {
+
+ // Distribution parameters
+ protected double mu;
+ protected double sigma;
+
+
+
+ /**
+ * Creates a new <EM>folded normal</EM> generator with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN>
+ * <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public FoldedNormalGen (RandomStream s, double mu, double sigma) {
+ super (s, new FoldedNormalDist (mu, sigma));
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public FoldedNormalGen (RandomStream s, FoldedNormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ /**
+ * Generates a variate from the <EM>folded normal</EM> distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>,
+ * using stream <TT>s</TT>.
+ *
+ * @param s the random stream
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return Generates a variate from the <EM>FoldedNormal</EM> distribution
+ *
+ */
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ return FoldedNormalDist.inverseF (mu, sigma, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ * @return the parameter mu
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ * @return the parameter mu
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/FoldedNormalGen.tex b/source/umontreal/iro/lecuyer/randvar/FoldedNormalGen.tex
new file mode 100644
index 0000000..f0db299
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FoldedNormalGen.tex
@@ -0,0 +1,139 @@
+\defmodule {FoldedNormalGen}
+
+This class implements methods for generating random variates from the
+{\em folded normal\/} distribution with
+parameters $\mu \ge 0$ and $\sigma > 0$.
+The density is
+\begin{htmlonly}
+\eq
+ f(x) = \phi \left((x-\mu/)\sigma\right) + \phi \left((-x-\mu)/\sigma\right)
+ \qquad \mbox {for } x \ge 0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \phi \left(\frac{x-\mu}{\sigma}\right) +
+ \phi \left(\frac{-x-\mu}{\sigma}\right)
+ \qquad \mbox {for } x \ge 0, \eqlabel{eq:fFoldedNormal}
+\endeq
+\end{latexonly}
+where $ \phi $ denotes the density function of a standard normal distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FoldedNormalGen
+ * Description: generator of random variates from the folded normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class FoldedNormalGen extends RandomVariateGen \begin{hide} {
+
+ // Distribution parameters
+ protected double mu;
+ protected double sigma;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public FoldedNormalGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ super (s, new FoldedNormalDist (mu, sigma));
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new {\em folded normal} generator with parameters $\mu =$
+ \texttt{mu} and $\sigma =$ \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public FoldedNormalGen (RandomStream s, FoldedNormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double mu, double sigma)\begin{hide} {
+ return FoldedNormalDist.inverseF (mu, sigma, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the {\em folded normal\/} distribution with
+ parameters $\mu = $~\texttt{mu} and $\sigma = $~\texttt{sigma},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{the random stream}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{Generates a variate from the {\em FoldedNormal\/} distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the parameter mu}
+\end{htmlonly}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the parameter mu}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ if (mu < 0.0)
+ throw new IllegalArgumentException ("mu < 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/FrechetGen.java b/source/umontreal/iro/lecuyer/randvar/FrechetGen.java
new file mode 100644
index 0000000..7733460
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FrechetGen.java
@@ -0,0 +1,142 @@
+
+
+/*
+ * Class: FrechetGen
+ * Description: generator of random variates from the Fréchet distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <SPAN CLASS="textit">Fréchet</SPAN> distribution, with location parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>, scale
+ * parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>, and shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>, where we use
+ * the notation
+ * <SPAN CLASS="MATH"><I>z</I> = (<I>x</I> - <I>δ</I>)/<I>β</I></SPAN>. It has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>αe</I><SUP>-z<SUP>-<I>α</I></SUP></SUP>/(<I>βz</I><SUP><I>α</I>+1</SUP>), for <I>x</I> > <I>δ</I>
+ * </DIV><P></P>
+ * The density is 0 for
+ * <SPAN CLASS="MATH"><I>x</I> <= <I>δ</I></SPAN>.
+ *
+ */
+public class FrechetGen extends RandomVariateGen {
+ private double delta;
+ private double beta;
+ private double alpha;
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">Fréchet</SPAN> random number generator with <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I> = 1</SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> = 0</SPAN> using stream <TT>s</TT>.
+ *
+ */
+ public FrechetGen (RandomStream s, double alpha) {
+ this (s, alpha, 1.0, 0.0);
+ }
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">Fréchet</SPAN> random number generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT> using stream <TT>s</TT>.
+ *
+ */
+ public FrechetGen (RandomStream s, double alpha, double beta,
+ double delta) {
+ super (s, new FrechetDist (alpha, beta, delta));
+ setParams (alpha, beta, delta);
+ }
+
+
+ /**
+ * Creates a new generator for the <SPAN CLASS="textit">Fréchet</SPAN> distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public FrechetGen (RandomStream s, FrechetDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getDelta());
+ }
+
+
+ /**
+ * Generates a new variate from the <SPAN CLASS="textit">Fréchet</SPAN> distribution with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT> and <SPAN CLASS="MATH"><I>δ</I> =</SPAN> <TT>delta</TT> using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha,
+ double beta, double delta) {
+ return FrechetDist.inverseF (alpha, beta, delta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double alpha, double beta, double delta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/FrechetGen.tex b/source/umontreal/iro/lecuyer/randvar/FrechetGen.tex
new file mode 100644
index 0000000..2416980
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/FrechetGen.tex
@@ -0,0 +1,150 @@
+\defmodule {FrechetGen}
+
+This class implements methods for generating random variates from the
+\emph{Fr\'echet} distribution, with location parameter $\delta$, scale
+ parameter $\beta > 0$, and shape parameter $\alpha > 0$, where we use
+ the notation $z = (x-\delta)/\beta$. It has density
+\begin{htmlonly}
+\eq
+ f (x) = \alpha e^{-z^{-\alpha}} / ( \beta z^{\alpha +1} ),
+ \qquad \mbox{for } x > \delta
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\[
+f (x) =
+ \frac{\alpha e^{-z^{-\alpha}}} {\beta z^{\alpha +1}},
+ \qquad \mbox{for } x > \delta.
+\]
+\end{latexonly}
+The density is 0 for $x \le \delta$.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: FrechetGen
+ * Description: generator of random variates from the Fréchet distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class FrechetGen extends RandomVariateGen \begin{hide} {
+ private double delta;
+ private double beta;
+ private double alpha;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public FrechetGen (RandomStream s, double alpha) \begin{hide} {
+ this (s, alpha, 1.0, 0.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{Fr\'echet} random number generator with $\alpha =$
+ \texttt{alpha}, $\beta = 1$ and $\delta = 0$ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public FrechetGen (RandomStream s, double alpha, double beta,
+ double delta) \begin{hide} {
+ super (s, new FrechetDist (alpha, beta, delta));
+ setParams (alpha, beta, delta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{Fr\'echet} random number generator with parameters
+ $\alpha$ = \texttt{alpha}, $\beta$ = \texttt{beta} and $\delta$ = \texttt{delta} using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public FrechetGen (RandomStream s, FrechetDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getDelta());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the \emph{Fr\'echet} distribution \texttt{dist}
+ and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha,
+ double beta, double delta)\begin{hide} {
+ return FrechetDist.inverseF (alpha, beta, delta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the \emph{Fr\'echet} distribution with parameters
+$\alpha =$ \texttt{alpha}, $\beta = $~\texttt{beta} and $\delta =
+ $~\texttt{delta} using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\delta$.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double alpha, double beta, double delta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }
+\end{code}
+\begin{tabb}
+ Sets the parameters $\alpha$, $\beta$ and $\delta$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/GammaAcceptanceRejectionGen.java b/source/umontreal/iro/lecuyer/randvar/GammaAcceptanceRejectionGen.java
new file mode 100644
index 0000000..55cadc8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GammaAcceptanceRejectionGen.java
@@ -0,0 +1,364 @@
+
+
+/*
+ * Class: GammaAcceptanceRejectionGen
+ * Description: gamma random variate generators using a method that combines
+ acceptance-rejection with acceptance-complement
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>gamma</EM> random variate generators using
+ * a method that combines acceptance-rejection
+ * with acceptance-complement.
+ * It uses acceptance-rejection for <SPAN CLASS="MATH"><I>α</I> < 1</SPAN> and acceptance-complement
+ * for
+ * <SPAN CLASS="MATH"><I>α</I> >= 1</SPAN>.
+ * For each gamma variate, the first uniform required is taken from the
+ * main stream and all additional uniforms (after the first rejection)
+ * are obtained from the auxiliary stream.
+ *
+ */
+public class GammaAcceptanceRejectionGen extends GammaGen {
+
+ private RandomStream auxStream;
+ private static final double q1 = 0.0416666664;
+ private static final double q2 = 0.0208333723;
+ private static final double q3 = 0.0079849875;
+ private static final double q4 = 0.0015746717;
+ private static final double q5 = -0.0003349403;
+ private static final double q6 = 0.0003340332;
+ private static final double q7 = 0.0006053049;
+ private static final double q8 = -0.0004701849;
+ private static final double q9 = 0.0001710320;
+ private static final double a1 = 0.333333333;
+ private static final double a2 = -0.249999949;
+ private static final double a3 = 0.199999867;
+ private static final double a4 = -0.166677482;
+ private static final double a5 = 0.142873973;
+ private static final double a6 = -0.124385581;
+ private static final double a7 = 0.110368310;
+ private static final double a8 = -0.112750886;
+ private static final double a9 = 0.104089866;
+ private static final double e1 = 1.000000000;
+ private static final double e2 = 0.499999994;
+ private static final double e3 = 0.166666848;
+ private static final double e4 = 0.041664508;
+ private static final double e5 = 0.008345522;
+ private static final double e6 = 0.001353826;
+ private static final double e7 = 0.000247453;
+
+ private int gen;
+ // UNURAN parameters for the distribution
+ private double beta;
+ private double gamma;
+ // Generator parameters
+ // Acceptance rejection combined with acceptance complement
+ private static final int gs = 0;
+ private static final int gd = 1;
+ private double b;
+ private double ss;
+ private double s;
+ private double d;
+ private double r;
+ private double q0;
+ private double c;
+ private double si;
+
+
+
+
+ /**
+ * Creates a gamma random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using main stream <TT>s</TT> and
+ * auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public GammaAcceptanceRejectionGen (RandomStream s, RandomStream aux,
+ double alpha, double lambda) {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, lambda);
+ beta = 1.0/lambda;
+ gamma = 0.0;
+ init ();
+ }
+
+
+ /**
+ * Creates a gamma random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public GammaAcceptanceRejectionGen (RandomStream s,
+ double alpha, double lambda) {
+ this (s, s, alpha, lambda);
+ }
+
+
+ /**
+ * Creates a new generator object for the gamma
+ * distribution <TT>dist</TT>, using main stream <TT>s</TT> and
+ * auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public GammaAcceptanceRejectionGen (RandomStream s, RandomStream aux,
+ GammaDist dist) {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ beta = 1.0/dist.getLambda();
+ gamma = 0.0;
+ init ();
+ }
+
+
+ /**
+ * Creates a new generator object for the gamma
+ * distribution <TT>dist</TT> and stream <TT>s</TT> for both the main and
+ * auxiliary stream.
+ *
+ */
+ public GammaAcceptanceRejectionGen (RandomStream s, GammaDist dist) {
+ this (s, s, dist);
+ }
+
+
+ /**
+ * Returns the auxiliary stream associated with this object.
+ *
+ */
+ public RandomStream getAuxStream() {
+ return auxStream;
+ }
+
+
+ /**
+ * Generates a new gamma variate with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using
+ * main stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, RandomStream aux,
+ double alpha, double lambda) {
+ int gen = gs;
+ double s_ = 0, ss = 0, d = 0, q0 = 0, r = 0, c = 0, si = 0, b = 0;
+
+ // Code taken from UNURAN
+ if (alpha < 1.0) {
+ gen = gs;
+ b = 1.0 + 0.36788794412*alpha; // Step 1
+ }
+ else {
+ gen = gd;
+ // Step 1. Preparations
+ ss = alpha - 0.5;
+ s_ = Math.sqrt (ss);
+ d = 5.656854249 - 12.0*s_;
+
+ // Step 4. Set-up for hat case
+ r = 1.0 / alpha;
+ q0 = ((((((((q9*r + q8)*r + q7)*r + q6)*r + q5)*r + q4)*
+ r + q3)*r + q2)*r + q1)*r;
+ if (alpha > 3.686) {
+ if (alpha > 13.022) {
+ b = 1.77;
+ si = 0.75;
+ c = 0.1515/s_;
+ }
+ else {
+ b = 1.654 + 0.0076 * ss;
+ si = 1.68/s_ + 0.275;
+ c = 0.062/s_ + 0.024;
+ }
+ }
+ else {
+ b = 0.463 + s_ - 0.178*ss;
+ si = 1.235;
+ c = 0.195/s_ - 0.079 + 0.016*s_;
+ }
+ }
+ return acceptanceRejection
+ (s, aux, alpha, 1.0/lambda, 0, gen, b, s_, ss, d, r, q0, c, si);
+ }
+
+
+ public double nextDouble() {
+ return acceptanceRejection
+ (stream, auxStream, alpha, beta, gamma, gen, b, s, ss, d, r, q0, c, si);
+ }
+
+ /**
+ * Same as <TT>nextDouble (s, s, alpha, lambda)</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda) {
+ return nextDouble (s, s, alpha, lambda);
+ }
+
+
+ private static double acceptanceRejection
+ (RandomStream stream, RandomStream auxStream,
+ double alpha, double beta, double gamma, int gen,
+ double b, double s, double ss,
+ double d, double r, double q0, double c, double si) {
+ // Code taken from UNURAN
+ double X, p, U, E;
+ double q,sign_U,t,v,w,x;
+ switch (gen) {
+ case gs:
+ while (true) {
+ p = b*stream.nextDouble();
+ stream = auxStream;
+ if (p <= 1.0) { // Step 2. Case gds <= 1
+ X = Math.exp (Math.log (p)/alpha);
+ if (Math.log (stream.nextDouble()) <= -X)
+ break;
+ }
+ else { // Step 3. Case gds > 1
+ X = -Math.log ((b - p) / alpha);
+ if ( Math.log (stream.nextDouble()) <= ((alpha - 1.0)*Math.log (X)))
+ break;
+ }
+ }
+ break;
+ case gd:
+ do {
+
+ // Step 2. Normal deviate
+ t = NormalDist.inverseF01 (stream.nextDouble());
+ stream = auxStream;
+ x = s + 0.5*t;
+ X = x*x;
+ if (t >= 0.)
+ break; // Immediate acceptance
+
+ // Step 3. Uniform random number
+ U = stream.nextDouble();
+ if (d*U <= t*t*t)
+ break; // Squeeze acceptance
+
+ // Step 5. Calculation of q
+ if (x > 0.) {
+ // Step 6.
+ v = t/(s + s);
+ if (Math.abs (v) > 0.25)
+ q = q0 - s*t + 0.25*t*t + (ss + ss)*Math.log (1. + v);
+ else
+ q = q0 + 0.5*t*t*((((((((a9*v + a8)*v + a7)*v + a6)*
+ v + a5)*v + a4)*v + a3)*v + a2)*v + a1)*v;
+ // Step 7. Quotient acceptance
+ if (Math.log (1. - U) <= q)
+ break;
+ }
+
+ // Step 8. Double exponential deviate t
+ while (true) {
+ do {
+ E = -Math.log (stream.nextDouble());
+ U = stream.nextDouble();
+ U = U + U - 1.;
+ sign_U = (U > 0) ? 1. : -1.;
+ t = b + (E*si)*sign_U;
+ } while (t <= -0.71874483771719); // Step 9. Rejection of t
+
+ // Step 10. New q(t)
+ v = t/(s + s);
+ if (Math.abs (v) > 0.25)
+ q = q0 - s*t + 0.25*t*t + (ss + ss)*Math.log (1. + v);
+ else
+ q = q0 + 0.5*t*t * ((((((((a9*v + a8)*v + a7)*v + a6)*
+ v + a5)*v + a4)*v + a3)*v + a2)*v + a1)*v;
+
+ // Step 11.
+ if (q <= 0.)
+ continue;
+
+ if (q > 0.5)
+ w = Math.exp (q) - 1.;
+ else
+ w = ((((((e7 * q + e6) * q + e5) * q + e4) * q + e3) * q + e2) *
+ q + e1) * q;
+
+ // Step 12. Hat acceptance
+ if ( c * U * sign_U <= w*Math.exp (E - 0.5*t*t)) {
+ x = s + 0.5 * t;
+ X = x * x;
+ break;
+ }
+ }
+ } while (false);
+ break;
+ default: throw new IllegalStateException();
+ }
+
+ return gamma + beta*X;
+ }
+
+ private void init() {
+ // Code taken from UNURAN
+ if (alpha < 1.0) {
+ gen = gs;
+ b = 1.0 + 0.36788794412*alpha; // Step 1
+ }
+ else {
+ gen = gd;
+ // Step 1. Preparations
+ ss = alpha - 0.5;
+ s = Math.sqrt (ss);
+ d = 5.656854249 - 12.0*s;
+
+ // Step 4. Set-up for hat case
+ r = 1.0 / alpha;
+ q0 = ((((((((q9*r + q8)*r + q7)*r + q6)*r + q5)*r + q4)*
+ r + q3)*r + q2)*r + q1)*r;
+ if (alpha > 3.686) {
+ if (alpha > 13.022) {
+ b = 1.77;
+ si = 0.75;
+ c = 0.1515/s;
+ }
+ else {
+ b = 1.654 + 0.0076 * ss;
+ si = 1.68/s + 0.275;
+ c = 0.062/s + 0.024;
+ }
+ }
+ else {
+ b = 0.463 + s - 0.178*ss;
+ si = 1.235;
+ c = 0.195/s - 0.079 + 0.016*s;
+ }
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/GammaAcceptanceRejectionGen.tex b/source/umontreal/iro/lecuyer/randvar/GammaAcceptanceRejectionGen.tex
new file mode 100644
index 0000000..6df5437
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GammaAcceptanceRejectionGen.tex
@@ -0,0 +1,368 @@
+\defmodule{GammaAcceptanceRejectionGen}
+
+This class implements {\em gamma\/} random variate generators using
+a method that combines acceptance-rejection
+with acceptance-complement\latex{, and proposed in \cite{rAHR72b,rAHR82a}}.
+It uses acceptance-rejection for $\alpha<1$ and acceptance-complement
+for $\alpha\ge 1$.
+For each gamma variate, the first uniform required is taken from the
+main stream and all additional uniforms (after the first rejection)
+are obtained from the auxiliary stream.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaAcceptanceRejectionGen
+ * Description: gamma random variate generators using a method that combines
+ acceptance-rejection with acceptance-complement
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class GammaAcceptanceRejectionGen extends GammaGen \begin{hide} {
+
+ private RandomStream auxStream;
+ private static final double q1 = 0.0416666664;
+ private static final double q2 = 0.0208333723;
+ private static final double q3 = 0.0079849875;
+ private static final double q4 = 0.0015746717;
+ private static final double q5 = -0.0003349403;
+ private static final double q6 = 0.0003340332;
+ private static final double q7 = 0.0006053049;
+ private static final double q8 = -0.0004701849;
+ private static final double q9 = 0.0001710320;
+ private static final double a1 = 0.333333333;
+ private static final double a2 = -0.249999949;
+ private static final double a3 = 0.199999867;
+ private static final double a4 = -0.166677482;
+ private static final double a5 = 0.142873973;
+ private static final double a6 = -0.124385581;
+ private static final double a7 = 0.110368310;
+ private static final double a8 = -0.112750886;
+ private static final double a9 = 0.104089866;
+ private static final double e1 = 1.000000000;
+ private static final double e2 = 0.499999994;
+ private static final double e3 = 0.166666848;
+ private static final double e4 = 0.041664508;
+ private static final double e5 = 0.008345522;
+ private static final double e6 = 0.001353826;
+ private static final double e7 = 0.000247453;
+
+ private int gen;
+ // UNURAN parameters for the distribution
+ private double beta;
+ private double gamma;
+ // Generator parameters
+ // Acceptance rejection combined with acceptance complement
+ private static final int gs = 0;
+ private static final int gd = 1;
+ private double b;
+ private double ss;
+ private double s;
+ private double d;
+ private double r;
+ private double q0;
+ private double c;
+ private double si;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaAcceptanceRejectionGen (RandomStream s, RandomStream aux,
+ double alpha, double lambda) \begin{hide} {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, lambda);
+ beta = 1.0/lambda;
+ gamma = 0.0;
+ init ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a gamma random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\lambda =$ \texttt{lambda}, using main stream \texttt{s} and
+ auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+\end{tabb}
+\begin{code}
+
+ public GammaAcceptanceRejectionGen (RandomStream s,
+ double alpha, double lambda) \begin{hide} {
+ this (s, s, alpha, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a gamma random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\lambda =$ \texttt{lambda}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public GammaAcceptanceRejectionGen (RandomStream s, RandomStream aux,
+ GammaDist dist) \begin{hide} {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ beta = 1.0/dist.getLambda();
+ gamma = 0.0;
+ init ();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator object for the gamma
+ distribution \texttt{dist}, using main stream \texttt{s} and
+ auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+ \end{tabb}
+\begin{code}
+
+ public GammaAcceptanceRejectionGen (RandomStream s, GammaDist dist) \begin{hide} {
+ this (s, s, dist);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator object for the gamma
+ distribution \texttt{dist} and stream \texttt{s} for both the main and
+ auxiliary stream.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public RandomStream getAuxStream()\begin{hide} {
+ return auxStream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the auxiliary stream associated with this object.
+\end{tabb}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, RandomStream aux,
+ double alpha, double lambda)\begin{hide} {
+ int gen = gs;
+ double s_ = 0, ss = 0, d = 0, q0 = 0, r = 0, c = 0, si = 0, b = 0;
+
+ // Code taken from UNURAN
+ if (alpha < 1.0) {
+ gen = gs;
+ b = 1.0 + 0.36788794412*alpha; // Step 1
+ }
+ else {
+ gen = gd;
+ // Step 1. Preparations
+ ss = alpha - 0.5;
+ s_ = Math.sqrt (ss);
+ d = 5.656854249 - 12.0*s_;
+
+ // Step 4. Set-up for hat case
+ r = 1.0 / alpha;
+ q0 = ((((((((q9*r + q8)*r + q7)*r + q6)*r + q5)*r + q4)*
+ r + q3)*r + q2)*r + q1)*r;
+ if (alpha > 3.686) {
+ if (alpha > 13.022) {
+ b = 1.77;
+ si = 0.75;
+ c = 0.1515/s_;
+ }
+ else {
+ b = 1.654 + 0.0076 * ss;
+ si = 1.68/s_ + 0.275;
+ c = 0.062/s_ + 0.024;
+ }
+ }
+ else {
+ b = 0.463 + s_ - 0.178*ss;
+ si = 1.235;
+ c = 0.195/s_ - 0.079 + 0.016*s_;
+ }
+ }
+ return acceptanceRejection
+ (s, aux, alpha, 1.0/lambda, 0, gen, b, s_, ss, d, r, q0, c, si);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a new gamma variate with parameters
+ $\alpha = $~\texttt{alpha} and $\lambda = $~\texttt{lambda}, using
+ main stream \texttt{s} and auxiliary stream \texttt{aux}.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return acceptanceRejection
+ (stream, auxStream, alpha, beta, gamma, gen, b, s, ss, d, r, q0, c, si);
+ }\end{hide}
+
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda)\begin{hide} {
+ return nextDouble (s, s, alpha, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Same as \texttt{nextDouble (s, s, alpha, lambda)}.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ private static double acceptanceRejection
+ (RandomStream stream, RandomStream auxStream,
+ double alpha, double beta, double gamma, int gen,
+ double b, double s, double ss,
+ double d, double r, double q0, double c, double si) {
+ // Code taken from UNURAN
+ double X, p, U, E;
+ double q,sign_U,t,v,w,x;
+ switch (gen) {
+ case gs:
+ while (true) {
+ p = b*stream.nextDouble();
+ stream = auxStream;
+ if (p <= 1.0) { // Step 2. Case gds <= 1
+ X = Math.exp (Math.log (p)/alpha);
+ if (Math.log (stream.nextDouble()) <= -X)
+ break;
+ }
+ else { // Step 3. Case gds > 1
+ X = -Math.log ((b - p) / alpha);
+ if ( Math.log (stream.nextDouble()) <= ((alpha - 1.0)*Math.log (X)))
+ break;
+ }
+ }
+ break;
+ case gd:
+ do {
+
+ // Step 2. Normal deviate
+ t = NormalDist.inverseF01 (stream.nextDouble());
+ stream = auxStream;
+ x = s + 0.5*t;
+ X = x*x;
+ if (t >= 0.)
+ break; // Immediate acceptance
+
+ // Step 3. Uniform random number
+ U = stream.nextDouble();
+ if (d*U <= t*t*t)
+ break; // Squeeze acceptance
+
+ // Step 5. Calculation of q
+ if (x > 0.) {
+ // Step 6.
+ v = t/(s + s);
+ if (Math.abs (v) > 0.25)
+ q = q0 - s*t + 0.25*t*t + (ss + ss)*Math.log (1. + v);
+ else
+ q = q0 + 0.5*t*t*((((((((a9*v + a8)*v + a7)*v + a6)*
+ v + a5)*v + a4)*v + a3)*v + a2)*v + a1)*v;
+ // Step 7. Quotient acceptance
+ if (Math.log (1. - U) <= q)
+ break;
+ }
+
+ // Step 8. Double exponential deviate t
+ while (true) {
+ do {
+ E = -Math.log (stream.nextDouble());
+ U = stream.nextDouble();
+ U = U + U - 1.;
+ sign_U = (U > 0) ? 1. : -1.;
+ t = b + (E*si)*sign_U;
+ } while (t <= -0.71874483771719); // Step 9. Rejection of t
+
+ // Step 10. New q(t)
+ v = t/(s + s);
+ if (Math.abs (v) > 0.25)
+ q = q0 - s*t + 0.25*t*t + (ss + ss)*Math.log (1. + v);
+ else
+ q = q0 + 0.5*t*t * ((((((((a9*v + a8)*v + a7)*v + a6)*
+ v + a5)*v + a4)*v + a3)*v + a2)*v + a1)*v;
+
+ // Step 11.
+ if (q <= 0.)
+ continue;
+
+ if (q > 0.5)
+ w = Math.exp (q) - 1.;
+ else
+ w = ((((((e7 * q + e6) * q + e5) * q + e4) * q + e3) * q + e2) *
+ q + e1) * q;
+
+ // Step 12. Hat acceptance
+ if ( c * U * sign_U <= w*Math.exp (E - 0.5*t*t)) {
+ x = s + 0.5 * t;
+ X = x * x;
+ break;
+ }
+ }
+ } while (false);
+ break;
+ default: throw new IllegalStateException();
+ }
+
+ return gamma + beta*X;
+ }
+
+ private void init() {
+ // Code taken from UNURAN
+ if (alpha < 1.0) {
+ gen = gs;
+ b = 1.0 + 0.36788794412*alpha; // Step 1
+ }
+ else {
+ gen = gd;
+ // Step 1. Preparations
+ ss = alpha - 0.5;
+ s = Math.sqrt (ss);
+ d = 5.656854249 - 12.0*s;
+
+ // Step 4. Set-up for hat case
+ r = 1.0 / alpha;
+ q0 = ((((((((q9*r + q8)*r + q7)*r + q6)*r + q5)*r + q4)*
+ r + q3)*r + q2)*r + q1)*r;
+ if (alpha > 3.686) {
+ if (alpha > 13.022) {
+ b = 1.77;
+ si = 0.75;
+ c = 0.1515/s;
+ }
+ else {
+ b = 1.654 + 0.0076 * ss;
+ si = 1.68/s + 0.275;
+ c = 0.062/s + 0.024;
+ }
+ }
+ else {
+ b = 0.463 + s - 0.178*ss;
+ si = 1.235;
+ c = 0.195/s - 0.079 + 0.016*s;
+ }
+ }
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/GammaGen.java b/source/umontreal/iro/lecuyer/randvar/GammaGen.java
new file mode 100644
index 0000000..72305eb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GammaGen.java
@@ -0,0 +1,139 @@
+
+
+/*
+ * Class: GammaGen
+ * Description: random variate generators for the gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the <EM>gamma</EM>
+ * distribution. Its parameters are <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>λ</I><SUP><I>α</I></SUP><I>x</I><SUP><I>α</I>-1</SUP><I>e</I><SUP>-<I>λ</I>x</SUP>/<I>Γ</I>(<I>α</I>) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>Γ</I>(<I>α</I>) = ∫<SUB>0</SUB><SUP>∞</SUP><I>x</I><SUP><I>α</I>-1</SUP><I>e</I><SUP>-x</SUP> <I>dx</I>.
+ * </DIV><P></P>
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class GammaGen extends RandomVariateGen {
+ protected double alpha = -1.0;
+ protected double lambda = -1.0;
+
+
+
+
+ /**
+ * Creates a gamma random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public GammaGen (RandomStream s, double alpha, double lambda) {
+ super (s, new GammaDist(alpha, lambda));
+ setParams (alpha, lambda);
+ }
+
+
+ /**
+ * Creates a gamma random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and
+ * <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public GammaGen (RandomStream s, double alpha) {
+ this (s, alpha, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator object for the gamma
+ * distribution <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public GammaGen (RandomStream s, GammaDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ }
+
+
+ /**
+ * Generates a new gamma random variate
+ * with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double alpha, double lambda) {
+ return GammaDist.inverseF (alpha, lambda, 15, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.lambda = lambda;
+ this.alpha = alpha;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/GammaGen.tex b/source/umontreal/iro/lecuyer/randvar/GammaGen.tex
new file mode 100644
index 0000000..c52560b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GammaGen.tex
@@ -0,0 +1,139 @@
+\defmodule{GammaGen}
+
+This class implements random variate generators for the {\em gamma\/}
+distribution. Its parameters are $\alpha>0$ and $\lambda>0$.
+Its density function is
+\eq
+ f(x) = \lambda^\alpha x^{\alpha - 1}e^{-\lambda x} / \Gamma(\alpha)
+ \qquad\mbox{ for } x>0, \eqlabel{eq:fgamma}
+\endeq
+where $\Gamma$ is the gamma function defined by
+\eq
+ \Gamma (\alpha) = \int_0^\infty x^{\alpha - 1} e^{-x}\ dx.
+ \eqlabel{eq:Gamma}
+\endeq
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaGen
+ * Description: random variate generators for the gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class GammaGen extends RandomVariateGen \begin{hide} {
+ protected double alpha = -1.0;
+ protected double lambda = -1.0;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaGen (RandomStream s, double alpha, double lambda) \begin{hide} {
+ super (s, new GammaDist(alpha, lambda));
+ setParams (alpha, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a gamma random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\lambda $ = \texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public GammaGen (RandomStream s, double alpha) \begin{hide} {
+ this (s, alpha, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a gamma random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\lambda = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public GammaGen (RandomStream s, GammaDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator object for the gamma
+ distribution \texttt{dist} and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double lambda)\begin{hide} {
+ return GammaDist.inverseF (alpha, lambda, 15, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a new gamma random variate
+ with parameters $\alpha = $~\texttt{alpha} and $\lambda = $~\texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\lambda$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ this.lambda = lambda;
+ this.alpha = alpha;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $\alpha$ and $\lambda$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/GammaRejectionLoglogisticGen.java b/source/umontreal/iro/lecuyer/randvar/GammaRejectionLoglogisticGen.java
new file mode 100644
index 0000000..9d8f581
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GammaRejectionLoglogisticGen.java
@@ -0,0 +1,190 @@
+
+
+/*
+ * Class: GammaRejectionLoglogisticGen
+ * Description: gamma random variate generators using a rejection method
+ with log-logistic envelopes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>gamma</EM> random variate generators using
+ * a rejection method with loglogistic envelopes,.
+ * For each gamma variate, the first two uniforms are taken from the
+ * main stream and all additional uniforms (after the first rejection)
+ * are obtained from the auxiliary stream.
+ *
+ */
+public class GammaRejectionLoglogisticGen extends GammaGen {
+
+ private RandomStream auxStream;
+
+ // UNURAN parameters for the distribution
+ private double beta;
+ private double gamma;
+ // Generator parameters
+ // Rejection with log-logistic envelopes
+ private double aa;
+ private double bb;
+ private double cc;
+
+
+
+ /**
+ * Creates a gamma random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using main stream <TT>s</TT> and
+ * auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public GammaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ double alpha, double lambda) {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, lambda);
+ beta = 1.0/lambda;
+ gamma = 0.0;
+ init ();
+ }
+
+
+ /**
+ * Creates a gamma random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public GammaRejectionLoglogisticGen (RandomStream s,
+ double alpha, double lambda) {
+ this (s, s, alpha, lambda);
+ }
+
+
+ /**
+ * Creates a new generator object for the gamma
+ * distribution <TT>dist</TT>, using main stream <TT>s</TT> and
+ * auxiliary stream <TT>aux</TT>.
+ * The auxiliary stream is used when a random number of uniforms
+ * is required for a rejection-type generation method.
+ *
+ */
+ public GammaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ GammaDist dist) {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ beta = 1.0/dist.getLambda();
+ gamma = 0.0;
+ init ();
+ }
+
+
+ /**
+ * Creates a new generator object for the gamma
+ * distribution <TT>dist</TT> and stream <TT>s</TT> for both the main and
+ * auxiliary stream.
+ *
+ */
+ public GammaRejectionLoglogisticGen (RandomStream s, GammaDist dist) {
+ this (s, s, dist);
+ }
+
+
+ /**
+ * Returns the auxiliary stream associated with this object.
+ *
+ */
+ public RandomStream getAuxStream() {
+ return auxStream;
+ }
+
+
+ public double nextDouble() {
+ return rejectionLogLogistic
+ (stream, auxStream, alpha, beta, gamma, aa, bb, cc);
+ }
+
+ /**
+ * Generates a new gamma variate with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using
+ * main stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, RandomStream aux,
+ double alpha, double lambda) {
+ double aa, bb, cc;
+
+ // Code taken from UNURAN
+ aa = (alpha > 1.0) ? Math.sqrt (alpha + alpha - 1.0) : alpha;
+ bb = alpha - 1.386294361;
+ cc = alpha + aa;
+
+ return rejectionLogLogistic (s, aux, alpha, 1.0/lambda, 0.0, aa, bb, cc);
+ }
+
+
+ /**
+ * Same as {@link #nextDouble nextDouble} <TT>(s, s, alpha, lambda)</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda) {
+ return nextDouble (s, s, alpha, lambda);
+ }
+
+
+ private static double rejectionLogLogistic
+ (RandomStream stream, RandomStream auxStream,
+ double alpha, double beta, double gamma,
+ double aa, double bb, double cc) {
+ // Code taken from UNURAN
+ double X;
+ double u1,u2,v,r,z;
+
+ while (true) {
+ u1 = stream.nextDouble();
+ u2 = stream.nextDouble();
+ stream = auxStream;
+ v = Math.log (u1/(1.0 - u1))/aa;
+ X = alpha*Math.exp (v);
+ r = bb + cc*v - X;
+ z = u1*u1*u2;
+ if (r + 2.504077397 >= 4.5*z) break;
+ if (r >= Math.log (z)) break;
+ }
+
+ return gamma + beta*X;
+ }
+
+ private void init() {
+ // Code taken from UNURAN
+ aa = (alpha > 1.0) ? Math.sqrt (alpha + alpha - 1.0) : alpha;
+ bb = alpha - 1.386294361;
+ cc = alpha + aa;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/GammaRejectionLoglogisticGen.tex b/source/umontreal/iro/lecuyer/randvar/GammaRejectionLoglogisticGen.tex
new file mode 100644
index 0000000..1735058
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GammaRejectionLoglogisticGen.tex
@@ -0,0 +1,194 @@
+\defmodule{GammaRejectionLoglogisticGen}
+
+This class implements {\em gamma\/} random variate generators using
+ a rejection method with loglogistic envelopes,\latex{ from \cite{rCHE77a}}.
+For each gamma variate, the first two uniforms are taken from the
+main stream and all additional uniforms (after the first rejection)
+are obtained from the auxiliary stream.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaRejectionLoglogisticGen
+ * Description: gamma random variate generators using a rejection method
+ with log-logistic envelopes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class GammaRejectionLoglogisticGen extends GammaGen \begin{hide} {
+
+ private RandomStream auxStream;
+
+ // UNURAN parameters for the distribution
+ private double beta;
+ private double gamma;
+ // Generator parameters
+ // Rejection with log-logistic envelopes
+ private double aa;
+ private double bb;
+ private double cc;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ double alpha, double lambda) \begin{hide} {
+ super (s, null);
+ auxStream = aux;
+ setParams (alpha, lambda);
+ beta = 1.0/lambda;
+ gamma = 0.0;
+ init ();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a gamma random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\lambda =$ \texttt{lambda}, using main stream \texttt{s} and
+ auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+\end{tabb}
+\begin{code}
+
+ public GammaRejectionLoglogisticGen (RandomStream s,
+ double alpha, double lambda) \begin{hide} {
+ this (s, s, alpha, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a gamma random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\lambda =$ \texttt{lambda}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public GammaRejectionLoglogisticGen (RandomStream s, RandomStream aux,
+ GammaDist dist) \begin{hide} {
+ super (s, dist);
+ auxStream = aux;
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ beta = 1.0/dist.getLambda();
+ gamma = 0.0;
+ init ();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator object for the gamma
+ distribution \texttt{dist}, using main stream \texttt{s} and
+ auxiliary stream \texttt{aux}.
+ The auxiliary stream is used when a random number of uniforms
+ is required for a rejection-type generation method.
+ \end{tabb}
+\begin{code}
+
+ public GammaRejectionLoglogisticGen (RandomStream s, GammaDist dist) \begin{hide} {
+ this (s, s, dist);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator object for the gamma
+ distribution \texttt{dist} and stream \texttt{s} for both the main and
+ auxiliary stream.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public RandomStream getAuxStream()\begin{hide} {
+ return auxStream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the auxiliary stream associated with this object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return rejectionLogLogistic
+ (stream, auxStream, alpha, beta, gamma, aa, bb, cc);
+ }\end{hide}
+
+ public static double nextDouble (RandomStream s, RandomStream aux,
+ double alpha, double lambda)\begin{hide} {
+ double aa, bb, cc;
+
+ // Code taken from UNURAN
+ aa = (alpha > 1.0) ? Math.sqrt (alpha + alpha - 1.0) : alpha;
+ bb = alpha - 1.386294361;
+ cc = alpha + aa;
+
+ return rejectionLogLogistic (s, aux, alpha, 1.0/lambda, 0.0, aa, bb, cc);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a new gamma variate with parameters
+ $\alpha = $~\texttt{alpha} and $\lambda = $~\texttt{lambda}, using
+ main stream \texttt{s} and auxiliary stream \texttt{aux}.
+ \end{tabb}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda)\begin{hide} {
+ return nextDouble (s, s, alpha, lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Same as \method{nextDouble}{}~\texttt{(s, s, alpha, lambda)}.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ private static double rejectionLogLogistic
+ (RandomStream stream, RandomStream auxStream,
+ double alpha, double beta, double gamma,
+ double aa, double bb, double cc) {
+ // Code taken from UNURAN
+ double X;
+ double u1,u2,v,r,z;
+
+ while (true) {
+ u1 = stream.nextDouble();
+ u2 = stream.nextDouble();
+ stream = auxStream;
+ v = Math.log (u1/(1.0 - u1))/aa;
+ X = alpha*Math.exp (v);
+ r = bb + cc*v - X;
+ z = u1*u1*u2;
+ if (r + 2.504077397 >= 4.5*z) break;
+ if (r >= Math.log (z)) break;
+ }
+
+ return gamma + beta*X;
+ }
+
+ private void init() {
+ // Code taken from UNURAN
+ aa = (alpha > 1.0) ? Math.sqrt (alpha + alpha - 1.0) : alpha;
+ bb = alpha - 1.386294361;
+ cc = alpha + aa;
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/GeometricGen.java b/source/umontreal/iro/lecuyer/randvar/GeometricGen.java
new file mode 100644
index 0000000..74a125e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GeometricGen.java
@@ -0,0 +1,107 @@
+
+
+/*
+ * Class: GeometricGen
+ * Description: random variate generator for the geometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements a random variate generator for the
+ * <EM>geometric</EM> distribution. Its has parameter <SPAN CLASS="MATH"><I>p</I></SPAN> and mass function
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = <I>p</I>(1 - <I>p</I>)<SUP>x</SUP> for <I>x</I> = 0, 1, 2,...,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN>.
+ * Random variates are generated by calling inversion on the distribution object.
+ *
+ */
+public class GeometricGen extends RandomVariateGenInt {
+ private double p;
+
+
+
+ /**
+ * Creates a geometric random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>p</I></SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public GeometricGen (RandomStream s, double p) {
+ super (s, new GeometricDist(p));
+ setParams (p);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+ public GeometricGen (RandomStream s, GeometricDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getP());
+ }
+
+
+ public int nextInt () {
+ return GeometricDist.inverseF (p, stream.nextDouble());
+ }
+
+ /**
+ * Generates a <EM>geometric</EM> random variate with parameter
+ * <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>p</TT>, using stream <TT>s</TT>, by inversion.
+ *
+ */
+ public static int nextInt (RandomStream s, double p) {
+ return GeometricDist.inverseF (p, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range [0, 1]");
+ this.p = p;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/GeometricGen.tex b/source/umontreal/iro/lecuyer/randvar/GeometricGen.tex
new file mode 100644
index 0000000..351c296
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GeometricGen.tex
@@ -0,0 +1,110 @@
+\defmodule {GeometricGen}
+
+This class implements a random variate generator for the
+{\em geometric\/} distribution. Its has parameter $p$ and mass function
+\eq
+ p(x) = p(1-p)^x \mbox{ for } x=0,1,2,\dots, \eqlabel{eq:fgeom}
+\endeq
+where $0\le p\le 1$.
+Random variates are generated by calling inversion on the distribution object.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GeometricGen
+ * Description: random variate generator for the geometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class GeometricGen extends RandomVariateGenInt \begin{hide} {
+ private double p;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GeometricGen (RandomStream s, double p) \begin{hide} {
+ super (s, new GeometricDist(p));
+ setParams (p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a geometric random variate generator with
+ parameter $p$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public GeometricGen (RandomStream s, GeometricDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getP());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist}, using
+ stream \texttt{s}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public int nextInt () {
+ return GeometricDist.inverseF (p, stream.nextDouble());
+ }\end{hide}
+
+ public static int nextInt (RandomStream s, double p) \begin{hide} {
+ return GeometricDist.inverseF (p, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a {\em geometric\/} random variate with parameter
+ $p = $~\texttt{p}, using stream \texttt{s}, by inversion.
+\end{tabb}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in range [0, 1]");
+ this.p = p;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $n$ and $p$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/GumbelGen.java b/source/umontreal/iro/lecuyer/randvar/GumbelGen.java
new file mode 100644
index 0000000..442dafa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GumbelGen.java
@@ -0,0 +1,125 @@
+
+
+/*
+ * Class: GumbelGen
+ * Description: generator of random variates from the Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <EM>Gumbel</EM> distribution. Its density is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>e</I><SUP>-z</SUP><I>e</I><SUP>-e<SUP>-z</SUP></SUP>/| <I>β</I>|, for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * where we use the notation
+ * <SPAN CLASS="MATH"><I>z</I> = (<I>x</I> - <I>δ</I>)/<I>β</I></SPAN>. The scale parameter <SPAN CLASS="MATH"><I>β</I></SPAN>
+ * can be positive (for the Gumbel distribution) or negative (for the reverse
+ * Gumbel distribution), but not 0.
+ *
+ */
+public class GumbelGen extends RandomVariateGen {
+ private double delta;
+ private double beta;
+
+
+ /**
+ * Creates a Gumbel random number generator with
+ * <SPAN CLASS="MATH"><I>β</I> = 1</SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> = 0</SPAN> using stream <TT>s</TT>.
+ *
+ */
+ public GumbelGen (RandomStream s) {
+ this (s, 1.0, 0.0);
+ }
+
+
+ /**
+ * Creates a Gumbel random number generator with parameters
+ * <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT> using stream <TT>s</TT>.
+ *
+ */
+ public GumbelGen (RandomStream s, double beta, double delta) {
+ super (s, new GumbelDist(beta, delta));
+ setParams (beta, delta);
+ }
+
+
+ /**
+ * Creates a new generator for the Gumbel distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public GumbelGen (RandomStream s, GumbelDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getBeta(), dist.getDelta());
+ }
+
+
+ /**
+ * Generates a new variate from the Gumbel distribution with parameters
+ * <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT> and <SPAN CLASS="MATH"><I>δ</I> =</SPAN> <TT>delta</TT> using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double beta, double delta) {
+ return GumbelDist.inverseF (beta, delta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>β</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double beta, double delta) {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+ this.delta = delta;
+ this.beta = beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/GumbelGen.tex b/source/umontreal/iro/lecuyer/randvar/GumbelGen.tex
new file mode 100644
index 0000000..413b34c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/GumbelGen.tex
@@ -0,0 +1,134 @@
+\defmodule {GumbelGen}
+
+This class implements methods for generating random variates from the
+{\em Gumbel\/} distribution. Its density is given by
+\eq
+ \begin{htmlonly}
+f (x) = e^{-z} e^{-e^{-z}}/|\beta|,
+ \qquad \mbox{for } -\infty < x < \infty.
+\end{htmlonly}
+\begin{latexonly}
+f (x) = \frac{e^{-z} e^{-e^{-z}}}{|\beta|},
+ \qquad \mbox{for } -\infty < x < \infty,
+\end{latexonly}
+ \eqlabel{eq:densgumbel}
+ \endeq
+where we use the notation $z = (x-\delta)/\beta$. The scale parameter $\beta$
+can be positive (for the Gumbel distribution) or negative (for the reverse
+Gumbel distribution), but not 0.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GumbelGen
+ * Description: generator of random variates from the Gumbel distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class GumbelGen extends RandomVariateGen \begin{hide} {
+ private double delta;
+ private double beta;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GumbelGen (RandomStream s) \begin{hide} {
+ this (s, 1.0, 0.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Gumbel random number generator with
+ $\beta = 1$ and $\delta = 0$ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public GumbelGen (RandomStream s, double beta, double delta) \begin{hide} {
+ super (s, new GumbelDist(beta, delta));
+ setParams (beta, delta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Gumbel random number generator with parameters
+$\beta$ = \texttt{beta} and $\delta$ = \texttt{delta} using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public GumbelGen (RandomStream s, GumbelDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getBeta(), dist.getDelta());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the Gumbel distribution \texttt{dist}
+ and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double beta, double delta)\begin{hide} {
+ return GumbelDist.inverseF (beta, delta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the Gumbel distribution with parameters
+ $\beta = $~\texttt{beta} and $\delta = $~\texttt{delta} using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\delta$.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double beta, double delta) {
+ if (beta == 0.0)
+ throw new IllegalArgumentException ("beta = 0");
+ this.delta = delta;
+ this.beta = beta;
+ }
+\end{code}
+\begin{tabb}
+ Sets the parameters $\beta$ and $\delta$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/HalfNormalGen.java b/source/umontreal/iro/lecuyer/randvar/HalfNormalGen.java
new file mode 100644
index 0000000..52818f7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HalfNormalGen.java
@@ -0,0 +1,122 @@
+
+
+/*
+ * Class: HalfNormalGen
+ * Description: generator of random variates from the half-normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <EM>half-normal</EM> distribution with parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = ((2/π)<SUP>1/2</SUP>/<I>σ</I>)<I>e</I><SUP>-(x-<I>μ</I>)<SUP>2</SUP>/(2<I>σ</I><SUP>2</SUP>)</SUP>, for <I>x</I> > = <I>μ</I>,
+ * </DIV><P></P>
+ *
+ */
+public class HalfNormalGen extends RandomVariateGen {
+
+ // Distribution parameters
+ protected double mu;
+ protected double sigma;
+
+
+
+ /**
+ * Creates a new <EM>half-normal</EM> generator with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN>
+ * <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public HalfNormalGen (RandomStream s, double mu, double sigma) {
+ super (s, new HalfNormalDist (mu, sigma));
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public HalfNormalGen (RandomStream s, HalfNormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ /**
+ * Generates a variate from the <EM>half-normal</EM> distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>,
+ * using stream <TT>s</TT>.
+ *
+ * @param s the random stream
+ *
+ * @param mu the parameter mu
+ *
+ * @param sigma the parameter sigma
+ *
+ * @return Generates a variate from the <EM>HalfNormal</EM> distribution
+ *
+ */
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ return HalfNormalDist.inverseF (mu, sigma, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ * @return the parameter mu
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ * @return the parameter mu
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/HalfNormalGen.tex b/source/umontreal/iro/lecuyer/randvar/HalfNormalGen.tex
new file mode 100644
index 0000000..a1a5cf8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HalfNormalGen.tex
@@ -0,0 +1,135 @@
+\defmodule {HalfNormalGen}
+
+This class implements methods for generating random variates from the
+{\em half-normal\/} distribution with parameters $\mu$ and $\sigma > 0$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = (\sqrt{2/\pi}/\sigma) e^{-(x-\mu)^2/(2\sigma^2)},
+ \qquad \mbox {for } x >= \mu,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\begin{eqnarray*}
+ f(x) &=& \frac{1}{\sigma}\sqrt{\frac2\pi}\; e^{-(x-\mu)^2/2\sigma^2},
+ \qquad \mbox {for } x \ge \mu. \\[6pt]
+f(x) &=& 0, \qquad \mbox {for } x < \mu.
+\end{eqnarray*}
+\end{latexonly}
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: HalfNormalGen
+ * Description: generator of random variates from the half-normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class HalfNormalGen extends RandomVariateGen \begin{hide} {
+
+ // Distribution parameters
+ protected double mu;
+ protected double sigma;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public HalfNormalGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ super (s, new HalfNormalDist (mu, sigma));
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new {\em half-normal} generator with parameters $\mu =$
+ \texttt{mu} and $\sigma =$ \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public HalfNormalGen (RandomStream s, HalfNormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double mu, double sigma)\begin{hide} {
+ return HalfNormalDist.inverseF (mu, sigma, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the {\em half-normal\/} distribution with
+ parameters $\mu = $~\texttt{mu} and $\sigma = $~\texttt{sigma},
+using stream \texttt{s}.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{the random stream}
+ \param{mu}{the parameter mu}
+ \param{sigma}{the parameter sigma}
+ \return{Generates a variate from the {\em HalfNormal\/} distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the parameter mu}
+\end{htmlonly}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the parameter mu}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/HyperbolicSecantGen.java b/source/umontreal/iro/lecuyer/randvar/HyperbolicSecantGen.java
new file mode 100644
index 0000000..84928ea
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HyperbolicSecantGen.java
@@ -0,0 +1,126 @@
+
+
+/*
+ * Class: HyperbolicSecantGen
+ * Description: random variate generators for the hyperbolic secant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>hyperbolic secant</EM> distribution with location
+ * parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 1/(2<I>σ</I>) sech(<I>π</I>/2(<I>x</I> - <I>μ</I>)/<I>σ</I>), - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ *
+ */
+public class HyperbolicSecantGen extends RandomVariateGen {
+ protected double mu;
+ protected double sigma;
+
+
+
+
+ /**
+ * Creates a <EM>hyperbolic secant</EM> random variate generator
+ * with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public HyperbolicSecantGen (RandomStream s, double mu, double sigma) {
+ super (s, new HyperbolicSecantDist(mu, sigma));
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a <EM>hyperbolic secant</EM> random variate generator
+ * with parameters <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>σ</I> = 1</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public HyperbolicSecantGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public HyperbolicSecantGen (RandomStream s, HyperbolicSecantDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ /**
+ * Generates a variate from the <EM>hyperbolic secant</EM> distribution with
+ * location parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> and scale parameter <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ return HyperbolicSecantDist.inverseF (mu, sigma, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/HyperbolicSecantGen.tex b/source/umontreal/iro/lecuyer/randvar/HyperbolicSecantGen.tex
new file mode 100644
index 0000000..2c69032
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HyperbolicSecantGen.tex
@@ -0,0 +1,137 @@
+\defmodule{HyperbolicSecantGen}
+
+This class implements random variate generators for the
+ {\em hyperbolic secant\/} distribution with location
+ parameter $\mu$ and scale parameter $\sigma$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = 1/(2\sigma) \mbox{ sech}(\pi/2 (x - \mu) / \sigma),
+ \qquad -\infty <x < \infty.
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \frac{1}{2 \sigma} \mbox{ sech}\left(\frac{\pi}{2} \frac{(x - \mu)}{\sigma}\right), \qquad -\infty <x < \infty.
+\eqlabel{eq:fHyperbolicSecant}
+\endeq
+\end{latexonly}%
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: HyperbolicSecantGen
+ * Description: random variate generators for the hyperbolic secant distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class HyperbolicSecantGen extends RandomVariateGen \begin{hide} {
+ protected double mu;
+ protected double sigma;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public HyperbolicSecantGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ super (s, new HyperbolicSecantDist(mu, sigma));
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a {\em hyperbolic secant\/} random variate generator
+ with parameters $\mu=$ \texttt{mu} and $\sigma=$ \texttt{sigma},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public HyperbolicSecantGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a {\em hyperbolic secant\/} random variate generator
+ with parameters $\mu=0$ and $\sigma=1$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public HyperbolicSecantGen (RandomStream s, HyperbolicSecantDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double mu, double sigma)\begin{hide} {
+ return HyperbolicSecantDist.inverseF (mu, sigma, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the {\em hyperbolic secant\/} distribution with
+ location parameter $\mu$ and scale parameter $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0.0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+\end{code}
+\begin{tabb}
+ Sets the parameters $\mu$ and $\sigma$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/HypergeometricGen.java b/source/umontreal/iro/lecuyer/randvar/HypergeometricGen.java
new file mode 100644
index 0000000..b9bbc28
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HypergeometricGen.java
@@ -0,0 +1,135 @@
+
+
+/*
+ * Class: HypergeometricGen
+ * Description: random variate generators for the hypergeometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>hypergeometric</EM> distribution. Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = nCr(<I>m</I>, <I>x</I>)nCr(<I>l</I> - <I>m</I>, <I>k</I> - <I>x</I>)/nCr(<I>l</I>, <I>k</I>) for <I>x</I> = max(0, <I>k</I> - <I>l</I> + <I>m</I>),..., min(<I>k</I>, <I>m</I>)
+ * </DIV><P></P>
+ * where nCr<SPAN CLASS="MATH">(<I>n</I>, <I>x</I>)</SPAN> is the number of possible combinations when choosing
+ * <SPAN CLASS="MATH"><I>x</I></SPAN> elements among a set of <SPAN CLASS="MATH"><I>n</I></SPAN> elements,
+ * <SPAN CLASS="MATH"><I>m</I></SPAN>, <SPAN CLASS="MATH"><I>l</I></SPAN> and <SPAN CLASS="MATH"><I>k</I></SPAN> are integers that satisfy <SPAN CLASS="MATH">0 < <I>m</I> <= <I>l</I></SPAN> and
+ * <SPAN CLASS="MATH">0 < <I>k</I> <= <I>l</I></SPAN>.
+ *
+ * <P>
+ * The generation method is inversion using the chop-down algorithm
+ *
+ */
+public class HypergeometricGen extends RandomVariateGenInt {
+ private int m;
+ private int l;
+ private int k;
+
+
+
+ /**
+ * Creates a hypergeometric generator with
+ * parameters <SPAN CLASS="MATH"><I>m</I> =</SPAN> <TT>m</TT>, <SPAN CLASS="MATH"><I>l</I> =</SPAN> <TT>l</TT> and <SPAN CLASS="MATH"><I>k</I> =</SPAN> <TT>k</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public HypergeometricGen (RandomStream s, int m, int l, int k) {
+ super (s, new HypergeometricDist (m, l, k));
+ setParams (m, l, k);
+ }
+
+
+ /**
+ * Creates a new generator for distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public HypergeometricGen (RandomStream s, HypergeometricDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getM(), dist.getL(), dist.getK());
+ }
+
+
+ /**
+ * Generates a new variate from the <EM>hypergeometric</EM> distribution with
+ * parameters <SPAN CLASS="MATH"><I>m</I> =</SPAN> <TT>m</TT>, <SPAN CLASS="MATH"><I>l</I> =</SPAN> <TT>l</TT> and <SPAN CLASS="MATH"><I>k</I> =</SPAN> <TT>k</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public static int nextInt (RandomStream s, int m, int l, int k) {
+ return HypergeometricDist.inverseF (m, l, k, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>m</I></SPAN> associated with this object.
+ *
+ */
+ public int getM() {
+ return m;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>l</I></SPAN> associated with this object.
+ *
+ */
+ public int getL() {
+ return l;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>k</I></SPAN> associated with this object.
+ *
+ *
+ */
+ public int getK() {
+ return k;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (int m, int l, int k) {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1<=m<l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1<=k<l");
+ this.m = m;
+ this.l = l;
+ this.k = k;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/HypergeometricGen.tex b/source/umontreal/iro/lecuyer/randvar/HypergeometricGen.tex
new file mode 100644
index 0000000..85ef110
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HypergeometricGen.tex
@@ -0,0 +1,155 @@
+\defmodule {HypergeometricGen}
+
+This class implements random variate generators for the
+{\em hypergeometric\/} distribution. Its mass function is
+\begin{htmlonly}
+\eq
+ p(x) =
+ \mbox{nCr}(m, x) \mbox{nCr}(l - m, k-x)/\mbox{nCr}(l, k)
+ \qquad \mbox {for }
+ x=\max(0,k-l+m), \dots, \min(k, m)
+\endeq
+where nCr$(n,x)$ is the number of possible combinations when choosing
+$x$ elements among a set of $n$ elements,
+\end{htmlonly}
+\begin{latexonly}
+(see, e.g., \cite[page 101]{rGEN98a})
+\eq
+ p(x) =
+ \frac{ {m \choose x} {l - m\choose k-x}}{{l \choose k}}
+ \qquad \mbox {for }
+ x=\max(0,k-l+m), \dots, \min(k, m), \eqlabel{eq:fheperg}
+\endeq
+where
+\end{latexonly}
+$m$, $l$ and $k$ are integers that satisfy $0< m\le l$ and $0 < k\le l$.
+
+The generation method is inversion using the chop-down algorithm \cite{sKAC85a}
+
+\bigskip\hrule
+
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: HypergeometricGen
+ * Description: random variate generators for the hypergeometric distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class HypergeometricGen extends RandomVariateGenInt \begin{hide} {
+ private int m;
+ private int l;
+ private int k;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public HypergeometricGen (RandomStream s, int m, int l, int k) \begin{hide} {
+ super (s, new HypergeometricDist (m, l, k));
+ setParams (m, l, k);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a hypergeometric generator with
+ parameters $m = $~\texttt{m}, $l = $~\texttt{l} and $k = $~\texttt{k},
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public HypergeometricGen (RandomStream s, HypergeometricDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getM(), dist.getL(), dist.getK());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static int nextInt (RandomStream s, int m, int l, int k)\begin{hide} {
+ return HypergeometricDist.inverseF (m, l, k, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the {\em hypergeometric\/} distribution with
+ parameters $m = $~\texttt{m}, $l = $~\texttt{l} and $k = $~\texttt{k},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public int getM()\begin{hide} {
+ return m;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $m$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public int getL()\begin{hide} {
+ return l;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $l$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public int getK()\begin{hide} {
+ return k;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $k$ associated with this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int m, int l, int k) {
+ if (l <= 0)
+ throw new IllegalArgumentException ("l must be greater than 0");
+ if (m <= 0 || m > l)
+ throw new IllegalArgumentException ("m is invalid: 1<=m<l");
+ if (k <= 0 || k > l)
+ throw new IllegalArgumentException ("k is invalid: 1<=k<l");
+ this.m = m;
+ this.l = l;
+ this.k = k;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $n$ and $p$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/HypoExponentialGen.java b/source/umontreal/iro/lecuyer/randvar/HypoExponentialGen.java
new file mode 100644
index 0000000..5db1cef
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HypoExponentialGen.java
@@ -0,0 +1,103 @@
+
+
+/*
+ * Class: HypoExponentialGen
+ * Description: random variate generators for the hypoexponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <SPAN CLASS="textit">hypoexponential</SPAN> distribution (see classes
+ * {@link umontreal.iro.lecuyer.probdist.HypoExponentialDist HypoExponentialDist} and
+ * {@link umontreal.iro.lecuyer.probdist.HypoExponentialDistQuick HypoExponentialDistQuick}
+ * in package <TT>probdist</TT> for the definition).
+ *
+ */
+public class HypoExponentialGen extends RandomVariateGen {
+
+
+ /**
+ * Creates a hypoexponential random variate generator with
+ * rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>,
+ * using stream <TT>stream</TT>.
+ *
+ */
+ public HypoExponentialGen (RandomStream stream, double[] lambda) {
+ super (stream, new HypoExponentialDist(lambda));
+ }
+
+
+ /**
+ * Creates a new generator for the hypoexponential
+ * distribution <TT>dist</TT> with stream <TT>stream</TT>.
+ *
+ */
+ public HypoExponentialGen (RandomStream stream, HypoExponentialDist dist) {
+ super (stream, dist);
+// if (dist != null)
+// setParams (dist.getLambda());
+ }
+
+
+ /**
+ * Uses inversion to generate a new hypoexponential variate
+ * with rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lambda[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>,
+ * using stream <TT>stream</TT>. The inversion uses a root-finding method
+ * and is very slow.
+ *
+ */
+ public static double nextDouble (RandomStream stream, double[] lambda) {
+ return HypoExponentialDist.inverseF (lambda, stream.nextDouble());
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB></SPAN> associated with this object.
+ *
+ */
+ public double[] getLambda() {
+ return ((HypoExponentialDist)dist).getLambda();
+ }
+
+
+
+ /**
+ * Sets the rates
+ * <SPAN CLASS="MATH"><I>λ</I><SUB>i</SUB> =</SPAN> <TT>lam[<SPAN CLASS="MATH"><I>i</I> - 1</SPAN>]</TT>,
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN> of this object.
+ *
+ */
+ public void setLambda (double[] lambda) {
+ ((HypoExponentialDist)dist).setLambda(lambda);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/HypoExponentialGen.tex b/source/umontreal/iro/lecuyer/randvar/HypoExponentialGen.tex
new file mode 100644
index 0000000..ee253b3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/HypoExponentialGen.tex
@@ -0,0 +1,102 @@
+\defmodule {HypoExponentialGen}
+
+This class implements random variate generators for the
+\emph{hypoexponential} distribution (see classes
+\externalclass{umontreal.iro.lecuyer.probdist}{HypoExponentialDist} and
+\externalclass{umontreal.iro.lecuyer.probdist}{HypoExponentialDistQuick}
+in package \texttt{probdist} for the definition).
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: HypoExponentialGen
+ * Description: random variate generators for the hypoexponential distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class HypoExponentialGen extends RandomVariateGen \begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public HypoExponentialGen (RandomStream stream, double[] lambda) \begin{hide} {
+ super (stream, new HypoExponentialDist(lambda));
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a hypoexponential random variate generator with
+ rates $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$,
+ using stream \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public HypoExponentialGen (RandomStream stream, HypoExponentialDist dist) \begin{hide} {
+ super (stream, dist);
+// if (dist != null)
+// setParams (dist.getLambda());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the hypoexponential
+ distribution \texttt{dist} with stream \texttt{stream}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream stream, double[] lambda) \begin{hide} {
+ return HypoExponentialDist.inverseF (lambda, stream.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Uses inversion to generate a new hypoexponential variate
+ with rates $\lambda_i = $ \texttt{lambda[$i-1$]}, $i = 1,\ldots,k$,
+ using stream \texttt{stream}. The inversion uses a root-finding method
+ and is very slow.
+ \end{tabb}
+\begin{code}
+
+ public double[] getLambda()\begin{hide} {
+ return ((HypoExponentialDist)dist).getLambda();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\lambda_i$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public void setLambda (double[] lambda)\begin{hide} {
+ ((HypoExponentialDist)dist).setLambda(lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the rates $\lambda_i = $ \texttt{lam[$i-1$]},
+$i = 1,\ldots,k$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseFromDensityGen.java b/source/umontreal/iro/lecuyer/randvar/InverseFromDensityGen.java
new file mode 100644
index 0000000..e470d96
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseFromDensityGen.java
@@ -0,0 +1,308 @@
+
+
+/*
+ * Class: InverseFromDensityGen
+ * Description: generator of random variates by numerical inversion of
+ an arbitrary continuous distribution when only the
+ probability density is known
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+ import umontreal.iro.lecuyer.functions.MathFunction;
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;
+ import umontreal.iro.lecuyer.probdist.InverseDistFromDensity;
+
+
+
+/**
+ * Implements a method for generating random variates by numerical inversion of
+ * an <SPAN CLASS="textit">arbitrary continuous</SPAN> distribution when only the probability density
+ * is known. The cumulative probabilities (cdf) are pre-computed by
+ * numerical quadrature of the
+ * density using Gauss-Lobatto integration over suitably small intervals to
+ * satisfy the required precision, and these values are kept in tables. Then the
+ * algorithm uses polynomial interpolation over the tabulated values to get
+ * the inverse cdf. The user can select the
+ * desired precision and the degree of the interpolating polynomials.
+ *
+ * <P>
+ * The algorithm may fail for some distributions for which the density
+ * becomes infinite at a point (for ex. the Gamma and the Beta distributions
+ * with
+ * <SPAN CLASS="MATH"><I>α</I> < 1</SPAN>) if one requires too high a precision
+ * (a too small <TT>eps</TT>, for ex.
+ * <SPAN CLASS="MATH"><I>ε</I>∼10<SUP>-15</SUP></SPAN>).
+ * However, it should work also for continuous densities with finite discontinuities.
+ *
+ * <P>
+ * While the setup time is relatively slow, the generation of random variables
+ * is extremely fast and practically independent of the required precision
+ * and of the specific distribution. The following table shows the time needed
+ * (in seconds) to generate <SPAN CLASS="MATH">10<SUP>8</SUP></SPAN> random numbers using inversion
+ * from a given class, then the numerical inversion with Gauss-Lobatto integration
+ * implemented here, and finally the speed ratios between the two methods.
+ * The speed ratio is the speed of the latter over the former.
+ * Thus for the beta distribution with parameters (5, 500), generating random
+ * variables with the Gauss-Lobatto integration implemented in this class is
+ * more than 1700
+ * times faster than using inversion from the <TT>BetaDist</TT> class.
+ * These tests were made on a machine with processor AMD Athlon 4000, running
+ * Red Hat Linux, with clock speed at 2403 MHz.
+ *
+ * <P>
+ * <DIV ALIGN="CENTER">
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="LEFT">Distribution</TD>
+ * <TD ALIGN="CENTER">Inversion</TD>
+ * <TD ALIGN="CENTER">Gauss-Lobatto</TD>
+ * <TD ALIGN="CENTER">speed ratio</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">9.19</TD>
+ * <TD ALIGN="CENTER">8.89</TD>
+ * <TD ALIGN="CENTER">1.03</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>ExponentialDist(5)</TT></TD>
+ * <TD ALIGN="CENTER">17.72</TD>
+ * <TD ALIGN="CENTER">8.82</TD>
+ * <TD ALIGN="CENTER">2.0</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>CauchyDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">18.30</TD>
+ * <TD ALIGN="CENTER">8.81</TD>
+ * <TD ALIGN="CENTER">2.1</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>BetaSymmetricalDist(10.5)</TT></TD>
+ * <TD ALIGN="CENTER">242.80</TD>
+ * <TD ALIGN="CENTER">8.85</TD>
+ * <TD ALIGN="CENTER">27.4</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>GammaDist(55)</TT></TD>
+ * <TD ALIGN="CENTER">899.50</TD>
+ * <TD ALIGN="CENTER">8.89</TD>
+ * <TD ALIGN="CENTER">101</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>ChiSquareNoncentralDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">5326.90</TD>
+ * <TD ALIGN="CENTER">8.85</TD>
+ * <TD ALIGN="CENTER">602</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>BetaDist(5, 500)</TT></TD>
+ * <TD ALIGN="CENTER">15469.10</TD>
+ * <TD ALIGN="CENTER">8.86</TD>
+ * <TD ALIGN="CENTER">1746</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ *
+ * <P>
+ * The following table gives the time (in sec.) needed to
+ * create an object (setup time) and to generate one random variable for
+ * this class compared to the same for the inversion method specific to each class,
+ * and the ratios of the times (init + one random variable) of the two methods.
+ * For inversion, we initialized <SPAN CLASS="MATH">10<SUP>8</SUP></SPAN> times; for this class, we
+ * initialized <SPAN CLASS="MATH">10<SUP>4</SUP></SPAN> times.
+ *
+ * <P>
+ * <DIV ALIGN="CENTER">
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="LEFT">Distribution</TD>
+ * <TD ALIGN="CENTER">Inversion</TD>
+ * <TD ALIGN="CENTER">Gauss-Lobatto</TD>
+ * <TD ALIGN="CENTER">time ratio</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"> </TD>
+ * <TD ALIGN="CENTER"><SPAN CLASS="MATH">10<SUP>8</SUP></SPAN> init</TD>
+ * <TD ALIGN="CENTER"><SPAN CLASS="MATH">10<SUP>4</SUP></SPAN> init</TD>
+ * <TD ALIGN="CENTER">for 1 init</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">5.30</TD>
+ * <TD ALIGN="CENTER">38.29</TD>
+ * <TD ALIGN="CENTER">26426</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>ExponentialDist(5)</TT></TD>
+ * <TD ALIGN="CENTER">3.98</TD>
+ * <TD ALIGN="CENTER">27.05</TD>
+ * <TD ALIGN="CENTER">12466</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>CauchyDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">5.05</TD>
+ * <TD ALIGN="CENTER">58.39</TD>
+ * <TD ALIGN="CENTER">25007</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>BetaSymmetricalDist(10.5)</TT></TD>
+ * <TD ALIGN="CENTER">90.66</TD>
+ * <TD ALIGN="CENTER">68.33</TD>
+ * <TD ALIGN="CENTER">2049</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>GammaDist(55)</TT></TD>
+ * <TD ALIGN="CENTER">13.15</TD>
+ * <TD ALIGN="CENTER">58.34</TD>
+ * <TD ALIGN="CENTER">639</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>ChiSquareNoncentralDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">190.48</TD>
+ * <TD ALIGN="CENTER">248.98</TD>
+ * <TD ALIGN="CENTER">451</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>BetaDist(5, 500)</TT></TD>
+ * <TD ALIGN="CENTER">63.60</TD>
+ * <TD ALIGN="CENTER">116.57</TD>
+ * <TD ALIGN="CENTER">75</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ *
+ * <P>
+ * If only a few random variables are needed, then using this class
+ * is not efficient because of the slow set-up. But if one wants to generate
+ * large samples from the same distribution with fixed parameters, then
+ * this class will be very efficient. The following table gives the number of
+ * random variables generated beyond which, using this class will be
+ * worthwhile.
+ *
+ * <P>
+ * <DIV ALIGN="CENTER">
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="LEFT">Distribution</TD>
+ * <TD ALIGN="CENTER">number of generated variables</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">41665</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>ExponentialDist(5)</TT></TD>
+ * <TD ALIGN="CENTER">15266</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>CauchyDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">31907</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>BetaSymmetricalDist(10.5)</TT></TD>
+ * <TD ALIGN="CENTER">2814</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>GammaDist(55)</TT></TD>
+ * <TD ALIGN="CENTER">649</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>ChiSquareNoncentralDist(10.5, 5)</TT></TD>
+ * <TD ALIGN="CENTER">467</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>BetaDist(5, 500)</TT></TD>
+ * <TD ALIGN="CENTER">75</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ *
+ * <P>
+ * Thus, for example, if one needs to generate less than 15266 exponential
+ * random variables, then using the <TT>InverseFromDensityGen</TT> class
+ * is not wortwhile: it will be faster to use inversion from the
+ * <TT>ExponentialGen</TT> class.
+ *
+ */
+public class InverseFromDensityGen extends RandomVariateGen {
+
+
+ /**
+ * Creates a new generator for the <SPAN CLASS="textit">continuous</SPAN> distribution
+ * <TT>dis</TT>, using stream <TT>stream</TT>. <TT>dis</TT> must have a well-defined
+ * density method; its other methods are unused. For a non-standard distribution
+ * <TT>dis</TT>, the user may wish to set the left and the right boundaries
+ * between which the density is non-zero by calling methods
+ * {@link umontreal.iro.lecuyer.probdist.ContinuousDistribution#setXinf setXinf}
+ * and
+ * {@link umontreal.iro.lecuyer.probdist.ContinuousDistribution#setXsup setXsup}
+ * of <TT>dis</TT>, for better efficiency.
+ * Argument <TT>xc</TT> can be the mean,
+ * the mode or any other <SPAN CLASS="MATH"><I>x</I></SPAN> for which the density is relatively large.
+ * The <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>eps</TT> is the desired absolute error in the CDF,
+ * and <TT>order</TT> is the degree of the
+ * Newton interpolating polynomial over each interval.
+ * An <TT>order</TT> of 3 or 5, and an <TT>eps</TT> of <SPAN CLASS="MATH">10<SUP>-6</SUP></SPAN> to <SPAN CLASS="MATH">10<SUP>-12</SUP></SPAN>
+ * are usually good choices.
+ * Restrictions:
+ * <SPAN CLASS="MATH">3 <= <texttt>order</texttt> <= 12</SPAN>.
+ *
+ */
+ public InverseFromDensityGen (RandomStream stream,
+ ContinuousDistribution dis,
+ double xc, double eps, int order) {
+ super (stream, null);
+ dist = new InverseDistFromDensity (dis, xc, eps, order);
+ }
+
+
+ /**
+ * Creates a new generator from the <SPAN CLASS="textit">continuous</SPAN> probability density
+ * <TT>dens</TT>. The left and the right boundaries of the density are
+ * <TT>xleft</TT> and <TT>xright</TT> (the density is 0 outside the
+ * interval <TT>[xleft, xright]</TT>).
+ * See the description of the other constructor.
+ *
+ */
+ public InverseFromDensityGen (RandomStream stream, MathFunction dens,
+ double xc, double eps, int order,
+ double xleft, double xright) {
+ super (stream, null);
+ dist = new InverseDistFromDensity (dens, xc, eps, order, xleft, xright);
+ }
+
+
+ /**
+ * Generates a new random variate.
+ *
+ */
+ public double nextDouble() {
+ return dist.inverseF (stream.nextDouble());
+ }
+
+
+ /**
+ * Returns the <TT>xc</TT> given in the constructor.
+ *
+ */
+ public double getXc() {
+ return ((InverseDistFromDensity)dist).getXc();
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>eps</TT>.
+ *
+ */
+ public double getEpsilon() {
+ return ((InverseDistFromDensity)dist).getEpsilon();
+ }
+
+
+
+ /**
+ * Returns the order of the interpolating polynomial.
+ *
+ *
+ */
+ public int getOrder() {
+ return ((InverseDistFromDensity)dist).getOrder();
+ }
+
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseFromDensityGen.tex b/source/umontreal/iro/lecuyer/randvar/InverseFromDensityGen.tex
new file mode 100644
index 0000000..517886a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseFromDensityGen.tex
@@ -0,0 +1,232 @@
+\defmodule {InverseFromDensityGen}
+
+Implements a method for generating random variates by numerical inversion of
+an \emph{arbitrary continuous} distribution when only the probability density
+is known \cite{rDER10a}. The cumulative probabilities (cdf) are pre-computed by
+ numerical quadrature of the
+density using Gauss-Lobatto integration over suitably small intervals to
+satisfy the required precision, and these values are kept in tables. Then the
+ algorithm uses polynomial interpolation over the tabulated values to get
+ the inverse cdf. The user can select the
+ desired precision and the degree of the interpolating polynomials.
+
+The algorithm may fail for some distributions for which the density
+ becomes infinite at a point (for ex. the Gamma and the Beta distributions
+ with $\alpha < 1$) if one requires too high a precision
+(a too small \texttt{eps}, for ex. $\epsilon \sim 10^{-15}$).
+However, it should work also for continuous densities with finite discontinuities.
+
+
+While the setup time is relatively slow, the generation of random variables
+is extremely fast and practically independent of the required precision
+and of the specific distribution. The following table shows the time needed
+(in seconds) to generate $10^8$ random numbers using inversion
+from a given class, then the numerical inversion with Gauss-Lobatto integration
+implemented here, and finally the speed ratios between the two methods.
+The speed ratio is the speed of the latter over the former.
+Thus for the beta distribution with parameters (5, 500), generating random
+variables with the Gauss-Lobatto integration implemented in this class is
+more than 1700
+times faster than using inversion from the \texttt{BetaDist} class.
+These tests were made on a machine with processor AMD Athlon 4000, running
+Red Hat Linux, with clock speed at 2403 MHz.
+% \hrichard {The set-up time is not included in these ratios.}
+
+\begin{center}
+\begin{tabular}{|l|c|c|c|}
+\hline
+ Distribution & Inversion & Gauss-Lobatto & speed ratio \\
+\hline
+\texttt{NormalDist(10.5, 5)} & \phantom{1521}9.19 & 8.89 & \phantom{16222}1.03 \\
+\texttt{ExponentialDist(5)} & \phantom{152}17.72 &8.82 & \phantom{1622}2.0 \\
+\texttt{CauchyDist(10.5, 5)} & \phantom{152}18.30 & 8.81 & \phantom{1622}2.1 \\
+\texttt{BetaSymmetricalDist(10.5)} & \phantom{15}242.80 & 8.85 & \phantom{162}27.4 \\
+\texttt{GammaDist(55)} & \phantom{15}899.50 &8.89 & \phantom{1}101 \\
+\texttt{ChiSquareNoncentralDist(10.5, 5)} &\phantom{1}5326.90 &8.85 & \phantom{1}602 \\
+\texttt{BetaDist(5, 500)} & 15469.10 &8.86 & 1746 \\
+\hline
+\end{tabular}
+\end{center}
+
+The following table gives the time (in sec.) needed to
+create an object (setup time) and to generate one random variable for
+this class compared to the same for the inversion method specific to each class,
+and the ratios of the times (init + one random variable) of the two methods.
+For inversion, we initialized $10^8$ times; for this class, we
+initialized $10^4$ times.
+
+\begin{center}
+\begin{tabular}{|l|c|c|c|}
+\hline
+ Distribution &Inversion & Gauss-Lobatto & time ratio \\
+ & $10^8$ init & $10^4$ init & for 1 init \\
+\hline
+\texttt{NormalDist(10.5, 5)} &\phantom{00}5.30 &\phantom{0}38.29 & 26426 \\
+\texttt{ExponentialDist(5)} &\phantom{00}3.98 &\phantom{0}27.05 & 12466 \\
+\texttt{CauchyDist(10.5, 5)} &\phantom{00}5.05 &\phantom{0}58.39 & 25007 \\
+\texttt{BetaSymmetricalDist(10.5)} &\phantom{0}90.66 & \phantom{0}68.33 & \phantom{0}2049 \\
+\texttt{GammaDist(55)} &\phantom{0}13.15 & \phantom{0}58.34 & \phantom{00}639 \\
+\texttt{ChiSquareNoncentralDist(10.5, 5)} &190.48 & 248.98& \phantom{00}451 \\
+\texttt{BetaDist(5, 500)} &\phantom{0}63.60 & 116.57 & \phantom{000}75 \\
+\hline
+\end{tabular}
+\end{center}
+
+If only a few random variables are needed, then using this class
+is not efficient because of the slow set-up. But if one wants to generate
+large samples from the same distribution with fixed parameters, then
+this class will be very efficient. The following table gives the number of
+random variables generated beyond which, using this class will be
+worthwhile.
+
+\begin{center}
+\begin{tabular}{|l|c|}
+\hline
+ Distribution & number of generated variables \\
+\hline
+\texttt{NormalDist(10.5, 5)} & 41665 \\
+\texttt{ExponentialDist(5)} & 15266 \\
+\texttt{CauchyDist(10.5, 5)} & 31907 \\
+\texttt{BetaSymmetricalDist(10.5)} & 2814 \\
+\texttt{GammaDist(55)} & 649 \\
+\texttt{ChiSquareNoncentralDist(10.5, 5)} & 467 \\
+\texttt{BetaDist(5, 500)} & 75 \\
+\hline
+\end{tabular}
+\end{center}
+
+Thus, for example, if one needs to generate less than 15266 exponential
+random variables, then using the \texttt{InverseFromDensityGen} class
+is not wortwhile: it will be faster to use inversion from the
+\texttt{ExponentialGen} class.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseFromDensityGen
+ * Description: generator of random variates by numerical inversion of
+ an arbitrary continuous distribution when only the
+ probability density is known
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;
+ import umontreal.iro.lecuyer.functions.MathFunction;
+ import umontreal.iro.lecuyer.rng.RandomStream;
+ import umontreal.iro.lecuyer.probdist.ContinuousDistribution;\begin{hide}
+ import umontreal.iro.lecuyer.probdist.InverseDistFromDensity;
+\end{hide}
+
+
+public class InverseFromDensityGen extends RandomVariateGen \begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseFromDensityGen (RandomStream stream,
+ ContinuousDistribution dis,
+ double xc, double eps, int order) \begin{hide} {
+ super (stream, null);
+ dist = new InverseDistFromDensity (dis, xc, eps, order);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the \emph{continuous} distribution
+\texttt{dis}, using stream \texttt{stream}. \texttt{dis} must have a well-defined
+density method; its other methods are unused. For a non-standard distribution
+\texttt{dis}, the user may wish to set the left and the right boundaries
+between which the density is non-zero by calling methods
+\externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{setXinf}{}
+and
+\externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{setXsup}{}
+of \texttt{dis}, for better efficiency.
+Argument \texttt{xc} can be the mean,
+the mode or any other $x$ for which the density is relatively large.
+The $u$-resolution \texttt{eps} is the desired absolute error in the CDF,
+and \texttt{order} is the degree of the
+Newton interpolating polynomial over each interval.
+An \texttt{order} of 3 or 5, and an \texttt{eps} of $10^{-6}$ to $10^{-12}$
+are usually good choices.
+ Restrictions: $3 \le \texttt{order} \le 12$.
+\end{tabb}
+\begin{code}
+
+ public InverseFromDensityGen (RandomStream stream, MathFunction dens,
+ double xc, double eps, int order,
+ double xleft, double xright) \begin{hide} {
+ super (stream, null);
+ dist = new InverseDistFromDensity (dens, xc, eps, order, xleft, xright);
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new generator from the \emph{continuous} probability density
+\texttt{dens}. The left and the right boundaries of the density are
+\texttt{xleft} and \texttt{xright} (the density is 0 outside the
+interval \texttt{[xleft, xright]}).
+See the description of the other constructor.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public double nextDouble() \begin{hide} {
+ return dist.inverseF (stream.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a new random variate.
+ \end{tabb}
+\begin{code}
+
+ public double getXc()\begin{hide} {
+ return ((InverseDistFromDensity)dist).getXc();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{xc} given in the constructor.
+\end{tabb}
+\begin{code}
+
+ public double getEpsilon()\begin{hide} {
+ return ((InverseDistFromDensity)dist).getEpsilon();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $u$-resolution \texttt{eps}.
+\end{tabb}
+\begin{code}
+
+ public int getOrder()\begin{hide} {
+ return ((InverseDistFromDensity)dist).getOrder();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the order of the interpolating polynomial.
+\end{tabb}
+\begin{hide}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseGammaGen.java b/source/umontreal/iro/lecuyer/randvar/InverseGammaGen.java
new file mode 100644
index 0000000..748a8a9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseGammaGen.java
@@ -0,0 +1,126 @@
+
+
+/*
+ * Class: InverseGammaGen
+ * Description: random variate generators for the inverse gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for
+ * the <SPAN CLASS="textit">inverse gamma</SPAN> distribution with shape parameter
+ *
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>β</I><SUP><I>α</I></SUP>exp<SUP>-<I>β</I>/x</SUP>)/(<I>Γ</I>(<I>α</I>)<I>x</I><SUP><I>α</I>+1</SUP>) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> otherwise,
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function.
+ *
+ */
+public class InverseGammaGen extends RandomVariateGen {
+ protected double alpha;
+ protected double beta;
+
+
+
+
+ /**
+ * Creates an inverse gamma random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public InverseGammaGen (RandomStream s, double alpha, double beta) {
+ super (s, new InverseGammaDist(alpha, beta));
+ setParams(alpha, beta);
+ }
+
+
+ /**
+ * Creates an inverse gamma random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public InverseGammaGen (RandomStream s, double alpha) {
+ this (s, alpha, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public InverseGammaGen (RandomStream s, InverseGammaDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }
+
+
+ /**
+ * Generates a variate from the inverse gamma distribution
+ * with shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta) {
+ return InverseGammaDist.inverseF (alpha, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseGammaGen.tex b/source/umontreal/iro/lecuyer/randvar/InverseGammaGen.tex
new file mode 100644
index 0000000..81a6022
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseGammaGen.tex
@@ -0,0 +1,140 @@
+\defmodule{InverseGammaGen}
+
+This class implements random variate generators for
+the \emph{inverse gamma} distribution with shape parameter
+$\alpha > 0$ and scale parameter $\beta > 0$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = (\beta^{\alpha}\exp^{-\beta / x}) / (\Gamma(\alpha) x^{\alpha + 1})
+ \qquad \mbox{for } x > 0,
+\endeq
+ and $f(x) = 0$ otherwise,
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \left\{\begin{array}{ll} \displaystyle
+ \frac{\beta^{\alpha}}{ \Gamma(\alpha)}
+ \frac{ e^{-\beta / x}}{ x^{\alpha + 1}}
+ & \quad \mbox{for } x > 0 \\[12pt]
+ 0 & \quad \mbox{otherwise,}
+ \end{array} \right.
+ \eqlabel{eq:finvgam}
+\endeq
+\end{latexonly}
+where $\Gamma$ is the gamma function.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGammaGen
+ * Description: random variate generators for the inverse gamma distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;
+\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class InverseGammaGen extends RandomVariateGen \begin{hide} {
+ protected double alpha;
+ protected double beta;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseGammaGen (RandomStream s, double alpha, double beta) \begin{hide} {
+ super (s, new InverseGammaDist(alpha, beta));
+ setParams(alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an inverse gamma random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\beta =$ \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public InverseGammaGen (RandomStream s, double alpha) \begin{hide} {
+ this (s, alpha, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an inverse gamma random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\beta = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public InverseGammaGen (RandomStream s, InverseGammaDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta)\begin{hide} {
+ return InverseGammaDist.inverseF (alpha, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the inverse gamma distribution
+ with shape parameter $\alpha > 0$ and scale parameter $\beta > 0$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\beta$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseGaussianGen.java b/source/umontreal/iro/lecuyer/randvar/InverseGaussianGen.java
new file mode 100644
index 0000000..13a5254
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseGaussianGen.java
@@ -0,0 +1,120 @@
+
+
+/*
+ * Class: InverseGaussianGen
+ * Description: random variate generators for the inverse Gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for
+ * the <EM>inverse Gaussian</EM> distribution with location parameter
+ * <SPAN CLASS="MATH"><I>μ</I> > 0</SPAN> and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (λ)<SUP>1/2</SUP>/(2<I>πx</I><SUP>3</SUP>) <I>e</I><SUP>-<I>λ</I>(x-<I>μ</I>)<SUP>2</SUP>/(2<I>μ</I><SUP>2</SUP>x)</SUP> for <I>x</I> > 0.
+ * </DIV><P></P>
+ *
+ */
+public class InverseGaussianGen extends RandomVariateGen {
+ protected double mu = -1.0;
+ protected double lambda = -1.0;
+
+
+
+
+ /**
+ * Creates an <EM>inverse Gaussian</EM> random variate generator
+ * with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public InverseGaussianGen (RandomStream s, double mu, double lambda) {
+ super (s, new InverseGaussianDist(mu, lambda));
+ setParams (mu, lambda);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public InverseGaussianGen (RandomStream s, InverseGaussianDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getLambda());
+ }
+
+
+ /**
+ * Generates a variate from the inverse gaussian distribution
+ * with location parameter <SPAN CLASS="MATH"><I>μ</I> > 0</SPAN> and scale parameter
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double mu, double lambda) {
+ return InverseGaussianDist.inverseF (mu, lambda, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double mu, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ this.mu = mu;
+ this.lambda = lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseGaussianGen.tex b/source/umontreal/iro/lecuyer/randvar/InverseGaussianGen.tex
new file mode 100644
index 0000000..455792a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseGaussianGen.tex
@@ -0,0 +1,131 @@
+\defmodule{InverseGaussianGen}
+
+This class implements random variate generators for
+the {\em inverse Gaussian\/} distribution with location parameter
+$\mu > 0$ and scale parameter $\lambda > 0$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = \sqrt{\lambda / (2\pi x^{3})\; e^{-\lambda(x - \mu)^2 / (2\mu^2x)}
+\qquad\mbox{ for } x > 0.
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \sqrt{\frac{\lambda}{2\pi x^{3}}}\; e^{{-\lambda(x - \mu)^2}/{(2\mu^2x)}}
+\qquad\mbox {for } x > 0.
+\eqlabel{eq:fInverseGaussian}
+\endeq
+\end{latexonly}%
+
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGaussianGen
+ * Description: random variate generators for the inverse Gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class InverseGaussianGen extends RandomVariateGen \begin{hide} {
+ protected double mu = -1.0;
+ protected double lambda = -1.0;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseGaussianGen (RandomStream s, double mu, double lambda) \begin{hide} {
+ super (s, new InverseGaussianDist(mu, lambda));
+ setParams (mu, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an {\em inverse Gaussian\/} random variate generator
+ with parameters $\mu = $ \texttt{mu} and $\lambda= $ \texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public InverseGaussianGen (RandomStream s, InverseGaussianDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getLambda());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double mu, double lambda)\begin{hide} {
+ return InverseGaussianDist.inverseF (mu, lambda, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the inverse gaussian distribution
+ with location parameter $\mu > 0$ and scale parameter $\lambda > 0$.
+\end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\lambda$ of this object.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double mu, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+ this.mu = mu;
+ this.lambda = lambda;
+ }
+\end{code}
+\begin{tabb}
+ Sets the parameters $\mu$ and $\lambda$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseGaussianMSHGen.java b/source/umontreal/iro/lecuyer/randvar/InverseGaussianMSHGen.java
new file mode 100644
index 0000000..ac9d6ed
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseGaussianMSHGen.java
@@ -0,0 +1,109 @@
+
+
+/*
+ * Class: InverseGaussianMSHGen
+ * Description: inverse gaussian random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <SPAN CLASS="textit">inverse gaussian</SPAN> random variate generators using
+ * the many-to-one transformation method of Michael, Schucany and Haas (MHS).
+ *
+ */
+public class InverseGaussianMSHGen extends InverseGaussianGen {
+ private NormalGen genN;
+
+
+ /**
+ * Creates an <SPAN CLASS="textit">inverse gaussian</SPAN> random variate generator with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>,
+ * using streams <TT>s</TT> and <TT>sn</TT>.
+ *
+ */
+ public InverseGaussianMSHGen (RandomStream s, NormalGen sn,
+ double mu, double lambda) {
+ super (s, null);
+ setParams (mu, lambda, sn);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>
+ * using streams <TT>s</TT> and <TT>sn</TT>.
+ *
+ */
+ public InverseGaussianMSHGen (RandomStream s, NormalGen sn,
+ InverseGaussianDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getLambda(), sn);
+ }
+
+
+ /**
+ * Generates a new variate from the <SPAN CLASS="textit">inverse gaussian</SPAN>
+ * distribution with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN>
+ * <TT>lambda</TT>, using streams <TT>s</TT> and <TT>sn</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, NormalGen sn,
+ double mu, double lambda) {
+ return mhs(s, sn, mu, lambda);
+ }
+
+
+ public double nextDouble() {
+ return mhs(stream, genN, mu, lambda);
+ }
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double mhs (RandomStream stream, NormalGen genN,
+ double mu, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+
+ double z = genN.nextDouble ();
+ double ymu = mu*z*z;
+ double x1 = mu + 0.5*mu*ymu/lambda - 0.5*mu/lambda *
+ Math.sqrt(4.0*ymu*lambda + ymu*ymu);
+ double u = stream.nextDouble();
+ if (u <= mu/(mu + x1))
+ return x1;
+ return mu*mu/x1;
+ }
+
+
+ protected void setParams (double mu, double lambda, NormalGen sn) {
+ setParams (mu, lambda);
+ this.genN = sn;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/InverseGaussianMSHGen.tex b/source/umontreal/iro/lecuyer/randvar/InverseGaussianMSHGen.tex
new file mode 100644
index 0000000..dc0fafa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/InverseGaussianMSHGen.tex
@@ -0,0 +1,118 @@
+\defmodule {InverseGaussianMSHGen}
+
+This class implements \emph{inverse gaussian} random variate generators using
+the many-to-one transformation method of Michael, Schucany and Haas (MHS)
+ \cite{rMIC76a,rDEV06a}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGaussianMSHGen
+ * Description: inverse gaussian random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class InverseGaussianMSHGen extends InverseGaussianGen \begin{hide} {
+ private NormalGen genN;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseGaussianMSHGen (RandomStream s, NormalGen sn,
+ double mu, double lambda) \begin{hide} {
+ super (s, null);
+ setParams (mu, lambda, sn);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an \emph{inverse gaussian} random variate generator with
+ parameters $\mu = $ \texttt{mu} and $\lambda= $ \texttt{lambda},
+ using streams \texttt{s} and \texttt{sn}.
+\end{tabb}
+\begin{code}
+
+ public InverseGaussianMSHGen (RandomStream s, NormalGen sn,
+ InverseGaussianDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getLambda(), sn);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist}
+ using streams \texttt{s} and \texttt{sn}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, NormalGen sn,
+ double mu, double lambda) \begin{hide} {
+ return mhs(s, sn, mu, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a new variate from the \emph{inverse gaussian}
+distribution with parameters $\mu = $ \texttt{mu} and $\lambda= $
+\texttt{lambda}, using streams \texttt{s} and \texttt{sn}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return mhs(stream, genN, mu, lambda);
+ }
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double mhs (RandomStream stream, NormalGen genN,
+ double mu, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (mu <= 0.0)
+ throw new IllegalArgumentException ("mu <= 0");
+
+ double z = genN.nextDouble ();
+ double ymu = mu*z*z;
+ double x1 = mu + 0.5*mu*ymu/lambda - 0.5*mu/lambda *
+ Math.sqrt(4.0*ymu*lambda + ymu*ymu);
+ double u = stream.nextDouble();
+ if (u <= mu/(mu + x1))
+ return x1;
+ return mu*mu/x1;
+ }
+
+
+ protected void setParams (double mu, double lambda, NormalGen sn) {
+ setParams (mu, lambda);
+ this.genN = sn;
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSBGen.java b/source/umontreal/iro/lecuyer/randvar/JohnsonSBGen.java
new file mode 100644
index 0000000..7f07fdc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSBGen.java
@@ -0,0 +1,77 @@
+
+
+/*
+ * Class: JohnsonSBGen
+ * Description: random variate generators for the Johnson $S_B$ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>Johnson <SPAN CLASS="MATH"><I>S</I><SUB>B</SUB></SPAN></EM> distribution.
+ *
+ */
+public class JohnsonSBGen extends JohnsonSystemG {
+
+
+ /**
+ * Creates a JohnsonSB random variate generator.
+ *
+ */
+ public JohnsonSBGen (RandomStream s, double gamma, double delta,
+ double xi, double lambda) {
+ super (s, new JohnsonSBDist(gamma, delta, xi, lambda));
+ setParams (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Creates a new generator for the JohnsonSB
+ * distribution <TT>dist</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public JohnsonSBGen (RandomStream s, JohnsonSBDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getDelta(), dist.getXi(),
+ dist.getLambda());
+ }
+
+
+ /**
+ * Uses inversion to generate a new JohnsonSB variate,
+ * using stream <TT>s</TT>.
+ *
+ *
+ */
+ public static double nextDouble (RandomStream s, double gamma,
+ double delta, double xi, double lambda) {
+ return JohnsonSBDist.inverseF (gamma, delta, xi, lambda,
+ s.nextDouble());
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSBGen.tex b/source/umontreal/iro/lecuyer/randvar/JohnsonSBGen.tex
new file mode 100644
index 0000000..f301539
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSBGen.tex
@@ -0,0 +1,83 @@
+\defmodule {JohnsonSBGen}
+
+This class implements random variate generators for the
+{\em Johnson $S_B$\/} distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSBGen
+ * Description: random variate generators for the Johnson $S_B$ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+
+public class JohnsonSBGen extends JohnsonSystemG \begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public JohnsonSBGen (RandomStream s, double gamma, double delta,
+ double xi, double lambda) \begin{hide} {
+ super (s, new JohnsonSBDist(gamma, delta, xi, lambda));
+ setParams (gamma, delta, xi, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a JohnsonSB random variate generator.
+\end{tabb}
+\begin{code}
+
+ public JohnsonSBGen (RandomStream s, JohnsonSBDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getDelta(), dist.getXi(),
+ dist.getLambda());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the JohnsonSB
+ distribution \texttt{dist}, using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double gamma,
+ double delta, double xi, double lambda) \begin{hide} {
+ return JohnsonSBDist.inverseF (gamma, delta, xi, lambda,
+ s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Uses inversion to generate a new JohnsonSB variate,
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{hide}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSLGen.java b/source/umontreal/iro/lecuyer/randvar/JohnsonSLGen.java
new file mode 100644
index 0000000..ddf59aa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSLGen.java
@@ -0,0 +1,76 @@
+
+
+/*
+ * Class: JohnsonSLGen
+ * Description: random variate generators for the Johnson $S_L$ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since July 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>Johnson <SPAN CLASS="MATH"><I>S</I><SUB>L</SUB></SPAN></EM> distribution.
+ *
+ */
+public class JohnsonSLGen extends JohnsonSystemG {
+
+
+ /**
+ * Creates a JohnsonSL random variate generator.
+ *
+ */
+ public JohnsonSLGen (RandomStream s, double gamma, double delta,
+ double xi, double lambda) {
+ super (s, new JohnsonSLDist(gamma, delta, xi, lambda));
+ setParams (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Creates a new generator for the JohnsonSL
+ * distribution <TT>dist</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public JohnsonSLGen (RandomStream s, JohnsonSLDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getDelta(), dist.getXi(),
+ dist.getLambda());
+ }
+
+
+ /**
+ * Uses inversion to generate a new JohnsonSL variate,
+ * using stream <TT>s</TT>.
+ *
+ *
+ */
+ public static double nextDouble (RandomStream s, double gamma,
+ double delta, double xi, double lambda) {
+ return JohnsonSLDist.inverseF (gamma, delta, xi, lambda,
+ s.nextDouble());
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSLGen.tex b/source/umontreal/iro/lecuyer/randvar/JohnsonSLGen.tex
new file mode 100644
index 0000000..7c639c9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSLGen.tex
@@ -0,0 +1,83 @@
+\defmodule {JohnsonSLGen}
+
+This class implements random variate generators for the
+{\em Johnson $S_L$\/} distribution.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSLGen
+ * Description: random variate generators for the Johnson $S_L$ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since July 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class JohnsonSLGen extends JohnsonSystemG \begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public JohnsonSLGen (RandomStream s, double gamma, double delta,
+ double xi, double lambda) \begin{hide} {
+ super (s, new JohnsonSLDist(gamma, delta, xi, lambda));
+ setParams (gamma, delta, xi, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a JohnsonSL random variate generator.
+\end{tabb}
+\begin{code}
+
+ public JohnsonSLGen (RandomStream s, JohnsonSLDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getDelta(), dist.getXi(),
+ dist.getLambda());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the JohnsonSL
+ distribution \texttt{dist}, using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double gamma,
+ double delta, double xi, double lambda) \begin{hide} {
+ return JohnsonSLDist.inverseF (gamma, delta, xi, lambda,
+ s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Uses inversion to generate a new JohnsonSL variate,
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{hide}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSUGen.java b/source/umontreal/iro/lecuyer/randvar/JohnsonSUGen.java
new file mode 100644
index 0000000..9c1f1db
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSUGen.java
@@ -0,0 +1,76 @@
+
+
+/*
+ * Class: JohnsonSUGen
+ * Description: random variate generators for the Johnson $S_U$ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>Johnson <SPAN CLASS="MATH"><I>S</I><SUB>U</SUB></SPAN></EM> distribution.
+ *
+ */
+public class JohnsonSUGen extends JohnsonSystemG {
+
+
+ /**
+ * Creates a JohnsonSU random variate generator.
+ *
+ */
+ public JohnsonSUGen (RandomStream s, double gamma, double delta,
+ double xi, double lambda) {
+ super (s, new JohnsonSUDist(gamma, delta, xi, lambda));
+ setParams (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Creates a new generator for the JohnsonSU
+ * distribution <TT>dist</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public JohnsonSUGen (RandomStream s, JohnsonSUDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getDelta(), dist.getXi(),
+ dist.getLambda());
+ }
+
+
+ /**
+ * Uses inversion to generate a new JohnsonSU variate,
+ * using stream <TT>s</TT>.
+ *
+ *
+ */
+ public static double nextDouble (RandomStream s, double gamma,
+ double delta, double xi, double lambda) {
+ return JohnsonSUDist.inverseF (gamma, delta, xi, lambda,
+ s.nextDouble());
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSUGen.tex b/source/umontreal/iro/lecuyer/randvar/JohnsonSUGen.tex
new file mode 100644
index 0000000..e3e5d13
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSUGen.tex
@@ -0,0 +1,83 @@
+\defmodule {JohnsonSUGen}
+
+This class implements random variate generators for the
+{\em Johnson $S_U$\/} distribution.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSUGen
+ * Description: random variate generators for the Johnson $S_U$ distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class JohnsonSUGen extends JohnsonSystemG \begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public JohnsonSUGen (RandomStream s, double gamma, double delta,
+ double xi, double lambda) \begin{hide} {
+ super (s, new JohnsonSUDist(gamma, delta, xi, lambda));
+ setParams (gamma, delta, xi, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a JohnsonSU random variate generator.
+\end{tabb}
+\begin{code}
+
+ public JohnsonSUGen (RandomStream s, JohnsonSUDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getDelta(), dist.getXi(),
+ dist.getLambda());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the JohnsonSU
+ distribution \texttt{dist}, using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double gamma,
+ double delta, double xi, double lambda) \begin{hide} {
+ return JohnsonSUDist.inverseF (gamma, delta, xi, lambda,
+ s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Uses inversion to generate a new JohnsonSU variate,
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{hide}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSystemG.java b/source/umontreal/iro/lecuyer/randvar/JohnsonSystemG.java
new file mode 100644
index 0000000..c08e85d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSystemG.java
@@ -0,0 +1,133 @@
+
+
+/*
+ * Class: JohnsonSystemG
+ * Description: Johnson system of distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since july 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+
+/**
+ * This class contains common parameters and methods for
+ * the random variate generators associated with
+ * the <EM>Johnson</EM> system of distributions.
+ * See the definitions of
+ * {@link umontreal.iro.lecuyer.probdist.JohnsonSLDist JohnsonSLDist},
+ * {@link umontreal.iro.lecuyer.probdist.JohnsonSBDist JohnsonSBDist}, and
+ * {@link umontreal.iro.lecuyer.probdist.JohnsonSUDist JohnsonSUDist}
+ * in package <TT>probdist</TT>.
+ *
+ */
+abstract class JohnsonSystemG extends RandomVariateGen {
+ protected double gamma;
+ protected double delta;
+ protected double xi;
+ protected double lambda;
+
+
+ /**
+ * Constructs a <TT>JohnsonSystemG</TT> object
+ * with shape parameters
+ * <SPAN CLASS="MATH"><I>γ</I> = <texttt>gamma</texttt></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> = <texttt>delta</texttt></SPAN>,
+ * location parameter
+ * <SPAN CLASS="MATH"><I>ξ</I> = <texttt>xi</texttt></SPAN>, and scale parameter
+ *
+ * <SPAN CLASS="MATH"><I>λ</I> = <texttt>lambda</texttt></SPAN>.
+ *
+ */
+ protected JohnsonSystemG (RandomStream s, double gamma, double delta,
+ double xi, double lambda) {
+ super (s, null);
+ setParams (gamma, delta, xi, lambda);
+ }
+
+
+ /**
+ * Constructs a <TT>JohnsonSystemG</TT> object with parameters
+ * obtained from distribution <TT>dist</TT>.
+ *
+ */
+ protected JohnsonSystemG (RandomStream s, ContinuousDistribution dist) {
+ super (s, dist);
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public double getGamma() {
+ return gamma;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>ξ</I></SPAN>.
+ *
+ */
+ public double getXi() {
+ return xi;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>γ</I></SPAN>, <SPAN CLASS="MATH"><I>δ</I></SPAN>, <SPAN CLASS="MATH"><I>ξ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I></SPAN> for this object.
+ *
+ */
+ protected void setParams (double gamma, double delta, double xi,
+ double lambda) {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ this.gamma = gamma;
+ this.delta = delta;
+ this.xi = xi;
+ this.lambda = lambda;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/JohnsonSystemG.tex b/source/umontreal/iro/lecuyer/randvar/JohnsonSystemG.tex
new file mode 100644
index 0000000..a1c240e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/JohnsonSystemG.tex
@@ -0,0 +1,143 @@
+\defmodule {JohnsonSystemG}
+
+This class contains common parameters and methods for
+the random variate generators associated with
+the {\em Johnson} system of distributions \cite{tJOH49a,tJOH95a}.
+See the definitions of
+\externalclass{umontreal.iro.lecuyer.probdist}{JohnsonSLDist},
+\externalclass{umontreal.iro.lecuyer.probdist}{JohnsonSBDist}, and
+\externalclass{umontreal.iro.lecuyer.probdist}{JohnsonSUDist}
+in package \texttt{probdist}.
+
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: JohnsonSystemG
+ * Description: Johnson system of distributions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since july 2012
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+
+abstract class JohnsonSystemG extends RandomVariateGen\begin{hide} {
+ protected double gamma;
+ protected double delta;
+ protected double xi;
+ protected double lambda;\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+
+\begin{code}
+
+ protected JohnsonSystemG (RandomStream s, double gamma, double delta,
+ double xi, double lambda)\begin{hide} {
+ super (s, null);
+ setParams (gamma, delta, xi, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{JohnsonSystemG} object
+ with shape parameters $\gamma = \texttt{gamma}$ and $\delta = \texttt{delta}$,
+ location parameter $\xi = \texttt{xi}$, and scale parameter
+ $\lambda = \texttt{lambda}$.
+\end{tabb}
+\begin{code}
+
+ protected JohnsonSystemG (RandomStream s, ContinuousDistribution dist)\begin{hide} {
+ super (s, dist);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{JohnsonSystemG} object with parameters
+ obtained from distribution \texttt{dist}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double getGamma()\begin{hide} {
+ return gamma;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\gamma$.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\delta$.
+ \end{tabb}
+\begin{code}
+
+ public double getXi()\begin{hide} {
+ return xi;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\xi$.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $\lambda$.
+ \end{tabb}
+\begin{code}
+
+ protected void setParams (double gamma, double delta, double xi,
+ double lambda)\begin{hide} {
+ if (lambda <= 0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (delta <= 0)
+ throw new IllegalArgumentException ("delta <= 0");
+ this.gamma = gamma;
+ this.delta = delta;
+ this.xi = xi;
+ this.lambda = lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $\gamma$, $\delta$, $\xi$ and
+ $\lambda$ for this object.
+ \end{tabb}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/KernelDensityGen.java b/source/umontreal/iro/lecuyer/randvar/KernelDensityGen.java
new file mode 100644
index 0000000..fab0c58
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/KernelDensityGen.java
@@ -0,0 +1,241 @@
+
+
+/*
+ * Class: KernelDensityGen
+ * Description: random variate generators for distributions obtained via
+ kernel density estimation methods
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+/**
+ * This class implements random variate generators for distributions
+ * obtained via <SPAN CLASS="textit">kernel density estimation</SPAN> methods from a set of
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> individual observations
+ * <SPAN CLASS="MATH"><I>x</I><SUB>1</SUB>,..., <I>x</I><SUB>n</SUB></SPAN>.
+ * The basic idea is to center a copy of the same symmetric density
+ * at each observation and take an equally weighted mixture of the <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * copies as an estimator of the density from which the observations come.
+ * The resulting kernel density has the general form
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I><SUB>n</SUB>(<I>x</I>) = (1/<I>nh</I>)∑<SUB>i=1</SUB><SUP>n</SUP><I>k</I>((<I>x</I> - <I>x</I><SUB>i</SUB>)/<I>h</I>),
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>k</I></SPAN> is a fixed pre-selected density called the <EM>kernel</EM>
+ * and <SPAN CLASS="MATH"><I>h</I></SPAN> is a positive constant called the <SPAN CLASS="textit">bandwidth</SPAN> or
+ * <SPAN CLASS="textit">smoothing factor</SPAN>.
+ * A difficult practical issue is the selection of <SPAN CLASS="MATH"><I>k</I></SPAN> and <SPAN CLASS="MATH"><I>h</I></SPAN>.
+ * Several approaches have been proposed for that; see, e.g.,.
+ *
+ * <P>
+ * The constructor of a generator from a kernel density requires a
+ * random stream <SPAN CLASS="MATH"><I>s</I></SPAN>, the <SPAN CLASS="MATH"><I>n</I></SPAN> observations in the form of an empirical
+ * distribution, a random variate generator for the kernel density <SPAN CLASS="MATH"><I>k</I></SPAN>,
+ * and the value of the bandwidth <SPAN CLASS="MATH"><I>h</I></SPAN>.
+ * The random variates are then generated as follows:
+ * select an observation <SPAN CLASS="MATH"><I>x</I><SUB>I</SUB></SPAN> at random, by inversion, using stream <SPAN CLASS="MATH"><I>s</I></SPAN>,
+ * then generate random variate <SPAN CLASS="MATH"><I>Y</I></SPAN> with the generator provided for
+ * the density <SPAN CLASS="MATH"><I>k</I></SPAN>, and return <SPAN CLASS="MATH"><I>x</I><SUB>I</SUB> + <I>hY</I></SPAN>.
+ *
+ * <P>
+ * A simple formula for the bandwidth, suggested in,
+ * is
+ * <SPAN CLASS="MATH"><I>h</I> = <I>α</I><SUB>k</SUB><I>h</I><SUB>0</SUB></SPAN>, where
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:bandwidth0"></A>
+ * <I>h</I><SUB>0</SUB> = 1.36374min(<I>s</I><SUB>n</SUB>, <I>q</I>/1.34)<I>n</I><SUP>-1/5</SUP>,
+ * </DIV><P></P>
+ * <SPAN CLASS="MATH"><I>s</I><SUB>n</SUB></SPAN> and <SPAN CLASS="MATH"><I>q</I></SPAN> are the empirical standard deviation and the
+ * interquartile range of the <SPAN CLASS="MATH"><I>n</I></SPAN> observations,
+ * and <SPAN CLASS="MATH"><I>α</I><SUB>k</SUB></SPAN> is a constant that depends on the type of kernel <SPAN CLASS="MATH"><I>k</I></SPAN>.
+ * It is defined by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>α</I><SUB>k</SUB> = (<I>σ</I><SUB>k</SUB><SUP>-4</SUP>∫<SUB>-∞</SUB><SUP>∞</SUP><I>k</I>(<I>x</I>)<I>dx</I>)<SUP>1/5</SUP>
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>σ</I><SUB>k</SUB></SPAN> is the standard deviation of the density <SPAN CLASS="MATH"><I>k</I></SPAN>.
+ * The static method {@link #getBaseBandwidth getBaseBandwidth} permits one to compute <SPAN CLASS="MATH"><I>h</I><SUB>0</SUB></SPAN>
+ * for a given empirical distribution.
+ *
+ * <P>
+ * <BR><P></P>
+ * <DIV ALIGN="CENTER">
+ * <DIV ALIGN="CENTER">
+ *
+ * </DIV>
+ * <A NAME="tab:kernels"></A><A NAME="34"></A>
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <CAPTION><STRONG>Table:</STRONG>
+ * Some suggested kernels</CAPTION>
+ * <TR><TD ALIGN="LEFT">name</TD>
+ * <TD ALIGN="LEFT">constructor</TD>
+ * <TD ALIGN="CENTER"><SPAN CLASS="MATH"><I>α</I><SUB>k</SUB></SPAN></TD>
+ * <TD ALIGN="CENTER">
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>k</SUB><SUP>2</SUP></SPAN></TD>
+ * <TD ALIGN="CENTER">efficiency</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT">Epanechnikov</TD>
+ * <TD ALIGN="LEFT"><TT>BetaSymmetricalDist(2, -1, 1)</TT></TD>
+ * <TD ALIGN="CENTER">1.7188</TD>
+ * <TD ALIGN="CENTER">1/5</TD>
+ * <TD ALIGN="CENTER">1.000</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT">triangular</TD>
+ * <TD ALIGN="LEFT"><TT>TriangularDist(-1, 1, 0)</TT></TD>
+ * <TD ALIGN="CENTER">1.8882</TD>
+ * <TD ALIGN="CENTER">1/6</TD>
+ * <TD ALIGN="CENTER">0.986</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT">Gaussian</TD>
+ * <TD ALIGN="LEFT"><TT>NormalDist()</TT></TD>
+ * <TD ALIGN="CENTER">0.7764</TD>
+ * <TD ALIGN="CENTER">1</TD>
+ * <TD ALIGN="CENTER">0.951</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT">boxcar</TD>
+ * <TD ALIGN="LEFT"><TT>UniformDist(-1, 1)</TT></TD>
+ * <TD ALIGN="CENTER">1.3510</TD>
+ * <TD ALIGN="CENTER">1/3</TD>
+ * <TD ALIGN="CENTER">0.930</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT">logistic</TD>
+ * <TD ALIGN="LEFT"><TT>LogisticDist()</TT></TD>
+ * <TD ALIGN="CENTER">0.4340</TD>
+ * <TD ALIGN="CENTER">3.2899</TD>
+ * <TD ALIGN="CENTER">0.888</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT">Student-t(3)</TD>
+ * <TD ALIGN="LEFT"><TT>StudentDist(3)</TT></TD>
+ * <TD ALIGN="CENTER">0.4802</TD>
+ * <TD ALIGN="CENTER">3</TD>
+ * <TD ALIGN="CENTER">0.674</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ * <BR>
+ *
+ * <P>
+ * Table gives the precomputed values of <SPAN CLASS="MATH"><I>σ</I><SUB>k</SUB></SPAN> and
+ * <SPAN CLASS="MATH"><I>α</I><SUB>k</SUB></SPAN> for selected (popular) kernels.
+ * The values are taken from.
+ * The second column gives the name of a function (in this package)
+ * that constructs the corresponding distribution.
+ * The <SPAN CLASS="textit">efficiency</SPAN> of a kernel is defined as the ratio of its
+ * mean integrated square error over that of the Epanechnikov kernel,
+ * which has optimal efficiency and corresponds to the beta distribution
+ * with parameters <SPAN CLASS="MATH">(2, 2)</SPAN> over the interval <SPAN CLASS="MATH">(- 1, 1)</SPAN>.
+ *
+ */
+public class KernelDensityGen extends RandomVariateGen {
+
+ protected RandomVariateGen kernelGen;
+ protected double bandwidth;
+ protected boolean positive; // If we want positive reflection.
+
+
+
+ /**
+ * Creates a new generator for a kernel density estimated
+ * from the observations given by the empirical distribution <TT>dist</TT>,
+ * using stream <TT>s</TT> to select the observations,
+ * generator <TT>kGen</TT> to generate the added noise from the kernel
+ * density, and bandwidth <TT>h</TT>.
+ *
+ */
+ public KernelDensityGen (RandomStream s, EmpiricalDist dist,
+ RandomVariateGen kGen, double h) {
+ super (s, dist);
+ if (h < 0.0)
+ throw new IllegalArgumentException ("h < 0");
+ if (kGen == null)
+ throw new IllegalArgumentException ("kGen == null");
+ kernelGen = kGen;
+ bandwidth = h;
+ }
+
+
+ /**
+ * This constructor uses a gaussian kernel and the default
+ * bandwidth
+ * <SPAN CLASS="MATH"><I>h</I> = <I>α</I><SUB>k</SUB><I>h</I><SUB>0</SUB></SPAN> with the <SPAN CLASS="MATH"><I>α</I><SUB>k</SUB></SPAN>
+ * suggested in Table for the gaussian distribution.
+ * This kernel has an efficiency of 0.951.
+ *
+ */
+ public KernelDensityGen (RandomStream s, EmpiricalDist dist,
+ NormalGen kGen) {
+ this (s, dist, kGen, 0.77639 * getBaseBandwidth (dist));
+ }
+
+
+ /**
+ * Computes and returns the value of <SPAN CLASS="MATH"><I>h</I><SUB>0</SUB></SPAN> in.
+ *
+ */
+ public static double getBaseBandwidth (EmpiricalDist dist) {
+ double r = dist.getInterQuartileRange() / 1.34;
+ double sigma = dist.getSampleStandardDeviation();
+ if (sigma < r) r = sigma;
+ return (1.36374 * r / Math.exp (0.2 * Math.log (dist.getN())));
+ }
+
+
+ /**
+ * Sets the bandwidth to <TT>h</TT>.
+ *
+ */
+ public void setBandwidth (double h) {
+ if (h < 0)
+ throw new IllegalArgumentException ("h < 0");
+ bandwidth = h;
+ }
+
+
+ /**
+ * After this method is called with <TT>true</TT>,
+ * the generator will produce only positive values, by using
+ * the <SPAN CLASS="textit">reflection method</SPAN>: replace all negative values by their
+ * <SPAN CLASS="textit">absolute values</SPAN>.
+ * That is, {@link #nextDouble nextDouble} will return <SPAN CLASS="MATH">| <I>x</I>|</SPAN> if <SPAN CLASS="MATH"><I>x</I></SPAN> is the
+ * generated variate. The mecanism is disabled when the method is
+ * called with <TT>false</TT>.
+ *
+ */
+ public void setPositiveReflection (boolean reflect) {
+ positive = reflect;
+ }
+
+
+ public double nextDouble() {
+ double x = (dist.inverseF (stream.nextDouble())
+ + bandwidth * kernelGen.nextDouble());
+ if (positive)
+ return Math.abs (x);
+ else
+ return x;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/KernelDensityGen.tex b/source/umontreal/iro/lecuyer/randvar/KernelDensityGen.tex
new file mode 100644
index 0000000..1e68aac
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/KernelDensityGen.tex
@@ -0,0 +1,209 @@
+\defmodule{KernelDensityGen}
+
+This class implements random variate generators for distributions
+obtained via \emph{kernel density estimation} methods from a set of
+$n$ individual observations $x_1,\dots,x_n$
+\cite{tDEV85a,rDEV86a,rHOR00a,rHOR04a,tSIL86a}.
+The basic idea is to center a copy of the same symmetric density
+at each observation and take an equally weighted mixture of the $n$
+copies as an estimator of the density from which the observations come.
+The resulting kernel density has the general form
+\begin{latexonly}
+\eq
+ f_n(x) = \frac{1}{nh} \sum_{i=1}^n k((x-x_i)/h),
+\endeq
+\end{latexonly}%
+\begin{htmlonly}
+\eq
+ f_n(x) = (1/nh) \sum_{i=1}^n k((x-x_i)/h),
+\endeq
+\end{htmlonly}%
+where $k$ is a fixed pre-selected density called the {\em kernel\/}
+and $h$ is a positive constant called the \emph{bandwidth} or
+\emph{smoothing factor}.
+A difficult practical issue is the selection of $k$ and $h$.
+Several approaches have been proposed for that; see, e.g.,
+\cite{tBER94a,tCAO94a,rHOR04a,tSIL86a}.
+
+The constructor of a generator from a kernel density requires a
+random stream $s$, the $n$ observations in the form of an empirical
+distribution, a random variate generator for the kernel density $k$,
+and the value of the bandwidth $h$.
+The random variates are then generated as follows:
+select an observation $x_I$ at random, by inversion, using stream $s$,
+then generate random variate $Y$ with the generator provided for
+the density $k$, and return $x_I + hY$.
+
+A simple formula for the bandwidth, suggested in \cite{tSIL86a,rHOR04a},
+is $h = \alpha_k h_0$, where
+\eq
+ h_0 = 1.36374 \min(s_n, q / 1.34) n^{-1/5}, \label{eq:bandwidth0}
+\endeq
+$s_n$ and $q$ are the empirical standard deviation and the
+interquartile range of the $n$ observations,
+and $\alpha_k$ is a constant that depends on the type of kernel $k$.
+It is defined by
+\eq
+ \alpha_k = \left(\sigma_k^{-4} \int_{-\infty}^\infty k(x)dx \right)^{1/5}
+\endeq
+where $\sigma_k$ is the standard deviation of the density $k$.
+The static method \method{getBaseBandwidth}{} permits one to compute $h_0$
+for a given empirical distribution.
+
+\begin{table}[hb]
+\centering
+\caption{Some suggested kernels}
+\label{tab:kernels}
+\begin{tabular}{llccc}
+\hline
+ name & constructor & $\alpha_k$ & $\sigma_k^2$ & efficiency \\
+\hline
+ Epanechnikov &\texttt{BetaSymmetricalDist(2, -1, 1)} & 1.7188 & 1/5 & 1.000\\
+ triangular &\texttt{TriangularDist(-1, 1, 0)} & 1.8882 & 1/6 & 0.986\\
+ Gaussian &\texttt{NormalDist()} & 0.7764 & 1 & 0.951\\
+ boxcar &\texttt{UniformDist(-1, 1)} & 1.3510 & 1/3 & 0.930\\
+ logistic &\texttt{LogisticDist()} & 0.4340 &3.2899& 0.888\\
+ Student-t(3) &\texttt{StudentDist(3)} & 0.4802 & 3 & 0.674\\
+\hline
+\end{tabular}
+\end{table}
+
+Table~\ref{tab:kernels} gives the precomputed values of $\sigma_k$ and
+$\alpha_k$ for selected (popular) kernels.
+The values are taken from \cite{rHOR04a}.
+The second column gives the name of a function (in this package)
+that constructs the corresponding distribution.
+The \emph{efficiency} of a kernel is defined as the ratio of its
+mean integrated square error over that of the Epanechnikov kernel,
+which has optimal efficiency and corresponds to the beta distribution
+with parameters $(2,2)$ over the interval $(-1,1)$.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: KernelDensityGen
+ * Description: random variate generators for distributions obtained via
+ kernel density estimation methods
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.RandomStream;\end{hide}
+
+public class KernelDensityGen extends RandomVariateGen\begin{hide} {
+
+ protected RandomVariateGen kernelGen;
+ protected double bandwidth;
+ protected boolean positive; // If we want positive reflection.
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public KernelDensityGen (RandomStream s, EmpiricalDist dist,
+ RandomVariateGen kGen, double h)\begin{hide} {
+ super (s, dist);
+ if (h < 0.0)
+ throw new IllegalArgumentException ("h < 0");
+ if (kGen == null)
+ throw new IllegalArgumentException ("kGen == null");
+ kernelGen = kGen;
+ bandwidth = h;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for a kernel density estimated
+ from the observations given by the empirical distribution \texttt{dist},
+ using stream \texttt{s} to select the observations,
+ generator \texttt{kGen} to generate the added noise from the kernel
+ density, and bandwidth \texttt{h}.
+ \end{tabb}
+\begin{code}
+
+ public KernelDensityGen (RandomStream s, EmpiricalDist dist,
+ NormalGen kGen)\begin{hide} {
+ this (s, dist, kGen, 0.77639 * getBaseBandwidth (dist));
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ This constructor uses a gaussian kernel and the default
+ bandwidth $h = \alpha_k h_0$ with the $\alpha_k$
+ suggested in Table~\ref{tab:kernels} for the gaussian distribution.
+ This kernel has an efficiency of 0.951.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Kernel selection and parameters}
+\begin{code}
+
+ public static double getBaseBandwidth (EmpiricalDist dist)\begin{hide} {
+ double r = dist.getInterQuartileRange() / 1.34;
+ double sigma = dist.getSampleStandardDeviation();
+ if (sigma < r) r = sigma;
+ return (1.36374 * r / Math.exp (0.2 * Math.log (dist.getN())));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Computes and returns the value of $h_0$ in (\ref{eq:bandwidth0}).
+\end{tabb}
+\begin{code}
+
+ public void setBandwidth (double h)\begin{hide} {
+ if (h < 0)
+ throw new IllegalArgumentException ("h < 0");
+ bandwidth = h;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the bandwidth to \texttt{h}.
+\end{tabb}
+\begin{code}
+
+ public void setPositiveReflection (boolean reflect)\begin{hide} {
+ positive = reflect;
+ }\end{hide}
+\end{code}
+\begin{tabb} After this method is called with \texttt{true},
+ the generator will produce only positive values, by using
+ the \emph{reflection method}: replace all negative values by their
+ \emph{absolute values}.
+ That is, \method{nextDouble}{} will return $|x|$ if $x$ is the
+ generated variate. The mecanism is disabled when the method is
+ called with \texttt{false}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ double x = (dist.inverseF (stream.nextDouble())
+ + bandwidth * kernelGen.nextDouble());
+ if (positive)
+ return Math.abs (x);
+ else
+ return x;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/KernelDensityVarCorrectGen.java b/source/umontreal/iro/lecuyer/randvar/KernelDensityVarCorrectGen.java
new file mode 100644
index 0000000..8619379
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/KernelDensityVarCorrectGen.java
@@ -0,0 +1,111 @@
+
+
+/*
+ * Class: KernelDensityVarCorrectGen
+ * Description: random variate generators for distributions obtained via
+ kernel density estimation methods
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+/**
+ * This class is a variant of {@link KernelDensityGen}, but with
+ * a rescaling of the empirical distribution so that the variance
+ * of the density used to generate the random variates is equal
+ * to the empirical variance,
+ * as suggested by Silverman.
+ *
+ * <P>
+ * Let <SPAN CLASS="MATH">bar(x)<SUB>n</SUB></SPAN> and <SPAN CLASS="MATH"><I>s</I><SUB>n</SUB><SUP>2</SUP></SPAN> be the sample mean and sample variance
+ * of the observations.
+ * The distance between each generated random variate and the
+ * sample mean <SPAN CLASS="MATH">bar(x)<SUB>n</SUB></SPAN> is multiplied by the correcting factor
+ *
+ * <SPAN CLASS="MATH">1/<I>σ</I><SUB>e</SUB></SPAN>, where
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>e</SUB><SUP>2</SUP> = 1 + (<I>hσ</I><SUB>k</SUB>/<I>s</I><SUB>n</SUB>)<SUP>2</SUP></SPAN>.
+ * The constant
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>k</SUB><SUP>2</SUP></SPAN> must be passed to the constructor.
+ * Its value can be found in
+ * the Table in {@link KernelDensityGen} for some popular
+ * kernels.
+ *
+ */
+public class KernelDensityVarCorrectGen extends KernelDensityGen {
+
+ protected double sigmak2; // Value of sigma_k^2.
+ protected double mean; // Sample mean of the observations.
+ protected double invSigmae; // 1 / sigma_e.
+
+
+
+ /**
+ * Creates a new generator for a kernel density estimated
+ * from the observations given by the empirical distribution <TT>dist</TT>,
+ * using stream <TT>s</TT> to select the observations,
+ * generator <TT>kGen</TT> to generate the added noise from the kernel
+ * density, bandwidth <TT>h</TT>, and
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>k</SUB><SUP>2</SUP> =</SPAN> <TT>sigmak2</TT> used for
+ * the variance correction.
+ *
+ */
+ public KernelDensityVarCorrectGen (RandomStream s, EmpiricalDist dist,
+ RandomVariateGen kGen, double h,
+ double sigmak2) {
+ super (s, dist, kGen, h);
+ this.sigmak2 = sigmak2;
+ mean = dist.getSampleMean();
+ double var = dist.getSampleVariance();
+ invSigmae = 1.0 / Math.sqrt (1.0 + h * h * sigmak2 / var);
+ }
+
+
+ /**
+ * This constructor uses a gaussian kernel and the default
+ * bandwidth suggested in Table for the gaussian
+ * distribution.
+ *
+ */
+ public KernelDensityVarCorrectGen (RandomStream s, EmpiricalDist dist,
+ NormalGen kGen) {
+ this (s, dist, kGen, 0.77639 * getBaseBandwidth (dist), 1.0);
+ }
+
+
+ public void setBandwidth (double h) {
+ if (h < 0)
+ throw new IllegalArgumentException ("h < 0");
+ bandwidth = h;
+ double var = ((EmpiricalDist) dist).getSampleVariance();
+ invSigmae = 1.0 / Math.sqrt (1.0 + h * h * sigmak2 / var);
+ }
+
+ public double nextDouble() {
+ double x = mean + invSigmae * (dist.inverseF (stream.nextDouble())
+ - mean + bandwidth * kernelGen.nextDouble());
+ if (positive)
+ return Math.abs (x);
+ else
+ return x;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/KernelDensityVarCorrectGen.tex b/source/umontreal/iro/lecuyer/randvar/KernelDensityVarCorrectGen.tex
new file mode 100644
index 0000000..d30e57a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/KernelDensityVarCorrectGen.tex
@@ -0,0 +1,116 @@
+\defmodule{KernelDensityVarCorrectGen}
+
+This class is a variant of \class{KernelDensityGen}{}, but with
+a rescaling of the empirical distribution so that the variance
+of the density used to generate the random variates is equal
+to the empirical variance,
+ as suggested by \latex{\cite{tSIL86a}}\html{Silverman}.
+
+Let $\bar x_n$ and $s_n^2$ be the sample mean and sample variance
+of the observations.
+The distance between each generated random variate and the
+sample mean $\bar x_n$ is multiplied by the correcting factor
+$1/\sigma_e$, where $\sigma_e^2 = 1 + (h\sigma_k/s_n)^2$.
+The constant $\sigma_k^2$ must be passed to the constructor.
+Its value can be found in \latex{Table~\ref{tab:kernels}}
+\html{the Table in \class{KernelDensityGen}{}} for some popular
+kernels.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: KernelDensityVarCorrectGen
+ * Description: random variate generators for distributions obtained via
+ kernel density estimation methods
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.RandomStream;\end{hide}
+
+public class KernelDensityVarCorrectGen extends KernelDensityGen\begin{hide} {
+
+ protected double sigmak2; // Value of sigma_k^2.
+ protected double mean; // Sample mean of the observations.
+ protected double invSigmae; // 1 / sigma_e.
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public KernelDensityVarCorrectGen (RandomStream s, EmpiricalDist dist,
+ RandomVariateGen kGen, double h,
+ double sigmak2)\begin{hide} {
+ super (s, dist, kGen, h);
+ this.sigmak2 = sigmak2;
+ mean = dist.getSampleMean();
+ double var = dist.getSampleVariance();
+ invSigmae = 1.0 / Math.sqrt (1.0 + h * h * sigmak2 / var);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for a kernel density estimated
+ from the observations given by the empirical distribution \texttt{dist},
+ using stream \texttt{s} to select the observations,
+ generator \texttt{kGen} to generate the added noise from the kernel
+ density, bandwidth \texttt{h}, and $\sigma_k^2 =$ \texttt{sigmak2} used for
+ the variance correction.
+ \end{tabb}
+\begin{code}
+
+ public KernelDensityVarCorrectGen (RandomStream s, EmpiricalDist dist,
+ NormalGen kGen)\begin{hide} {
+ this (s, dist, kGen, 0.77639 * getBaseBandwidth (dist), 1.0);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ This constructor uses a gaussian kernel and the default
+ bandwidth suggested in Table~\ref{tab:kernels} for the gaussian
+ distribution.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+
+ public void setBandwidth (double h) {
+ if (h < 0)
+ throw new IllegalArgumentException ("h < 0");
+ bandwidth = h;
+ double var = ((EmpiricalDist) dist).getSampleVariance();
+ invSigmae = 1.0 / Math.sqrt (1.0 + h * h * sigmak2 / var);
+ }
+
+ public double nextDouble() {
+ double x = mean + invSigmae * (dist.inverseF (stream.nextDouble())
+ - mean + bandwidth * kernelGen.nextDouble());
+ if (positive)
+ return Math.abs (x);
+ else
+ return x;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/LaplaceGen.java b/source/umontreal/iro/lecuyer/randvar/LaplaceGen.java
new file mode 100644
index 0000000..87529ad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LaplaceGen.java
@@ -0,0 +1,125 @@
+
+
+/*
+ * Class: LaplaceGen
+ * Description: generator of random variates from the Laplace distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <EM>Laplace</EM> distribution. Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (1/(2<I>β</I>))<I>e</I><SUP>-| x-<I>μ</I>|/<I>β</I></SUP> for - ∞ < <I>x</I> < ∞
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class LaplaceGen extends RandomVariateGen {
+ private double mu;
+ private double beta;
+
+
+ /**
+ * Creates a Laplace random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT> and <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public LaplaceGen (RandomStream s, double mu, double beta) {
+ super (s, new LaplaceDist(mu, beta));
+ setParams (mu, beta);
+ }
+
+
+ /**
+ * Creates a Laplace random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public LaplaceGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the Laplace distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public LaplaceGen (RandomStream s, LaplaceDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getBeta());
+ }
+
+
+ /**
+ * Generates a new variate from the Laplace distribution with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double mu, double beta) {
+ return LaplaceDist.inverseF (mu, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double mu, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.mu = mu;
+ this.beta = beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/LaplaceGen.tex b/source/umontreal/iro/lecuyer/randvar/LaplaceGen.tex
new file mode 100644
index 0000000..25e4d44
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LaplaceGen.tex
@@ -0,0 +1,136 @@
+\defmodule {LaplaceGen}
+
+This class implements methods for generating random variates from the
+{\em Laplace\/} distribution. Its density is
+\begin{htmlonly}
+\eq
+ f(x) = (1/(2\beta)) e^{-|x - \mu|/\beta}\mbox{ for }-\infty < x < \infty
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+(see \cite[page 165]{tJOH95b})
+\eq
+ f(x) = \frac{1}{2\beta}e^{-|x-\mu|/\beta}
+ \qquad \mbox {for } -\infty < x < \infty, \eqlabel{eq:flaplace}
+\endeq
+\end{latexonly}
+where $\beta > 0$.
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LaplaceGen
+ * Description: generator of random variates from the Laplace distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class LaplaceGen extends RandomVariateGen \begin{hide} {
+ private double mu;
+ private double beta;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public LaplaceGen (RandomStream s, double mu, double beta) \begin{hide} {
+ super (s, new LaplaceDist(mu, beta));
+ setParams (mu, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Laplace random variate generator with parameters
+ $\mu$ = \texttt{mu} and $\beta$ = \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LaplaceGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Laplace random variate generator with parameters
+ $\mu = 0$ and $\beta = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LaplaceGen (RandomStream s, LaplaceDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getBeta());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the Laplace distribution \texttt{dist}
+ and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double mu, double beta)\begin{hide} {
+ return LaplaceDist.inverseF (mu, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the Laplace distribution with parameters
+ $\mu = $~\texttt{mu} and $\beta = $~\texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double mu, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.mu = mu;
+ this.beta = beta;
+ }
+\end{code}
+\begin{tabb}
+ Sets the parameters $\mu$ and $\beta$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/LogarithmicGen.java b/source/umontreal/iro/lecuyer/randvar/LogarithmicGen.java
new file mode 100644
index 0000000..ff727ca
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LogarithmicGen.java
@@ -0,0 +1,201 @@
+
+
+/*
+ * Class: LogarithmicGen
+ * Description: random variate generators for the (discrete) logarithmic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * This class implements random variate generators for the (discrete)
+ * <EM>logarithmic</EM> distribution. Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = -<I>θ</I><SUP>x</SUP>/<I>x</I> log(1-<I>θ</I>) for <I>x</I> = 1, 2,…,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH">0 < <I>θ</I> < 1</SPAN>.
+ * It uses inversion with the LS chop-down algorithm if
+ * <SPAN CLASS="MATH"><I>θ</I> < <I>θ</I><SUB>0</SUB></SPAN>
+ * and the LK transformation algorithm if
+ * <SPAN CLASS="MATH"><I>θ</I> >= <I>θ</I><SUB>0</SUB></SPAN>,
+ * as described in.
+ * The threshold <SPAN CLASS="MATH"><I>θ</I><SUB>0</SUB></SPAN> can be specified when invoking the constructor.
+ * Its default value is
+ * <SPAN CLASS="MATH"><I>θ</I><SUB>0</SUB> = 0.96</SPAN>, as suggested in.
+ *
+ * <P>
+ * A local copy of the parameter <SPAN CLASS="MATH"><I>θ</I></SPAN> is maintained in this class.
+ *
+ */
+public class LogarithmicGen extends RandomVariateGenInt {
+ private static final double default_theta_limit = 0.96;
+
+ private double theta_limit = default_theta_limit;
+ private double theta;
+ private double t; // = log (1.0-theta).
+ private double h; // = -theta/log (1.0-theta).
+
+
+
+ /**
+ * Creates a logarithmic random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>θ</I> =</SPAN> <TT>theta</TT> and default value
+ * <SPAN CLASS="MATH"><I>θ</I><SUB>0</SUB> = 0.96</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public LogarithmicGen (RandomStream s, double theta) {
+ this (s, theta, default_theta_limit);
+ }
+
+
+ /**
+ * Creates a logarithmic random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>θ</I> =</SPAN> <TT>theta</TT> and
+ * <SPAN CLASS="MATH"><I>θ</I><SUB>0</SUB> = <texttt>theta0</texttt></SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public LogarithmicGen (RandomStream s, double theta, double theta0) {
+ super (s, null);
+ this.theta = theta;
+ theta_limit = theta0;
+ init();
+ }
+
+
+ /**
+ * Creates a new generator with distribution <TT>dist</TT> and
+ * stream <TT>s</TT>, with default value
+ * <SPAN CLASS="MATH"><I>θ</I><SUB>0</SUB> = 0.96</SPAN>.
+ *
+ */
+ public LogarithmicGen (RandomStream s, LogarithmicDist dist) {
+ this (s, dist, default_theta_limit);
+ }
+
+
+ /**
+ * Creates a new generator with distribution <TT>dist</TT>
+ * and stream <TT>s</TT>, with
+ * <SPAN CLASS="MATH"><I>θ</I><SUB>0</SUB> = <texttt>theta0</texttt></SPAN>.
+ *
+ */
+ public LogarithmicGen (RandomStream s, LogarithmicDist dist,
+ double theta0) {
+ super (s, dist);
+ theta_limit = theta0;
+ if (dist != null)
+ theta = dist.getTheta();
+ init();
+ }
+
+
+ private void init () {
+ if (theta <= 0.0 || theta >= 1.0)
+ throw new IllegalArgumentException ("theta not in (0, 1)");
+ if (theta >= theta_limit)
+ h = Math.log1p(-theta);
+ else
+ t = -theta / Math.log1p(-theta);
+ }
+
+ public int nextInt() {
+ if (theta < theta_limit)
+ return ls (stream, theta, t);
+ else // Transformation
+ return lk (stream, theta, h);
+ }
+
+ /**
+ * Uses stream <TT>s</TT> to generate
+ * a new variate from the <EM>logarithmic</EM> distribution with parameter
+ * <SPAN CLASS="MATH"><I>θ</I> =</SPAN> <TT>theta</TT>.
+ *
+ */
+ public static int nextInt (RandomStream s, double theta) {
+ if (theta < default_theta_limit)
+ return ls (s, theta, -theta/Math.log1p(-theta));
+ else // Transformation
+ return lk (s, theta, Math.log1p(-theta));
+ }
+
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+
+ private static int ls (RandomStream stream, double theta, double t) {
+ double u = stream.nextDouble();
+ int x = 1;
+
+ double p = t;
+
+ while (u > p) {
+ u -= p;
+ x++;
+ p *= theta*((double) x - 1.0)/((double)x);
+ }
+ return x;
+ }
+
+ private static int lk (RandomStream stream, double theta, double h) {
+ double u, v, p, q;
+ int x;
+
+ u = stream.nextDouble();
+ if (u > theta)
+ return 1;
+ v = stream.nextDouble();
+ q = 1.0 - Math.exp(v * h);
+ if ( u <= q * q) {
+ x = (int)(1. + (Math.log(u) / Math.log(q)));
+ return x;
+ }
+ return ((u > q) ? 1 : 2);
+ }
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>θ</I></SPAN> associated with this object.
+ *
+ */
+ public double getTheta() {
+ return theta;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>θ</I><SUB>0</SUB></SPAN> associated with this object.
+ *
+ */
+ public double getTheta0() {
+ return theta_limit;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/LogarithmicGen.tex b/source/umontreal/iro/lecuyer/randvar/LogarithmicGen.tex
new file mode 100644
index 0000000..92cf5a9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LogarithmicGen.tex
@@ -0,0 +1,209 @@
+\defmodule {LogarithmicGen}
+
+This class implements random variate generators for the (discrete)
+{\em logarithmic\/} distribution. Its mass function is
+\eq
+\begin{latexonly}
+ p(x) = \frac{-\theta^x}{x \log(1 - \theta)} \qquad\mbox{ for } x = 1,2,\ldots,
+ \eqlabel{eq:flogar}
+\end{latexonly}
+\begin{htmlonly}
+ p(x) = {-\theta^x} / {x \log(1 - \theta)} \qquad\mbox{ for } x = 1,2,\ldots,
+ \eqlabel{eq:flogar}
+\end{htmlonly}
+\endeq
+where $0 < \theta <1$.
+It uses inversion with the LS chop-down algorithm if $\theta < \theta_0$
+and the LK transformation algorithm if $\theta \ge \theta_0$,
+as described in \cite{rKEM81a}.
+The threshold $\theta_0$ can be specified when invoking the constructor.
+Its default value is $\theta_0 = 0.96$, as suggested in \cite{rKEM81a}.
+%
+\hpierre{Does this work for any $\theta_0$? Should we add constraints?}
+
+% With chop-down, we get wrong inverses and it is used
+% in UNURAN and Colt.
+% With linear search, we get an infinite loop.
+
+A local copy of the parameter $\theta$ is maintained in this class.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LogarithmicGen
+ * Description: random variate generators for the (discrete) logarithmic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class LogarithmicGen extends RandomVariateGenInt \begin{hide} {
+ private static final double default_theta_limit = 0.96;
+
+ private double theta_limit = default_theta_limit;
+ private double theta;
+ private double t; // = log (1.0-theta).
+ private double h; // = -theta/log (1.0-theta).
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public LogarithmicGen (RandomStream s, double theta)\begin{hide} {
+ this (s, theta, default_theta_limit);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a logarithmic random variate generator with parameters
+ $\theta = $ \texttt{theta} and default value $\theta_0 = 0.96$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LogarithmicGen (RandomStream s, double theta, double theta0)\begin{hide} {
+ super (s, null);
+ this.theta = theta;
+ theta_limit = theta0;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a logarithmic random variate generator with parameters
+ $\theta = $ \texttt{theta} and $\theta_0 = \texttt{theta0}$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LogarithmicGen (RandomStream s, LogarithmicDist dist)\begin{hide} {
+ this (s, dist, default_theta_limit);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator with distribution \texttt{dist} and
+ stream \texttt{s}, with default value $\theta_0 = 0.96$.
+\end{tabb}
+\begin{code}
+
+ public LogarithmicGen (RandomStream s, LogarithmicDist dist,
+ double theta0)\begin{hide} {
+ super (s, dist);
+ theta_limit = theta0;
+ if (dist != null)
+ theta = dist.getTheta();
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator with distribution \texttt{dist}
+ and stream \texttt{s}, with $\theta_0 = \texttt{theta0}$.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ private void init () {
+ if (theta <= 0.0 || theta >= 1.0)
+ throw new IllegalArgumentException ("theta not in (0, 1)");
+ if (theta >= theta_limit)
+ h = Math.log1p(-theta);
+ else
+ t = -theta / Math.log1p(-theta);
+ }
+
+ public int nextInt() {
+ if (theta < theta_limit)
+ return ls (stream, theta, t);
+ else // Transformation
+ return lk (stream, theta, h);
+ }\end{hide}
+
+ public static int nextInt (RandomStream s, double theta)\begin{hide} {
+ if (theta < default_theta_limit)
+ return ls (s, theta, -theta/Math.log1p(-theta));
+ else // Transformation
+ return lk (s, theta, Math.log1p(-theta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses stream \texttt{s} to generate
+ a new variate from the {\em logarithmic\/} distribution with parameter
+ $\theta =$ \texttt{theta}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+
+ private static int ls (RandomStream stream, double theta, double t) {
+ double u = stream.nextDouble();
+ int x = 1;
+
+ double p = t;
+
+ while (u > p) {
+ u -= p;
+ x++;
+ p *= theta*((double) x - 1.0)/((double)x);
+ }
+ return x;
+ }
+
+ private static int lk (RandomStream stream, double theta, double h) {
+ double u, v, p, q;
+ int x;
+
+ u = stream.nextDouble();
+ if (u > theta)
+ return 1;
+ v = stream.nextDouble();
+ q = 1.0 - Math.exp(v * h);
+ if ( u <= q * q) {
+ x = (int)(1. + (Math.log(u) / Math.log(q)));
+ return x;
+ }
+ return ((u > q) ? 1 : 2);
+ }\end{hide}
+
+ public double getTheta()\begin{hide} {
+ return theta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\theta$ associated with this object.
+\end{tabb}
+\begin{code}
+
+ public double getTheta0()\begin{hide} {
+ return theta_limit;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\theta_0$ associated with this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/LogisticGen.java b/source/umontreal/iro/lecuyer/randvar/LogisticGen.java
new file mode 100644
index 0000000..4a55bdf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LogisticGen.java
@@ -0,0 +1,133 @@
+
+
+/*
+ * Class: LogisticGen
+ * Description: random variate generators for the logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>logistic</EM> distribution. Its parameters are <SPAN CLASS="MATH"><I>α</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ * Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>λe</I><SUP>-<I>λ</I>(x-<I>α</I>)</SUP>/[(1 + <I>e</I><SUP>-<I>λ</I>(x-<I>α</I>)</SUP>)<SUP>2</SUP>] for - ∞ < <I>x</I> < ∞.
+ * </DIV><P></P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class LogisticGen extends RandomVariateGen {
+ protected double alpha = -1.0;
+ protected double lambda = -1.0;
+
+
+
+
+ /**
+ * Creates a logistic random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public LogisticGen (RandomStream s, double alpha, double lambda) {
+ super (s, new LogisticDist(alpha, lambda));
+ setParams (alpha, lambda);
+ }
+
+
+ /**
+ * Creates a logistic random variate generator with parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public LogisticGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the logistic distribution
+ * <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public LogisticGen (RandomStream s, LogisticDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ }
+
+
+ /**
+ * Generates a new variate from the <EM>logistic</EM> distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double alpha, double lambda) {
+ return LogisticDist.inverseF (alpha, lambda, s.nextDouble());
+ }
+
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lambda;
+ this.alpha = alpha;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/LogisticGen.tex b/source/umontreal/iro/lecuyer/randvar/LogisticGen.tex
new file mode 100644
index 0000000..6770453
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LogisticGen.tex
@@ -0,0 +1,143 @@
+\defmodule {LogisticGen}
+
+This class implements random variate generators for the
+{\em logistic\/} distribution. Its parameters are $\alpha$ and $\lambda > 0$.
+Its density function is
+\begin{htmlonly}
+\eq
+ f(x) = \lambda e^{-\lambda(x-\alpha)}/
+ [(1 + e^{-\lambda(x-\alpha)})^2] \mbox{ for } -\infty<x<\infty.
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac {\lambda e^{-\lambda(x-\alpha)}}
+ {\left(1 + e^{-\lambda(x-\alpha)}\right)^2}
+ \qquad\mbox{ for } -\infty<x<\infty. \eqlabel{eq:flogistic}
+\endeq
+\end{latexonly}
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LogisticGen
+ * Description: random variate generators for the logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class LogisticGen extends RandomVariateGen \begin{hide} {
+ protected double alpha = -1.0;
+ protected double lambda = -1.0;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public LogisticGen (RandomStream s, double alpha, double lambda) \begin{hide} {
+ super (s, new LogisticDist(alpha, lambda));
+ setParams (alpha, lambda);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a logistic random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\lambda =$ \texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LogisticGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a logistic random variate generator with parameters
+ $\alpha = 0$ and $\lambda =1$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LogisticGen (RandomStream s, LogisticDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the logistic distribution
+ \texttt{dist} and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double lambda)\begin{hide} {
+ return LogisticDist.inverseF (alpha, lambda, s.nextDouble());
+ }
+\end{hide}
+\end{code}
+\begin{tabb} Generates a new variate from the {\em logistic\/} distribution
+ with parameters $\alpha = $~\texttt{alpha} and $\lambda = $~\texttt{lambda},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\lambda$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double alpha, double lambda) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lambda;
+ this.alpha = alpha;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $\alpha$ and $\lambda$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/LoglogisticGen.java b/source/umontreal/iro/lecuyer/randvar/LoglogisticGen.java
new file mode 100644
index 0000000..4e73127
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LoglogisticGen.java
@@ -0,0 +1,113 @@
+
+
+/*
+ * Class: LoglogisticGen
+ * Description: random variate generators for the log-logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>log-logistic</EM> distribution with shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>
+ * and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>α</I>(<I>x</I>/<I>β</I>)<SUP><I>α</I>-1</SUP>)/(<I>β</I>[1 + (<I>x</I>/<I>β</I>)<SUP><I>α</I></SUP>]<SUP>2</SUP>) for <I>x</I> > 0.
+ * </DIV><P></P>
+ *
+ */
+public class LoglogisticGen extends RandomVariateGen {
+ protected double alpha;
+ protected double beta;
+
+
+
+
+ /**
+ * Creates a log-logistic random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public LoglogisticGen (RandomStream s, double alpha, double beta) {
+ super (s, new LoglogisticDist(alpha, beta));
+ setParams (alpha, beta);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public LoglogisticGen (RandomStream s, LoglogisticDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta());
+ }
+
+
+ /**
+ * Generates a variate from the <EM>log-logistic</EM> distribution
+ * with shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta) {
+ return LoglogisticDist.inverseF (alpha, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/LoglogisticGen.tex b/source/umontreal/iro/lecuyer/randvar/LoglogisticGen.tex
new file mode 100644
index 0000000..8deb6c2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LoglogisticGen.tex
@@ -0,0 +1,124 @@
+\defmodule{LoglogisticGen}
+
+This class implements random variate generators for the
+{\em log-logistic\/} distribution with shape parameter $\alpha > 0$
+and scale parameter $\beta > 0$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = (\alpha (x / \beta)^{\alpha - 1}) / (\beta [1 + (x / \beta)^\alpha]^2)
+\qquad \qquad \mbox{for } x > 0.
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac{\alpha (x / \beta)^{\alpha - 1}}{\beta [1 + (x / \beta)^\alpha]^2}
+\qquad \qquad \mbox{for } x > 0.
+\eqlabel{eq:floglogistic}
+\endeq
+\end{latexonly}
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LoglogisticGen
+ * Description: random variate generators for the log-logistic distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class LoglogisticGen extends RandomVariateGen \begin{hide} {
+ protected double alpha;
+ protected double beta;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public LoglogisticGen (RandomStream s, double alpha, double beta) \begin{hide} {
+ super (s, new LoglogisticDist(alpha, beta));
+ setParams (alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a log-logistic random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\beta =$ \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LoglogisticGen (RandomStream s, LoglogisticDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta)\begin{hide} {
+ return LoglogisticDist.inverseF (alpha, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the {\em log-logistic\/} distribution
+ with shape parameter $\alpha > 0$ and scale parameter $\beta > 0$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/LognormalGen.java b/source/umontreal/iro/lecuyer/randvar/LognormalGen.java
new file mode 100644
index 0000000..0181035
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LognormalGen.java
@@ -0,0 +1,136 @@
+
+
+/*
+ * Class: LognormalGen
+ * Description: random variate generator for the lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <EM>lognormal</EM> distribution. Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (1/((2π)<SUP>1/2</SUP><I>σx</I>)<I>e</I><SUP>-(ln(x)-<I>μ</I>)<SUP>2</SUP>/(2<I>σ</I><SUP>2</SUP>)</SUP> for <I>x</I> > 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * lognormal distribution object.
+ * One can also generate a lognormal random variate <SPAN CLASS="MATH"><I>X</I></SPAN> via
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TT>X = Math.exp (NormalGen.nextDouble (s, mu, sigma))</TT>,
+ * </DIV><P></P>
+ * in which
+ * <TT>NormalGen</TT> can actually be replaced by any subclass of <TT>NormalGen</TT>.
+ *
+ */
+public class LognormalGen extends RandomVariateGen {
+ private double mu;
+ private double sigma = -1.0;
+
+
+
+ /**
+ * Creates a lognormal random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public LognormalGen (RandomStream s, double mu, double sigma) {
+ this (s, new LognormalDist(mu, sigma));
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a lognormal random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public LognormalGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Create a random variate generator for the lognormal
+ * distribution <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public LognormalGen (RandomStream s, LognormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ /**
+ * Generates a new variate from the <EM>lognormal</EM>
+ * distribution with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and
+ * <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ return LognormalDist.inverseF (mu, sigma, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/LognormalGen.tex b/source/umontreal/iro/lecuyer/randvar/LognormalGen.tex
new file mode 100644
index 0000000..f7f4f8d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LognormalGen.tex
@@ -0,0 +1,144 @@
+\defmodule {LognormalGen}
+
+This class implements methods for generating random variates from the
+{\em lognormal\/} distribution. Its density is
+\begin{htmlonly}
+\eq
+ f(x) = (1/(\sqrt{2\pi}\sigma x) e^{-(\ln (x) - \mu)^2/(2\sigma^2)}
+ \mbox{ for }x>0,
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac{1}{\sqrt{2\pi}\sigma x} e^{-(\ln (x) - \mu)^2/(2\sigma^2)}
+ \qquad\mbox{ for }x>0, \eqlabel{eq:flognormal}
+\endeq
+\end{latexonly}
+where $\sigma>0$.
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+lognormal distribution object.
+One can also generate a lognormal random variate $X$ via
+\[
+ \mbox{\texttt{X = Math.exp (NormalGen.nextDouble (s, mu, sigma))}},
+\]
+in which
+\texttt{NormalGen} can actually be replaced by any subclass of \texttt{NormalGen}.
+% This may be a bit faster?
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LognormalGen
+ * Description: random variate generator for the lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class LognormalGen extends RandomVariateGen \begin{hide} {
+ private double mu;
+ private double sigma = -1.0;
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public LognormalGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ this (s, new LognormalDist(mu, sigma));
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a lognormal random variate generator with parameters
+ $\mu =$ \texttt{mu} and $\sigma =$ \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LognormalGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a lognormal random variate generator with parameters
+ $\mu = 0$ and $\sigma = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public LognormalGen (RandomStream s, LognormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+\begin{tabb} Create a random variate generator for the lognormal
+ distribution \texttt{dist} and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double mu, double sigma)\begin{hide} {
+ return LognormalDist.inverseF (mu, sigma, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a new variate from the {\em lognormal\/}
+ distribution with parameters $\mu = $~\texttt{mu} and
+ $\sigma = $~\texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\mu$ and $\sigma$ of this object.
+ \end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/LognormalSpecialGen.java b/source/umontreal/iro/lecuyer/randvar/LognormalSpecialGen.java
new file mode 100644
index 0000000..60cf1c5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LognormalSpecialGen.java
@@ -0,0 +1,59 @@
+
+
+/*
+ * Class: LognormalSpecialGen
+ * Description: random variates from the lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+
+
+/**
+ * Implements methods for generating random variates from the
+ * <EM>lognormal</EM> distribution using an arbitrary normal random
+ * variate generator.
+ * The (non-static) <TT>nextDouble</TT> method calls the <TT>nextDouble</TT>
+ * method of the normal generator and takes the exponential of the result.
+ *
+ */
+public class LognormalSpecialGen extends RandomVariateGen {
+
+ NormalGen myGen;
+
+ /**
+ * Create a lognormal random variate generator
+ * using the normal generator <TT>g</TT> and with the same parameters.
+ *
+ */
+ public LognormalSpecialGen (NormalGen g) {
+ // Necessary to compile, but we do not want to use stream and dist
+ super (g.stream, null);
+ stream = null;
+ myGen = g;
+ }
+
+
+ public double nextDouble() {
+ return Math.exp (myGen.nextDouble());
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/LognormalSpecialGen.tex b/source/umontreal/iro/lecuyer/randvar/LognormalSpecialGen.tex
new file mode 100644
index 0000000..7b2a119
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/LognormalSpecialGen.tex
@@ -0,0 +1,69 @@
+\defmodule {LognormalSpecialGen}
+
+Implements methods for generating random variates from the
+{\em lognormal\/} distribution using an arbitrary normal random
+variate generator.
+The (non-static) \texttt{nextDouble} method calls the \texttt{nextDouble}
+method of the normal generator and takes the exponential of the result.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LognormalSpecialGen
+ * Description: random variates from the lognormal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+\end{hide}
+
+public class LognormalSpecialGen extends RandomVariateGen \begin{hide} {
+
+ NormalGen myGen;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+
+\begin{code}
+ public LognormalSpecialGen (NormalGen g) \begin{hide} {
+ // Necessary to compile, but we do not want to use stream and dist
+ super (g.stream, null);
+ stream = null;
+ myGen = g;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Create a lognormal random variate generator
+ using the normal generator \texttt{g} and with the same parameters.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+% \subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return Math.exp (myGen.nextDouble());
+ }\end{hide}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/NakagamiGen.java b/source/umontreal/iro/lecuyer/randvar/NakagamiGen.java
new file mode 100644
index 0000000..cf95cf4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NakagamiGen.java
@@ -0,0 +1,134 @@
+
+
+/*
+ * Class: NakagamiGen
+ * Description: random variate generators for the Nakagami distribution.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the <EM>Nakagami</EM>
+ * distribution. See the definition in
+ * {@link umontreal.iro.lecuyer.probdist.NakagamiDist NakagamiDist} of package
+ * <TT>probdist</TT>.
+ *
+ */
+public class NakagamiGen extends RandomVariateGen {
+ // Distribution parameters
+ protected double a;
+ protected double lambda;
+ protected double c;
+
+
+
+ /**
+ * Creates a new Nakagami generator with parameters <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT>,
+ * <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NakagamiGen (RandomStream s, double a, double lambda, double c) {
+ super (s, new NakagamiDist (a, lambda, c));
+ setParams (a, lambda, c);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public NakagamiGen (RandomStream s, NakagamiDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getLambda(), dist.getC());
+ }
+
+
+ /**
+ * Generates a variate from the <EM>Nakagami</EM> distribution with
+ * parameters <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT>,
+ * <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>, using stream <TT>s</TT>.
+ *
+ * @param s the random stream
+ *
+ * @param a the location parameter
+ *
+ * @param lambda the scale parameter
+ *
+ * @param c the shape parameter
+ *
+ * @return Generates a variate from the <EM>Nakagami</EM> distribution
+ *
+ */
+ public static double nextDouble (RandomStream s, double a, double lambda,
+ double c) {
+ return NakagamiDist.inverseF (a, lambda, c, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the location parameter <SPAN CLASS="MATH"><I>a</I></SPAN> of this object.
+ *
+ * @return the location parameter mu
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the scale parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> of this object.
+ *
+ * @return the scale parameter mu
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Returns the shape parameter <SPAN CLASS="MATH"><I>c</I></SPAN> of this object.
+ *
+ * @return the shape parameter mu
+ *
+ */
+ public double getC() {
+ return c;
+ }
+
+
+ protected void setParams (double a, double lambda, double c) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ this.a = a;
+ this.lambda = lambda;
+ this.c = c;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/NakagamiGen.tex b/source/umontreal/iro/lecuyer/randvar/NakagamiGen.tex
new file mode 100644
index 0000000..b39bdd0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NakagamiGen.tex
@@ -0,0 +1,139 @@
+\defmodule {NakagamiGen}
+
+This class implements random variate generators for the {\em Nakagami\/}
+distribution. See the definition in
+\externalclass{umontreal.iro.lecuyer.probdist}{NakagamiDist} of package
+\texttt{probdist}.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NakagamiGen
+ * Description: random variate generators for the Nakagami distribution.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NakagamiGen extends RandomVariateGen \begin{hide} {
+ // Distribution parameters
+ protected double a;
+ protected double lambda;
+ protected double c;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NakagamiGen (RandomStream s, double a, double lambda, double c) \begin{hide} {
+ super (s, new NakagamiDist (a, lambda, c));
+ setParams (a, lambda, c);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new Nakagami generator with parameters $a=$ \texttt{a},
+ $\lambda =$ \texttt{lambda} and $c =$ \texttt{c}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NakagamiGen (RandomStream s, NakagamiDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getLambda(), dist.getC());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double a, double lambda,
+ double c)\begin{hide} {
+ return NakagamiDist.inverseF (a, lambda, c, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the {\em Nakagami\/} distribution with
+ parameters $a=$ \texttt{a},
+ $\lambda =$ \texttt{lambda} and $c =$ \texttt{c}, using stream \texttt{s}.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{the random stream}
+ \param{a}{the location parameter}
+ \param{lambda}{the scale parameter}
+ \param{c}{the shape parameter}
+ \return{Generates a variate from the {\em Nakagami\/} distribution}
+\end{htmlonly}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the location parameter $a$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the location parameter mu}
+\end{htmlonly}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the scale parameter $\lambda$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the scale parameter mu}
+\end{htmlonly}
+\begin{code}
+
+ public double getC()\begin{hide} {
+ return c;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the shape parameter $c$ of this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the shape parameter mu}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ protected void setParams (double a, double lambda, double c) {
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ if (c <= 0.0)
+ throw new IllegalArgumentException ("c <= 0");
+ this.a = a;
+ this.lambda = lambda;
+ this.c = c;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/NegativeBinomialGen.java b/source/umontreal/iro/lecuyer/randvar/NegativeBinomialGen.java
new file mode 100644
index 0000000..d6ccc69
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NegativeBinomialGen.java
@@ -0,0 +1,121 @@
+
+
+/*
+ * Class: NegativeBinomialGen
+ * Description: random variate generators for the negative binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators having the
+ * <EM>negative binomial</EM> distribution. Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:fmass-negbin"></A>
+ * <I>p</I>(<I>x</I>) = <I>Γ</I>(<I>γ</I> + <I>x</I>)/(<I>x</I>! <I>Γ</I>(<I>γ</I>)) <I>p</I><SUP><I>γ</I></SUP>(1 - <I>p</I>)<SUP>x</SUP>, for <I>x</I> = 0, 1, 2,…
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function,
+ *
+ * <SPAN CLASS="MATH"><I>γ</I> > 0</SPAN> and
+ * <SPAN CLASS="MATH">0 <= <I>p</I> <= 1</SPAN>.
+ * No local copy of the parameters <SPAN CLASS="MATH"><I>γ</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> is maintained in this class.
+ * The (non-static) <TT>nextInt</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class NegativeBinomialGen extends RandomVariateGenInt {
+ protected double gamma;
+ protected double p;
+
+
+
+ /**
+ * Creates a negative binomial random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>γ</I> =</SPAN> <TT>gamma</TT> and <SPAN CLASS="MATH"><I>p</I></SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public NegativeBinomialGen (RandomStream s, double gamma, double p) {
+ super (s, new NegativeBinomialDist (gamma, p));
+ setParams (gamma, p);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+ public NegativeBinomialGen (RandomStream s, NegativeBinomialDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getP());
+ }
+
+
+ /**
+ * Generates a new variate from the <EM>negative binomial</EM> distribution,
+ * with parameters <SPAN CLASS="MATH"><I>γ</I> =</SPAN> <TT>gamma</TT> and <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>p</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public static int nextInt (RandomStream s, double gamma, double p) {
+ return NegativeBinomialDist.inverseF (gamma, p, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>γ</I></SPAN> of this object.
+ *
+ */
+ public double getGamma() {
+ return gamma;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>γ</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double gamma, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ this.p = p;
+ this.gamma = gamma;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/NegativeBinomialGen.tex b/source/umontreal/iro/lecuyer/randvar/NegativeBinomialGen.tex
new file mode 100644
index 0000000..2c3b451
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NegativeBinomialGen.tex
@@ -0,0 +1,134 @@
+\defmodule {NegativeBinomialGen}
+
+This class implements random variate generators having the
+{\em negative binomial\/} distribution. Its mass function is
+\begin{htmlonly}
+\eq
+ p(x) = \Gamma (\gamma + x) / (x!\: \Gamma (\gamma))\: p^\gamma (1 - p)^x,
+ \qquad\mbox{for } x = 0, 1, 2, \ldots\label{eq:fmass-negbin}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ p(x) = \frac{\Gamma(\gamma + x)}{x!\: \Gamma (\gamma)}\: p^\gamma (1 - p)^x,
+ \qquad\mbox{for } x = 0, 1, 2, \ldots \label{eq:fmass-negbin}
+\endeq
+\end{latexonly}
+where $\Gamma$ is the gamma function,
+$\gamma > 0$ and $0\le p\le 1$.
+No local copy of the parameters $\gamma$ and $p$ is maintained in this class.
+The (non-static) \texttt{nextInt} method simply calls \texttt{inverseF} on the
+distribution.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NegativeBinomialGen
+ * Description: random variate generators for the negative binomial distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NegativeBinomialGen extends RandomVariateGenInt \begin{hide} {
+ protected double gamma;
+ protected double p;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NegativeBinomialGen (RandomStream s, double gamma, double p) \begin{hide} {
+ super (s, new NegativeBinomialDist (gamma, p));
+ setParams (gamma, p);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a negative binomial random variate generator with parameters
+ $\gamma = $ \texttt{gamma} and $p$, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public NegativeBinomialGen (RandomStream s, NegativeBinomialDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getGamma(), dist.getP());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a new generator for the distribution \texttt{dist}, using
+ stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static int nextInt (RandomStream s, double gamma, double p) \begin{hide} {
+ return NegativeBinomialDist.inverseF (gamma, p, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the {\em negative binomial\/} distribution,
+ with parameters $\gamma = $~\texttt{gamma} and $p = $~\texttt{p},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getGamma()\begin{hide} {
+ return gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\gamma$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double gamma, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (gamma <= 0.0)
+ throw new IllegalArgumentException ("gamma <= 0");
+ this.p = p;
+ this.gamma = gamma;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $\gamma$ and $p$ of this object.
+\end{tabb}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalACRGen.java b/source/umontreal/iro/lecuyer/randvar/NormalACRGen.java
new file mode 100644
index 0000000..4127d0f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalACRGen.java
@@ -0,0 +1,169 @@
+
+
+/*
+ * Class: NormalACRGen
+ * Description: normal random variate generators using the
+ acceptance-complement ratio method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>normal</EM> random variate generators using
+ * the <EM>acceptance-complement ratio</EM> method.
+ * For all the methods, the code was taken from UNURAN.
+ *
+ */
+public class NormalACRGen extends NormalGen {
+
+
+
+ /**
+ * Creates a normal random variate generator with mean <TT>mu</TT>
+ * and standard deviation <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalACRGen (RandomStream s, double mu, double sigma) {
+ super (s, null);
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a standard normal random variate generator with mean
+ * <TT>0</TT> and standard deviation <TT>1</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalACRGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a random variate generator for the normal distribution
+ * <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public NormalACRGen (RandomStream s, NormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ public double nextDouble() {
+ return nextDouble (stream, mu, sigma);
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+/* ****************************************************************************
+* SAMPLING A RANDOM NUMBER FROM THE
+* STANDARD NORMAL DISTRIBUTION N(0,1).
+*---------------------------------------------------------------------------
+* GENERATION METHOD : Acceptance-Complement Ratio
+*---------------------------------------------------------------------------
+* REFERENCE :
+* - UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien
+*
+* - W. Hoermann and G. Derflinger (1990):
+* The ACR Method for generating normal random variables,
+* OR Spektrum 12 (1990), 181-185.
+*****************************************************************************/
+ final double c1 = 1.448242853;
+ final double c2 = 3.307147487;
+ final double c3 = 1.46754004;
+ final double d1 = 1.036467755;
+ final double d2 = 5.295844968;
+ final double d3 = 3.631288474;
+ final double hm = 0.483941449;
+ final double zm = 0.107981933;
+ final double hp = 4.132731354;
+ final double zp = 18.52161694;
+ final double phln = 0.4515827053;
+ final double hm1 = 0.516058551;
+ final double hp1 = 3.132731354;
+ final double hzm = 0.375959516;
+ final double hzmp = 0.591923442;
+ final double as = 0.8853395638;
+ final double bs = 0.2452635696;
+ final double cs = 0.2770276848;
+ final double b = 0.5029324303;
+ final double x0 = 0.4571828819;
+ final double ym = 0.187308492;
+ final double ss = 0.7270572718;
+ final double t = 0.03895759111;
+
+ double X;
+ double rn,x,y,z;
+
+ do {
+ y = s.nextDouble();
+ if (y > hm1) { X = hp*y - hp1; break; }
+ else if (y < zm) {
+ rn = zp*y - 1;
+ X = (rn > 0) ? (1 + rn) : (-1 + rn);
+ break;
+ }
+ else if (y < hm) {
+ rn = s.nextDouble();
+ rn = rn - 1 + rn;
+ z = (rn > 0) ? 2 - rn : -2 - rn;
+ if ((c1 - y)*(c3 + Math.abs (z)) < c2) { X = z; break; }
+ else {
+ x = rn*rn;
+ if ((y + d1)*(d3 + x) < d2) { X = rn; break; }
+ else if (hzmp - y < Math.exp (-(z*z + phln)/2)) { X = z; break; }
+ else if (y + hzm < Math.exp (-(x + phln)/2)) { X = rn; break; }
+ }
+ }
+
+ while (true) {
+ x = s.nextDouble();
+ y = ym*s.nextDouble();
+ z = x0 - ss*x - y;
+ if (z > 0) rn = 2 + y/x;
+ else {
+ x = 1 - x;
+ y = ym - y;
+ rn = -(2 + y/x);
+ }
+ if ((y - as + x)*(cs + x) + bs < 0) { X = rn; break; }
+ else if (y < x + t)
+ if (rn*rn < 4*(b - Math.log (x))) { X = rn; break; }
+ }
+
+ } while (false);
+
+ return mu + sigma*X;
+
+ }
+ /**
+ * Generates a variate from the normal distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+
+ }
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalACRGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalACRGen.tex
new file mode 100644
index 0000000..ba30162
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalACRGen.tex
@@ -0,0 +1,176 @@
+\defmodule {NormalACRGen}
+
+This class implements {\em normal\/} random variate generators using
+the {\em acceptance-complement ratio\/} method \cite{rHOR90a}.
+For all the methods, the code was taken from UNURAN \cite{iLEY02a}.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalACRGen
+ * Description: normal random variate generators using the
+ acceptance-complement ratio method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalACRGen extends NormalGen \begin{hide} {
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NormalACRGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ super (s, null);
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a normal random variate generator with mean \texttt{mu}
+ and standard deviation \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalACRGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a standard normal random variate generator with mean
+ \texttt{0} and standard deviation \texttt{1}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalACRGen (RandomStream s, NormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a random variate generator for the normal distribution
+ \texttt{dist} and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return nextDouble (stream, mu, sigma);
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+/*****************************************************************************
+* SAMPLING A RANDOM NUMBER FROM THE
+* STANDARD NORMAL DISTRIBUTION N(0,1).
+*---------------------------------------------------------------------------
+* GENERATION METHOD : Acceptance-Complement Ratio
+*---------------------------------------------------------------------------
+* REFERENCE :
+* - UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien
+*
+* - W. Hoermann and G. Derflinger (1990):
+* The ACR Method for generating normal random variables,
+* OR Spektrum 12 (1990), 181-185.
+*****************************************************************************/
+ final double c1 = 1.448242853;
+ final double c2 = 3.307147487;
+ final double c3 = 1.46754004;
+ final double d1 = 1.036467755;
+ final double d2 = 5.295844968;
+ final double d3 = 3.631288474;
+ final double hm = 0.483941449;
+ final double zm = 0.107981933;
+ final double hp = 4.132731354;
+ final double zp = 18.52161694;
+ final double phln = 0.4515827053;
+ final double hm1 = 0.516058551;
+ final double hp1 = 3.132731354;
+ final double hzm = 0.375959516;
+ final double hzmp = 0.591923442;
+ final double as = 0.8853395638;
+ final double bs = 0.2452635696;
+ final double cs = 0.2770276848;
+ final double b = 0.5029324303;
+ final double x0 = 0.4571828819;
+ final double ym = 0.187308492;
+ final double ss = 0.7270572718;
+ final double t = 0.03895759111;
+
+ double X;
+ double rn,x,y,z;
+
+ do {
+ y = s.nextDouble();
+ if (y > hm1) { X = hp*y - hp1; break; }
+ else if (y < zm) {
+ rn = zp*y - 1;
+ X = (rn > 0) ? (1 + rn) : (-1 + rn);
+ break;
+ }
+ else if (y < hm) {
+ rn = s.nextDouble();
+ rn = rn - 1 + rn;
+ z = (rn > 0) ? 2 - rn : -2 - rn;
+ if ((c1 - y)*(c3 + Math.abs (z)) < c2) { X = z; break; }
+ else {
+ x = rn*rn;
+ if ((y + d1)*(d3 + x) < d2) { X = rn; break; }
+ else if (hzmp - y < Math.exp (-(z*z + phln)/2)) { X = z; break; }
+ else if (y + hzm < Math.exp (-(x + phln)/2)) { X = rn; break; }
+ }
+ }
+
+ while (true) {
+ x = s.nextDouble();
+ y = ym*s.nextDouble();
+ z = x0 - ss*x - y;
+ if (z > 0) rn = 2 + y/x;
+ else {
+ x = 1 - x;
+ y = ym - y;
+ rn = -(2 + y/x);
+ }
+ if ((y - as + x)*(cs + x) + bs < 0) { X = rn; break; }
+ else if (y < x + t)
+ if (rn*rn < 4*(b - Math.log (x))) { X = rn; break; }
+ }
+
+ } while (false);
+
+ return mu + sigma*X;
+
+ }
+\end{code}
+ \begin{tabb} Generates a variate from the normal distribution with
+ parameters $\mu = $~\texttt{mu} and $\sigma = $~\texttt{sigma}, using
+ stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+ } \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalBoxMullerGen.java b/source/umontreal/iro/lecuyer/randvar/NormalBoxMullerGen.java
new file mode 100644
index 0000000..d798c8b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalBoxMullerGen.java
@@ -0,0 +1,118 @@
+
+
+/*
+ * Class: NormalBoxMullerGen
+ * Description: normal random variate generators using the Box-Muller method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>normal</EM> random variate generators using
+ * the <EM>Box-Muller</EM> method.
+ * Since the method generates two variates at a time,
+ * the second variate is returned upon the next call to the {@link #nextDouble nextDouble}.
+ *
+ */
+public class NormalBoxMullerGen extends NormalGen {
+ private boolean available = false;
+ private double[] variates = new double[2];
+ private static double[] staticVariates = new double[2];
+ // used by polar method which calculate always two random values;
+
+
+
+ /**
+ * Creates a normal random variate generator with mean <TT>mu</TT>
+ * and standard deviation <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalBoxMullerGen (RandomStream s, double mu, double sigma) {
+ super (s, null);
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a standard normal random variate generator with mean
+ * <TT>0</TT> and standard deviation <TT>1</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalBoxMullerGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a random variate generator for the normal distribution
+ * <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public NormalBoxMullerGen (RandomStream s, NormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ public double nextDouble() {
+ if (available) {
+ available = false;
+ return mu + sigma*variates[1];
+ }
+ else {
+ boxMuller (stream, mu, sigma, variates);
+ available = true;
+ return mu + sigma*variates[0];
+ }
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ boxMuller (s, mu, sigma, staticVariates);
+ return mu + sigma*staticVariates[0];
+ }
+ /**
+ * Generates a variate from the normal distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static void boxMuller (RandomStream stream, double mu,
+ double sigma, double[] variates) {
+ final double pi = Math.PI;
+ double u,v,s;
+
+ u = stream.nextDouble();
+ v = stream.nextDouble();
+ s = Math.sqrt (-2.0*Math.log (u));
+ variates[1] = s*Math.sin (2*pi*v);
+ variates[0] = s*Math.cos (2*pi*v);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalBoxMullerGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalBoxMullerGen.tex
new file mode 100644
index 0000000..82d45d1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalBoxMullerGen.tex
@@ -0,0 +1,127 @@
+\defmodule {NormalBoxMullerGen}
+
+This class implements {\em normal\/} random variate generators using
+ the {\em Box-Muller\/} method\latex{ from \cite{rBOX58a}}.
+Since the method generates two variates at a time,
+the second variate is returned upon the next call to the \method{nextDouble}{}.
+% For all the methods, the code was taken from UNURAN \cite{iLEY02a}.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalBoxMullerGen
+ * Description: normal random variate generators using the Box-Muller method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalBoxMullerGen extends NormalGen \begin{hide} {
+ private boolean available = false;
+ private double[] variates = new double[2];
+ private static double[] staticVariates = new double[2];
+ // used by polar method which calculate always two random values;
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NormalBoxMullerGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ super (s, null);
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a normal random variate generator with mean \texttt{mu}
+ and standard deviation \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalBoxMullerGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a standard normal random variate generator with mean
+ \texttt{0} and standard deviation \texttt{1}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalBoxMullerGen (RandomStream s, NormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a random variate generator for the normal distribution
+ \texttt{dist} and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ if (available) {
+ available = false;
+ return mu + sigma*variates[1];
+ }
+ else {
+ boxMuller (stream, mu, sigma, variates);
+ available = true;
+ return mu + sigma*variates[0];
+ }
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ boxMuller (s, mu, sigma, staticVariates);
+ return mu + sigma*staticVariates[0];
+ }
+\end{code}
+ \begin{tabb} Generates a variate from the normal distribution with
+ parameters $\mu = $~\texttt{mu} and $\sigma = $~\texttt{sigma}, using
+ stream \texttt{s}.
+ \end{tabb}
+
+\begin{code}
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static void boxMuller (RandomStream stream, double mu,
+ double sigma, double[] variates) {
+ final double pi = Math.PI;
+ double u,v,s;
+
+ u = stream.nextDouble();
+ v = stream.nextDouble();
+ s = Math.sqrt (-2.0*Math.log (u));
+ variates[1] = s*Math.sin (2*pi*v);
+ variates[0] = s*Math.cos (2*pi*v);
+ }
+
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalGen.java b/source/umontreal/iro/lecuyer/randvar/NormalGen.java
new file mode 100644
index 0000000..6874448
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalGen.java
@@ -0,0 +1,172 @@
+
+
+/*
+ * Class: NormalGen
+ * Description: random variates generator from the normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <EM>normal</EM> distribution
+ * <SPAN CLASS="MATH"><I>N</I>(<I>μ</I>, <I>σ</I>)</SPAN>.
+ * It has mean <SPAN CLASS="MATH"><I>μ</I></SPAN> and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN>, where <SPAN CLASS="MATH"><I>σ</I> > 0</SPAN>.
+ * Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 1/(2π)<SUP>1/2</SUP><I>σe</I><SUP>(x-<I>μ</I>)<SUP>2</SUP>/(2<I>σ</I><SUP>2</SUP>)</SUP>
+ * </DIV><P></P>
+ * The <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ * <P>
+ * The following table gives the CPU time needed to generate <SPAN CLASS="MATH">10<SUP>8</SUP></SPAN> standard
+ * normal random variates using the different implementations available in SSJ.
+ * The first time is for a generator object (non-static method), and
+ * the second time is for the static method
+ * where no object is created.
+ * These tests were made on a machine with processor AMD Athlon 4000, running
+ * Red Hat Linux, with clock speed at 2403 MHz. The static method
+ * <TT>nextDouble()</TT> for <TT>NormalBoxMullerGen</TT> and
+ * <TT>NormalPolarGen</TT> uses only one number out of two that are
+ * generated; thus they are twice slower than the non-static method.
+ *
+ * <P>
+ * <DIV ALIGN="CENTER">
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="LEFT">Generator</TD>
+ * <TD ALIGN="CENTER">time in seconds</TD>
+ * <TD ALIGN="CENTER">time in seconds</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"> </TD>
+ * <TD ALIGN="CENTER">(object)</TD>
+ * <TD ALIGN="CENTER">(static)</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalGen</TT></TD>
+ * <TD ALIGN="CENTER">7.67</TD>
+ * <TD ALIGN="CENTER">7.72</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalACRGen</TT></TD>
+ * <TD ALIGN="CENTER">4.71</TD>
+ * <TD ALIGN="CENTER">4.76</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalBoxMullerGen</TT></TD>
+ * <TD ALIGN="CENTER">16.07</TD>
+ * <TD ALIGN="CENTER">31.45</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalPolarGen</TT></TD>
+ * <TD ALIGN="CENTER">7.31</TD>
+ * <TD ALIGN="CENTER">13.74</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>NormalKindermannRamageGen</TT></TD>
+ * <TD ALIGN="CENTER">5.38</TD>
+ * <TD ALIGN="CENTER">5.34</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ *
+ */
+public class NormalGen extends RandomVariateGen {
+ protected double mu;
+ protected double sigma = -1.0;
+
+
+ /**
+ * Creates a normal random variate generator with mean <TT>mu</TT>
+ * and standard deviation <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalGen (RandomStream s, double mu, double sigma) {
+ super (s, new NormalDist(mu, sigma));
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a standard normal random variate generator with mean
+ * <TT>0</TT> and standard deviation <TT>1</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a random variate generator for the normal distribution
+ * <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public NormalGen (RandomStream s, NormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ /**
+ * Generates a variate from the normal distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ return NormalDist.inverseF (mu, sigma, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (double mu, double sigma) {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalGen.tex
new file mode 100644
index 0000000..07b0232
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalGen.tex
@@ -0,0 +1,162 @@
+\defmodule {NormalGen}
+
+This class implements methods for generating random variates from the
+{\em normal\/} distribution $N(\mu, \sigma)$.
+It has mean $\mu$ and variance $\sigma^2$, where $\sigma>0$.
+ Its density function is
+\begin{htmlonly}
+\eq
+ f(x) = 1/\sqrt{2\pi}\sigma e^{(x-\mu)^2/(2\sigma^2)}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{(x-\mu)^2/(2\sigma^2)}
+ \eqlabel{eq:fnormal}
+\endeq
+\end{latexonly}
+%
+The \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+The following table gives the CPU time needed to generate $10^8$ standard
+normal random variates using the different implementations available in SSJ.
+The first time is for a generator object (non-static method), and
+the second time is for the static method
+where no object is created.
+These tests were made on a machine with processor AMD Athlon 4000, running
+Red Hat Linux, with clock speed at 2403 MHz. The static method
+ \texttt{nextDouble()} for \texttt{NormalBoxMullerGen} and
+ \texttt{NormalPolarGen} uses only one number out of two that are
+ generated; thus they are twice slower than the non-static method.
+
+\begin{center}
+\begin{tabular}{|l|c|c|}
+\hline
+ Generator & time in seconds & time in seconds \\
+ & (object) & (static) \\
+\hline
+\texttt{NormalGen} & 7.67 & 7.72 \\
+\texttt{NormalACRGen} & 4.71 & 4.76 \\
+\texttt{NormalBoxMullerGen} & 16.07 & 31.45 \\
+\texttt{NormalPolarGen} & 7.31 & 13.74 \\
+\texttt{NormalKindermannRamageGen} & 5.38 & 5.34 \\
+\hline
+\end{tabular}
+\end{center}
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalGen
+ * Description: random variates generator from the normal distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalGen extends RandomVariateGen \begin{hide} {
+ protected double mu;
+ protected double sigma = -1.0;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NormalGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ super (s, new NormalDist(mu, sigma));
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a normal random variate generator with mean \texttt{mu}
+ and standard deviation \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a standard normal random variate generator with mean
+ \texttt{0} and standard deviation \texttt{1}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalGen (RandomStream s, NormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a random variate generator for the normal distribution
+ \texttt{dist} and stream \texttt{s}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) \begin{hide} {
+ return NormalDist.inverseF (mu, sigma, s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a variate from the normal distribution with
+ parameters $\mu = $~\texttt{mu} and $\sigma = $~\texttt{sigma}, using
+ stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\sigma$ of this object.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double mu, double sigma)\begin{hide} {
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.mu = mu;
+ this.sigma = sigma;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the parameters $\mu$ and $\sigma$ of this object.
+ \end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalInverseFromDensityGen.java b/source/umontreal/iro/lecuyer/randvar/NormalInverseFromDensityGen.java
new file mode 100644
index 0000000..b0c7a28
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalInverseFromDensityGen.java
@@ -0,0 +1,126 @@
+
+
+/*
+ * Class: NormalInverseFromDensityGen
+ * Description: random variate generators using numerical inversion of
+ the normal density
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <SPAN CLASS="textit">normal</SPAN> random variate generators
+ * using numerical inversion of the <SPAN CLASS="textit">normal</SPAN> density
+ * as described in. It makes use of the class
+ * {@link umontreal.iro.lecuyer.probdist.InverseDistFromDensity InverseDistFromDensity}.
+ * A set of tables are precomputed to speed up the generation of normal random
+ * variables by numerical inversion. This will be useful if one
+ * wants to generate a large number of random variables.
+ *
+ */
+public class NormalInverseFromDensityGen extends NormalGen {
+
+
+
+ /**
+ * Creates a normal random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I></SPAN> = <TT>sigma</TT>, using stream <TT>stream</TT>.
+ * It uses numerical inversion with precomputed tables.
+ * The <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>ueps</TT> is the desired absolute error in the
+ * <TT>cdf</TT>, and <TT>order</TT> is the degree of the Newton interpolating
+ * polynomial over each interval.
+ *
+ */
+ public NormalInverseFromDensityGen (RandomStream stream, double mu,
+ double sigma, double ueps, int order) {
+ // dist is the normal distribution
+ super (stream, mu, sigma);
+ double xc = mu;
+
+ // member (NormalDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ dist = new InverseDistFromDensity ((ContinuousDistribution) dist,
+ xc, ueps, order);
+ }
+
+
+ /**
+ * Similar to the first constructor, with the normal
+ * distribution <TT>dist</TT>.
+ *
+ */
+ public NormalInverseFromDensityGen (RandomStream stream, NormalDist dist,
+ double ueps, int order) {
+ super (stream, dist);
+ double xc = mu;
+
+ // member (NormalDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ this.dist = new InverseDistFromDensity (dist, xc, ueps, order);
+ }
+
+
+ /**
+ * Creates a new normal generator using the <SPAN CLASS="textit">normal</SPAN>
+ * distribution <TT>dist</TT> and stream <TT>stream</TT>. <TT>dist</TT>
+ * may be obtained by calling method {@link #getDistribution(()) getDistribution},
+ * after using one of the other constructors to create the
+ * precomputed tables. This is useful when one needs many generators
+ * using the same normal distribution.
+ * Precomputing tables for numerical inversion is
+ * costly; thus using only one set of tables for many generators
+ * is more efficient. The first {@link NormalInverseFromDensityGen} generator
+ * using the other constructors creates the precomputed tables.
+ * Then all other streams use this constructor with the same set of tables.
+ *
+ */
+ public NormalInverseFromDensityGen (RandomStream stream,
+ InverseDistFromDensity dist) {
+ super (stream, null);
+ mu = dist.getXc();
+ this.dist = dist;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>u</I></SPAN>-resolution <TT>ueps</TT>.
+ *
+ */
+ public double getUepsilon() {
+ return ((InverseDistFromDensity)dist).getEpsilon();
+ }
+
+
+
+ /**
+ * Returns the order of the interpolating polynomial.
+ *
+ */
+ public int getOrder() {
+ return ((InverseDistFromDensity)dist).getOrder();
+ }
+
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalInverseFromDensityGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalInverseFromDensityGen.tex
new file mode 100644
index 0000000..72faab0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalInverseFromDensityGen.tex
@@ -0,0 +1,136 @@
+\defmodule {NormalInverseFromDensityGen}
+
+This class implements \emph{normal} random variate generators
+ using numerical inversion of the \emph{normal} density
+ as described in \cite{rDER10a}. It makes use of the class
+\externalclass{umontreal.iro.lecuyer.probdist}{InverseDistFromDensity}.
+A set of tables are precomputed to speed up the generation of normal random
+variables by numerical inversion. This will be useful if one
+wants to generate a large number of random variables.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalInverseFromDensityGen
+ * Description: random variate generators using numerical inversion of
+ the normal density
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalInverseFromDensityGen extends NormalGen \begin{hide} {
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NormalInverseFromDensityGen (RandomStream stream, double mu,
+ double sigma, double ueps, int order) \begin{hide} {
+ // dist is the normal distribution
+ super (stream, mu, sigma);
+ double xc = mu;
+
+ // member (NormalDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ dist = new InverseDistFromDensity ((ContinuousDistribution) dist,
+ xc, ueps, order);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a normal random variate generator with parameters
+$\mu=$ \texttt{mu} and $\sigma $ = \texttt{sigma}, using stream \texttt{stream}.
+It uses numerical inversion with precomputed tables.
+The $u$-resolution \texttt{ueps} is the desired absolute error in the
+\texttt{cdf}, and \texttt{order} is the degree of the Newton interpolating
+polynomial over each interval.
+\end{tabb}
+\begin{code}
+
+ public NormalInverseFromDensityGen (RandomStream stream, NormalDist dist,
+ double ueps, int order) \begin{hide} {
+ super (stream, dist);
+ double xc = mu;
+
+ // member (NormalDist) dist is replaced by
+ // (InverseDistFromDensity) dist
+ this.dist = new InverseDistFromDensity (dist, xc, ueps, order);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Similar to the first constructor, with the normal
+ distribution \texttt{dist}.
+ \end{tabb}
+\begin{code}
+
+ public NormalInverseFromDensityGen (RandomStream stream,
+ InverseDistFromDensity dist) \begin{hide} {
+ super (stream, null);
+ mu = dist.getXc();
+ this.dist = dist;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Creates a new normal generator using the \emph{normal}
+ distribution \texttt{dist} and stream \texttt{stream}. \texttt{dist}
+ may be obtained by calling method \method{getDistribution}{()},
+ after using one of the other constructors to create the
+ precomputed tables. This is useful when one needs many generators
+ using the same normal distribution.
+ Precomputing tables for numerical inversion is
+ costly; thus using only one set of tables for many generators
+is more efficient. The first \class{NormalInverseFromDensityGen} generator
+ using the other constructors creates the precomputed tables.
+Then all other streams use this constructor with the same set of tables.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public double getUepsilon()\begin{hide} {
+ return ((InverseDistFromDensity)dist).getEpsilon();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $u$-resolution \texttt{ueps}.
+\end{tabb}
+\begin{code}
+
+ public int getOrder()\begin{hide} {
+ return ((InverseDistFromDensity)dist).getOrder();
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the order of the interpolating polynomial.
+\end{tabb}
+
+\begin{hide}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianGen.java b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianGen.java
new file mode 100644
index 0000000..93a81dc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianGen.java
@@ -0,0 +1,143 @@
+
+
+/*
+ * Class: NormalInverseGaussianGen
+ * Description: random variate generators for the normal inverse gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for
+ * the <SPAN CLASS="textit">normal inverse gaussian</SPAN> (<SPAN CLASS="MATH"><I>NIG</I></SPAN>) distribution. See the definition of
+ * {@link umontreal.iro.lecuyer.probdist.NormalInverseGaussianDist NormalInverseGaussianDist}
+ *
+ */
+public class NormalInverseGaussianGen extends RandomVariateGen {
+ protected double mu;
+ protected double delta = -1.0;
+ protected double alpha = -1.0;
+ protected double beta = -2.0;
+ protected double gamma = -1.0;
+
+
+
+ /**
+ * Creates an <SPAN CLASS="textit">normal inverse gaussian</SPAN> random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN> = <TT>alpha</TT>, <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>, <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT>
+ * and <SPAN CLASS="MATH"><I>δ</I></SPAN> = <TT>delta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalInverseGaussianGen (RandomStream s, double alpha,
+ double beta, double mu, double delta) {
+ super (s, new NormalInverseGaussianDist(alpha, beta, mu, delta));
+ setParams (alpha, beta, mu, delta);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public NormalInverseGaussianGen (RandomStream s,
+ NormalInverseGaussianDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getMu(),
+ dist.getDelta());
+ }
+
+
+ /**
+ * NOT IMPLEMENTED. Use the daughter classes.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha,
+ double beta, double mu, double delta) {
+ return NormalInverseGaussianDist.inverseF (alpha, beta, mu, delta,
+ s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> of this object.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double alpha, double beta, double mu,
+ double delta) {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ gamma = Math.sqrt(alpha*alpha - beta*beta);
+
+ this.mu = mu;
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianGen.tex
new file mode 100644
index 0000000..b54bbb8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianGen.tex
@@ -0,0 +1,155 @@
+\defmodule{NormalInverseGaussianGen}
+
+\newcommand{\nig}{\emph{normal inverse gaussian}}
+
+This class implements random variate generators for
+the \nig{} ($\mathcal{NIG}$) distribution. See the definition of
+\externalclass{umontreal.iro.lecuyer.probdist}{NormalInverseGaussianDist}
+\begin{latexonly}%
+in package \texttt{probdist}.
+\end{latexonly}
+
+
+\bigskip\hrule\bigskip
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalInverseGaussianGen
+ * Description: random variate generators for the normal inverse gaussian distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalInverseGaussianGen extends RandomVariateGen \begin{hide} {
+ protected double mu;
+ protected double delta = -1.0;
+ protected double alpha = -1.0;
+ protected double beta = -2.0;
+ protected double gamma = -1.0;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NormalInverseGaussianGen (RandomStream s, double alpha,
+ double beta, double mu, double delta) \begin{hide} {
+ super (s, new NormalInverseGaussianDist(alpha, beta, mu, delta));
+ setParams (alpha, beta, mu, delta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an \nig{} random variate generator with parameters
+ $\alpha$ = \texttt{alpha}, $\beta$ = \texttt{beta}, $\mu$ = \texttt{mu}
+ and $\delta$ = \texttt{delta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalInverseGaussianGen (RandomStream s,
+ NormalInverseGaussianDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getBeta(), dist.getMu(),
+ dist.getDelta());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha,
+ double beta, double mu, double delta)\begin{hide} {
+ return NormalInverseGaussianDist.inverseF (alpha, beta, mu, delta,
+ s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ NOT IMPLEMENTED. Use the daughter classes.
+% Generates a variate from the inverse gaussian distribution
+% with location parameter $\mu > 0$ and scale parameter $\lambda > 0$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\alpha$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\beta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\mu$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the parameter $\delta$ of this object.
+ \end{tabb}
+\begin{code}
+
+ public void setParams (double alpha, double beta, double mu,
+ double delta)\begin{hide} {
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ gamma = Math.sqrt(alpha*alpha - beta*beta);
+
+ this.mu = mu;
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the parameters $\alpha$, $\beta$, $\mu$ and $\delta$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianIGGen.java b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianIGGen.java
new file mode 100644
index 0000000..5b17a74
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianIGGen.java
@@ -0,0 +1,121 @@
+
+
+/*
+ * Class: NormalInverseGaussianIGGen
+ * Description: normal inverse gaussian random variate generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * .
+ *
+ * This class implements a (<SPAN CLASS="MATH"><I>NIG</I></SPAN>) random variate generator by
+ * using a normal generator (<SPAN CLASS="MATH"><I>N</I></SPAN>) and an inverse gaussian generator (<SPAN CLASS="MATH"><I>IG</I></SPAN>),
+ * as described in the following
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="nig2"></A>
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>Y</I></TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>∼</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>IG</I>(<I>δ</I>/<I>γ</I>, <I>δ</I><SUP>2</SUP>)</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>X</I> | (<I>Y</I> = <I>y</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>∼</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>N</I>(<I>μ</I> + <I>βy</I>, <I>y</I>).</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * The normal
+ * <SPAN CLASS="MATH"><I>N</I>(<I>μ</I>, <I>σ</I><SUP>2</SUP>)</SPAN> has mean <SPAN CLASS="MATH"><I>μ</I></SPAN> and variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN>, while
+ * the inverse gaussian has the parametrization described in{@link umontreal.iro.lecuyer.randvar.InverseGaussianGen InverseGaussianGen}.
+ * If
+ * <SPAN CLASS="MATH"><I>γ</I> = (α^2 - β^2)<SUP>1/2</SUP></SPAN> with
+ * <SPAN CLASS="MATH">0 <= | <I>β</I>| < <I>α</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> > 0</SPAN>, then
+ * <SPAN CLASS="MATH"><I>X</I>∼<I>NIG</I>(<I>α</I>, <I>β</I>, <I>μ</I>, <I>δ</I>)</SPAN>.
+ *
+ */
+public class NormalInverseGaussianIGGen extends NormalInverseGaussianGen {
+ private NormalGen genN;
+ private InverseGaussianGen genIG;
+
+
+ /**
+ * Creates a random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>, <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN>,
+ * using generators <TT>ig</TT> and <TT>ng</TT>, as described
+ * above. The parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> are included in generator <TT>ig</TT>.
+ *
+ */
+ public NormalInverseGaussianIGGen (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) {
+ super (null, null);
+ setParams (ig, ng, beta, mu);
+ }
+
+
+ /**
+ * Generates a new variate from the distribution with
+ * parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>β</I></SPAN> = <TT>beta</TT>, <SPAN CLASS="MATH"><I>μ</I></SPAN> = <TT>mu</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN>,
+ * using generators <TT>ig</TT> and <TT>ng</TT>, as described in eq..
+ * The parameters <SPAN CLASS="MATH"><I>α</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> are included in generator <TT>ig</TT>.
+ *
+ */
+ public static double nextDouble (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) {
+ return mynig (ig, ng, beta, mu);
+ }
+
+
+ public double nextDouble() {
+ return mynig (genIG, genN, beta, mu);
+ }
+
+
+// >>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<
+
+ private static double mynig (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) {
+
+ double y = ig.nextDouble ();
+ double x = mu + beta*y + Math.sqrt(y)*ng.nextDouble ();
+ return x;
+ }
+
+
+ protected void setParams (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) {
+ delta = Math.sqrt(ig.getLambda());
+ gamma = delta / ig.getMu();
+ alpha = Math.sqrt(gamma*gamma + beta*beta);
+ setParams (alpha, beta, mu, delta);
+ this.genN = ng;
+ this.genIG = ig;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianIGGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianIGGen.tex
new file mode 100644
index 0000000..6f780eb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalInverseGaussianIGGen.tex
@@ -0,0 +1,123 @@
+\defmodule {NormalInverseGaussianIGGen}
+
+% \newcommand{\nig}{\emph{normal inverse gaussian}}
+
+This class implements a \nig{} (${NIG}$) random variate generator by
+using a normal generator ($N$) and an inverse gaussian generator ($IG$),
+ as described in the following \cite{fWEB03a,fKAL07a}
+\begin {eqnarray}
+ Y &\sim& {IG}(\delta/\gamma, \delta^2) \label{nig2} \\
+ X \mid (Y=y) &\sim& N(\mu + \beta y, y). \nonumber
+\end {eqnarray}
+The normal $N(\mu, \sigma^2)$ has mean $\mu$ and variance $\sigma^2$, while
+the inverse gaussian has the parametrization described in%
+\begin{htmlonly}
+\externalclass{umontreal.iro.lecuyer.randvar}{InverseGaussianGen}.
+\end{htmlonly}
+\begin{latexonly}%
+equation (\ref{eq:fInverseGaussian}) on page \pageref{eq:fInverseGaussian}.
+\end{latexonly}
+If $\gamma = \sqrt{\alpha^2 - \beta^2}$ with $0 \le |\beta| < \alpha$ and
+$\delta >0$, then $X \sim {NIG}(\alpha, \beta, \mu, \delta)$.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalInverseGaussianIGGen
+ * Description: normal inverse gaussian random variate generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since June 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalInverseGaussianIGGen extends NormalInverseGaussianGen \begin{hide} {
+ private NormalGen genN;
+ private InverseGaussianGen genIG;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NormalInverseGaussianIGGen (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) \begin{hide} {
+ super (null, null);
+ setParams (ig, ng, beta, mu);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \nig{} random variate generator with parameters
+ $\alpha$, $\beta$ = \texttt{beta}, $\mu$ = \texttt{mu} and $\delta$,
+ using generators \texttt{ig} and \texttt{ng}, as described
+\begin{htmlonly} above. \end{htmlonly}\begin{latexonly}
+ in eq. (\ref{nig2}).\end{latexonly}
+ The parameters $\alpha$ and $\delta$ are included in generator \texttt{ig}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) \begin{hide} {
+ return mynig (ig, ng, beta, mu);
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a new variate from the \nig{} distribution with
+parameters $\alpha$, $\beta$ = \texttt{beta}, $\mu$ = \texttt{mu} and $\delta$,
+using generators \texttt{ig} and \texttt{ng}, as described in eq. (\ref{nig2}).
+The parameters $\alpha$ and $\delta$ are included in generator \texttt{ig}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return mynig (genIG, genN, beta, mu);
+ }
+
+
+// >>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<
+
+ private static double mynig (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) {
+
+ double y = ig.nextDouble ();
+ double x = mu + beta*y + Math.sqrt(y)*ng.nextDouble ();
+ return x;
+ }
+
+
+ protected void setParams (InverseGaussianGen ig, NormalGen ng,
+ double beta, double mu) {
+ delta = Math.sqrt(ig.getLambda());
+ gamma = delta / ig.getMu();
+ alpha = Math.sqrt(gamma*gamma + beta*beta);
+ setParams (alpha, beta, mu, delta);
+ this.genN = ng;
+ this.genIG = ig;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalKindermannRamageGen.java b/source/umontreal/iro/lecuyer/randvar/NormalKindermannRamageGen.java
new file mode 100644
index 0000000..e5f11cb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalKindermannRamageGen.java
@@ -0,0 +1,157 @@
+
+
+/*
+ * Class: NormalKindermannRamageGen
+ * Description: normal random variate generators using the Kindermann-Ramage method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>normal</EM> random variate generators using
+ * the <EM>Kindermann-Ramage</EM> method.
+ * The code was taken from UNURAN. It includes the correction
+ * of the error in the original <EM>Kindermann-Ramage</EM> method found by the
+ * authors in.
+ *
+ */
+public class NormalKindermannRamageGen extends NormalGen {
+
+
+
+ /**
+ * Creates a normal random variate generator with mean <TT>mu</TT>
+ * and standard deviation <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalKindermannRamageGen (RandomStream s,
+ double mu, double sigma) {
+ super (s, null);
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a standard normal random variate generator with mean
+ * <TT>0</TT> and standard deviation <TT>1</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalKindermannRamageGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a random variate generator for the normal distribution
+ * <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public NormalKindermannRamageGen (RandomStream s, NormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ public double nextDouble() {
+ return kindermanRamage (stream, mu, sigma);
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ return kindermanRamage (s, mu, sigma);
+ }
+ /**
+ * Generates a variate from the normal distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double kindermanRamage (RandomStream stream, double mu, double sigma) {
+ final double XI = 2.216035867166471;
+ final double PIhochK = 0.3989422804;
+ double U, V, W, X;
+ double t, z;
+
+ U = stream.nextDouble();
+ if (U < 0.884070402298758) {
+ V = stream.nextDouble();
+ X = XI*(1.131131635444180*U + V - 1.);
+ }
+
+ else if (U >= 0.973310954173898) {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ if (W == 0.) { t=0.; continue; }
+ t = XI*XI/2. - Math.log (W);
+ } while ( (V*V*t) > (XI*XI/2.) );
+ X = (U < 0.986655477086949) ? Math.pow (2*t,0.5) : -Math.pow (2*t,0.5);
+ }
+
+ else if (U >= 0.958720824790463) {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ z = V - W;
+ t = XI - 0.630834801921960*Math.min (V, W);
+ } while (Math.max (V, W) > 0.755591531667601 && 0.034240503750111*
+ Math.abs (z) > (PIhochK*Math.exp (t*t/(-2.)) -
+ 0.180025191068563*(XI - Math.abs (t))) );
+ X = (z < 0) ? t : -t;
+ }
+
+ else if (U >= 0.911312780288703) {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ z = V - W;
+ t = 0.479727404222441 + 1.105473661022070*Math.min (V, W);
+ } while (Math.max (V, W) > 0.872834976671790 && 0.049264496373128*
+ Math.abs (z) > (PIhochK*Math.exp (t*t/(-2))
+ - 0.180025191068563*(XI - Math.abs (t))) );
+ X = (z < 0) ? t : -t;
+ }
+
+ else {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ z = V - W;
+ t = 0.479727404222441 - 0.595507138015940*Math.min (V, W);
+ if (t < 0.0)
+ continue;
+ } while (Math.max (V, W) > 0.805777924423817 && 0.053377549506886*
+ Math.abs (z) > (PIhochK*Math.exp (t*t/(-2)) -
+ 0.180025191068563*(XI - Math.abs (t))) );
+ X = (z < 0) ? t : -t;
+ }
+
+ return mu + sigma*X;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalKindermannRamageGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalKindermannRamageGen.tex
new file mode 100644
index 0000000..b8c68e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalKindermannRamageGen.tex
@@ -0,0 +1,165 @@
+\defmodule {NormalKindermannRamageGen}
+
+This class implements {\em normal\/} random variate generators using
+ the {\em Kindermann-Ramage\/} method \cite{rKIN76a}.
+The code was taken from UNURAN \cite{iLEY02a}. It includes the correction
+of the error in the original {\em Kindermann-Ramage\/} method found by the
+authors in \cite{rTIR04a}.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalKindermannRamageGen
+ * Description: normal random variate generators using the Kindermann-Ramage method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalKindermannRamageGen extends NormalGen \begin{hide} {
+
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NormalKindermannRamageGen (RandomStream s,
+ double mu, double sigma) \begin{hide} {
+ super (s, null);
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a normal random variate generator with mean \texttt{mu}
+ and standard deviation \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalKindermannRamageGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a standard normal random variate generator with mean
+ \texttt{0} and standard deviation \texttt{1}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalKindermannRamageGen (RandomStream s, NormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a random variate generator for the normal distribution
+ \texttt{dist} and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+%%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ return kindermanRamage (stream, mu, sigma);
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ return kindermanRamage (s, mu, sigma);
+ }
+\end{code}
+ \begin{tabb} Generates a variate from the normal distribution with
+ parameters $\mu = $~\texttt{mu} and $\sigma = $~\texttt{sigma}, using
+ stream \texttt{s}.
+ \end{tabb}
+
+\begin{code}
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+ private static double kindermanRamage (RandomStream stream, double mu, double sigma) {
+ final double XI = 2.216035867166471;
+ final double PIhochK = 0.3989422804;
+ double U, V, W, X;
+ double t, z;
+
+ U = stream.nextDouble();
+ if (U < 0.884070402298758) {
+ V = stream.nextDouble();
+ X = XI*(1.131131635444180*U + V - 1.);
+ }
+
+ else if (U >= 0.973310954173898) {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ if (W == 0.) { t=0.; continue; }
+ t = XI*XI/2. - Math.log (W);
+ } while ( (V*V*t) > (XI*XI/2.) );
+ X = (U < 0.986655477086949) ? Math.pow (2*t,0.5) : -Math.pow (2*t,0.5);
+ }
+
+ else if (U >= 0.958720824790463) {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ z = V - W;
+ t = XI - 0.630834801921960*Math.min (V, W);
+ } while (Math.max (V, W) > 0.755591531667601 && 0.034240503750111*
+ Math.abs (z) > (PIhochK*Math.exp (t*t/(-2.)) -
+ 0.180025191068563*(XI - Math.abs (t))) );
+ X = (z < 0) ? t : -t;
+ }
+
+ else if (U >= 0.911312780288703) {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ z = V - W;
+ t = 0.479727404222441 + 1.105473661022070*Math.min (V, W);
+ } while (Math.max (V, W) > 0.872834976671790 && 0.049264496373128*
+ Math.abs (z) > (PIhochK*Math.exp (t*t/(-2))
+ - 0.180025191068563*(XI - Math.abs (t))) );
+ X = (z < 0) ? t : -t;
+ }
+
+ else {
+ do {
+ V = stream.nextDouble();
+ W = stream.nextDouble();
+ z = V - W;
+ t = 0.479727404222441 - 0.595507138015940*Math.min (V, W);
+ if (t < 0.0)
+ continue;
+ } while (Math.max (V, W) > 0.805777924423817 && 0.053377549506886*
+ Math.abs (z) > (PIhochK*Math.exp (t*t/(-2)) -
+ 0.180025191068563*(XI - Math.abs (t))) );
+ X = (z < 0) ? t : -t;
+ }
+
+ return mu + sigma*X;
+ }
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalPolarGen.java b/source/umontreal/iro/lecuyer/randvar/NormalPolarGen.java
new file mode 100644
index 0000000..d535ff4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalPolarGen.java
@@ -0,0 +1,121 @@
+
+
+/*
+ * Class: NormalPolarGen
+ * Description: normal random variate generators using the polar method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>normal</EM> random variate generators using
+ * the <EM>polar method with rejection</EM>.
+ * Since the method generates two variates at a time,
+ * the second variate is returned upon the next call to {@link #nextDouble nextDouble}.
+ *
+ */
+public class NormalPolarGen extends NormalGen {
+
+ // used by polar method which calculate always two random values;
+ private boolean available = false;
+ private double[] variates = new double[2];
+ private static double[] staticVariates = new double[2];
+
+
+ /**
+ * Creates a normal random variate generator with mean <TT>mu</TT>
+ * and standard deviation <TT>sigma</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalPolarGen (RandomStream s, double mu, double sigma) {
+ super (s, null);
+ setParams (mu, sigma);
+ }
+
+
+ /**
+ * Creates a standard normal random variate generator with <SPAN CLASS="MATH"><I>μ</I> = 0</SPAN>
+ * and <SPAN CLASS="MATH"><I>σ</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public NormalPolarGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a random variate generator for
+ * the normal distribution <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public NormalPolarGen (RandomStream s, NormalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }
+
+
+ public double nextDouble() {
+ if (available) {
+ available = false;
+ return mu + sigma*variates[1];
+ }
+ else {
+ polar (stream, mu, sigma, variates);
+ available = true;
+ return mu + sigma*variates[0];
+ }
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ polar (s, mu, sigma, staticVariates);
+ return mu + sigma*staticVariates[0];
+ }
+ /**
+ * Generates a variate from the normal distribution with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+ // Polar method with rejection
+ private static void polar (RandomStream stream, double mu,
+ double sigma, double[] variates) {
+ double x, y, s;
+ do {
+ x = 2*stream.nextDouble() - 1;
+ y = 2*stream.nextDouble() - 1;
+ s = x*x + y*y;
+ } while (s > 1.0 || s == 0.0);
+
+ double temp = Math.sqrt (-2.0*Math.log (s)/s);
+
+ variates[0] = y*temp;
+ variates[1] = x*temp;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/NormalPolarGen.tex b/source/umontreal/iro/lecuyer/randvar/NormalPolarGen.tex
new file mode 100644
index 0000000..3f88b50
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/NormalPolarGen.tex
@@ -0,0 +1,130 @@
+\defmodule {NormalPolarGen}
+
+This class implements {\em normal\/} random variate generators using
+ the {\em polar method with rejection} \cite{rMAR62a}.
+Since the method generates two variates at a time,
+the second variate is returned upon the next call to \method{nextDouble}{}.
+% For all the methods, the code was taken from UNURAN \cite{iLEY02a}.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalPolarGen
+ * Description: normal random variate generators using the polar method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class NormalPolarGen extends NormalGen \begin{hide} {
+
+ // used by polar method which calculate always two random values;
+ private boolean available = false;
+ private double[] variates = new double[2];
+ private static double[] staticVariates = new double[2];
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public NormalPolarGen (RandomStream s, double mu, double sigma) \begin{hide} {
+ super (s, null);
+ setParams (mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a normal random variate generator with mean \texttt{mu}
+ and standard deviation \texttt{sigma}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalPolarGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a standard normal random variate generator with $\mu = 0$
+ and $\sigma=1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public NormalPolarGen (RandomStream s, NormalDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getMu(), dist.getSigma());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a random variate generator for
+ the normal distribution \texttt{dist} and stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+%%%%%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ if (available) {
+ available = false;
+ return mu + sigma*variates[1];
+ }
+ else {
+ polar (stream, mu, sigma, variates);
+ available = true;
+ return mu + sigma*variates[0];
+ }
+ }
+
+ public static double nextDouble (RandomStream s, double mu, double sigma) {
+ polar (s, mu, sigma, staticVariates);
+ return mu + sigma*staticVariates[0];
+ }
+\end{code}
+ \begin{tabb} Generates a variate from the normal distribution with
+ parameters $\mu = $~\texttt{mu} and $\sigma = $~\texttt{sigma}, using
+ stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
+ // Polar method with rejection
+ private static void polar (RandomStream stream, double mu,
+ double sigma, double[] variates) {
+ double x, y, s;
+ do {
+ x = 2*stream.nextDouble() - 1;
+ y = 2*stream.nextDouble() - 1;
+ s = x*x + y*y;
+ } while (s > 1.0 || s == 0.0);
+
+ double temp = Math.sqrt (-2.0*Math.log (s)/s);
+
+ variates[0] = y*temp;
+ variates[1] = x*temp;
+ }
+
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/ParetoGen.java b/source/umontreal/iro/lecuyer/randvar/ParetoGen.java
new file mode 100644
index 0000000..7c7f22e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ParetoGen.java
@@ -0,0 +1,121 @@
+
+
+/*
+ * Class: ParetoGen
+ * Description: random variate generators for the Pareto distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for one of the <EM>Pareto</EM>
+ * distributions, with parameters <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>αβ</I><SUP><I>α</I></SUP> / <I>x</I><SUP><I>α</I>+1</SUP> for <I>x</I> > <I>β</I>, 0 otherwise.
+ * </DIV><P></P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class ParetoGen extends RandomVariateGen {
+ protected double alpha;
+ protected double beta;
+
+
+ /**
+ * Creates a Pareto random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public ParetoGen (RandomStream s, double alpha, double beta) {
+ super (s, new ParetoDist(alpha, beta));
+ setParams(alpha, beta);
+ }
+
+
+ /**
+ * Creates a Pareto random variate generator with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN>
+ * <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public ParetoGen (RandomStream s, double alpha) {
+ this (s, alpha, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the Pareto distribution
+ * <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public ParetoGen (RandomStream s, ParetoDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }
+
+
+ /**
+ * Generates a new variate from the Pareto distribution
+ * with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta) {
+ return ParetoDist.inverseF (alpha, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/ParetoGen.tex b/source/umontreal/iro/lecuyer/randvar/ParetoGen.tex
new file mode 100644
index 0000000..90417a3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/ParetoGen.tex
@@ -0,0 +1,135 @@
+\defmodule {ParetoGen}
+
+This class implements random variate generators for one of the {\em Pareto\/}
+distributions, with parameters $\alpha>0$ and $\beta>0$.
+Its density function is
+\begin{htmlonly}
+\eq
+f(x) = \alpha\beta^{\alpha}\,/\, x^{\alpha+1}\mbox{ for }x>\beta, 0 \mbox{ otherwise.}
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f (x) = \left\{\begin{array}{ll}
+ {\displaystyle\frac{\alpha\beta^\alpha}{x^{\alpha+1}}}
+ &\mbox{ for }x>\beta\\[12pt]
+ 0 & \mbox{ for }x\le\beta
+ \end{array}\right. \eqlabel{eq:fpareto}
+\endeq
+\end{latexonly}
+%
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ParetoGen
+ * Description: random variate generators for the Pareto distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class ParetoGen extends RandomVariateGen \begin{hide} {
+ protected double alpha;
+ protected double beta;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ParetoGen (RandomStream s, double alpha, double beta) \begin{hide} {
+ super (s, new ParetoDist(alpha, beta));
+ setParams(alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Pareto random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta = $ \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ParetoGen (RandomStream s, double alpha) \begin{hide} {
+ this (s, alpha, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Pareto random variate generator with parameters $\alpha =$
+ \texttt{alpha} and $\beta = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public ParetoGen (RandomStream s, ParetoDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the Pareto distribution
+ \texttt{dist} and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta)\begin{hide} {
+ return ParetoDist.inverseF (alpha, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a new variate from the Pareto distribution
+ with parameters $\alpha = $~\texttt{alpha} and $\beta = $~\texttt{beta},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\beta$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/PascalConvolutionGen.java b/source/umontreal/iro/lecuyer/randvar/PascalConvolutionGen.java
new file mode 100644
index 0000000..2310ecb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PascalConvolutionGen.java
@@ -0,0 +1,91 @@
+
+
+/*
+ * Class: PascalConvolutionGen
+ * Description: Pascal random variate generators using the convolution method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * Implements <SPAN CLASS="textit">Pascal</SPAN> random variate generators by
+ * the <SPAN CLASS="textit">convolution</SPAN> method.
+ * The method generates <SPAN CLASS="MATH"><I>n</I></SPAN> geometric variates with probability <SPAN CLASS="MATH"><I>p</I></SPAN>
+ * and adds them up.
+ *
+ * <P>
+ * The algorithm is slow if <SPAN CLASS="MATH"><I>n</I></SPAN> is large.
+ *
+ */
+public class PascalConvolutionGen extends PascalGen {
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">Pascal</SPAN> random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public PascalConvolutionGen (RandomStream s, int n, double p) {
+ super (s, null);
+ setParams (n, p);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+ public PascalConvolutionGen (RandomStream s, PascalDist dist) {
+ super (s, dist);
+ }
+
+
+ public int nextInt() {
+ int x = 0;
+ for (int i = 0; i < n; i++)
+ x += GeometricDist.inverseF (p, stream.nextDouble());
+ return x;
+
+ }
+
+ public static int nextInt (RandomStream s, int n, double p) {
+ return convolution (s, n, p);
+ }
+ /**
+ * Generates a new variate from the <SPAN CLASS="textit">Pascal</SPAN> distribution,
+ * with parameters <SPAN CLASS="MATH"><I>n</I> =</SPAN> <TT>n</TT> and <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>p</TT>, using the stream <TT>s</TT>.
+ *
+ */
+
+
+ private static int convolution (RandomStream stream, int n, double p) {
+ int x = 0;
+ for (int i = 0; i < n; i++)
+ x += GeometricDist.inverseF (p, stream.nextDouble());
+ return x;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/PascalConvolutionGen.tex b/source/umontreal/iro/lecuyer/randvar/PascalConvolutionGen.tex
new file mode 100644
index 0000000..c346e5f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PascalConvolutionGen.tex
@@ -0,0 +1,103 @@
+\defmodule {PascalConvolutionGen}
+
+Implements \emph{Pascal} random variate generators by
+the \emph{convolution} method\latex{ (see \cite{sLAW00a})}.
+The method generates $n$ geometric variates with probability $p$
+and adds them up.
+
+The algorithm is slow if $n$ is large.
+% A local copy of the parameters $n$ and $p$ is maintained in this class.
+
+
+\bigskip\hrule
+
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PascalConvolutionGen
+ * Description: Pascal random variate generators using the convolution method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class PascalConvolutionGen extends PascalGen \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public PascalConvolutionGen (RandomStream s, int n, double p)\begin{hide} {
+ super (s, null);
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a \emph{Pascal} random variate generator with parameters
+ $n$ and $p$, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public PascalConvolutionGen (RandomStream s, PascalDist dist)\begin{hide} {
+ super (s, dist);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates a new generator for the distribution \texttt{dist}, using
+ stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public int nextInt() {
+ int x = 0;
+ for (int i = 0; i < n; i++)
+ x += GeometricDist.inverseF (p, stream.nextDouble());
+ return x;
+
+ }
+
+ public static int nextInt (RandomStream s, int n, double p) {
+ return convolution (s, n, p);
+ }
+\end{code}
+ \begin{tabb}
+ Generates a new variate from the \emph{Pascal} distribution,
+ with parameters $n = $~\texttt{n} and $p = $~\texttt{p}, using the stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ private static int convolution (RandomStream stream, int n, double p) {
+ int x = 0;
+ for (int i = 0; i < n; i++)
+ x += GeometricDist.inverseF (p, stream.nextDouble());
+ return x;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/PascalGen.java b/source/umontreal/iro/lecuyer/randvar/PascalGen.java
new file mode 100644
index 0000000..8995561
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PascalGen.java
@@ -0,0 +1,111 @@
+
+
+/*
+ * Class: PascalGen
+ * Description: Pascal random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * Implements Pascal random variate generators, which is a special case of the
+ * negative binomial generator with parameter <SPAN CLASS="MATH"><I>γ</I></SPAN> equal to a positive integer.
+ * See {@link NegativeBinomialGen} for a description.
+ *
+ */
+public class PascalGen extends RandomVariateGenInt {
+ protected int n;
+ protected double p;
+
+
+
+ /**
+ * Creates a Pascal random variate generator with parameters <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public PascalGen (RandomStream s, int n, double p) {
+ super (s, new PascalDist (n, p));
+ setParams (n, p);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+ public PascalGen (RandomStream s, PascalDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN1(), dist.getP());
+ }
+
+
+ /**
+ * Generates a new variate from the <SPAN CLASS="textit">Pascal</SPAN> distribution,
+ * with parameters <SPAN CLASS="MATH"><I>n</I> =</SPAN> <TT>n</TT> and <SPAN CLASS="MATH"><I>p</I> =</SPAN> <TT>p</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static int nextInt (RandomStream s, int n, double p) {
+ return PascalDist.inverseF (n, p, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> of this object.
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ *
+ */
+ public double getP() {
+ return p;
+ }
+
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of this object.
+ *
+ */
+ protected void setParams (int n, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.p = p;
+ this.n = n;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/PascalGen.tex b/source/umontreal/iro/lecuyer/randvar/PascalGen.tex
new file mode 100644
index 0000000..2df4c90
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PascalGen.tex
@@ -0,0 +1,119 @@
+\defmodule {PascalGen}
+
+Implements Pascal random variate generators, which is a special case of the
+negative binomial generator with parameter $\gamma$ equal to a positive integer.
+See \class{NegativeBinomialGen} for a description.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PascalGen
+ * Description: Pascal random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class PascalGen extends RandomVariateGenInt \begin{hide} {
+ protected int n;
+ protected double p;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public PascalGen (RandomStream s, int n, double p)\begin{hide} {
+ super (s, new PascalDist (n, p));
+ setParams (n, p);
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates a Pascal random variate generator with parameters $n$ and $p$,
+ using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public PascalGen (RandomStream s, PascalDist dist)\begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getN1(), dist.getP());
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Creates a new generator for the distribution \texttt{dist}, using
+ stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static int nextInt (RandomStream s, int n, double p)\begin{hide} {
+ return PascalDist.inverseF (n, p, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the \emph{Pascal} distribution,
+ with parameters $n = $~\texttt{n} and $p = $~\texttt{p}, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $n$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getP()\begin{hide} {
+ return p;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $p$ of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int n, double p) {
+ if (p < 0.0 || p > 1.0)
+ throw new IllegalArgumentException ("p not in [0, 1]");
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.p = p;
+ this.n = n;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $n$ and $p$ of this object.
+\end{tabb}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/Pearson5Gen.java b/source/umontreal/iro/lecuyer/randvar/Pearson5Gen.java
new file mode 100644
index 0000000..92e5296
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/Pearson5Gen.java
@@ -0,0 +1,131 @@
+
+
+/*
+ * Class: Pearson5Gen
+ * Description: random variate generators for the Pearson type V distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+ at Deprecated
+/**
+ * <SPAN CLASS="textbf">THIS CLASS HAS BEEN RENAMED {@link InverseGammaGen}</SPAN>.
+ *
+ * <P>
+ * This class implements random variate generators for
+ * the <EM>Pearson type V</EM> distribution with shape parameter
+ *
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>x</I><SUP>-(<I>α</I>+1)</SUP>exp<SUP>-<I>β</I>/x</SUP>)/(<I>β</I><SUP>-<I>α</I></SUP><I>Γ</I>(<I>α</I>)) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> otherwise,
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function.
+ *
+ */
+public class Pearson5Gen extends RandomVariateGen {
+ protected double alpha;
+ protected double beta;
+
+
+
+
+ /**
+ * <SPAN CLASS="textbf">THIS CLASS HAS BEEN RENAMED {@link InverseGammaGen}</SPAN>.
+ * Creates a Pearson5 random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public Pearson5Gen (RandomStream s, double alpha, double beta) {
+ super (s, new Pearson5Dist(alpha, beta));
+ setParams(alpha, beta);
+ }
+
+
+ /**
+ * Creates a Pearson5 random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public Pearson5Gen (RandomStream s, double alpha) {
+ this (s, alpha, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public Pearson5Gen (RandomStream s, Pearson5Dist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }
+
+
+ /**
+ * Generates a variate from the Pearson V distribution
+ * with shape parameter
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN> and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta) {
+ return Pearson5Dist.inverseF (alpha, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN> of this object.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/Pearson5Gen.tex b/source/umontreal/iro/lecuyer/randvar/Pearson5Gen.tex
new file mode 100644
index 0000000..35d0be6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/Pearson5Gen.tex
@@ -0,0 +1,143 @@
+\defmodule{Pearson5Gen}
+
+\textbf{THIS CLASS HAS BEEN RENAMED \class{InverseGammaGen}}.
+
+This class implements random variate generators for
+the {\em Pearson type V\/} distribution with shape parameter
+$\alpha > 0$ and scale parameter $\beta > 0$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = (x^{-(\alpha + 1)}\exp^{-\beta / x}) / (\beta^{-\alpha} \Gamma(\alpha))
+ \qquad \mbox{for } x > 0,
+\endeq
+ and $f(x) = 0$ otherwise,
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \left\{\begin{array}{ll} \displaystyle
+ \frac{x^{-(\alpha + 1)}e^{-\beta / x}}{\beta^{-\alpha} \Gamma(\alpha)}
+ & \quad \mbox{for } x > 0 \\[12pt]
+ 0 & \quad \mbox{otherwise,}
+ \end{array} \right.
+ \eqlabel{eq:fpearson5}
+\endeq
+\end{latexonly}
+where $\Gamma$ is the gamma function.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: Pearson5Gen
+ * Description: random variate generators for the Pearson type V distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;
+\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+ at Deprecated
+public class Pearson5Gen extends RandomVariateGen \begin{hide} {
+ protected double alpha;
+ protected double beta;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Pearson5Gen (RandomStream s, double alpha, double beta) \begin{hide} {
+ super (s, new Pearson5Dist(alpha, beta));
+ setParams(alpha, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb}\textbf{THIS CLASS HAS BEEN RENAMED \class{InverseGammaGen}}.
+ Creates a Pearson5 random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\beta =$ \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public Pearson5Gen (RandomStream s, double alpha) \begin{hide} {
+ this (s, alpha, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Pearson5 random variate generator with parameters
+ $\alpha =$ \texttt{alpha} and $\beta = 1$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public Pearson5Gen (RandomStream s, Pearson5Dist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha(), dist.getBeta());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s,
+ double alpha, double beta)\begin{hide} {
+ return Pearson5Dist.inverseF (alpha, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the Pearson V distribution
+ with shape parameter $\alpha > 0$ and scale parameter $\beta > 0$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$ of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\beta$ of this object.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void setParams (double alpha, double beta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.alpha = alpha;
+ this.beta = beta;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/Pearson6Gen.java b/source/umontreal/iro/lecuyer/randvar/Pearson6Gen.java
new file mode 100644
index 0000000..bc72df4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/Pearson6Gen.java
@@ -0,0 +1,152 @@
+
+
+/*
+ * Class: Pearson6Gen
+ * Description: random variate generators for the Pearson type VI distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for
+ * the <EM>Pearson type VI</EM> distribution with shape parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB> > 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB> > 0</SPAN>, and scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ * The density function of this distribution is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = (<I>x</I>/<I>β</I>)<SUP><I>α</I><SUB>1</SUB>-1</SUP>/(<I>βB</I>(<I>α</I><SUB>1</SUB>, <I>α</I><SUB>2</SUB>)[1 + <I>x</I>/<I>β</I>]<SUP><I>α</I><SUB>1</SUB>+<I>α</I><SUB>2</SUB></SUP>) for <I>x</I> > 0,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> otherwise,
+ * where <SPAN CLASS="MATH"><I>B</I></SPAN> is the beta function.
+ *
+ */
+public class Pearson6Gen extends RandomVariateGen {
+ protected double alpha1;
+ protected double alpha2;
+ protected double beta;
+
+
+
+
+ /**
+ * Creates a Pearson6 random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> = <TT>alpha1</TT>, <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> = <TT>alpha2</TT> and <SPAN CLASS="MATH"><I>β</I></SPAN> =
+ * <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public Pearson6Gen (RandomStream s, double alpha1, double alpha2,
+ double beta) {
+ super (s, new Pearson6Dist(alpha1, alpha2, beta));
+ setParams (alpha1, alpha2, beta);
+ }
+
+
+ /**
+ * Creates a Pearson6 random variate generator with parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB> =</SPAN> <TT>alpha1</TT>, <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> = <TT>alpha2</TT> and <SPAN CLASS="MATH"><I>β</I> = 1</SPAN>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public Pearson6Gen (RandomStream s, double alpha1, double alpha2) {
+ this (s, alpha1, alpha2, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public Pearson6Gen (RandomStream s, Pearson6Dist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha1(), dist.getAlpha2(), dist.getBeta());
+ }
+
+
+ /**
+ * Generates a variate from the Pearson VI distribution
+ * with shape parameters
+ * <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB> > 0</SPAN> and
+ * <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB> > 0</SPAN>, and
+ * scale parameter <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha1,
+ double alpha2, double beta) {
+ return Pearson6Dist.inverseF (alpha1, alpha2, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN> parameter of this object.
+ *
+ */
+ public double getAlpha1() {
+ return alpha1;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> parameter of this object.
+ *
+ */
+ public double getAlpha2() {
+ return alpha2;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>β</I></SPAN> parameter of this object.
+ *
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB></SPAN>, <SPAN CLASS="MATH"><I>α</I><SUB>2</SUB></SPAN> and <SPAN CLASS="MATH"><I>β</I></SPAN> of this object.
+ *
+ */
+ public void setParams (double alpha1, double alpha2, double beta) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ this.alpha1 = alpha1;
+ this.alpha2 = alpha2;
+ this.beta = beta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/Pearson6Gen.tex b/source/umontreal/iro/lecuyer/randvar/Pearson6Gen.tex
new file mode 100644
index 0000000..2ef3d8e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/Pearson6Gen.tex
@@ -0,0 +1,164 @@
+\defmodule{Pearson6Gen}
+
+This class implements random variate generators for
+the {\em Pearson type VI\/} distribution with shape parameters
+$\alpha_1 > 0$ and $\alpha_2 > 0$, and scale parameter $\beta > 0$.
+The density function of this distribution is
+\begin{htmlonly}
+\eq
+ f(x) = (x / \beta)^{\alpha_{1} - 1} / (\beta \mathcal{B}(\alpha_{1}, \alpha_{2})[1 + x/\beta]^{\alpha_{1} + \alpha_{2}})
+ \qquad \mbox{for } x > 0,
+\endeq
+ and $f(x) = 0$ otherwise,
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) =\left\{\begin{array}{ll} \displaystyle
+ \frac{\left({x}/{\beta}\right)^{\alpha_{1} - 1}}{\beta \mathcal{B}(\alpha_{1}, \alpha_{2})(1 + {x}/{\beta})^{\alpha_{1} + \alpha_{2}}}
+ & \quad \mbox{for } x > 0, \\[14pt]
+ 0 & \quad \mbox{otherwise,}
+ \end{array} \right.
+ \eqlabel{eq:fpearson6}
+\endeq
+\end{latexonly}
+where $\mathcal{B}$ is the beta function.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: Pearson6Gen
+ * Description: random variate generators for the Pearson type VI distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;
+\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class Pearson6Gen extends RandomVariateGen \begin{hide} {
+ protected double alpha1;
+ protected double alpha2;
+ protected double beta;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Pearson6Gen (RandomStream s, double alpha1, double alpha2,
+ double beta) \begin{hide} {
+ super (s, new Pearson6Dist(alpha1, alpha2, beta));
+ setParams (alpha1, alpha2, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Pearson6 random variate generator with parameters
+ $\alpha_1$ = \texttt{alpha1}, $\alpha_2$ = \texttt{alpha2} and $\beta$ =
+ \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public Pearson6Gen (RandomStream s, double alpha1, double alpha2) \begin{hide} {
+ this (s, alpha1, alpha2, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Pearson6 random variate generator with parameters
+ $\alpha_1 =$ \texttt{alpha1}, $\alpha_2$ = \texttt{alpha2} and $\beta=1$,
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public Pearson6Gen (RandomStream s, Pearson6Dist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams(dist.getAlpha1(), dist.getAlpha2(), dist.getBeta());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist},
+ using stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha1,
+ double alpha2, double beta)\begin{hide} {
+ return Pearson6Dist.inverseF (alpha1, alpha2, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a variate from the Pearson VI distribution
+ with shape parameters $\alpha_1 > 0$ and $\alpha_2 > 0$, and
+ scale parameter $\beta > 0$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha1()\begin{hide} {
+ return alpha1;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\alpha_1$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha2()\begin{hide} {
+ return alpha2;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\alpha_2$ parameter of this object.
+\end{tabb}
+\begin{code}
+
+ public double getBeta()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\beta$ parameter of this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public void setParams (double alpha1, double alpha2, double beta) {
+ if (alpha1 <= 0.0)
+ throw new IllegalArgumentException("alpha1 <= 0");
+ if (alpha2 <= 0.0)
+ throw new IllegalArgumentException("alpha2 <= 0");
+ if (beta <= 0.0)
+ throw new IllegalArgumentException("beta <= 0");
+ this.alpha1 = alpha1;
+ this.alpha2 = alpha2;
+ this.beta = beta;
+ }
+\end{code}
+\begin{tabb}
+ Sets the parameters $\alpha_1$, $\alpha_2$ and $\beta$ of this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/PoissonGen.java b/source/umontreal/iro/lecuyer/randvar/PoissonGen.java
new file mode 100644
index 0000000..0624343
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PoissonGen.java
@@ -0,0 +1,108 @@
+
+
+/*
+ * Class: PoissonGen
+ * Description: random variate generators having the Poisson distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;
+
+/**
+ * This class implements random variate generators having the <EM>Poisson</EM>
+ * distribution. Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = <I>e</I><SUP>-<I>λ</I></SUP><I>λ</I><SUP>x</SUP>/(<I>x</I>!) for <I>x</I> = 0, 1,...
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN> is a real valued parameter equal to the mean.
+ *
+ * <P>
+ * No local copy of the parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>
+ * is maintained in this class.
+ * The (non-static) <TT>nextInt</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class PoissonGen extends RandomVariateGenInt {
+ protected double lambda;
+
+
+
+ /**
+ * Creates a Poisson random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public PoissonGen (RandomStream s, double lambda) {
+ super (s, new PoissonDist (lambda));
+ setParams (lambda);
+ }
+
+
+ /**
+ * Creates a new random variate generator using the Poisson
+ * distribution <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public PoissonGen (RandomStream s, PoissonDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getLambda());
+ }
+
+
+ /**
+ * A static method for generating a random variate from a
+ * <EM>Poisson</EM> distribution with parameter <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT>.
+ *
+ */
+ public static int nextInt (RandomStream s, double lambda) {
+ return PoissonDist.inverseF (lambda, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>λ</I></SPAN> associated with this object.
+ *
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+
+ /**
+ * Sets the parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lam</TT> of this object.
+ *
+ */
+ protected void setParams (double lam) {
+ if (lam <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lam;
+ }
+
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/PoissonGen.tex b/source/umontreal/iro/lecuyer/randvar/PoissonGen.tex
new file mode 100644
index 0000000..ff8e3d2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PoissonGen.tex
@@ -0,0 +1,119 @@
+\defmodule {PoissonGen}
+
+This class implements random variate generators having the {\em Poisson\/}
+distribution. Its mass function is
+\begin{htmlonly}
+\eq
+p(x) = e^{-\lambda}\lambda^x/(x!)\mbox{ for }x=0,1,\dots
+\endeq
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ p(x) = \frac{e^{-\lambda} \lambda^x}{x!} \qquad\mbox{for } x=0,1,\dots,
+ \label{eq:fmass-Poisson}
+\endeq
+\end{latexonly}
+where $\lambda > 0$ is a real valued parameter equal to the mean.
+
+No local copy of the parameter $\lambda = $ \texttt{lambda}
+is maintained in this class.
+The (non-static) \texttt{nextInt} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: PoissonGen
+ * Description: random variate generators having the Poisson distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;\end{hide}
+
+public class PoissonGen extends RandomVariateGenInt \begin{hide} {
+ protected double lambda;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public PoissonGen (RandomStream s, double lambda) \begin{hide} {
+ super (s, new PoissonDist (lambda));
+ setParams (lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a Poisson random variate generator with
+ parameter $\lambda = $ \texttt{lambda}, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public PoissonGen (RandomStream s, PoissonDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getLambda());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new random variate generator using the Poisson
+ distribution \texttt{dist} and stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static int nextInt (RandomStream s, double lambda) \begin{hide} {
+ return PoissonDist.inverseF (lambda, s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ A static method for generating a random variate from a
+ {\em Poisson\/} distribution with parameter $\lambda$ = \texttt{lambda}.
+ \end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $\lambda$ associated with this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (double lam) {
+ if (lam <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.lambda = lam;
+ }
+\end{code}
+\begin{tabb} Sets the parameter $\lambda = $ \texttt{lam} of this object.
+\end{tabb}
+\begin{code}
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/PoissonTIACGen.java b/source/umontreal/iro/lecuyer/randvar/PoissonTIACGen.java
new file mode 100644
index 0000000..3121dfb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PoissonTIACGen.java
@@ -0,0 +1,289 @@
+
+
+/*
+ * Class: PoissonTIACGen
+ * Description: random variate generators having the Poisson distribution
+ using the tabulated inversion combined with the acceptance
+ complement method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;
+
+/**
+ * This class implements random variate generators having the <EM>Poisson</EM>
+ * distribution (see {@link PoissonGen}).
+ * Uses the tabulated inversion combined with the acceptance complement
+ * (<EM>TIAC</EM>) method of.
+ * The implementation is adapted from UNURAN.
+ *
+ */
+public class PoissonTIACGen extends PoissonGen {
+
+ private double[] pp = new double[36];
+ private int[] llref = {0};
+ private static double[] staticPP = new double[36];
+ private static int[] staticllref = {0};
+ // Used by TIAC, avoid creating a table upon each call.
+
+
+
+ /**
+ * Creates a Poisson random variate generator with
+ * parameter <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public PoissonTIACGen (RandomStream s, double lambda) {
+ super (s, null);
+ init (lambda);
+ }
+
+
+ /**
+ * Creates a new random variate generator using the Poisson
+ * distribution <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public PoissonTIACGen (RandomStream s, PoissonDist dist) {
+ super (s, dist);
+ init (dist.getLambda ());
+ }
+
+
+ public int nextInt() {
+ return tiac (stream, lambda, pp, llref);
+ }
+
+ public static int nextInt (RandomStream s, double lambda) {
+ return tiac (s, lambda, staticPP, staticllref);
+ }
+
+
+
+ private void init (double lam) {
+ setParams (lam);
+ for (int i = 0; i < pp.length; i++)
+ pp[i] = 0.0;
+ }
+
+
+/* **************************************************************************
+ * GENERATION METHOD : Tabulated Inversion combined with *
+ * Acceptance Complement *
+ * *
+ *****************************************************************************
+ * *
+ * METHOD : - samples a random number from the Poisson distribution with *
+ * parameter lambda > 0. *
+ * Tabulated Inversion for lambda < 10 *
+ * Acceptance Complement for lambda >= 10. *
+ *---------------------------------------------------------------------------*
+ * *
+ * CODE REFERENCE : The code is adapted from UNURAN *
+ * UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien *
+ * *
+ *---------------------------------------------------------------------------*
+ * *
+ * REFERENCE: - J.H. Ahrens, U. Dieter (1982): Computer generation of *
+ * Poisson deviates from modified normal distributions, *
+ * ACM Trans. Math. Software 8, 163-179. *
+ * *
+ * Implemented by R. Kremer, August 1990 *
+ * Revised by E. Stadlober, April 1992 *
+ *****************************************************************************
+ * WinRand (c) 1995 Ernst Stadlober, Institut fuer Statistitk, TU Graz *
+ *****************************************************************************
+ * UNURAN :
+ ****************************************************************************/
+ private static int tiac (RandomStream s, double lambda,
+ double[] pp, int[] llref) {
+ final double a0 = -0.5000000002;
+ final double a1 = 0.3333333343;
+ final double a2 = -0.2499998565;
+ final double a3 = 0.1999997049;
+ final double a4 = -0.1666848753;
+ final double a5 = 0.1428833286;
+ final double a6 = -0.1241963125;
+ final double a7 = 0.1101687109;
+ final double a8 = -0.1142650302;
+ final double a9 = 0.1055093006;
+ // factorial for 0 <= k <= 9
+ final int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
+
+ double u;
+ int i;
+ if (lambda < 10) {
+ int m = lambda > 1 ? (int)lambda : 1;
+ int ll = llref[0];
+ double p0, q, p;
+ p0 = q = p = Math.exp (-lambda);
+ int k = 0;
+ while (true) {
+ u = s.nextDouble(); // Step u. Uniform sample
+ k = 0;
+ if (u <= p0)
+ return k;
+
+ // Step T. Table comparison
+ if (ll != 0) {
+ i = (u > 0.458) ? Math.min(ll,m) : 1;
+ for (k = i; k <=ll; k++)
+ if (u <= pp[k]) return k;
+ if (ll == 35) continue;
+ }
+
+ // Step C. Creation of new prob.
+ for (k = ll +1; k <= 35; k++) {
+ p *= lambda / (double)k;
+ q += p;
+ pp[k] = q;
+ if (u <= q) {
+ ll = k;
+ llref[0] = ll;
+ return k;
+ }
+ }
+ ll = 35;
+ llref[0] = ll;
+ }
+ }
+ else { // lambda > 10, we use acceptance complement
+ double sl = Math.sqrt (lambda);
+ double d = 6*lambda*lambda;
+ int l = (int)(lambda - 1.1484);
+
+ // Step P. Preparations for steps Q and H
+ double omega = 0.3989423 / sl;
+ double b1 = 0.416666666667e-1/lambda;
+ double b2 = 0.3*b1*b1;
+ double c3 = 0.1428571*b1*b2;
+ double c2 = b2 - 15.0*c3;
+ double c1 = b1 - 6.0*b2 + 45.0*c3;
+ double c0 = 1.0 - b1 + 3.0*b2 - 15.0*c3;
+ double c = 0.1069/lambda;
+
+ double t,g,lambda_k;
+ double gx,gy,px,py,x,xx,delta,v;
+ int sign;
+
+ double e;
+ int k;
+
+ // Step N. Normal sample
+ // We don't use NormalGen because this could create bad side effects.
+ // With NormalGen, the behavior of this method would depend
+ // on the selected static method of NormalGen.
+ t = NormalDist.inverseF (0, 1, s.nextDouble());
+ g = lambda + sl*t;
+
+ if (g >= 0) {
+ k = (int)g;
+ // Step I. Immediate acceptance
+ if (k >= l)
+ return k;
+ // Step S. Squeeze acceptance
+ u = s.nextDouble();
+ lambda_k = lambda - k;
+ if (d*u >= lambda_k*lambda_k*lambda_k)
+ return k;
+
+ // FUNCTION F
+ if (k < 10) {
+ px = -lambda;
+ py = Math.exp (k*Math.log (lambda))/fac[k];
+ }
+ else { // k >= 10
+ delta = 0.83333333333e-1/(double)k;
+ delta = delta - 4.8*delta*delta*delta*(1.-1./(3.5*k*k));
+ v = (lambda_k)/(double)k;
+ if (Math.abs (v) > 0.25)
+ px = k*Math.log (1. + v) - lambda_k - delta;
+ else {
+ px = k*v*v;
+ px *= ((((((((a9*v+a8)*v+a7)*v+a6)*v+a5)*v+
+ a4)*v+a3)*v+a2)*v+a1)*v+a0;
+ px -= delta;
+ }
+ py = 0.3989422804 / Math.sqrt((double)k);
+ }
+ x = (0.5 - lambda_k)/sl;
+ xx = x*x;
+ gx = -0.5*xx;
+ gy = omega*(((c3*xx + c2)*xx + c1)*xx + c0);
+ // end FUNCTION F
+
+ // Step Q. Quotient acceptance
+ if (gy*(1.0 - u) <= py*Math.exp (px - gx))
+ return k;
+ }
+
+ // Step E. Double exponential sample
+ while (true) {
+ do {
+ e = - Math.log (s.nextDouble());
+ u = s.nextDouble();
+ u = u + u - 1;
+ sign = u < 0 ? -1 : 1;
+ t = 1.8 + e*sign;
+ } while (t <= -0.6744);
+ k = (int)(lambda + sl*t);
+ lambda_k = lambda - k;
+
+ // FUNCTION F
+ if (k < 10) {
+ px = -lambda;
+ py = Math.exp(k*Math.log (lambda))/fac[k];
+ }
+ else { // k >= 10
+ delta = 0.83333333333e-1/(double)k;
+ delta = delta - 4.8*delta*delta*delta*(1.0-1.0/(3.5*k*k));
+ v = lambda_k/(double)k;
+ if (Math.abs (v) > 0.25)
+ px = k*Math.log (1. + v) - lambda_k - delta;
+ else {
+ px = k*v*v;
+ px *= ((((((((a9*v+a8)*v+a7)*v+a6)*v+a5)*v+
+ a4)*v+a3)*v+a2)*v+a1)*v+a0;
+ px -= delta;
+ }
+ py = 0.3989422804/Math.sqrt((double)k);
+ }
+ x = (0.5 - lambda_k)/sl;
+ xx = x*x;
+ gx = -0.5*xx;
+ gy = omega*(((c3*xx + c2)*xx + c1)*xx + c0);
+ // end FUNCTION F
+
+ // Step H. Hat acceptance
+ if (c*sign*u <= py*Math.exp (px + e) - gy*Math.exp (gx + e))
+ return k;
+ }
+ }
+ }
+
+ static {
+ for (int i = 0; i < staticPP.length; i++)
+ staticPP[i] = 0.0;
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/PoissonTIACGen.tex b/source/umontreal/iro/lecuyer/randvar/PoissonTIACGen.tex
new file mode 100644
index 0000000..89e6cba
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PoissonTIACGen.tex
@@ -0,0 +1,307 @@
+\defmodule {PoissonTIACGen}
+
+This class implements random variate generators having the {\em Poisson\/}
+distribution (see \class{PoissonGen}).
+Uses the tabulated inversion combined with the acceptance complement
+({\em TIAC\/}) method of \cite{rAHR82b}.
+The implementation is adapted from UNURAN \cite{iLEY02a}.
+
+% A local copy of the parameter \texttt{lambda} is maintained
+% in this class.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PoissonTIACGen
+ * Description: random variate generators having the Poisson distribution
+ using the tabulated inversion combined with the acceptance
+ complement method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.rng.*;\end{hide}
+
+public class PoissonTIACGen extends PoissonGen \begin{hide} {
+
+ private double[] pp = new double[36];
+ private int[] llref = {0};
+ private static double[] staticPP = new double[36];
+ private static int[] staticllref = {0};
+ // Used by TIAC, avoid creating a table upon each call.
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public PoissonTIACGen (RandomStream s, double lambda) \begin{hide} {
+ super (s, null);
+ init (lambda);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a Poisson random variate generator with
+ parameter $\lambda = $ \texttt{lambda}, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public PoissonTIACGen (RandomStream s, PoissonDist dist) \begin{hide} {
+ super (s, dist);
+ init (dist.getLambda ());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new random variate generator using the Poisson
+ distribution \texttt{dist} and stream \texttt{s}.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%\subsubsection* {Methods}
+\begin{hide} \begin{code}
+
+ public int nextInt() {
+ return tiac (stream, lambda, pp, llref);
+ }
+
+ public static int nextInt (RandomStream s, double lambda) {
+ return tiac (s, lambda, staticPP, staticllref);
+ }
+\end{code}
+\begin{tabb} A static method for generating a random variate from a
+{\em Poisson\/} distribution with parameter $\lambda$ = \texttt{lambda}.
+\end{tabb}
+\end{hide}
+\begin{code}
+\begin{hide}
+
+ private void init (double lam) {
+ setParams (lam);
+ for (int i = 0; i < pp.length; i++)
+ pp[i] = 0.0;
+ }
+
+
+/* **************************************************************************
+ * GENERATION METHOD : Tabulated Inversion combined with *
+ * Acceptance Complement *
+ * *
+ *****************************************************************************
+ * *
+ * METHOD : - samples a random number from the Poisson distribution with *
+ * parameter lambda > 0. *
+ * Tabulated Inversion for lambda < 10 *
+ * Acceptance Complement for lambda >= 10. *
+ *---------------------------------------------------------------------------*
+ * *
+ * CODE REFERENCE : The code is adapted from UNURAN *
+ * UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien *
+ * *
+ *---------------------------------------------------------------------------*
+ * *
+ * REFERENCE: - J.H. Ahrens, U. Dieter (1982): Computer generation of *
+ * Poisson deviates from modified normal distributions, *
+ * ACM Trans. Math. Software 8, 163-179. *
+ * *
+ * Implemented by R. Kremer, August 1990 *
+ * Revised by E. Stadlober, April 1992 *
+ *****************************************************************************
+ * WinRand (c) 1995 Ernst Stadlober, Institut fuer Statistitk, TU Graz *
+ *****************************************************************************
+ * UNURAN :
+ ****************************************************************************/
+ private static int tiac (RandomStream s, double lambda,
+ double[] pp, int[] llref) {
+ final double a0 = -0.5000000002;
+ final double a1 = 0.3333333343;
+ final double a2 = -0.2499998565;
+ final double a3 = 0.1999997049;
+ final double a4 = -0.1666848753;
+ final double a5 = 0.1428833286;
+ final double a6 = -0.1241963125;
+ final double a7 = 0.1101687109;
+ final double a8 = -0.1142650302;
+ final double a9 = 0.1055093006;
+ // factorial for 0 <= k <= 9
+ final int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
+
+ double u;
+ int i;
+ if (lambda < 10) {
+ int m = lambda > 1 ? (int)lambda : 1;
+ int ll = llref[0];
+ double p0, q, p;
+ p0 = q = p = Math.exp (-lambda);
+ int k = 0;
+ while (true) {
+ u = s.nextDouble(); // Step u. Uniform sample
+ k = 0;
+ if (u <= p0)
+ return k;
+
+ // Step T. Table comparison
+ if (ll != 0) {
+ i = (u > 0.458) ? Math.min(ll,m) : 1;
+ for (k = i; k <=ll; k++)
+ if (u <= pp[k]) return k;
+ if (ll == 35) continue;
+ }
+
+ // Step C. Creation of new prob.
+ for (k = ll +1; k <= 35; k++) {
+ p *= lambda / (double)k;
+ q += p;
+ pp[k] = q;
+ if (u <= q) {
+ ll = k;
+ llref[0] = ll;
+ return k;
+ }
+ }
+ ll = 35;
+ llref[0] = ll;
+ }
+ }
+ else { // lambda > 10, we use acceptance complement
+ double sl = Math.sqrt (lambda);
+ double d = 6*lambda*lambda;
+ int l = (int)(lambda - 1.1484);
+
+ // Step P. Preparations for steps Q and H
+ double omega = 0.3989423 / sl;
+ double b1 = 0.416666666667e-1/lambda;
+ double b2 = 0.3*b1*b1;
+ double c3 = 0.1428571*b1*b2;
+ double c2 = b2 - 15.0*c3;
+ double c1 = b1 - 6.0*b2 + 45.0*c3;
+ double c0 = 1.0 - b1 + 3.0*b2 - 15.0*c3;
+ double c = 0.1069/lambda;
+
+ double t,g,lambda_k;
+ double gx,gy,px,py,x,xx,delta,v;
+ int sign;
+
+ double e;
+ int k;
+
+ // Step N. Normal sample
+ // We don't use NormalGen because this could create bad side effects.
+ // With NormalGen, the behavior of this method would depend
+ // on the selected static method of NormalGen.
+ t = NormalDist.inverseF (0, 1, s.nextDouble());
+ g = lambda + sl*t;
+
+ if (g >= 0) {
+ k = (int)g;
+ // Step I. Immediate acceptance
+ if (k >= l)
+ return k;
+ // Step S. Squeeze acceptance
+ u = s.nextDouble();
+ lambda_k = lambda - k;
+ if (d*u >= lambda_k*lambda_k*lambda_k)
+ return k;
+
+ // FUNCTION F
+ if (k < 10) {
+ px = -lambda;
+ py = Math.exp (k*Math.log (lambda))/fac[k];
+ }
+ else { // k >= 10
+ delta = 0.83333333333e-1/(double)k;
+ delta = delta - 4.8*delta*delta*delta*(1.-1./(3.5*k*k));
+ v = (lambda_k)/(double)k;
+ if (Math.abs (v) > 0.25)
+ px = k*Math.log (1. + v) - lambda_k - delta;
+ else {
+ px = k*v*v;
+ px *= ((((((((a9*v+a8)*v+a7)*v+a6)*v+a5)*v+
+ a4)*v+a3)*v+a2)*v+a1)*v+a0;
+ px -= delta;
+ }
+ py = 0.3989422804 / Math.sqrt((double)k);
+ }
+ x = (0.5 - lambda_k)/sl;
+ xx = x*x;
+ gx = -0.5*xx;
+ gy = omega*(((c3*xx + c2)*xx + c1)*xx + c0);
+ // end FUNCTION F
+
+ // Step Q. Quotient acceptance
+ if (gy*(1.0 - u) <= py*Math.exp (px - gx))
+ return k;
+ }
+
+ // Step E. Double exponential sample
+ while (true) {
+ do {
+ e = - Math.log (s.nextDouble());
+ u = s.nextDouble();
+ u = u + u - 1;
+ sign = u < 0 ? -1 : 1;
+ t = 1.8 + e*sign;
+ } while (t <= -0.6744);
+ k = (int)(lambda + sl*t);
+ lambda_k = lambda - k;
+
+ // FUNCTION F
+ if (k < 10) {
+ px = -lambda;
+ py = Math.exp(k*Math.log (lambda))/fac[k];
+ }
+ else { // k >= 10
+ delta = 0.83333333333e-1/(double)k;
+ delta = delta - 4.8*delta*delta*delta*(1.0-1.0/(3.5*k*k));
+ v = lambda_k/(double)k;
+ if (Math.abs (v) > 0.25)
+ px = k*Math.log (1. + v) - lambda_k - delta;
+ else {
+ px = k*v*v;
+ px *= ((((((((a9*v+a8)*v+a7)*v+a6)*v+a5)*v+
+ a4)*v+a3)*v+a2)*v+a1)*v+a0;
+ px -= delta;
+ }
+ py = 0.3989422804/Math.sqrt((double)k);
+ }
+ x = (0.5 - lambda_k)/sl;
+ xx = x*x;
+ gx = -0.5*xx;
+ gy = omega*(((c3*xx + c2)*xx + c1)*xx + c0);
+ // end FUNCTION F
+
+ // Step H. Hat acceptance
+ if (c*sign*u <= py*Math.exp (px + e) - gy*Math.exp (gx + e))
+ return k;
+ }
+ }
+ }
+
+ static {
+ for (int i = 0; i < staticPP.length; i++)
+ staticPP[i] = 0.0;
+ }
+}
+\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/PowerGen.java b/source/umontreal/iro/lecuyer/randvar/PowerGen.java
new file mode 100644
index 0000000..15efeb6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PowerGen.java
@@ -0,0 +1,137 @@
+
+
+/*
+ * Class: PowerGen
+ * Description: random variate generators for the power distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>power</EM> distribution with shape parameter
+ * <SPAN CLASS="MATH"><I>c</I> > 0</SPAN>, over the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ * Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>c</I>(<I>x</I> - <I>a</I>)<SUP>c-1</SUP>/(<I>b</I> - <I>a</I>)<SUP>c</SUP>
+ * </DIV><P></P>
+ * for
+ * <SPAN CLASS="MATH"><I>a</I> <= <I>x</I> <= <I>b</I></SPAN>, and 0 elsewhere.
+ *
+ */
+public class PowerGen extends RandomVariateGen {
+ private double a;
+ private double b;
+ private double c;
+
+
+ /**
+ * Creates a Power random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT>, <SPAN CLASS="MATH"><I>b</I> =</SPAN> <TT>b</TT> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>,
+ * using stream <TT>s</TT>.
+ *
+ */
+ public PowerGen (RandomStream s, double a, double b, double c) {
+ super (s, new PowerDist(a, b, c));
+ setParams (a, b, c);
+ }
+
+
+ /**
+ * Creates a Power random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>a</I> = 0</SPAN>, <SPAN CLASS="MATH"><I>b</I> = 1</SPAN> and <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public PowerGen (RandomStream s, double c) {
+ super (s, new PowerDist(0.0, 1.0, c));
+ setParams (0.0, 1.0, c);
+ }
+
+
+ /**
+ * Creates a new generator for the power distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public PowerGen (RandomStream s, PowerDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getB(), dist.getC());
+ }
+
+
+ /**
+ * Uses inversion to generate a new variate from the power
+ * distribution with parameters <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT>, <SPAN CLASS="MATH"><I>b</I> =</SPAN> <TT>b</TT>, and
+ * <SPAN CLASS="MATH"><I>c</I> =</SPAN> <TT>c</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double a, double b,
+ double c) {
+ return PowerDist.inverseF (a, b, c, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ */
+ public double getC() {
+ return c;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>c</I></SPAN> for this object.
+ *
+ *
+ */
+ public void setParams (double a, double b, double c) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/PowerGen.tex b/source/umontreal/iro/lecuyer/randvar/PowerGen.tex
new file mode 100644
index 0000000..4c17fc9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/PowerGen.tex
@@ -0,0 +1,146 @@
+\defmodule {PowerGen}
+
+This class implements random variate generators for the
+{\em power\/} distribution with shape parameter
+$c > 0$, over the interval $[a,b]$.
+Its density is
+\begin{htmlonly}
+\eq
+ f(x) = c(x - a)^{c - 1}/(b - a)^{c}
+\endeq
+\end{htmlonly}%
+\begin{latexonly}%
+\eq
+ f(x) = \frac{c(x-a)^{c - 1}} {(b - a)^{c}}, \eqlabel{eq:fpower}
+\endeq
+\end{latexonly}
+for $a \le x \le b$, and 0 elsewhere.
+
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PowerGen
+ * Description: random variate generators for the power distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class PowerGen extends RandomVariateGen \begin{hide} {
+ private double a;
+ private double b;
+ private double c;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public PowerGen (RandomStream s, double a, double b, double c) \begin{hide} {
+ super (s, new PowerDist(a, b, c));
+ setParams (a, b, c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Power random variate generator with parameters
+ $a =$ \texttt{a}, $b =$ \texttt{b} and $c =$ \texttt{c},
+ using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public PowerGen (RandomStream s, double c) \begin{hide} {
+ super (s, new PowerDist(0.0, 1.0, c));
+ setParams (0.0, 1.0, c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Power random variate generator with parameters
+ $a =0$, $b =1$ and $c =$ \texttt{c}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public PowerGen (RandomStream s, PowerDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getB(), dist.getC());
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the power distribution \texttt{dist}
+ and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double a, double b,
+ double c) \begin{hide} {
+ return PowerDist.inverseF (a, b, c, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses inversion to generate a new variate from the power
+ distribution with parameters $a = $~\texttt{a}, $b = $~\texttt{b}, and
+ $c = $~\texttt{c}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $a$.
+\end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $b$.
+\end{tabb}
+\begin{code}
+
+ public double getC()\begin{hide} {
+ return c;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $c$.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double a, double b, double c) \begin{hide} {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameters $a$, $b$ and $c$ for this object.
+\end{tabb}
+\begin{hide}\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandUnuran.c b/source/umontreal/iro/lecuyer/randvar/RandUnuran.c
new file mode 100644
index 0000000..b026632
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandUnuran.c
@@ -0,0 +1,713 @@
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <unuran.h>
+#include "umontreal_iro_lecuyer_randvar_RandUnuran.h"
+
+/* Native interface for UNURAN with Java
+ *
+ * UNURAN has been set up to use a custom generator represented
+ * by a data structure containing a function pointer and a parameter
+ * pointer. This setup is different from the two supported configuration.
+ * When using UNUR_URNG_POINTER, only a function pointer is used.
+ * When using UNUR_URNG_PRNG, a PRNG generator is used and an extra
+ * library (prng) is needed to compile UNURAN and RandUnuran.
+ * When a uniform must be generated, the function pointed to
+ * by the pointer getrand is called using the parameter pointer params.
+ * The function has to return a double, which will be the generated
+ * uniform. This structure has been defined in unuran.h so UNUR_URNG
+ * is a typedef of it. This structure, called UNUR_URNG_FUNCTION_POINTER
+ * is general and can solve other cases than the JNI.
+ *
+ * For our special case, we would like to be able to call a Java
+ * method of a RandomStream Java object to get the uniform numbers.
+ * The UNUR_URNG will act as a wrapper around the Java method.
+ * So we need a function to issue a JNI call. However, such a call
+ * requires some information: the JNI environment, the reference
+ * to the object and the ID of the method.
+ * We are using a struct GenParams as the parameter
+ * block. This block contains all the needed information to allow
+ * the function unif, which getrand will be assigned to, to call the JNI.
+ * We have one GenParams structure for each UNUR_URNG generator
+ * used and two UNUR_URNG's are used for each RandUnuran Java object,
+ * one for the main stream and one for the auxiliary stream.
+ *
+ * This uniform generator structure allows us to change the function
+ * dynamically. We can therefore perform optimization by executing
+ * as many RandomStream method calls as possible on the Java side
+ * instead of the native side. When generating a single variate,
+ * we cache one uniform and use the cons function to get it.
+ * When generating arrays of variate, the function arr is used.
+ * It gets elements from an array and can refill it with
+ * new random number if necessary.
+ *
+ * Additionally, we need another structure called NativeParams.
+ * One such structure is assigned to each RandUnuran Java object.
+ * It contains native-side specific parameters such as pointers
+ * to the UNUR_GEN structure and the two UNUR_URNG's.
+ *
+ *
+ * The life of a RandUnuran object
+ *
+ * When constructed, a RandUnuran object will always call
+ * the native method init. The Java call init will be
+ * translated to a C call to Java_umontreal_iro_lecuyer_randvar_RandUnuran_init with
+ * the proper arguments. This method will allocate the
+ * NativeParams structure and assign its pointer to an integer
+ * instance field named nativeParams. This will allow us to attach the
+ * NativeParams structure to the Java object and get it back
+ * from calls to calls. Then UNURAN is called in order
+ * to create a generator object which will be stored in
+ * the NativeParams C structure. Two UNUR_URNG structures are then
+ * allocated to store information about main and auxiliary RandomStream
+ * objects. The newly-allocated uniform random number generators will
+ * be attached to the UNURAN generator by UNURAN function calls.
+ *
+ * When the user asks for a random number, a Java method uses
+ * the mainStream RandomStream object to get a first uniform from the
+ * main stream. This uniform u is passed to the native side by a call
+ * to getRandDisc or getRandCont and stored
+ * inside the GenParams structure stored in the UNUR_URNG structure
+ * for the main stream. The getrand pointer of the main generator is also
+ * changed to cons. Environment and object reference are also
+ * stored into the GenParams structure. UNURAN can then be called.
+ * During the computation process performed by UNURAN, at least
+ * one uniform random number will be needed. This will call the cons
+ * function with a void pointer. This pointer will be casted to
+ * the GenParams pointer assigned to the main stream. The cons
+ * function will set the function pointer of the UNUR_URNG structure
+ * assigned to the main stream to unif. If UNURAN requests
+ * extra numbers from the main stream during the same variate generation
+ * process, unif is called.
+ * It will issue a JNI call that will return to Java side in order
+ * to use the nextDouble method of the RandomStream object.
+ *
+ * When no references point to the RandUnuran object, the Java garbage
+ * collector will free it. Before releasing the memory, the finalize
+ * method is called, which will call the native close method. This method
+ * frees all memory allocated by the native part of the interface,
+ * including the UNURAN generator object.
+ */
+
+/* cached data for efficiency
+ * These ids are needed to issue JNI calls for getting fields
+ * and calling methods.
+ */
+/* fields for RandUnuran objects */
+static jfieldID fidNativeParams = 0;
+static jfieldID fidMainStream = 0;
+static jfieldID fidAuxStream = 0;
+
+/* methods for RandomStream objects */
+static jmethodID midNextDouble = 0;
+static jmethodID midNextArrayOfDouble;
+
+/* We must keep a default uniform generator for the
+ * generators' setup phases (call to unur_str2gen).
+ * This generator does not creates any Java RandomStream objects. */
+static UNUR_URNG* default_urng = 0;
+
+struct GenParams {
+ /* Parameters block attached to one uniform random number generator
+ * This will be passed as a void pointer to the function pointed to by
+ * the urng. */
+
+ /* JNI information */
+ JNIEnv* env; /* the JNI environment */
+ jobject rsObj; /* the RandomStream object reference */
+
+ /* caching of one uniform */
+ double u; /* first random number obtained on the Java side */
+ UNUR_URNG* urng; /* needed to set back to unif after cons is called */
+
+ /* array of uniforms */
+ jdoubleArray junifArray; /* Reference to the Java array */
+ jdouble* unifArray; /* array of uniforms */
+ int n; /* number of generated values */
+ int nextIndex; /* index of the next uniform */
+};
+
+static double unif (void* p) {
+ /* Function used to generate uniforms using a Java RandomStream. */
+
+ struct GenParams* params = (struct GenParams*)p;
+ return (*(params->env))->CallDoubleMethod (params->env, params->rsObj,
+ midNextDouble);
+}
+
+static double cons (void* p) {
+ /* We can prevent some native Java calls by precomputing one
+ * uniform before entering the native side. If UNURAN asks for
+ * additional random numbers, this will call the unif function.
+ * This function avoids an if inside the unif function. */
+
+ struct GenParams* params = (struct GenParams*)p;
+ params->urng->getrand = unif;
+ return params->u;
+}
+
+static double arr (void* p) {
+ /* Gets a uniform from a previously generated
+ * array of uniforms. If no more uniforms are available,
+ * the array is refilled. */
+
+ struct GenParams* params = (struct GenParams*)p;
+ if (params->nextIndex >= params->n) {
+ /* a refill is needed */
+ (*(params->env))->ReleaseDoubleArrayElements (params->env,
+ params->junifArray,
+ params->unifArray, 0);
+ /* equivalent to
+ * mainStream.nextArrayOfDouble (unifArray, 0, n)
+ * or
+ * auxStream.nextArrayOfDouble (...)
+ */
+ (*(params->env))->CallVoidMethod (params->env, params->rsObj,
+ midNextArrayOfDouble,
+ params->junifArray, 0, params->n);
+ params->unifArray =
+ (*(params->env))->GetDoubleArrayElements (params->env,
+ params->junifArray, 0);
+ params->nextIndex = 0;
+ }
+
+ /* gets a uniform */
+ return params->unifArray[params->nextIndex++];
+}
+
+/* Per-object parameters used in C */
+struct NativeParams {
+ /* We store it in a single structure in order to limit
+ * the necessary calls to the JNI. Additionnally, some
+ * information cannot be directly stored in Java fields, like
+ * pointers.
+ * The objMainStream and objAuxStream fields are replicated inside
+ * the parameter block of the urng and urng_aux structures but
+ * it is more simple to free the references inside the
+ * NativeParams structure. */
+
+ UNUR_GEN* unurgen; /* non-uniform generator */
+ jobject objMainStream; /* uniform generator RandomStream */
+ jobject objAuxStream; /* uniform auxiliary generator RandomStream */
+ UNUR_URNG* urng; /* uniform generator structure */
+ UNUR_URNG* urng_aux; /* uniform auxiliary generator structure */
+
+ /* The dimension of the distribution is used for array size
+ * checking in case of multivariate distributions. */
+ int dim;
+};
+
+static void urng_jni_free (UNUR_URNG* gen) {
+ /* This function frees the custom JNI generator. */
+
+ if (!gen)
+ return;
+ if (gen->params)
+ free (gen->params);
+ free (gen);
+}
+
+static UNUR_URNG* urng_jni_new () {
+ /* Creates a new JNI generator structure. */
+
+ UNUR_URNG* gen = (UNUR_URNG*)malloc (sizeof (UNUR_URNG));
+ if (!gen)
+ return 0;
+ gen->getrand = unif;
+ gen->params = malloc (sizeof (struct GenParams));
+ if (!gen->params) {
+ urng_jni_free (gen);
+ return 0;
+ }
+ return gen;
+}
+
+static int initClass (JNIEnv* env) {
+ /* we perform some class initialization tasks.
+ * If this function returns 0, the initialization failed. */
+
+ jclass cls;
+
+ /* Create and set the default uniform random number generator. */
+ if (!default_urng) {
+ default_urng = urng_jni_new ();
+ if (!default_urng) {
+ cls = (*env)->FindClass (env, "umontreal/iro/lecuyer/randvar/UnuranException");
+ if (!cls)
+ return 0;
+ (*env)->ThrowNew (env, cls, "cannot create default uniform rng");
+ return 0;
+ }
+ unur_set_default_urng (default_urng);
+ /* After this setup, UNURAN won't work properly
+ * until we set a valid parameter block for the unif
+ * function. This will be done in the init method only. */
+ }
+
+ cls = (*env)->FindClass (env, "umontreal/iro/lecuyer/randvar/RandUnuran");
+ if (!cls)
+ return 0;
+ if (!fidNativeParams &&
+ !(fidNativeParams = (*env)->GetFieldID (env, cls,
+ "nativeParams", "I")))
+ return 0;
+ if (!fidMainStream &&
+ !(fidMainStream = (*env)->GetFieldID
+ (env, cls, "mainStream", "Lumontreal/iro/lecuyer/rng/RandomStream;")))
+ return 0;
+ if (!fidAuxStream &&
+ !(fidAuxStream = (*env)->GetFieldID
+ (env, cls, "auxStream", "Lumontreal/iro/lecuyer/rng/RandomStream;")))
+ return 0;
+
+ cls = (*env)->FindClass (env, "umontreal/iro/lecuyer/rng/RandomStream");
+ if (!cls)
+ return 0;
+ if (!midNextDouble &&
+ !(midNextDouble = (*env)->GetMethodID (env, cls, "nextDouble", "()D")))
+ return 0;
+ if (!midNextArrayOfDouble &&
+ !(midNextArrayOfDouble = (*env)->GetMethodID (env, cls,
+ "nextArrayOfDouble",
+ "([DII)V")))
+ return 0;
+
+ return 1;
+}
+
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_init
+ (JNIEnv *env, jobject obj, jstring genStr)
+{
+ jclass expcls;
+ jobject ob;
+ const char* genStrUTF;
+ struct NativeParams* np;
+ struct GenParams *gp;
+
+ if (!initClass (env))
+ return;
+
+ /* Allocate the object parameter block. */
+ np = (struct NativeParams*)malloc (sizeof (struct NativeParams));
+ if (!np) {
+ expcls = (*env)->FindClass (env, "umontreal/iro/lecuyer/randvar/UnuranException");
+ if (!expcls)
+ return;
+ (*env)->ThrowNew (env, expcls, "cannot create UNURAN generator");
+ return;
+ }
+ /* This will allow us to call close upon errors */
+ memset (np, 0, sizeof (struct NativeParams));
+ /*
+ np->unurgen = 0;
+ np->objMainStream = 0;
+ np->objAuxStream = 0;
+ np->urng = 0;
+ np->urng_aux = 0; */
+
+ /* save the parameters with the Java object */
+ (*env)->SetIntField (env, obj, fidNativeParams, (jint)np);
+
+ /* Set the parameter block for the default generator.
+ * This will allow setup-time random number generation
+ * by the Java RandomStream. The setup has to be made
+ * all the times because the env and obj vary from calls to calls. */
+ gp = (struct GenParams*)default_urng->params;
+ gp->env = env;
+ gp->rsObj = (*env)->GetObjectField (env, obj, fidMainStream);
+
+ /* create the UNURAN generator */
+ genStrUTF = (*env)->GetStringUTFChars (env, genStr, 0);
+ if (!genStrUTF) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ return;
+ }
+ np->unurgen = unur_str2gen (genStrUTF);
+ (*env)->ReleaseStringUTFChars (env, genStr, genStrUTF);
+ if (!np->unurgen) {
+ /* The creation of the generator has failed.
+ * Throws an exception that will report the error,
+ * including the UNURAN error message. */
+ const char* unurerr = unur_get_strerror (unur_errno);
+ const char* errmsg = "cannot create UNURAN generator: ";
+ char* errstr;
+ expcls = (*env)->FindClass (env, "umontreal/iro/lecuyer/randvar/UnuranException");
+ if (!expcls) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ return;
+ }
+ errstr = (char*)malloc (strlen(errmsg) + strlen(unurerr) + 1);
+ if (!errstr) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ (*env)->ThrowNew (env, expcls, errmsg);
+ return;
+ }
+ strcpy (errstr, errmsg);
+ strcat (errstr, unurerr);
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ (*env)->ThrowNew (env, expcls, errstr);
+ free (errstr);
+ return;
+ }
+ np->dim = unur_get_dimension (np->unurgen);
+
+ /* now we must setup the uniform generator */
+ np->urng = urng_jni_new ();
+ if (!np->urng) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ expcls = (*env)->FindClass (env, "umontreal/iro/lecuyer/randvar/UnuranException");
+ if (!expcls)
+ return;
+ (*env)->ThrowNew (env, expcls, "cannot allocate uniform "
+ "random number generator");
+ return;
+ }
+ gp = (struct GenParams*)np->urng->params;
+ gp->urng = np->urng;
+ unur_chg_urng (np->unurgen, np->urng);
+
+ /* now we must setup the auxiliary uniform generator */
+ np->urng_aux = urng_jni_new ();
+ if (!np->urng_aux) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ expcls = (*env)->FindClass (env, "umontreal/iro/lecuyer/randvar/UnuranException");
+ if (!expcls)
+ return;
+ (*env)->ThrowNew (env, expcls, "cannot allocate uniform auxiliary "
+ "random number generator.");
+ return;
+ }
+ unur_chg_urng_aux (np->unurgen, np->urng_aux);
+
+ /* Get reference to the RandomStream mainStream.
+ * The references to the RandomStream objects have
+ * to be global because they are kept inside the NativeParams
+ * structure which is passed to other instance methods. */
+ ob = (*env)->GetObjectField (env, obj, fidMainStream);
+ if (!ob) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ return;
+ }
+ np->objMainStream = (*env)->NewGlobalRef (env, ob);
+ if (!np->objMainStream) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ return;
+ }
+ /* store a copy of the global reference inside the
+ * random number generator parameter block. */
+ gp = (struct GenParams*)np->urng->params;
+ gp->rsObj = np->objMainStream;
+
+ /* Get reference to the RandomStream auxStream. */
+ ob = (*env)->GetObjectField (env, obj, fidAuxStream);
+ if (!ob) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ return;
+ }
+ np->objAuxStream = (*env)->NewGlobalRef (env, ob);
+ if (!np->objAuxStream) {
+ Java_umontreal_iro_lecuyer_randvar_RandUnuran_close (env, obj);
+ return;
+ }
+ gp = (struct GenParams*)np->urng_aux->params;
+ gp->rsObj = np->objAuxStream;
+}
+
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_close
+ (JNIEnv *env, jobject obj)
+{
+ struct NativeParams* np;
+ jthrowable ex;
+
+ /* Since this method is also called upon errors inside init,
+ * we must ensure that no exception is pending before
+ * calling JNI. */
+ ex = (*env)->ExceptionOccurred (env);
+ (*env)->ExceptionClear (env);
+
+ np = (struct NativeParams*)((*env)->GetIntField
+ (env, obj, fidNativeParams));
+ /* We check whether the pointer is null to prevent segmentation
+ * fault that would result in a VM crash.
+ * Without this checking, a VM crash would occur every time
+ * the user calls this method manually because the finalizer
+ * would call it and get an invalid pointer. */
+ if (!np) {
+ if (ex)
+ (*env)->Throw (env, ex);
+ return;
+ }
+
+ /* We free memory previously allocated */
+ urng_jni_free (np->urng_aux);
+ urng_jni_free (np->urng);
+ unur_free (np->unurgen);
+
+ if (np->objAuxStream)
+ (*env)->DeleteGlobalRef (env, np->objAuxStream);
+ if (np->objMainStream)
+ (*env)->DeleteGlobalRef (env, np->objMainStream);
+ free (np);
+
+ /* to avoid receiving an invalid pointer in a subsequent
+ * call to close. */
+ (*env)->SetIntField (env, obj, fidNativeParams, 0);
+
+ /* If an exception was pending at the beginning of this method,
+ * we can now throw it back. */
+ if (ex)
+ (*env)->Throw (env, ex);
+}
+
+JNIEXPORT jint JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandDisc
+ (JNIEnv *env, jobject obj, jdouble u, jint _np)
+{
+ struct NativeParams* np;
+ struct GenParams *gp;
+
+ np = (struct NativeParams*)_np;
+
+ gp = (struct GenParams*)np->urng->params;
+ gp->env = env;
+
+ /* We put the pregenerated uniform into the u field
+ * and change the generation function. */
+ gp->u = u;
+ np->urng->getrand = cons;
+
+ gp = (struct GenParams*)np->urng_aux->params;
+ gp->env = env;
+ np->urng_aux->getrand = unif;
+
+ return unur_sample_discr (np->unurgen);
+}
+
+JNIEXPORT jdouble JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandCont
+ (JNIEnv *env, jobject obj, jdouble u, jint _np)
+{
+ struct NativeParams* np;
+ struct GenParams *gp;
+
+ np = (struct NativeParams*)_np;
+
+ gp = (struct GenParams*)np->urng->params;
+
+ gp->env = env;
+
+ /* We put the pregenerated uniform into the u field
+ * and change the generation function. */
+ gp->u = u;
+ np->urng->getrand = cons;
+
+ gp = (struct GenParams*)np->urng_aux->params;
+ gp->env = env;
+ np->urng_aux->getrand = unif;
+
+ return unur_sample_cont (np->unurgen);
+}
+
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandVec
+ (JNIEnv *env, jobject obj, jdouble u, jint _np, jdoubleArray jvec)
+{
+ struct NativeParams* np;
+ struct GenParams *gp;
+ jdouble* vec;
+
+ np = (struct NativeParams*)_np;
+
+ gp = (struct GenParams*)np->urng->params;
+
+ /* We put the pregenerated uniform into the u field
+ * and change the generation function. */
+ gp->u = u;
+ np->urng->getrand = cons;
+
+ gp->env = env;
+
+ gp = (struct GenParams*)np->urng_aux->params;
+ gp->env = env;
+ np->urng_aux->getrand = unif;
+
+ /* size checking is required because a VM crash
+ * would occur if the array is too short. */
+ if ((*env)->GetArrayLength (env, jvec) < np->dim) {
+ jclass expcls = (*env)->FindClass (env, "umontreal/iro/lecuyer/randvar/UnuranException");
+ if (!expcls)
+ return;
+ (*env)->ThrowNew (env, expcls, "array too short");
+ return;
+ }
+ vec = (*env)->GetDoubleArrayElements (env, jvec, 0);
+ if (!vec)
+ return;
+ unur_sample_vec (np->unurgen, vec);
+ (*env)->ReleaseDoubleArrayElements (env, jvec, vec, 0);
+}
+
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandDiscArray
+ (JNIEnv *env, jobject obj, jint _np,
+ jdoubleArray ju, jdoubleArray juaux, jintArray jv,
+ jint start, jint n)
+{
+ struct NativeParams* np;
+ struct GenParams *gp;
+ jdouble* u;
+ jdouble* uaux;
+ jint* v;
+ int i;
+
+ np = (struct NativeParams*)_np;
+
+ gp = (struct GenParams*)np->urng->params;
+ gp->env = env;
+ gp->junifArray = ju;
+ u = gp->unifArray = (*env)->GetDoubleArrayElements (env, ju, 0);
+ gp->n = gp->nextIndex = n; /* schedule an array refill */
+ np->urng->getrand = arr;
+
+ gp = (struct GenParams*)np->urng_aux->params;
+ if (juaux) {
+ gp->env = env;
+ gp->junifArray = juaux;
+ uaux = gp->unifArray = (*env)->GetDoubleArrayElements (env, juaux, 0);
+ gp->n = gp->nextIndex = n;
+ np->urng->getrand = arr;
+ }
+ else {
+ /* If the main and auxiliary streams are the same,
+ * we use the same array for both, so the parameter
+ * blocks must be equal. */
+ np->urng_aux->params = np->urng->params;
+ uaux = 0;
+ }
+
+ v = (*env)->GetIntArrayElements (env, jv, 0);
+ for (i = 0; i < n; i++)
+ v[start+i] = unur_sample_discr (np->unurgen);
+ (*env)->ReleaseIntArrayElements (env, jv, v, 0);
+ if (juaux) {
+ (*env)->ReleaseDoubleArrayElements (env, juaux, uaux, 0);
+ }
+ else {
+ np->urng_aux->params = gp;
+ }
+ (*env)->ReleaseDoubleArrayElements (env, ju, u, 0);
+}
+
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandContArray
+ (JNIEnv *env, jobject obj, jint _np,
+ jdoubleArray ju, jdoubleArray juaux, jdoubleArray jv,
+ jint start, jint n)
+{
+ struct NativeParams* np;
+ struct GenParams *gp;
+ jdouble* u;
+ jdouble* uaux;
+ jdouble* v;
+ int i;
+
+ np = (struct NativeParams*)_np;
+
+ gp = (struct GenParams*)np->urng->params;
+ gp->env = env;
+ gp->junifArray = ju;
+ u = gp->unifArray = (*env)->GetDoubleArrayElements (env, ju, 0);
+ gp->n = gp->nextIndex = n;
+ np->urng->getrand = arr;
+
+ gp = (struct GenParams*)np->urng_aux->params;
+ if (juaux) {
+ gp->env = env;
+ gp->junifArray = juaux;
+ uaux = gp->unifArray = (*env)->GetDoubleArrayElements (env, juaux, 0);
+ gp->n = gp->nextIndex = n;
+ np->urng->getrand = arr;
+ }
+ else {
+ np->urng_aux->params = np->urng->params;
+ uaux = 0;
+ }
+
+ v = (*env)->GetDoubleArrayElements (env, jv, 0);
+ for (i = 0; i < n; i++)
+ v[start+i] = unur_sample_cont (np->unurgen);
+ (*env)->ReleaseDoubleArrayElements (env, jv, v, 0);
+ if (juaux) {
+ (*env)->ReleaseDoubleArrayElements (env, juaux, uaux, 0);
+ }
+ else {
+ np->urng_aux->params = gp;
+ }
+ (*env)->ReleaseDoubleArrayElements (env, ju, u, 0);
+}
+
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isDiscrete
+ (JNIEnv *env, jobject obj)
+{
+ const UNUR_DISTR* distr;
+ struct NativeParams* np = (struct NativeParams*)(*env)->GetIntField
+ (env, obj, fidNativeParams);
+
+ if (!np)
+ return JNI_FALSE;
+
+ distr = unur_get_distr (np->unurgen);
+ return unur_distr_is_discr (distr) ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isContinuous
+ (JNIEnv *env, jobject obj)
+{
+ const UNUR_DISTR* distr;
+ struct NativeParams* np = (struct NativeParams*)(*env)->GetIntField
+ (env, obj, fidNativeParams);
+
+ if (!np)
+ return JNI_FALSE;
+
+ distr = unur_get_distr (np->unurgen);
+ return unur_distr_is_cont (distr) ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isContinuousMultivariate
+ (JNIEnv *env, jobject obj)
+{
+ const UNUR_DISTR* distr;
+ struct NativeParams* np = (struct NativeParams*)(*env)->GetIntField
+ (env, obj, fidNativeParams);
+
+ if (!np)
+ return JNI_FALSE;
+
+ distr = unur_get_distr (np->unurgen);
+ return unur_distr_is_cvec (distr) ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isEmpirical
+ (JNIEnv *env, jobject obj)
+{
+ const UNUR_DISTR* distr;
+ struct NativeParams* np = (struct NativeParams*)(*env)->GetIntField
+ (env, obj, fidNativeParams);
+
+ if (!np)
+ return JNI_FALSE;
+
+ distr = unur_get_distr (np->unurgen);
+ return unur_distr_is_cemp (distr) ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isEmpiricalMultivariate
+ (JNIEnv *env, jobject obj)
+{
+ const UNUR_DISTR* distr;
+ struct NativeParams* np = (struct NativeParams*)(*env)->GetIntField
+ (env, obj, fidNativeParams);
+
+ if (!np)
+ return JNI_FALSE;
+
+ distr = unur_get_distr (np->unurgen);
+ return unur_distr_is_cvemp (distr) ? JNI_TRUE : JNI_FALSE;
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandUnuran.java b/source/umontreal/iro/lecuyer/randvar/RandUnuran.java
new file mode 100644
index 0000000..05b8e24
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandUnuran.java
@@ -0,0 +1,80 @@
+
+
+/*
+ * Class: RandUnuran
+ * Description: provides the access point to the C package UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+
+
+/**
+ * This internal class provides the access point to the C
+ * package UNURAN. It provides native methods used
+ * by {@link UnuranContinuous}, {@link UnuranDiscrete} and
+ * {@link UnuranEmpirical}.
+ *
+ */
+class RandUnuran {
+
+ // These arrays are used to store precomputed uniforms
+ // from main and auxiliary streams when generating
+ // arrays of variates.
+ protected double[] unifArray = null;
+ protected double[] unifAuxArray = null;
+
+ protected RandomStream mainStream;
+ protected RandomStream auxStream;
+ protected int nativeParams = 0;
+
+ protected RandUnuran() {}
+
+ protected native void init (String genStr);
+ protected void finalize() {
+ close();
+ }
+
+ public native void close();
+
+ // random variate generation native methods
+ protected native int getRandDisc (double u, int np);
+ protected native double getRandCont (double u, int np);
+ protected native void getRandVec (double u, int np, double[] vec);
+
+ // random array of variates generation native methods
+ protected native void getRandDiscArray (int np, double[] u, double[] uaux,
+ int[] v, int start, int n);
+ protected native void getRandContArray (int np, double[] u, double[] uaux,
+ double[] v, int start, int n);
+
+ // methods to query the type of distribution for error checking
+ protected native boolean isDiscrete();
+ protected native boolean isContinuous();
+ protected native boolean isContinuousMultivariate();
+ protected native boolean isEmpirical();
+ protected native boolean isEmpiricalMultivariate();
+
+ static {
+ System.loadLibrary ("randvar");
+ }
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/RandUnuran.tex b/source/umontreal/iro/lecuyer/randvar/RandUnuran.tex
new file mode 100644
index 0000000..a756b72
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandUnuran.tex
@@ -0,0 +1,82 @@
+\defmodule{RandUnuran}
+
+This internal class provides the access point to the C
+package UNURAN. It provides native methods used
+by \class{UnuranContinuous}, \class{UnuranDiscrete} and
+\class{UnuranEmpirical}.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandUnuran
+ * Description: provides the access point to the C package UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+\end{hide}
+
+class RandUnuran\begin{hide} {
+
+ // These arrays are used to store precomputed uniforms
+ // from main and auxiliary streams when generating
+ // arrays of variates.
+ protected double[] unifArray = null;
+ protected double[] unifAuxArray = null;
+
+ protected RandomStream mainStream;
+ protected RandomStream auxStream;
+ protected int nativeParams = 0;
+
+ protected RandUnuran() {}
+
+ protected native void init (String genStr);
+ protected void finalize() {
+ close();
+ }
+
+ public native void close();
+
+ // random variate generation native methods
+ protected native int getRandDisc (double u, int np);
+ protected native double getRandCont (double u, int np);
+ protected native void getRandVec (double u, int np, double[] vec);
+
+ // random array of variates generation native methods
+ protected native void getRandDiscArray (int np, double[] u, double[] uaux,
+ int[] v, int start, int n);
+ protected native void getRandContArray (int np, double[] u, double[] uaux,
+ double[] v, int start, int n);
+
+ // methods to query the type of distribution for error checking
+ protected native boolean isDiscrete();
+ protected native boolean isContinuous();
+ protected native boolean isContinuousMultivariate();
+ protected native boolean isEmpirical();
+ protected native boolean isEmpiricalMultivariate();
+
+ static {
+ System.loadLibrary ("randvar");
+ }
+}\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandomVariateGen.java b/source/umontreal/iro/lecuyer/randvar/RandomVariateGen.java
new file mode 100644
index 0000000..4792f38
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandomVariateGen.java
@@ -0,0 +1,164 @@
+
+
+/*
+ * Class: RandomVariateGen
+ * Description: base class for all random variate generators over the reals
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.probdist.Distribution;
+
+/**
+ * This is the base class for all random variate generators over the real line.
+ * It specifies the signature of the {@link #nextDouble nextDouble} method, which is
+ * normally called to generate a real-valued random variate whose distribution
+ * has been previously selected.
+ * A random variate generator object can be created simply by invoking the
+ * constructor of this class with previously created
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} and
+ * {@link umontreal.iro.lecuyer.probdist.Distribution Distribution}
+ * objects, or by invoking the constructor of a subclass.
+ * By default, all random variates will be generated via
+ * inversion by calling the
+ * {@link umontreal.iro.lecuyer.probdist.Distribution#inverseF inverseF}
+ * method for the distribution, even though this can be inefficient in some cases.
+ * For some of the distributions, there are subclasses with special and
+ * more efficient methods to generate the random variates.
+ *
+ * <P>
+ * For generating many random variates, creating an object and calling
+ * the non-static method is more efficient when the generating algorithm
+ * involves a significant setup.
+ * When no work is done at setup time, the static methods are usually
+ * slightly faster.
+ *
+ */
+public class RandomVariateGen {
+
+ protected RandomStream stream;
+ // the stream used for generating random variates
+
+ protected Distribution dist;
+ // the distribution used by this generator
+
+
+
+ // This constructor is needed for subclasses with no associated distribution.
+ protected RandomVariateGen() {}
+
+ /**
+ * Creates a new random variate generator from the
+ * distribution <TT>dist</TT>, using stream <TT>s</TT>.
+ *
+ * @param s random stream used for generating uniforms
+ *
+ * @param dist continuous distribution object of the generated values
+ *
+ */
+ public RandomVariateGen (RandomStream s, Distribution dist) {
+ this.stream = s;
+ this.dist = dist;
+ }
+
+
+ /**
+ * Generates a random number from the continuous distribution
+ * contained in this object.
+ * By default, this method uses inversion by calling the
+ * {@link umontreal.iro.lecuyer.probdist.ContinuousDistribution#inverseF inverseF}
+ * method of the distribution object.
+ * Alternative generating methods are provided in subclasses.
+ *
+ * @return the generated value
+ *
+ */
+ public double nextDouble() {
+ return dist.inverseF (stream.nextDouble());
+ }
+
+
+ /**
+ * Generates <TT>n</TT> random numbers from the continuous distribution
+ * contained in this object. These numbers are stored in the array <TT>v</TT>,
+ * starting from index <TT>start</TT>.
+ * By default, this method calls {@link #nextDouble() nextDouble()} <TT>n</TT>
+ * times, but one can override it in subclasses for better efficiency.
+ *
+ * @param v array in which the variates will be stored
+ *
+ * @param start starting index, in <TT>v</TT>, of the new variates
+ *
+ * @param n number of variates to generate
+ *
+ *
+ */
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = 0; i < n; i++)
+ v[start + i] = nextDouble();
+ }
+
+
+ /**
+ * Returns the {@link RandomStream} used by this generator.
+ *
+ * @return the stream associated to this object
+ *
+ */
+ public RandomStream getStream() { return stream; }
+
+
+
+ /**
+ * Sets the {@link RandomStream} used by this generator to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ this.stream = stream;
+ }
+
+
+ /**
+ * Returns the {@link Distribution} used by this generator.
+ *
+ * @return the distribution associated to that object
+ *
+ */
+ public Distribution getDistribution() {
+ return dist;
+ }
+
+
+ /**
+ * Returns a <TT>String</TT> containing information about the current generator.
+ *
+ */
+ public String toString () {
+ if (dist != null)
+ return getClass().getSimpleName() + " with " + dist.toString();
+ else
+ return getClass().getSimpleName() ;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandomVariateGen.tex b/source/umontreal/iro/lecuyer/randvar/RandomVariateGen.tex
new file mode 100644
index 0000000..2b8e504
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandomVariateGen.tex
@@ -0,0 +1,184 @@
+\defmodule {RandomVariateGen}
+
+This is the base class for all random variate generators over the real line.
+It specifies the signature of the \method{nextDouble}{} method, which is
+normally called to generate a real-valued random variate whose distribution
+has been previously selected.
+A random variate generator object can be created simply by invoking the
+constructor of this class with previously created
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} and
+\externalclass{umontreal.iro.lecuyer.probdist}{Distribution}
+objects, or by invoking the constructor of a subclass.
+By default, all random variates will be generated via
+inversion by calling the
+\externalmethod{umontreal.iro.lecuyer.probdist}{Distribution}{inverseF}{}
+method for the distribution, even though this can be inefficient in some cases.
+For some of the distributions, there are subclasses with special and
+more efficient methods to generate the random variates.
+
+%% The generation algorithm can be changed for a given distribution object
+%% by calling an appropriate method on it.
+%% Each distribution also has a static method for generating random variates,
+%% which also uses inversion by default, unless specified otherwise.
+%% The generating algorithm can be changed by invoking an appropriate static
+%% method. To avoid side effects, it is recommended to set the static algorithm
+%% once and keep it for the duration of the program.
+%% \pierre{Another good reason for using subclasses instead!}
+%% If one wants to generate random variates with more than one algorithms,
+%% one should create separate objects.
+
+For generating many random variates, creating an object and calling
+the non-static method is more efficient when the generating algorithm
+involves a significant setup.
+When no work is done at setup time, the static methods are usually
+slightly faster.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomVariateGen
+ * Description: base class for all random variate generators over the reals
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.probdist.Distribution;\end{hide}
+
+public class RandomVariateGen \begin{hide} {
+
+ protected RandomStream stream;
+ // the stream used for generating random variates
+
+ protected Distribution dist;
+ // the distribution used by this generator
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}\begin{hide}
+ // This constructor is needed for subclasses with no associated distribution.
+ protected RandomVariateGen() {}\end{hide}
+
+ public RandomVariateGen (RandomStream s, Distribution dist) \begin{hide} {
+ this.stream = s;
+ this.dist = dist;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new random variate generator from the
+ distribution \texttt{dist}, using stream \texttt{s}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{s}{random stream used for generating uniforms}
+ \param{dist}{continuous distribution object of the generated values}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double nextDouble() \begin{hide} {
+ return dist.inverseF (stream.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a random number from the continuous distribution
+ contained in this object.
+ By default, this method uses inversion by calling the
+ \externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{inverseF}{}
+ method of the distribution object.
+ Alternative generating methods are provided in subclasses.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the generated value}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfDouble (double[] v, int start, int n) \begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = 0; i < n; i++)
+ v[start + i] = nextDouble();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates \texttt{n} random numbers from the continuous distribution
+ contained in this object. These numbers are stored in the array \texttt{v},
+ starting from index \texttt{start}.
+ By default, this method calls \method{nextDouble()}{} \texttt{n}
+ times, but one can override it in subclasses for better efficiency.
+ \end{tabb}
+\begin{htmlonly}
+ \param{v}{array in which the variates will be stored}
+ \param{start}{starting index, in \texttt{v}, of the new variates}
+ \param{n}{number of variates to generate}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} { return stream; }
+\end{hide}
+\end{code}
+ \begin{tabb} Returns the \class{RandomStream} used by this generator.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the stream associated to this object}
+\end{htmlonly}
+\begin{code}
+
+ public void setStream (RandomStream stream)\begin{hide} {
+ this.stream = stream;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the \class{RandomStream} used by this generator to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public Distribution getDistribution() \begin{hide} {
+ return dist;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \class{Distribution} used by this generator.
+\end{tabb}
+\begin{htmlonly}
+ \return{the distribution associated to that object}
+\end{htmlonly}
+\begin{code}
+
+ public String toString ()\begin{hide} {
+ if (dist != null)
+ return getClass().getSimpleName() + " with " + dist.toString();
+ else
+ return getClass().getSimpleName() ;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \texttt{String} containing information about the current generator.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandomVariateGenInt.java b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenInt.java
new file mode 100644
index 0000000..650f4fa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenInt.java
@@ -0,0 +1,108 @@
+
+
+/*
+ * Class: RandomVariateGenInt
+ * Description: base class for all generators of discrete random variates
+over the integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.probdist.DiscreteDistributionInt;
+
+/**
+ * This is the base class for all generators of discrete random variates
+ * over the set of integers.
+ * Similar to {@link RandomVariateGen}, except that the generators
+ * produce integers, via the {@link #nextInt nextInt} method, instead of real numbers.
+ *
+ */
+public class RandomVariateGenInt extends RandomVariateGen {
+
+
+
+ protected RandomVariateGenInt() {}
+
+ /**
+ * Creates a new random variate generator for the discrete
+ * distribution <TT>dist</TT>, using stream <TT>s</TT>.
+ *
+ * @param s random stream used for generating uniforms
+ *
+ * @param dist discrete distribution object of the generated values
+ *
+ */
+ public RandomVariateGenInt (RandomStream s, DiscreteDistributionInt dist) {
+ this.stream = s;
+ this.dist = dist;
+ }
+
+
+ /**
+ * Generates a random number (an integer) from the discrete
+ * distribution contained in this object.
+ * By default, this method uses inversion by calling the <TT>inverseF</TT>
+ * method of the distribution object.
+ * Alternative generating methods are provided in subclasses.
+ *
+ * @return the generated value
+ *
+ */
+ public int nextInt() {
+ return ((DiscreteDistributionInt) dist).inverseFInt (stream.nextDouble());
+ }
+
+
+ /**
+ * Generates <TT>n</TT> random numbers from the discrete distribution
+ * contained in this object. The results are stored into the array <TT>v</TT>,
+ * starting from index <TT>start</TT>.
+ * By default, this method calls {@link #nextInt() nextInt()} <TT>n</TT>
+ * times, but one can reimplement it in subclasses for better efficiency.
+ *
+ * @param v array into which the variates will be stored
+ *
+ * @param start starting index, in <TT>v</TT>, of the new variates
+ *
+ * @param n number of variates being generated
+ *
+ *
+ */
+ public void nextArrayOfInt (int[] v, int start, int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = 0; i < n; i++)
+ v[start + i] = nextInt();
+ }
+
+
+ /**
+ * Returns the {@link DiscreteDistributionInt} used by this generator.
+ *
+ * @return the distribution associated to that object
+ *
+ */
+ public DiscreteDistributionInt getDistribution() {
+ return (DiscreteDistributionInt) dist;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandomVariateGenInt.tex b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenInt.tex
new file mode 100644
index 0000000..3ee1556
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenInt.tex
@@ -0,0 +1,117 @@
+\defmodule {RandomVariateGenInt}
+
+This is the base class for all generators of discrete random variates
+over the set of integers.
+Similar to \class{RandomVariateGen}{}, except that the generators
+produce integers, via the \method{nextInt}{} method, instead of real numbers.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomVariateGenInt
+ * Description: base class for all generators of discrete random variates
+over the integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.probdist.DiscreteDistributionInt;\end{hide}
+
+public class RandomVariateGenInt extends RandomVariateGen\begin{hide} {
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}\begin{hide}
+ protected RandomVariateGenInt() {}\end{hide}
+
+ public RandomVariateGenInt (RandomStream s, DiscreteDistributionInt dist) \begin{hide} {
+ this.stream = s;
+ this.dist = dist;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new random variate generator for the discrete
+ distribution \texttt{dist}, using stream \texttt{s}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{s}{random stream used for generating uniforms}
+ \param{dist}{discrete distribution object of the generated values}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public int nextInt() \begin{hide} {
+ return ((DiscreteDistributionInt) dist).inverseFInt (stream.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Generates a random number (an integer) from the discrete
+ distribution contained in this object.
+ By default, this method uses inversion by calling the \texttt{inverseF}
+ method of the distribution object.
+ Alternative generating methods are provided in subclasses.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the generated value}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfInt (int[] v, int start, int n) \begin{hide} {
+ if (n < 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = 0; i < n; i++)
+ v[start + i] = nextInt();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates \texttt{n} random numbers from the discrete distribution
+ contained in this object. The results are stored into the array \texttt{v},
+ starting from index \texttt{start}.
+ By default, this method calls \method{nextInt()}{} \texttt{n}
+ times, but one can reimplement it in subclasses for better efficiency.
+ \end{tabb}
+\begin{htmlonly}
+ \param{v}{array into which the variates will be stored}
+ \param{start}{starting index, in \texttt{v}, of the new variates}
+ \param{n}{number of variates being generated}
+\end{htmlonly}
+\begin{code}
+
+ public DiscreteDistributionInt getDistribution() \begin{hide} {
+ return (DiscreteDistributionInt) dist;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \class{DiscreteDistributionInt} used by this generator.
+\end{tabb}
+\begin{htmlonly}
+ \return{the distribution associated to that object}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandomVariateGenWithCache.java b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenWithCache.java
new file mode 100644
index 0000000..03627fa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenWithCache.java
@@ -0,0 +1,336 @@
+
+
+/*
+ * Class: RandomVariateGenWithCache
+ * Description: random variate generator whose values are cached for efficiency
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+import cern.colt.list.DoubleArrayList;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.probdist.Distribution;
+
+
+/**
+ * This class represents a random variate generator whose values
+ * are cached for more efficiency when using
+ * common random numbers. An object
+ * from this class is constructed with a reference to a
+ * {@link RandomVariateGen} instance used to
+ * get the random numbers. These numbers
+ * are stored in an internal array to be retrieved later.
+ * The dimension of the array increases as the values
+ * are generated.
+ * If the {@link #nextDouble(()) nextDouble} method is called after
+ * the object is reset (by calling
+ * {@link #setCachedValues((DoubleArrayList)) setCachedValues}), it gives back the cached
+ * values instead of computing new ones.
+ * If the cache is exhausted before the generator is reset,
+ * new values are computed and added to the cache.
+ *
+ * <P>
+ * Such caching allows for a better performance with
+ * common random numbers, when
+ * generating random variates is time-consuming.
+ * However, using such caching may lead to memory problems if
+ * a large quantity of random numbers are needed.
+ *
+ */
+public class RandomVariateGenWithCache extends RandomVariateGen {
+ private RandomVariateGen rvg;
+ private DoubleArrayList values;
+ private int index = 0;
+ private boolean caching = true;
+
+
+
+ /**
+ * Constructs a new cached random variate generator with
+ * internal generator <TT>rvg</TT>.
+ *
+ * @param rvg the random variate generator whose values are cached.
+ *
+ * @exception NullPointerException if <TT>rvg</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public RandomVariateGenWithCache (RandomVariateGen rvg) {
+ if (rvg == null)
+ throw new NullPointerException
+ ("The given random variate generator cannot be null");
+ this.rvg = rvg;
+ values = new DoubleArrayList();
+ }
+
+
+ /**
+ * Constructs a new cached random variate generator
+ * with internal generator <TT>rvg</TT>. The <TT>initialCapacity</TT>
+ * parameter is used to set the initial capacity of the internal array
+ * which can grow as needed; it does not limit the maximal
+ * number of cached values.
+ *
+ * @param rvg the random variate generator whose values are cached.
+ *
+ * @param initialCapacity the number of cached values.
+ *
+ * @exception NullPointerException if <TT>rvg</TT> is <TT>null</TT>.
+ *
+ */
+ public RandomVariateGenWithCache (RandomVariateGen rvg,
+ int initialCapacity) {
+ if (rvg == null)
+ throw new NullPointerException
+ ("The given random variate generator cannot be null");
+ this.rvg = rvg;
+ values = new DoubleArrayList (initialCapacity);
+ }
+
+
+ /**
+ * Determines if the random variate generator is caching values,
+ * default being <TT>true</TT>.
+ * When caching is turned OFF, the {@link #nextDouble(()) nextDouble}
+ * method simply calls the corresponding method on the internal
+ * random variate generator, without storing the generated values.
+ *
+ * @return the caching indicator.
+ *
+ */
+ public boolean isCaching() {
+ return caching;
+ }
+
+
+ /**
+ * Sets the caching indicator to <TT>caching</TT>.
+ * If caching is turned OFF, this method calls {@link #clearCache(()) clearCache}
+ * to clear the cached values.
+ *
+ * @param caching the new value of the caching indicator.
+ *
+ *
+ */
+ public void setCaching (boolean caching) {
+ if (this.caching && !caching)
+ clearCache();
+ this.caching = caching;
+ }
+
+
+ /**
+ * Returns a reference to the random variate generator
+ * whose values are cached.
+ *
+ * @return a reference to the random variate generator whose values are cached.
+ *
+ */
+ public RandomVariateGen getCachedGen() {
+ return rvg;
+ }
+
+
+ /**
+ * Sets the random variate generator whose values are cached to
+ * <TT>rvg</TT>. If the generator is changed, the {@link #clearCache(()) clearCache}
+ * method is called.
+ *
+ * @param rvg the new random variate generator whose values are cached.
+ *
+ * @exception NullPointerException if <TT>rvg</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public void setCachedGen (RandomVariateGen rvg) {
+ if (rvg == null)
+ throw new NullPointerException
+ ("The given random variate generator cannot be null");
+ if (rvg == this.rvg)
+ return;
+ this.rvg = rvg;
+ clearCache();
+ }
+
+
+ /**
+ * Clears the cached values for this cached generator.
+ * Any subsequent call will then obtain new values
+ * from the internal generator.
+ *
+ */
+ public void clearCache() {
+ //values.clear();
+ // Keep the array previously returned by getCachedValues
+ // intact to allow caching values for several
+ // replications.
+ values = new DoubleArrayList();
+ index = 0;
+ }
+
+
+ /**
+ * Resets this generator to recover values from the cache.
+ * Subsequent calls
+ * to {@link #nextDouble(()) nextDouble} will return the cached random
+ * values until all the values are returned. When the array
+ * of cached values is exhausted, the internal random variate
+ * generator is used to generate new values which are added
+ * to the internal array as well.
+ * This method is equivalent to calling {@link #setCacheIndex((int)) setCacheIndex}.
+ *
+ */
+ public void initCache() {
+ index = 0;
+ }
+
+
+ /**
+ * Returns the total number of values cached by this generator.
+ *
+ * @return the total number of cached values.
+ *
+ */
+ public int getNumCachedValues() {
+ return values.size();
+ }
+
+
+ /**
+ * Return the index of the next cached value that will be
+ * returned by the generator.
+ * If the cache is exhausted, the
+ * returned value corresponds to the value returned by
+ * {@link #getNumCachedValues(()) getNumCachedValues}, and a subsequent call to
+ * {@link #nextDouble(()) nextDouble} will generate a new variate rather than
+ * reading a previous one from the cache.
+ * If caching is disabled, this always returns 0.
+ *
+ * @return the index of the next cached value.
+ *
+ */
+ public int getCacheIndex() {
+ return index;
+ }
+
+
+ /**
+ * Sets the index, in the cache, of the next value returned
+ * by {@link #nextDouble(()) nextDouble}.
+ * If <TT>newIndex</TT> is 0, this is equivalent to
+ * calling {@link #initCache(()) initCache}.
+ * If <TT>newIndex</TT> is {@link #getNumCachedValues(()) getNumCachedValues},
+ * subsequent calls to {@link #nextDouble(()) nextDouble} will add
+ * new values to the cache.
+ *
+ * @param newIndex the new index.
+ *
+ * @exception IllegalArgumentException if <TT>newIndex</TT>
+ * is negative or greater than or equal to the cache size.
+ *
+ *
+ */
+ public void setCacheIndex (int newIndex) {
+ if (newIndex < 0 || newIndex > values.size())
+ throw new IllegalArgumentException
+ ("newIndex must not be negative or greater than the cache size");
+ index = newIndex;
+ }
+
+
+ /**
+ * Returns an array list containing the values
+ * cached by this random variate generator.
+ *
+ * @return the array of cached values.
+ *
+ */
+ public DoubleArrayList getCachedValues() {
+ return values;
+ }
+
+
+ /**
+ * Sets the array list containing the cached
+ * values to <TT>values</TT>.
+ * This resets the cache index to
+ * the size of the given array.
+ *
+ * @param values the array list of cached values.
+ *
+ * @exception NullPointerException if <TT>values</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public void setCachedValues (DoubleArrayList values) {
+ if (values == null)
+ throw new NullPointerException();
+ this.values = values;
+ index = values.size();
+ }
+
+
+ public double nextDouble() {
+ if (!caching)
+ return rvg.nextDouble();
+ else if (index >= values.size()) {
+ double v = rvg.nextDouble();
+ values.add (v);
+ ++index;
+ return v;
+ }
+ else
+ return values.getQuick (index++);
+ }
+
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ if (!caching) {
+ rvg.nextArrayOfDouble (v, start, n);
+ return;
+ }
+ int remainingValues = values.size() - index;
+ if (remainingValues < 0)
+ remainingValues = 0;
+ int ncpy = Math.min (n, remainingValues);
+ if (ncpy > 0) {
+ System.arraycopy (values.elements(), index, v, start, ncpy);
+ index += ncpy;
+ }
+ int ngen = n - ncpy;
+ if (ngen > 0) {
+ rvg.nextArrayOfDouble (v, start + ncpy, ngen);
+ for (int i = ncpy; i < n; i++) {
+ values.add (v[start + i]);
+ ++index;
+ }
+ }
+ }
+
+ public RandomStream getStream() {
+ return rvg.getStream();
+ }
+
+ public Distribution getDistribution() {
+ return rvg.getDistribution();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/RandomVariateGenWithCache.tex b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenWithCache.tex
new file mode 100644
index 0000000..45a1d82
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RandomVariateGenWithCache.tex
@@ -0,0 +1,332 @@
+\defmodule{RandomVariateGenWithCache}
+
+This class represents a random variate generator whose values
+are cached for more efficiency when using
+common random numbers. An object
+from this class is constructed with a reference to a
+\class{RandomVariateGen} instance used to
+get the random numbers. These numbers
+are stored in an internal array to be retrieved later.
+The dimension of the array increases as the values
+are generated.
+If the \method{nextDouble}{()} method is called after
+the object is reset (by calling
+\method{setCachedValues}{(DoubleArrayList)}), it gives back the cached
+values instead of computing new ones.
+If the cache is exhausted before the generator is reset,
+new values are computed and added to the cache.
+
+Such caching allows for a better performance with
+common random numbers, when
+generating random variates is time-consuming.
+However, using such caching may lead to memory problems if
+a large quantity of random numbers are needed.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomVariateGenWithCache
+ * Description: random variate generator whose values are cached for efficiency
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+import cern.colt.list.DoubleArrayList;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.probdist.Distribution;
+\end{hide}
+
+public class RandomVariateGenWithCache extends RandomVariateGen\begin{hide} {
+ private RandomVariateGen rvg;
+ private DoubleArrayList values;
+ private int index = 0;
+ private boolean caching = true;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public RandomVariateGenWithCache (RandomVariateGen rvg)\begin{hide} {
+ if (rvg == null)
+ throw new NullPointerException
+ ("The given random variate generator cannot be null");
+ this.rvg = rvg;
+ values = new DoubleArrayList();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new cached random variate generator with
+ internal generator \texttt{rvg}.
+\end{tabb}
+\begin{htmlonly}
+ \param{rvg}{the random variate generator whose values are cached.}
+ \exception{NullPointerException}{if \texttt{rvg} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomVariateGenWithCache (RandomVariateGen rvg,
+ int initialCapacity)\begin{hide} {
+ if (rvg == null)
+ throw new NullPointerException
+ ("The given random variate generator cannot be null");
+ this.rvg = rvg;
+ values = new DoubleArrayList (initialCapacity);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new cached random variate generator
+ with internal generator \texttt{rvg}. The \texttt{initialCapacity}
+ parameter is used to set the initial capacity of the internal array
+ which can grow as needed; it does not limit the maximal
+ number of cached values.
+\end{tabb}
+\begin{htmlonly}
+ \param{rvg}{the random variate generator whose values are cached.}
+ \param{initialCapacity}{the number of cached values.}
+ \exception{NullPointerException}{if \texttt{rvg} is \texttt{null}.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public boolean isCaching()\begin{hide} {
+ return caching;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if the random variate generator is caching values,
+ default being \texttt{true}.
+ When caching is turned OFF, the \method{nextDouble}{()}
+ method simply calls the corresponding method on the internal
+ random variate generator, without storing the generated values.
+\end{tabb}
+\begin{htmlonly}
+ \return{the caching indicator.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCaching (boolean caching)\begin{hide} {
+ if (this.caching && !caching)
+ clearCache();
+ this.caching = caching;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the caching indicator to \texttt{caching}.
+ If caching is turned OFF, this method calls \method{clearCache}{()}
+ to clear the cached values.
+\end{tabb}
+\begin{htmlonly}
+ \param{caching}{the new value of the caching indicator.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomVariateGen getCachedGen()\begin{hide} {
+ return rvg;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to the random variate generator
+ whose values are cached.
+\end{tabb}
+\begin{htmlonly}
+ \return{a reference to the random variate generator whose values are cached.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCachedGen (RandomVariateGen rvg)\begin{hide} {
+ if (rvg == null)
+ throw new NullPointerException
+ ("The given random variate generator cannot be null");
+ if (rvg == this.rvg)
+ return;
+ this.rvg = rvg;
+ clearCache();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the random variate generator whose values are cached to
+ \texttt{rvg}. If the generator is changed, the \method{clearCache}{()}
+ method is called.
+\end{tabb}
+\begin{htmlonly}
+ \param{rvg}{the new random variate generator whose values are cached.}
+ \exception{NullPointerException}{if \texttt{rvg} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public void clearCache()\begin{hide} {
+ //values.clear();
+ // Keep the array previously returned by getCachedValues
+ // intact to allow caching values for several
+ // replications.
+ values = new DoubleArrayList();
+ index = 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Clears the cached values for this cached generator.
+ Any subsequent call will then obtain new values
+ from the internal generator.
+\end{tabb}
+\begin{code}
+
+ public void initCache()\begin{hide} {
+ index = 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Resets this generator to recover values from the cache.
+ Subsequent calls
+ to \method{nextDouble}{()} will return the cached random
+ values until all the values are returned. When the array
+ of cached values is exhausted, the internal random variate
+ generator is used to generate new values which are added
+ to the internal array as well.
+ This method is equivalent to calling \method{setCacheIndex}{(int)}.
+\end{tabb}
+\begin{code}
+
+ public int getNumCachedValues()\begin{hide} {
+ return values.size();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the total number of values cached by this generator.
+\end{tabb}
+\begin{htmlonly}
+ \return{the total number of cached values.}
+\end{htmlonly}
+\begin{code}
+
+ public int getCacheIndex()\begin{hide} {
+ return index;
+ }\end{hide}
+\end{code}
+\begin{tabb} Return the index of the next cached value that will be
+ returned by the generator.
+ If the cache is exhausted, the
+ returned value corresponds to the value returned by
+ \method{getNumCachedValues}{()}, and a subsequent call to
+ \method{nextDouble}{()} will generate a new variate rather than
+ reading a previous one from the cache.
+ If caching is disabled, this always returns 0.
+\end{tabb}
+\begin{htmlonly}
+ \return{the index of the next cached value.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCacheIndex (int newIndex)\begin{hide} {
+ if (newIndex < 0 || newIndex > values.size())
+ throw new IllegalArgumentException
+ ("newIndex must not be negative or greater than the cache size");
+ index = newIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the index, in the cache, of the next value returned
+ by \method{nextDouble}{()}.
+ If \texttt{newIndex} is 0, this is equivalent to
+ calling \method{initCache}{()}.
+ If \texttt{newIndex} is \method{getNumCachedValues}{()},
+ subsequent calls to \method{nextDouble}{()} will add
+ new values to the cache.
+\end{tabb}
+\begin{htmlonly}
+ \param{newIndex}{the new index.}
+ \exception{IllegalArgumentException}{if \texttt{newIndex}
+ is negative or greater than or equal to the cache size.}
+\end{htmlonly}
+\begin{code}
+
+ public DoubleArrayList getCachedValues()\begin{hide} {
+ return values;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns an array list containing the values
+ cached by this random variate generator.
+\end{tabb}
+\begin{htmlonly}
+ \return{the array of cached values.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCachedValues (DoubleArrayList values)\begin{hide} {
+ if (values == null)
+ throw new NullPointerException();
+ this.values = values;
+ index = values.size();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the array list containing the cached
+ values to \texttt{values}.
+ This resets the cache index to
+ the size of the given array.
+\end{tabb}
+\begin{htmlonly}
+ \param{values}{the array list of cached values.}
+ \exception{NullPointerException}{if \texttt{values} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ if (!caching)
+ return rvg.nextDouble();
+ else if (index >= values.size()) {
+ double v = rvg.nextDouble();
+ values.add (v);
+ ++index;
+ return v;
+ }
+ else
+ return values.getQuick (index++);
+ }
+
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ if (!caching) {
+ rvg.nextArrayOfDouble (v, start, n);
+ return;
+ }
+ int remainingValues = values.size() - index;
+ if (remainingValues < 0)
+ remainingValues = 0;
+ int ncpy = Math.min (n, remainingValues);
+ if (ncpy > 0) {
+ System.arraycopy (values.elements(), index, v, start, ncpy);
+ index += ncpy;
+ }
+ int ngen = n - ncpy;
+ if (ngen > 0) {
+ rvg.nextArrayOfDouble (v, start + ncpy, ngen);
+ for (int i = ncpy; i < n; i++) {
+ values.add (v[start + i]);
+ ++index;
+ }
+ }
+ }
+
+ public RandomStream getStream() {
+ return rvg.getStream();
+ }
+
+ public Distribution getDistribution() {
+ return rvg.getDistribution();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/RayleighGen.java b/source/umontreal/iro/lecuyer/randvar/RayleighGen.java
new file mode 100644
index 0000000..d170256
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RayleighGen.java
@@ -0,0 +1,136 @@
+
+
+/*
+ * Class: RayleighGen
+ * Description: random variate generators for the Rayleigh distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>Rayleigh</EM> distribution.
+ * Its density is
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ *
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>f</I> (<I>x</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>(<I>x</I>-<I>a</I>) <I>e</I><SUP>-(x-a)<SUP>2</SUP>/(2<I>β</I><SUP>2</SUP>)</SUP>/<I>β</I><SUP>2</SUP> for <I>x</I> >= <I>a</I></TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>f</I> (<I>x</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>0 for <I>x</I> < <I>a</I>,</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ * where <SPAN CLASS="MATH"><I>β</I> > 0</SPAN>.
+ *
+ */
+public class RayleighGen extends RandomVariateGen {
+ private double a;
+ private double beta;
+
+
+ /**
+ * Creates a Rayleigh random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public RayleighGen (RandomStream s, double a, double beta) {
+ super (s, new RayleighDist(a, beta));
+ setParams (a, beta);
+ }
+
+
+ /**
+ * Creates a Rayleigh random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>a</I> = 0</SPAN> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public RayleighGen (RandomStream s, double beta) {
+ this (s, 0.0, beta);
+ }
+
+
+ /**
+ * Creates a new generator for the Rayleigh distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public RayleighGen (RandomStream s, RayleighDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getSigma());
+ }
+
+
+ /**
+ * Uses inversion to generate a new variate from the Rayleigh
+ * distribution with parameters <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT> and
+ * <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double a, double beta) {
+ return RayleighDist.inverseF (a, beta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>a</I></SPAN>.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>β</I></SPAN>.
+ *
+ */
+ public double getSigma() {
+ return beta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT> and <SPAN CLASS="MATH"><I>β</I> =</SPAN> <TT>beta</TT>
+ * for this object.
+ *
+ */
+ public void setParams (double a, double beta) {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.a = a;
+ this.beta = beta;
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/RayleighGen.tex b/source/umontreal/iro/lecuyer/randvar/RayleighGen.tex
new file mode 100644
index 0000000..448cf6b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/RayleighGen.tex
@@ -0,0 +1,142 @@
+\defmodule {RayleighGen}
+
+This class implements random variate generators for the
+{\em Rayleigh\/} distribution.
+ Its density is
+\begin{latexonly}
+\[
+ f(x) = \left\{
+\begin{array}{ll}
+ \frac{(x-a)}{\beta^2}\, e^{-(x-a)^2/2\beta^2} \quad
+ & \mbox{ for } x \ge a \\[6pt]
+ 0 & \mbox{ for } x < a,
+\end{array}
+ \right.
+\]
+\end{latexonly}
+\begin{htmlonly}
+\begin{eqnarray*}
+ f(x) &=& {(x-a)}\, e^{-(x-a)^2/(2\beta^2)} / {\beta^2}
+ \qquad\mbox{for } x \ge a \\[8pt]
+ f(x) &=& 0 \qquad\mbox{for } x < a,
+\end{eqnarray*}
+\end{htmlonly}
+where $\beta > 0$.
+
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RayleighGen
+ * Description: random variate generators for the Rayleigh distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class RayleighGen extends RandomVariateGen \begin{hide} {
+ private double a;
+ private double beta;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public RayleighGen (RandomStream s, double a, double beta) \begin{hide} {
+ super (s, new RayleighDist(a, beta));
+ setParams (a, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Rayleigh random variate generator with parameters
+ $a =$ \texttt{a} and $\beta =$ \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public RayleighGen (RandomStream s, double beta) \begin{hide} {
+ this (s, 0.0, beta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Rayleigh random variate generator with parameters
+ $a = 0$ and $\beta = $ \texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public RayleighGen (RandomStream s, RayleighDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getSigma());
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the Rayleigh distribution \texttt{dist}
+ and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double a, double beta) \begin{hide} {
+ return RayleighDist.inverseF (a, beta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses inversion to generate a new variate from the Rayleigh
+ distribution with parameters $a = $~\texttt{a} and
+ $\beta = $~\texttt{beta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $a$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma()\begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\beta$.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double a, double beta) \begin{hide} {
+ if (beta <= 0.0)
+ throw new IllegalArgumentException ("beta <= 0");
+ this.a = a;
+ this.beta = beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameters $a = $~\texttt{a} and $\beta=$~\texttt{beta}
+ for this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/StudentGen.java b/source/umontreal/iro/lecuyer/randvar/StudentGen.java
new file mode 100644
index 0000000..0742c5b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/StudentGen.java
@@ -0,0 +1,130 @@
+
+
+/*
+ * Class: StudentGen
+ * Description: Student-t random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements methods for generating random variates from the
+ * <EM>Student</EM> distribution with <SPAN CLASS="MATH"><I>n</I> > 0</SPAN> degrees of freedom.
+ * Its density function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = [<I>Γ</I>((<I>n</I> + 1)/2)/(<I>Γ</I>(<I>n</I>/2)(πn)<SUP>1/2</SUP>)][1 + <I>x</I><SUP>2</SUP>/<I>n</I>]<SUP>-(n+1)/2</SUP> for - ∞ < <I>x</I> < ∞,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN> is the gamma function defined in
+ * {@link GammaGen}.
+ *
+ * <P>
+ * The <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ * <P>
+ * The following table gives the CPU time needed to generate <SPAN CLASS="MATH">10<SUP>7</SUP></SPAN> Student
+ * random variates using the different implementations available in SSJ.
+ * The second test (Q) was made with the inverse in
+ * {@link StudentDistQuick},
+ * while the first test was made with the inverse in {@link StudentDist}.
+ * These tests were made on a machine with processor AMD Athlon 4000, running
+ * Red Hat Linux, with clock speed at 2400 MHz.
+ *
+ * <P>
+ * <DIV ALIGN="CENTER">
+ * <TABLE CELLPADDING=3 BORDER="1">
+ * <TR><TD ALIGN="LEFT">Generator</TD>
+ * <TD ALIGN="CENTER">time in seconds</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>StudentGen</TT></TD>
+ * <TD ALIGN="CENTER">22.4</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>StudentGen(Q)</TT></TD>
+ * <TD ALIGN="CENTER">6.5</TD>
+ * </TR>
+ * <TR><TD ALIGN="LEFT"><TT>StudentPolarGen</TT></TD>
+ * <TD ALIGN="CENTER">1.4</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV>
+ *
+ */
+public class StudentGen extends RandomVariateGen {
+ protected int n = -1;
+
+
+ /**
+ * Creates a Student random variate generator with
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public StudentGen (RandomStream s, int n) {
+ super (s, new StudentDist(n));
+ setN (n);
+ }
+
+
+ /**
+ * Creates a new generator for the Student distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public StudentGen (RandomStream s, StudentDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setN (dist.getN ());
+ }
+
+
+ /**
+ * Generates a new variate from the Student distribution
+ * with <SPAN CLASS="MATH"><I>n</I> =</SPAN> <TT>n</TT> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, int n) {
+ return StudentDist.inverseF (n, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>n</I></SPAN> for this object.
+ *
+ *
+ */
+ public int getN() {
+ return n;
+ }
+
+
+
+ protected void setN (int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = nu;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/StudentGen.tex b/source/umontreal/iro/lecuyer/randvar/StudentGen.tex
new file mode 100644
index 0000000..3b29dab
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/StudentGen.tex
@@ -0,0 +1,140 @@
+\defmodule {StudentGen}
+
+This class implements methods for generating random variates from the
+{\em Student\/} distribution with $n>0$ degrees of freedom.
+Its density function is
+\begin{htmlonly}
+ \eq
+ f (x) = [\Gamma((n + 1)/2 )/
+ (\Gamma (n/2) \sqrt{\pi n})]
+ [1 + x^2/n]^{-(n+1)/2}
+ \qquad \qquad \mbox {for } -\infty < x < \infty,
+ \endeq
+\end{htmlonly}
+\begin{latexonly}
+ \eq
+ f (x) = \frac{\Gamma\left ((n + 1)/2 \right)}
+ {\Gamma (n/2) \sqrt{\pi n}}
+ \left[1 + \frac{x^2}{n}\right]^{-(n+1)/2}
+ \qquad \qquad \mbox {for } -\infty < x < \infty, \eqlabel{eq:fstudent}
+ \endeq
+\end{latexonly}
+where $\Gamma (x)$ is the gamma function defined in
+\latex{(\ref{eq:Gamma})}\html{\class{GammaGen}}.
+
+The \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+The following table gives the CPU time needed to generate $10^7$ Student
+ random variates using the different implementations available in SSJ.
+ The second test ({Q}) was made with the inverse in
+ \class{StudentDistQuick},
+ while the first test was made with the inverse in \class{StudentDist}.
+These tests were made on a machine with processor AMD Athlon 4000, running
+Red Hat Linux, with clock speed at 2400 MHz.
+
+\begin{center}
+\begin{tabular}{|l|c|}
+\hline
+ Generator & time in seconds \\
+\hline
+\texttt{StudentGen} & 22.4 \\
+\texttt{StudentGen(Q)} & \phantom{2}6.5 \\
+\texttt{StudentPolarGen} & \phantom{2}1.4 \\
+\hline
+\end{tabular}
+\end{center}
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: StudentGen
+ * Description: Student-t random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class StudentGen extends RandomVariateGen \begin{hide} {
+ protected int n = -1;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public StudentGen (RandomStream s, int n) \begin{hide} {
+ super (s, new StudentDist(n));
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Student random variate generator with
+ $n$ degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public StudentGen (RandomStream s, StudentDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setN (dist.getN ());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the Student distribution \texttt{dist}
+ and stream \texttt{s}.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, int n) \begin{hide} {
+ return StudentDist.inverseF (n, s.nextDouble());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Generates a new variate from the Student distribution
+ with $n = $~\texttt{n} degrees of freedom, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public int getN()\begin{hide} {
+ return n;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the value of $n$ for this object.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setN (int nu) {
+ if (nu <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ this.n = nu;
+ }
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/StudentNoncentralGen.java b/source/umontreal/iro/lecuyer/randvar/StudentNoncentralGen.java
new file mode 100644
index 0000000..8e65f79
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/StudentNoncentralGen.java
@@ -0,0 +1,95 @@
+
+
+/*
+ * Class: StudentNoncentralGen
+ * Description: random variate generator for the noncentral Student-t distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <SPAN CLASS="textit">noncentral Student-t</SPAN> distribution with <SPAN CLASS="MATH"><I>n</I> > 0</SPAN> degrees of freedom and
+ * noncentrality parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>. If <SPAN CLASS="MATH"><I>X</I></SPAN> is distributed according to a
+ * normal distribution with mean <SPAN CLASS="MATH"><I>δ</I></SPAN> and variance 1, and <SPAN CLASS="MATH"><I>Y</I></SPAN> (statistically
+ * independent of <SPAN CLASS="MATH"><I>X</I></SPAN>) is distributed according to a chi-square distribution
+ * with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom, then
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>T</I> ' = <I>X</I>/(Y/n)<SUP>1/2</SUP>
+ * </DIV><P></P>
+ * has a noncentral <SPAN CLASS="MATH"><I>t</I></SPAN>-distribution with <SPAN CLASS="MATH"><I>n</I></SPAN> degrees of freedom and
+ * noncentrality parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+public class StudentNoncentralGen extends RandomVariateGen {
+ private NormalGen normgen;
+ private ChiSquareGen chigen;
+ private int n; // degrees of freedom of chi-square
+
+ public double nextDouble() {
+ double x = normgen.nextDouble();
+ double y = chigen.nextDouble();
+ return x / Math.sqrt(y/n);
+ }
+
+
+
+ /**
+ * Creates a <SPAN CLASS="textit">noncentral-t</SPAN> random variate generator
+ * using normal generator <TT>ngen</TT> and chi-square generator <TT>cgen</TT>.
+ *
+ */
+ public StudentNoncentralGen (NormalGen ngen, ChiSquareGen cgen) {
+ super (null, null);
+ setNormalGen (ngen);
+ setChiSquareGen (cgen);
+ }
+
+
+ /**
+ * Sets the normal generator to <TT>ngen</TT>.
+ *
+ */
+ public void setNormalGen (NormalGen ngen) {
+ if (1.0 != ngen.getSigma())
+ throw new IllegalArgumentException (" variance of normal must be 1");
+ normgen = ngen;
+ }
+
+
+ /**
+ * Sets the chi-square generator to <TT>cgen</TT>.
+ *
+ */
+ public void setChiSquareGen (ChiSquareGen cgen) {
+ chigen = cgen;
+ n = cgen.getN();
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/StudentNoncentralGen.tex b/source/umontreal/iro/lecuyer/randvar/StudentNoncentralGen.tex
new file mode 100644
index 0000000..7b5e442
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/StudentNoncentralGen.tex
@@ -0,0 +1,109 @@
+\defmodule {StudentNoncentralGen}
+
+This class implements random variate generators for the
+\emph{noncentral Student-t} distribution with $n>0$ degrees of freedom and
+noncentrality parameter $\delta$. If $X$ is distributed according to a
+normal distribution with mean $\delta$ and variance 1, and $Y$ (statistically
+independent of $X$) is distributed according to a chi-square distribution
+with $n$ degrees of freedom, then
+\begin{latexonly}%
+\[
+ T{'} = \frac{X}{\sqrt{Y/n}}
+\]
+\end{latexonly}%
+\begin{htmlonly}%
+\[
+ T\,' = {X} / {\sqrt{Y/n}}
+\]
+\end{htmlonly}%
+has a noncentral $t$-distribution with $n$ degrees of freedom and
+noncentrality parameter $\delta$.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: StudentNoncentralGen
+ * Description: random variate generator for the noncentral Student-t distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class StudentNoncentralGen extends RandomVariateGen \begin{hide} {
+ private NormalGen normgen;
+ private ChiSquareGen chigen;
+ private int n; // degrees of freedom of chi-square
+
+ public double nextDouble() {
+ double x = normgen.nextDouble();
+ double y = chigen.nextDouble();
+ return x / Math.sqrt(y/n);
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public StudentNoncentralGen (NormalGen ngen, ChiSquareGen cgen) \begin{hide} {
+ super (null, null);
+ setNormalGen (ngen);
+ setChiSquareGen (cgen);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a \emph{noncentral-t} random variate generator
+ using normal generator \texttt{ngen} and chi-square generator \texttt{cgen}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public void setNormalGen (NormalGen ngen)\begin{hide} {
+ if (1.0 != ngen.getSigma())
+ throw new IllegalArgumentException (" variance of normal must be 1");
+ normgen = ngen;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the normal generator to \texttt{ngen}.
+ \end{tabb}
+\begin{code}
+
+ public void setChiSquareGen (ChiSquareGen cgen)\begin{hide} {
+ chigen = cgen;
+ n = cgen.getN();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the chi-square generator to \texttt{cgen}.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/StudentPolarGen.java b/source/umontreal/iro/lecuyer/randvar/StudentPolarGen.java
new file mode 100644
index 0000000..1cbde61
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/StudentPolarGen.java
@@ -0,0 +1,143 @@
+
+
+/*
+ * Class: StudentPolarGen
+ * Description: Student-t random variate generators using the polar method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements <EM>Student</EM> random variate generators using
+ * the <EM>polar</EM> method of.
+ * The code is adapted from UNURAN.
+ *
+ * <P>
+ * The non-static <TT>nextDouble</TT> method generates two variates at a time
+ * and the second one is saved for the next call.
+ * A pair of variates is generated every second call.
+ * In the static case, two variates are generated per
+ * call but only the first one is returned and the second is discarded.
+ *
+ */
+public class StudentPolarGen extends StudentGen {
+
+ private boolean available = false;
+ private double[] variates = new double[2];
+ private static double[] staticVariates = new double[2];
+ // Used by the polar method.
+
+
+ /**
+ * Creates a Student random variate generator with <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+ public StudentPolarGen (RandomStream s, int n) {
+ super (s, null);
+ setN (n);
+ }
+
+
+ /**
+ * Creates a new generator for the Student distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public StudentPolarGen (RandomStream s, StudentDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setN (dist.getN ());
+ }
+
+
+ public double nextDouble() {
+ if (available) {
+ available = false;
+ return variates[1];
+ }
+ else {
+ polar (stream, n, variates);
+ available = true;
+ return variates[0];
+ }
+ }
+
+ public static double nextDouble (RandomStream s, int n) {
+ polar (s, n, staticVariates);
+ return staticVariates[0];
+ }
+ /**
+ * Generates a new variate from the Student distribution
+ * with <SPAN CLASS="MATH"><I>n</I> =</SPAN> <TT>n</TT> degrees of freedom, using stream <TT>s</TT>.
+ *
+ */
+
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+
+/* ****************************************************************************
+ * *
+ * Student's t Distribution: Polar Method *
+ * *
+ *****************************************************************************
+ * *
+ * FUNCTION: - samples a random number from Student's t distribution with *
+ * parameters n > 0. *
+ * *
+ * REFERENCE: - R.W. Bailey (1994): Polar generation of random variates *
+ * with the t-distribution, *
+ * Mathematics of Computation 62, 779-781. *
+ * *
+ * Implemented by F. Niederl, 1994 *
+ *****************************************************************************
+ * *
+ * The polar method of Box/Muller for generating Normal variates is adapted *
+ * to the Student-t distribution. The two generated variates are not *
+ * independent and the expected no. of uniforms per variate is 2.5464. *
+ * *
+ *****************************************************************************
+ * WinRand (c) 1995 Ernst Stadlober, Institut fuer Statistitk, TU Graz *
+ *****************************************************************************
+ * UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien *
+ ****************************************************************************/
+
+ private static void polar (RandomStream stream, int n, double[] variates) {
+ double u,v,w;
+ do {
+ u = 2. * stream.nextDouble() - 1.;
+ v = 2. * stream.nextDouble() - 1.;
+ w = u*u + v*v;
+ } while (w > 1.);
+
+ double temp = Math.sqrt (n*(Math.exp (-2./n*Math.log (w)) - 1.)/w);
+ variates[0] = u*temp;
+ variates[1] = v*temp;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/StudentPolarGen.tex b/source/umontreal/iro/lecuyer/randvar/StudentPolarGen.tex
new file mode 100644
index 0000000..ba00f10
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/StudentPolarGen.tex
@@ -0,0 +1,150 @@
+\defmodule {StudentPolarGen}
+
+This class implements {\em Student\/} random variate generators using
+ the {\em polar\/} method of \cite{rBAI94a}.
+The code is adapted from UNURAN\latex{ (see \cite{iLEY02a})}.
+
+The non-static \texttt{nextDouble} method generates two variates at a time
+and the second one is saved for the next call.
+A pair of variates is generated every second call.
+In the static case, two variates are generated per
+call but only the first one is returned and the second is discarded.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: StudentPolarGen
+ * Description: Student-t random variate generators using the polar method
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class StudentPolarGen extends StudentGen \begin{hide} {
+
+ private boolean available = false;
+ private double[] variates = new double[2];
+ private static double[] staticVariates = new double[2];
+ // Used by the polar method.
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public StudentPolarGen (RandomStream s, int n) \begin{hide} {
+ super (s, null);
+ setN (n);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Student random variate generator with $n$
+ degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public StudentPolarGen (RandomStream s, StudentDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setN (dist.getN ());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the Student distribution \texttt{dist}
+ and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ if (available) {
+ available = false;
+ return variates[1];
+ }
+ else {
+ polar (stream, n, variates);
+ available = true;
+ return variates[0];
+ }
+ }
+
+ public static double nextDouble (RandomStream s, int n) {
+ polar (s, n, staticVariates);
+ return staticVariates[0];
+ }
+\end{code}
+\begin{tabb} Generates a new variate from the Student distribution
+ with $n = $~\texttt{n} degrees of freedom, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+
+//>>>>>>>>>>>>>>>>>>>> P R I V A T E S M E T H O D S <<<<<<<<<<<<<<<<<<<<
+
+
+/*****************************************************************************
+ * *
+ * Student's t Distribution: Polar Method *
+ * *
+ *****************************************************************************
+ * *
+ * FUNCTION: - samples a random number from Student's t distribution with *
+ * parameters n > 0. *
+ * *
+ * REFERENCE: - R.W. Bailey (1994): Polar generation of random variates *
+ * with the t-distribution, *
+ * Mathematics of Computation 62, 779-781. *
+ * *
+ * Implemented by F. Niederl, 1994 *
+ *****************************************************************************
+ * *
+ * The polar method of Box/Muller for generating Normal variates is adapted *
+ * to the Student-t distribution. The two generated variates are not *
+ * independent and the expected no. of uniforms per variate is 2.5464. *
+ * *
+ *****************************************************************************
+ * WinRand (c) 1995 Ernst Stadlober, Institut fuer Statistitk, TU Graz *
+ *****************************************************************************
+ * UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien *
+ ****************************************************************************/
+
+ private static void polar (RandomStream stream, int n, double[] variates) {
+ double u,v,w;
+ do {
+ u = 2. * stream.nextDouble() - 1.;
+ v = 2. * stream.nextDouble() - 1.;
+ w = u*u + v*v;
+ } while (w > 1.);
+
+ double temp = Math.sqrt (n*(Math.exp (-2./n*Math.log (w)) - 1.)/w);
+ variates[0] = u*temp;
+ variates[1] = v*temp;
+ }
+
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvar/TriangularGen.java b/source/umontreal/iro/lecuyer/randvar/TriangularGen.java
new file mode 100644
index 0000000..93abe4d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/TriangularGen.java
@@ -0,0 +1,158 @@
+
+
+/*
+ * Class: TriangularGen
+ * Description: random variate generators for the triangular distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>triangular</EM> distribution. Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">2(<I>x</I> - <I>a</I>)/[(<I>b</I> - <I>a</I>)(<I>m</I> - <I>a</I>)]</TD>
+ * <TD ALIGN="LEFT"> for <I>a</I> <= <I>x</I> <= <I>m</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">2(<I>b</I> - <I>x</I>)/[(<I>b</I> - <I>a</I>)(<I>b</I> - <I>m</I>)]</TD>
+ * <TD ALIGN="LEFT"> for <I>m</I> <= <I>x</I> <= <I>b</I>,</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>f</I> (<I>x</I>) =</TD>
+ * <TD ALIGN="LEFT">0</TD>
+ * <TD ALIGN="LEFT"> elsewhere,</TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>a</I> <= <I>m</I> <= <I>b</I></SPAN> (see, e.g.,).
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class TriangularGen extends RandomVariateGen {
+ private double a;
+ private double b;
+ private double m;
+
+
+ /**
+ * Creates a triangular random variate generator over the interval
+ * (<TT>a</TT>, <TT>b</TT>), with parameter <TT>m</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public TriangularGen (RandomStream s, double a, double b, double m) {
+ super (s, new TriangularDist(a, b, m));
+ setParams (a, b, m);
+ }
+
+
+ /**
+ * Creates a triangular random variate generator over the interval
+ * <SPAN CLASS="MATH">(0, 1)</SPAN>, with parameter <TT>m</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public TriangularGen (RandomStream s, double m) {
+ this (s, 0.0, 1.0, m);
+ }
+
+
+ /**
+ * Creates a new generator for the triangular distribution
+ * <TT>dist</TT> and stream <TT>s</TT>.
+ *
+ */
+ public TriangularGen (RandomStream s, TriangularDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getB(), dist.getM());
+ }
+
+
+ /**
+ * Generates a new variate from the triangular distribution with parameters
+ * <SPAN CLASS="MATH"><I>a</I> =</SPAN> <TT>a</TT>, <SPAN CLASS="MATH"><I>b</I> =</SPAN> <TT>b</TT> and <SPAN CLASS="MATH"><I>m</I> =</SPAN> <TT>m</TT> and stream <TT>s</TT>,
+ * using inversion.
+ *
+ */
+ public static double nextDouble (RandomStream s, double a,
+ double b, double m) {
+ // the code is taken and adapted from unuran
+ // file /distributions/c_triangular_gen.c
+ return TriangularDist.inverseF (a, b, m, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>a</I></SPAN> for this object.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN> for this object.
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>m</I></SPAN> for this object.
+ *
+ *
+ */
+ public double getM() {
+ return m;
+ }
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>a</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>m</I></SPAN> for this object.
+ *
+ */
+ private void setParams (double a, double b, double m) {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/TriangularGen.tex b/source/umontreal/iro/lecuyer/randvar/TriangularGen.tex
new file mode 100644
index 0000000..d6c2397
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/TriangularGen.tex
@@ -0,0 +1,161 @@
+\defmodule {TriangularGen}
+
+This class implements random variate generators for the
+{\em triangular\/} distribution. Its density is
+\begin{htmlonly}
+\[\begin{array}{rll}
+f(x) =& 2(x-a)/[(b-a)(m-a)]&\qquad\mbox{ for }a\le x\le m, \\
+f(x) =& 2(b-x)/[(b-a)(b-m)]&\qquad\mbox{ for }m\le x\le b, \\
+f(x) =& 0&\qquad\mbox{ elsewhere,}
+\end{array}\]
+\end{htmlonly}
+\begin{latexonly}
+\eq
+ f(x) = \left \{\begin{array}{ll}
+ \frac {2(x-a)}{(b-a)(m-a)} & \mbox {for } a\le x\le m, \\ [6pt]
+ \frac {2(b-x)}{(b-a)(b-m)} & \mbox { for } m\le x\le b, \\ [6pt]
+ 0 & \mbox { elsewhere, }
+ \end{array}\right. \eqlabel{eq:ftrian}
+\endeq
+\end{latexonly}
+where $a\le m\le b$ (see, e.g., \cite{sLAW00a}).
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: TriangularGen
+ * Description: random variate generators for the triangular distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class TriangularGen extends RandomVariateGen \begin{hide} {
+ private double a;
+ private double b;
+ private double m;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public TriangularGen (RandomStream s, double a, double b, double m) \begin{hide} {
+ super (s, new TriangularDist(a, b, m));
+ setParams (a, b, m);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a triangular random variate generator over the interval
+(\texttt{a}, \texttt{b}), with parameter \texttt{m}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public TriangularGen (RandomStream s, double m) \begin{hide} {
+ this (s, 0.0, 1.0, m);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a triangular random variate generator over the interval
+ $(0, 1)$, with parameter \texttt{m}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public TriangularGen (RandomStream s, TriangularDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getB(), dist.getM());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the triangular distribution
+ \texttt{dist} and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double a,
+ double b, double m) \begin{hide} {
+ // the code is taken and adapted from unuran
+ // file /distributions/c_triangular_gen.c
+ return TriangularDist.inverseF (a, b, m, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a new variate from the triangular distribution with parameters
+ $a = $~\texttt{a}, $b = $~\texttt{b} and $m = $~\texttt{m} and stream \texttt{s},
+ using inversion.
+\end{tabb}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $a$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $b$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double getM()\begin{hide} {
+ return m;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $m$ for this object.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ private void setParams (double a, double b, double m) {
+ if ((a == 0.0 && b == 1.0) && (m < 0 || m > 1))
+ throw new IllegalArgumentException ("m is not in [0,1]");
+ else if (a >= b)
+ throw new IllegalArgumentException ("a >= b");
+ else if (m < a || m > b)
+ throw new IllegalArgumentException ("m is not in [a,b]");
+ this.a = a;
+ this.b = b;
+ this.m = m;
+ }
+\end{code}
+ \begin{tabb}
+ Sets the value of the parameters $a$, $b$ and $m$ for this object.
+ \end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/UniformGen.java b/source/umontreal/iro/lecuyer/randvar/UniformGen.java
new file mode 100644
index 0000000..cd6e079
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UniformGen.java
@@ -0,0 +1,126 @@
+
+
+/*
+ * Class: UniformGen
+ * Description: random variate generators for the uniform distribution over the reals
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for
+ * the (continuous) <EM>uniform</EM> distribution over the interval <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>)</SPAN>,
+ * where <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> are real numbers with <SPAN CLASS="MATH"><I>a</I> < <I>b</I></SPAN>.
+ * The density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = 1/(<I>b</I> - <I>a</I>) for <I>a</I> <= <I>x</I> <= <I>b</I>.
+ * </DIV><P></P>
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class UniformGen extends RandomVariateGen {
+ private double a;
+ private double b;
+
+
+ /**
+ * Creates a uniform random variate generator over the interval
+ * <SPAN CLASS="MATH">(</SPAN><TT>a</TT>, <TT>b</TT><SPAN CLASS="MATH">)</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public UniformGen (RandomStream s, double a, double b) {
+ super (s, new UniformDist(a, b));
+ setParams (a, b);
+ }
+
+
+ /**
+ * Creates a uniform random variate generator over the interval
+ * <SPAN CLASS="MATH">(0, 1)</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public UniformGen (RandomStream s) {
+ this (s, 0.0, 1.0);
+ }
+
+
+ /**
+ * Creates a new generator for the uniform distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public UniformGen (RandomStream s, UniformDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getB());
+ }
+
+
+ /**
+ * Generates a uniform random variate over the interval
+ * <SPAN CLASS="MATH">(</SPAN><TT>a</TT>, <TT>b</TT><SPAN CLASS="MATH">)</SPAN> by inversion, using stream <TT>s</TT>.
+ *
+ */
+ static public double nextDouble (RandomStream s, double a, double b) {
+ return UniformDist.inverseF (a, b, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>a</I></SPAN> for this object.
+ *
+ */
+ public double getA() {
+ return a;
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN> for this object.
+ *
+ *
+ */
+ public double getB() {
+ return b;
+ }
+
+
+ /**
+ * Sets the value of the parameters <SPAN CLASS="MATH"><I>a</I></SPAN> and <SPAN CLASS="MATH"><I>b</I></SPAN> for this object.
+ *
+ */
+ private void setParams (double a, double b) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ this.a = a;
+ this.b = b;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/UniformGen.tex b/source/umontreal/iro/lecuyer/randvar/UniformGen.tex
new file mode 100644
index 0000000..e732f45
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UniformGen.tex
@@ -0,0 +1,130 @@
+\defmodule {UniformGen}
+
+This class implements random variate generators for
+the (continuous) {\em uniform\/} distribution over the interval $(a,b)$,
+where $a$ and $b$ are real numbers with $a < b$.
+The density is
+\eq
+ f(x) = 1/(b - a) \qquad\mbox{ for }a\le x\le b.
+ \eqlabel{eq:funiform}
+\endeq
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: UniformGen
+ * Description: random variate generators for the uniform distribution over the reals
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class UniformGen extends RandomVariateGen \begin{hide} {
+ private double a;
+ private double b;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public UniformGen (RandomStream s, double a, double b) \begin{hide} {
+ super (s, new UniformDist(a, b));
+ setParams (a, b);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a uniform random variate generator over the interval
+ $($\texttt{a}, \texttt{b}$)$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public UniformGen (RandomStream s) \begin{hide} {
+ this (s, 0.0, 1.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a uniform random variate generator over the interval
+ $(0, 1)$, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public UniformGen (RandomStream s, UniformDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getA(), dist.getB());
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the uniform distribution \texttt{dist}
+ and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ static public double nextDouble (RandomStream s, double a, double b) \begin{hide} {
+ return UniformDist.inverseF (a, b, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates a uniform random variate over the interval
+ $($\texttt{a}, \texttt{b}$)$ by inversion, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getA()\begin{hide} {
+ return a;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $a$ for this object.
+ \end{tabb}
+\begin{code}
+
+ public double getB()\begin{hide} {
+ return b;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the value of $b$ for this object.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ private void setParams (double a, double b) {
+ if (b <= a)
+ throw new IllegalArgumentException ("b <= a");
+ this.a = a;
+ this.b = b;
+ }
+\end{code}
+\begin{tabb}
+ Sets the value of the parameters $a$ and $b$ for this object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/UniformIntGen.java b/source/umontreal/iro/lecuyer/randvar/UniformIntGen.java
new file mode 100644
index 0000000..8d99b61
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UniformIntGen.java
@@ -0,0 +1,109 @@
+
+
+/*
+ * Class: UniformIntGen
+ * Description: random variate generator for the uniform distribution over integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements a random variate generator for the
+ * <EM>uniform</EM> distribution over integers, over the interval <SPAN CLASS="MATH">[<I>i</I>, <I>j</I>]</SPAN>.
+ * Its mass function is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>p</I>(<I>x</I>) = 1/(<I>j</I> - <I>i</I> + 1) for <I>x</I> = <I>i</I>, <I>i</I> + 1,…, <I>j</I>
+ * </DIV><P></P>
+ * and 0 elsewhere.
+ *
+ */
+public class UniformIntGen extends RandomVariateGenInt {
+ protected int left; // the left limit of the interval
+ protected int right; // the right limit of the interval
+
+
+
+
+ /**
+ * Creates a uniform random variate generator over the integers
+ * in the closed interval <SPAN CLASS="MATH">[<I>i</I>, <I>j</I>]</SPAN>, using stream <TT>s</TT>.
+ *
+ */
+ public UniformIntGen (RandomStream s, int i, int j) {
+ super (s, new UniformIntDist (i, j));
+ setParams (i, j);
+ }
+
+
+ /**
+ * Creates a new generator for the distribution <TT>dist</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+ public UniformIntGen (RandomStream s, UniformIntDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getI(), dist.getJ());
+ }
+
+
+ /**
+ * Generates a new <EM>uniform</EM> random variate over the interval
+ * <SPAN CLASS="MATH">[<I>i</I>, <I>j</I>]</SPAN>, using stream <TT>s</TT>, by inversion.
+ *
+ */
+ public static int nextInt (RandomStream s, int i, int j) {
+ return UniformIntDist.inverseF (i, j, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ *
+ */
+ public int getI() {
+ return left;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>j</I></SPAN>.
+ *
+ *
+ */
+ public int getJ() {
+ return right;
+ }
+
+
+ protected void setParams (int i, int j) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ left = i;
+ right = j;
+ }
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/randvar/UniformIntGen.tex b/source/umontreal/iro/lecuyer/randvar/UniformIntGen.tex
new file mode 100644
index 0000000..eaa7468
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UniformIntGen.tex
@@ -0,0 +1,125 @@
+\defmodule {UniformIntGen}
+
+This class implements a random variate generator for the
+{\em uniform\/} distribution over integers, over the interval $[i,j]$.
+Its mass function is
+\begin{htmlonly}
+\eq
+ p(x) = 1 / (j - i + 1) \qquad\mbox{ for } x = i, i + 1, \ldots, j
+ \eqlabel{eq:fmassuniformint}
+\endeq
+\end{htmlonly}%
+\begin{latexonly}
+\eq
+ p(x) = \frac{1}{j - i + 1} \qquad\mbox{ for } x = i, i + 1, \ldots, j
+ \eqlabel{eq:fmassuniformint}
+\endeq
+\end{latexonly}
+and 0 elsewhere.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: UniformIntGen
+ * Description: random variate generator for the uniform distribution over integers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class UniformIntGen extends RandomVariateGenInt \begin{hide} {
+ protected int left; // the left limit of the interval
+ protected int right; // the right limit of the interval
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public UniformIntGen (RandomStream s, int i, int j) \begin{hide} {
+ super (s, new UniformIntDist (i, j));
+ setParams (i, j);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a uniform random variate generator over the integers
+ in the closed interval $[i, j]$, using stream \texttt{s}.
+ \end{tabb}
+\begin{code}
+
+ public UniformIntGen (RandomStream s, UniformIntDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getI(), dist.getJ());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Creates a new generator for the distribution \texttt{dist}, using
+ stream \texttt{s}.
+ \end{tabb}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static int nextInt (RandomStream s, int i, int j) \begin{hide} {
+ return UniformIntDist.inverseF (i, j, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a new {\em uniform\/} random variate over the interval
+ $[i,j]$, using stream \texttt{s}, by inversion.
+\end{tabb}
+\begin{code}
+
+ public int getI()\begin{hide} {
+ return left;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $i$.
+ \end{tabb}
+\begin{code}
+
+ public int getJ()\begin{hide} {
+ return right;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the parameter $j$.
+ \end{tabb}
+\begin{hide}\begin{code}
+
+ protected void setParams (int i, int j) {
+ if (j < i)
+ throw new IllegalArgumentException ("j < i");
+ left = i;
+ right = j;
+ }
+}\end{code}
+\end{hide}
+
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranContinuous.java b/source/umontreal/iro/lecuyer/randvar/UnuranContinuous.java
new file mode 100644
index 0000000..977bbe4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranContinuous.java
@@ -0,0 +1,115 @@
+
+
+/*
+ * Class: UnuranContinuous
+ * Description: create continuous univariate generators using UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.Distribution;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This class permits one to create continuous univariate
+ * generators using UNURAN via its string API.
+ *
+ */
+public class UnuranContinuous extends RandomVariateGen {
+
+ private RandUnuran unuran = new RandUnuran();
+
+
+ /**
+ * Same as {@link #UnuranContinuous UnuranContinuous}<TT>(s, s, genStr)</TT>.
+ *
+ */
+ public UnuranContinuous (RandomStream s, String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ unuran.init (genStr);
+ if (!unuran.isContinuous()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a continuous distribution");
+ }
+ }
+
+
+ /**
+ * Constructs a new continuous random number generator using
+ * the UNURAN generator specification string <TT>genStr</TT>,
+ * main stream <TT>s</TT>, and auxiliary stream <TT>aux</TT>.
+ *
+ */
+ public UnuranContinuous (RandomStream s, RandomStream aux,
+ String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("aux must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ unuran.init (genStr);
+ if (!unuran.isContinuous()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a continuous distribution");
+ }
+ }
+
+
+ public double nextDouble() {
+ if (unuran.nativeParams == 0)
+ throw new IllegalStateException();
+ return unuran.getRandCont (unuran.mainStream.nextDouble(), unuran.nativeParams);
+ }
+
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ if (v == null || start < 0 || n < 0 || (start+n) > v.length)
+ throw new IllegalArgumentException();
+ if (unuran.unifArray == null || unuran.unifArray.length < n)
+ unuran.unifArray = new double[n];
+ if (unuran.mainStream != unuran.auxStream &&
+ (unuran.unifAuxArray == null || unuran.unifAuxArray.length < n))
+ unuran.unifAuxArray = new double[n];
+ unuran.getRandContArray (unuran.nativeParams, unuran.unifArray,
+ unuran.unifAuxArray, v, start, n);
+ }
+
+ protected void finalize() {
+ unuran.close();
+ }
+
+ public Distribution getDistribution() { return null; }
+
+ public RandomStream getStream() { return unuran.mainStream; }
+
+ /**
+ * Returns the auxiliary random number stream.
+ *
+ */
+ public RandomStream getAuxStream() {
+ return unuran.auxStream;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranContinuous.tex b/source/umontreal/iro/lecuyer/randvar/UnuranContinuous.tex
new file mode 100644
index 0000000..86d15da
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranContinuous.tex
@@ -0,0 +1,123 @@
+\defmodule{UnuranContinuous}
+
+This class permits one to create continuous univariate
+generators using UNURAN via its string API.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: UnuranContinuous
+ * Description: create continuous univariate generators using UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.Distribution;
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+public class UnuranContinuous extends RandomVariateGen\begin{hide} {
+
+ private RandUnuran unuran = new RandUnuran();
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public UnuranContinuous (RandomStream s, String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ unuran.init (genStr);
+ if (!unuran.isContinuous()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a continuous distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{UnuranContinuous}{}\texttt{(s, s, genStr)}.
+\end{tabb}
+\begin{code}
+
+ public UnuranContinuous (RandomStream s, RandomStream aux,
+ String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("aux must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ unuran.init (genStr);
+ if (!unuran.isContinuous()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a continuous distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new continuous random number generator using
+ the UNURAN generator specification string \texttt{genStr},
+ main stream \texttt{s}, and auxiliary stream \texttt{aux}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ if (unuran.nativeParams == 0)
+ throw new IllegalStateException();
+ return unuran.getRandCont (unuran.mainStream.nextDouble(), unuran.nativeParams);
+ }
+
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ if (v == null || start < 0 || n < 0 || (start+n) > v.length)
+ throw new IllegalArgumentException();
+ if (unuran.unifArray == null || unuran.unifArray.length < n)
+ unuran.unifArray = new double[n];
+ if (unuran.mainStream != unuran.auxStream &&
+ (unuran.unifAuxArray == null || unuran.unifAuxArray.length < n))
+ unuran.unifAuxArray = new double[n];
+ unuran.getRandContArray (unuran.nativeParams, unuran.unifArray,
+ unuran.unifAuxArray, v, start, n);
+ }
+
+ protected void finalize() {
+ unuran.close();
+ }
+
+ public Distribution getDistribution() { return null; }
+
+ public RandomStream getStream() { return unuran.mainStream; }\end{hide}
+
+ public RandomStream getAuxStream()\begin{hide} {
+ return unuran.auxStream;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Returns the auxiliary random number stream.
+\end{tabb}
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranDiscreteInt.java b/source/umontreal/iro/lecuyer/randvar/UnuranDiscreteInt.java
new file mode 100644
index 0000000..f5288d3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranDiscreteInt.java
@@ -0,0 +1,115 @@
+
+
+/*
+ * Class: UnuranDiscreteInt
+ * Description: create a discrete generator using UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.DiscreteDistributionInt;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * This class permits one to create a discrete univariate
+ * generator using UNURAN via its string API.
+ *
+ */
+public class UnuranDiscreteInt extends RandomVariateGenInt {
+
+ private RandUnuran unuran = new RandUnuran();
+
+
+ /**
+ * Same as {@link #UnuranDiscreteInt UnuranDiscreteInt} <TT>(s, s, genStr)</TT>.
+ *
+ */
+ public UnuranDiscreteInt (RandomStream s, String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("mainStream must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ unuran.init (genStr);
+ if (!unuran.isDiscrete()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a discrete distribution");
+ }
+ }
+
+
+ /**
+ * Constructs a new discrete random number generator using
+ * the UNURAN generator specification string <TT>genStr</TT>,
+ * main stream <TT>s</TT>, and auxiliary stream <TT>aux</TT>.
+ *
+ */
+ public UnuranDiscreteInt (RandomStream s, RandomStream aux,
+ String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("mainStream must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("auxStream must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ unuran.init (genStr);
+ if (!unuran.isDiscrete()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a discrete distribution");
+ }
+ }
+
+
+ public int nextInt() {
+ if (unuran.nativeParams == 0)
+ throw new IllegalStateException();
+ return unuran.getRandDisc (unuran.mainStream.nextDouble(), unuran.nativeParams);
+ }
+
+ public void nextArrayOfInt (int[] v, int start, int n) {
+ if (v == null || start < 0 || n < 0 || (start+n) > v.length)
+ throw new IllegalArgumentException();
+ if (unuran.unifArray == null || unuran.unifArray.length < n)
+ unuran.unifArray = new double[n];
+ if (unuran.mainStream != unuran.auxStream &&
+ (unuran.unifAuxArray == null || unuran.unifAuxArray.length < n))
+ unuran.unifAuxArray = new double[n];
+ unuran.getRandDiscArray (unuran.nativeParams, unuran.unifArray,
+ unuran.unifAuxArray, v, start, n);
+ }
+
+ protected void finalize() {
+ unuran.close();
+ }
+
+ public DiscreteDistributionInt getDistribution() { return null; }
+
+ public RandomStream getStream() { return unuran.mainStream; }
+
+ /**
+ * Returns the auxiliary random number stream.
+ *
+ */
+ public RandomStream getAuxStream() {
+ return unuran.auxStream;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranDiscreteInt.tex b/source/umontreal/iro/lecuyer/randvar/UnuranDiscreteInt.tex
new file mode 100644
index 0000000..7997eb3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranDiscreteInt.tex
@@ -0,0 +1,124 @@
+\defmodule{UnuranDiscreteInt}
+
+This class permits one to create a discrete univariate
+generator using UNURAN via its string API.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: UnuranDiscreteInt
+ * Description: create a discrete generator using UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.DiscreteDistributionInt;
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+public class UnuranDiscreteInt extends RandomVariateGenInt\begin{hide} {
+
+ private RandUnuran unuran = new RandUnuran();
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public UnuranDiscreteInt (RandomStream s, String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("mainStream must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ unuran.init (genStr);
+ if (!unuran.isDiscrete()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a discrete distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{UnuranDiscreteInt}{}~\texttt{(s, s, genStr)}.
+\end{tabb}
+\begin{code}
+
+ public UnuranDiscreteInt (RandomStream s, RandomStream aux,
+ String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("mainStream must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("auxStream must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ unuran.init (genStr);
+ if (!unuran.isDiscrete()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not a discrete distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new discrete random number generator using
+ the UNURAN generator specification string \texttt{genStr},
+ main stream \texttt{s}, and auxiliary stream \texttt{aux}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+
+ public int nextInt() {
+ if (unuran.nativeParams == 0)
+ throw new IllegalStateException();
+ return unuran.getRandDisc (unuran.mainStream.nextDouble(), unuran.nativeParams);
+ }
+
+ public void nextArrayOfInt (int[] v, int start, int n) {
+ if (v == null || start < 0 || n < 0 || (start+n) > v.length)
+ throw new IllegalArgumentException();
+ if (unuran.unifArray == null || unuran.unifArray.length < n)
+ unuran.unifArray = new double[n];
+ if (unuran.mainStream != unuran.auxStream &&
+ (unuran.unifAuxArray == null || unuran.unifAuxArray.length < n))
+ unuran.unifAuxArray = new double[n];
+ unuran.getRandDiscArray (unuran.nativeParams, unuran.unifArray,
+ unuran.unifAuxArray, v, start, n);
+ }
+
+ protected void finalize() {
+ unuran.close();
+ }
+
+ public DiscreteDistributionInt getDistribution() { return null; }
+
+ public RandomStream getStream() { return unuran.mainStream; }\end{hide}
+
+ public RandomStream getAuxStream()\begin{hide} {
+ return unuran.auxStream;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Returns the auxiliary random number stream.
+\end{tabb}
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranEmpirical.java b/source/umontreal/iro/lecuyer/randvar/UnuranEmpirical.java
new file mode 100644
index 0000000..61ca37b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranEmpirical.java
@@ -0,0 +1,180 @@
+
+
+/*
+ * Class: UnuranEmpirical
+ * Description: create generators for empirical distributions using UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.probdist.Distribution;
+import umontreal.iro.lecuyer.probdist.PiecewiseLinearEmpiricalDist;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+/**
+ * This class permits one to create generators for empirical and
+ * quasi-empirical univariate distributions
+ * using UNURAN via its string interface.
+ * The empirical data can be read from a file, from an array, or simply
+ * encoded into the generator specification string.
+ * When reading from a file or an array, the generator specification
+ * string must <SPAN CLASS="textit">not</SPAN> contain a distribution specification string.
+ *
+ */
+public class UnuranEmpirical extends RandomVariateGen {
+ private RandUnuran unuran = new RandUnuran();
+
+
+ /**
+ * Constructs a new empirical univariate generator using the specification string
+ * <TT>genStr</TT> and stream <TT>s</TT>.
+ *
+ */
+ public UnuranEmpirical (RandomStream s, String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ unuran.init (genStr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }
+
+
+ /**
+ * Constructs a new empirical univariate generator using the specification string
+ * <TT>genStr</TT>, with main stream <TT>s</TT> and auxiliary stream <TT>aux</TT>.
+ *
+ */
+ public UnuranEmpirical (RandomStream s, RandomStream aux, String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("aux must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ unuran.init (genStr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }
+
+
+ /**
+ * Same as {@link #UnuranEmpirical UnuranEmpirical}<TT>(s, s, dist, genStr)</TT>.
+ *
+ */
+ public UnuranEmpirical (RandomStream s,
+ PiecewiseLinearEmpiricalDist dist, String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ String gstr = readDistr (dist) +
+ (genStr == null || genStr.equals ("") ? "" : "&" + genStr);
+ unuran.init (gstr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }
+
+
+ /**
+ * Same as {@link #UnuranEmpirical UnuranEmpirical}<TT>(s, aux, genStr)</TT>, but reading
+ * the observations from the empirical distribution <TT>dist</TT>.
+ * The <TT>genStr</TT> argument must not contain a distribution part
+ * because the distribution will be generated from the input stream reader.
+ *
+ */
+ public UnuranEmpirical (RandomStream s, RandomStream aux,
+ PiecewiseLinearEmpiricalDist dist, String genStr) {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("aux must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ String gstr = readDistr (dist) +
+ (genStr == null || genStr.equals ("") ? "" : "&" + genStr);
+ unuran.init (gstr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }
+
+ // Constructs and returns a distribution string for empirical distr.
+ private String readDistr (PiecewiseLinearEmpiricalDist dist) {
+ StringBuffer sb = new StringBuffer ("distr=cemp; data=(");
+ boolean first = true;
+
+ for (int i = 0; i < dist.getN(); i++) {
+ if (first)
+ first = false;
+ else
+ sb.append (",");
+ sb.append (dist.getObs (i));
+ }
+ sb.append (")");
+ return sb.toString();
+ }
+
+
+
+ public double nextDouble() {
+ if (unuran.nativeParams == 0)
+ throw new IllegalStateException();
+ return unuran.getRandCont (unuran.mainStream.nextDouble(), unuran.nativeParams);
+ }
+
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ if (v == null || start < 0 || n < 0 || (start+n) > v.length)
+ throw new IllegalArgumentException();
+ if (unuran.unifArray == null || unuran.unifArray.length < n)
+ unuran.unifArray = new double[n];
+ if (unuran.mainStream != unuran.auxStream &&
+ (unuran.unifAuxArray == null || unuran.unifAuxArray.length < n))
+ unuran.unifAuxArray = new double[n];
+ unuran.getRandContArray (unuran.nativeParams, unuran.unifArray,
+ unuran.unifAuxArray, v, start, n);
+ }
+
+ protected void finalize() {
+ unuran.close();
+ }
+
+ public Distribution getDistribution() { return null; }
+ public RandomStream getStream() { return unuran.mainStream; }
+
+ /**
+ * Returns the auxiliary random number stream.
+ *
+ */
+ public RandomStream getAuxStream() {
+ return unuran.auxStream;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranEmpirical.tex b/source/umontreal/iro/lecuyer/randvar/UnuranEmpirical.tex
new file mode 100644
index 0000000..ecdb795
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranEmpirical.tex
@@ -0,0 +1,188 @@
+\defmodule{UnuranEmpirical}
+
+This class permits one to create generators for empirical and
+quasi-empirical univariate distributions
+using UNURAN via its string interface.
+The empirical data can be read from a file, from an array, or simply
+encoded into the generator specification string.
+When reading from a file or an array, the generator specification
+string must \emph{not} contain a distribution specification string.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: UnuranEmpirical
+ * Description: create generators for empirical distributions using UNURAN
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.probdist.Distribution;
+import umontreal.iro.lecuyer.probdist.PiecewiseLinearEmpiricalDist;
+import umontreal.iro.lecuyer.rng.RandomStream;\end{hide}
+
+public class UnuranEmpirical extends RandomVariateGen\begin{hide} {
+ private RandUnuran unuran = new RandUnuran();
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public UnuranEmpirical (RandomStream s, String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ unuran.init (genStr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new empirical univariate generator using the specification string
+ \texttt{genStr} and stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public UnuranEmpirical (RandomStream s, RandomStream aux, String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("aux must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ unuran.init (genStr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new empirical univariate generator using the specification string
+ \texttt{genStr}, with main stream \texttt{s} and auxiliary stream \texttt{aux}.
+\end{tabb}
+\begin{code}
+
+ public UnuranEmpirical (RandomStream s,
+ PiecewiseLinearEmpiricalDist dist, String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+
+ unuran.mainStream = unuran.auxStream = s;
+ String gstr = readDistr (dist) +
+ (genStr == null || genStr.equals ("") ? "" : "&" + genStr);
+ unuran.init (gstr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{UnuranEmpirical}{}\texttt{(s, s, dist, genStr)}.
+\end{tabb}
+\begin{code}
+
+ public UnuranEmpirical (RandomStream s, RandomStream aux,
+ PiecewiseLinearEmpiricalDist dist, String genStr)\begin{hide} {
+ if (s == null)
+ throw new IllegalArgumentException ("s must not be null.");
+ if (aux == null)
+ throw new IllegalArgumentException ("aux must not be null.");
+
+ unuran.mainStream = s;
+ unuran.auxStream = aux;
+ String gstr = readDistr (dist) +
+ (genStr == null || genStr.equals ("") ? "" : "&" + genStr);
+ unuran.init (gstr);
+ if (!unuran.isEmpirical()) {
+ unuran.close();
+ throw new IllegalArgumentException ("not an empirical distribution");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{UnuranEmpirical}{}\texttt{(s, aux, genStr)}, but reading
+ the observations from the empirical distribution \texttt{dist}.
+ The \texttt{genStr} argument must not contain a distribution part
+ because the distribution will be generated from the input stream reader.
+\end{tabb}
+\begin{code}\begin{hide}
+ // Constructs and returns a distribution string for empirical distr.
+ private String readDistr (PiecewiseLinearEmpiricalDist dist) {
+ StringBuffer sb = new StringBuffer ("distr=cemp; data=(");
+ boolean first = true;
+
+ for (int i = 0; i < dist.getN(); i++) {
+ if (first)
+ first = false;
+ else
+ sb.append (",");
+ sb.append (dist.getObs (i));
+ }
+ sb.append (")");
+ return sb.toString();
+ }
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextDouble() {
+ if (unuran.nativeParams == 0)
+ throw new IllegalStateException();
+ return unuran.getRandCont (unuran.mainStream.nextDouble(), unuran.nativeParams);
+ }
+
+ public void nextArrayOfDouble (double[] v, int start, int n) {
+ if (v == null || start < 0 || n < 0 || (start+n) > v.length)
+ throw new IllegalArgumentException();
+ if (unuran.unifArray == null || unuran.unifArray.length < n)
+ unuran.unifArray = new double[n];
+ if (unuran.mainStream != unuran.auxStream &&
+ (unuran.unifAuxArray == null || unuran.unifAuxArray.length < n))
+ unuran.unifAuxArray = new double[n];
+ unuran.getRandContArray (unuran.nativeParams, unuran.unifArray,
+ unuran.unifAuxArray, v, start, n);
+ }
+
+ protected void finalize() {
+ unuran.close();
+ }
+
+ public Distribution getDistribution() { return null; }
+ public RandomStream getStream() { return unuran.mainStream; }\end{hide}
+
+ public RandomStream getAuxStream()\begin{hide} {
+ return unuran.auxStream;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Returns the auxiliary random number stream.
+\end{tabb}
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranException.java b/source/umontreal/iro/lecuyer/randvar/UnuranException.java
new file mode 100644
index 0000000..6a11557
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranException.java
@@ -0,0 +1,56 @@
+
+
+/*
+ * Class: UnuranException
+ * Description: unchecked exception for errors inside the UNURAN package
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+
+/**
+ * This type of unchecked exception is thrown when an error occurs
+ * <SPAN CLASS="textit">inside</SPAN> the UNURAN package.
+ * Usually, such an exception will come from the native side.
+ *
+ */
+public class UnuranException extends RuntimeException {
+
+ /**
+ * Constructs a new generic UNURAN exception.
+ *
+ */
+ public UnuranException() {
+ super();
+ }
+
+
+ /**
+ * Constructs a UNURAN exception with the error
+ * message <TT>message</TT>
+ *
+ * @param message error message describing the problem that occurred
+ *
+ */
+ public UnuranException (String message) {
+ super (message);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/UnuranException.tex b/source/umontreal/iro/lecuyer/randvar/UnuranException.tex
new file mode 100644
index 0000000..72b5d49
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/UnuranException.tex
@@ -0,0 +1,60 @@
+\defmodule{UnuranException}
+
+This type of unchecked exception is thrown when an error occurs
+\emph{inside} the UNURAN package.
+Usually, such an exception will come from the native side.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: UnuranException
+ * Description: unchecked exception for errors inside the UNURAN package
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;
+
+public class UnuranException extends RuntimeException\begin{hide} {\end{hide}
+\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+ public UnuranException()\begin{hide} {
+ super();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new generic UNURAN exception.
+\end{tabb}
+\begin{code}
+
+ public UnuranException (String message)\begin{hide} {
+ super (message);
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Constructs a UNURAN exception with the error
+ message \texttt{message}
+\end{tabb}
+\begin{htmlonly}
+ \param{message}{error message describing the problem that occurred}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/randvar/WeibullGen.java b/source/umontreal/iro/lecuyer/randvar/WeibullGen.java
new file mode 100644
index 0000000..4b12076
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/WeibullGen.java
@@ -0,0 +1,149 @@
+
+
+/*
+ * Class: WeibullGen
+ * Description: Weibull random number generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvar;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+
+
+/**
+ * This class implements random variate generators for the
+ * <EM>Weibull</EM> distribution. Its density is
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I>) = <I>αλ</I><SUP><I>α</I></SUP>(<I>x</I> - <I>δ</I>)<SUP><I>α</I>-1</SUP>exp[- (<I>λ</I>(<I>x</I> - <I>δ</I>))<SUP><I>α</I></SUP>] for <I>x</I> > <I>δ</I>,
+ * </DIV><P></P>
+ * and <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN> elsewhere, where
+ * <SPAN CLASS="MATH"><I>α</I> > 0</SPAN>, and
+ * <SPAN CLASS="MATH"><I>λ</I> > 0</SPAN>.
+ *
+ * <P>
+ * The (non-static) <TT>nextDouble</TT> method simply calls <TT>inverseF</TT> on the
+ * distribution.
+ *
+ */
+public class WeibullGen extends RandomVariateGen {
+ private double alpha = -1.0;
+ private double lambda = -1.0;
+ private double delta = -1.0;
+
+
+ /**
+ * Creates a Weibull random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, <SPAN CLASS="MATH"><I>λ</I></SPAN> = <TT>lambda</TT> and <SPAN CLASS="MATH"><I>δ</I></SPAN> =
+ * <TT>delta</TT>, using stream <TT>s</TT>.
+ *
+ */
+ public WeibullGen (RandomStream s, double alpha, double lambda,
+ double delta) {
+ super (s, new WeibullDist(alpha, lambda, delta));
+ setParams (alpha, lambda, delta);
+ }
+
+
+ /**
+ * Creates a Weibull random variate generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>,
+ * <SPAN CLASS="MATH"><I>λ</I> = 1</SPAN> and
+ * <SPAN CLASS="MATH"><I>δ</I> = 0</SPAN>, using stream
+ * <TT>s</TT>.
+ *
+ */
+ public WeibullGen (RandomStream s, double alpha) {
+ this (s, alpha, 1.0, 0.0);
+ }
+
+
+ /**
+ * Creates a new generator for the Weibull distribution <TT>dist</TT>
+ * and stream <TT>s</TT>.
+ *
+ */
+ public WeibullGen (RandomStream s, WeibullDist dist) {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda(), dist.getDelta());
+ }
+
+
+ /**
+ * Uses inversion to generate a new variate from the Weibull
+ * distribution with parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>,
+ * <SPAN CLASS="MATH"><I>λ</I> =</SPAN> <TT>lambda</TT>, and <SPAN CLASS="MATH"><I>δ</I> =</SPAN> <TT>delta</TT>, using
+ * stream <TT>s</TT>.
+ *
+ */
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda, double delta) {
+ return WeibullDist.inverseF (alpha, lambda, delta, s.nextDouble());
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+ public double getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Returns the parameter <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Sets the parameters <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>λ</I></SPAN> and <SPAN CLASS="MATH"><I>δ</I></SPAN> for this
+ * object.
+ *
+ */
+ public void setParams (double alpha, double lambda, double delta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.alpha = alpha;
+ this.lambda = lambda;
+ this.delta = delta;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/WeibullGen.tex b/source/umontreal/iro/lecuyer/randvar/WeibullGen.tex
new file mode 100644
index 0000000..76417fc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/WeibullGen.tex
@@ -0,0 +1,146 @@
+\defmodule {WeibullGen}
+
+This class implements random variate generators for the
+{\em Weibull\/} distribution. Its density is
+\eq
+ f(x) = \alpha\lambda^{\alpha} (x - \delta)^{\alpha - 1}
+ \exp[-(\lambda(x-\delta))^\alpha]
+ \qquad\mbox{ for } x>\delta, \eqlabel{eq:weibull}
+\endeq
+and $f(x)=0$ elsewhere, where $\alpha > 0$, and $\lambda > 0$.
+
+The (non-static) \texttt{nextDouble} method simply calls \texttt{inverseF} on the
+distribution.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: WeibullGen
+ * Description: Weibull random number generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvar;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+\end{hide}
+
+public class WeibullGen extends RandomVariateGen \begin{hide} {
+ private double alpha = -1.0;
+ private double lambda = -1.0;
+ private double delta = -1.0;
+\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public WeibullGen (RandomStream s, double alpha, double lambda,
+ double delta) \begin{hide} {
+ super (s, new WeibullDist(alpha, lambda, delta));
+ setParams (alpha, lambda, delta);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Weibull random variate generator with parameters
+ $\alpha =$ \texttt{alpha}, $\lambda $ = \texttt{lambda} and $\delta$ =
+ \texttt{delta}, using stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public WeibullGen (RandomStream s, double alpha) \begin{hide} {
+ this (s, alpha, 1.0, 0.0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a Weibull random variate generator with parameters
+ $\alpha =$ \texttt{alpha}, $\lambda = 1$ and $\delta = 0$, using stream
+ \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public WeibullGen (RandomStream s, WeibullDist dist) \begin{hide} {
+ super (s, dist);
+ if (dist != null)
+ setParams (dist.getAlpha(), dist.getLambda(), dist.getDelta());
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new generator for the Weibull distribution \texttt{dist}
+ and stream \texttt{s}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double nextDouble (RandomStream s, double alpha,
+ double lambda, double delta) \begin{hide} {
+ return WeibullDist.inverseF (alpha, lambda, delta, s.nextDouble());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses inversion to generate a new variate from the Weibull
+ distribution with parameters $\alpha = $~\texttt{alpha},
+ $\lambda = $~\texttt{lambda}, and $\delta = $~\texttt{delta}, using
+ stream \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha()\begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\alpha$.
+\end{tabb}
+\begin{code}
+
+ public double getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\lambda$.
+\end{tabb}
+\begin{code}
+
+ public double getDelta()\begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the parameter $\delta$.
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public void setParams (double alpha, double lambda, double delta) {
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (lambda <= 0.0)
+ throw new IllegalArgumentException ("lambda <= 0");
+ this.alpha = alpha;
+ this.lambda = lambda;
+ this.delta = delta;
+ }
+\end{code}
+\begin{tabb} Sets the parameters $\alpha$, $\lambda$ and $\delta$ for this
+ object.
+\end{tabb}
+\begin{code}
+}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/randvar/continuous.tex b/source/umontreal/iro/lecuyer/randvar/continuous.tex
new file mode 100644
index 0000000..47a202b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/continuous.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Generators for Continuous Distributions}
diff --git a/source/umontreal/iro/lecuyer/randvar/discrete.tex b/source/umontreal/iro/lecuyer/randvar/discrete.tex
new file mode 100644
index 0000000..e5df0da
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/discrete.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{Generators for Discrete Distributions over the Integers}
diff --git a/source/umontreal/iro/lecuyer/randvar/exam/normaltest.java b/source/umontreal/iro/lecuyer/randvar/exam/normaltest.java
new file mode 100644
index 0000000..417f396
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/exam/normaltest.java
@@ -0,0 +1,31 @@
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+public class normaltest
+{
+ public static void main (String[] args) {
+ // Create 3 parallel streams of random numbers
+ RandomStream stream1 = new MRG31k3p();
+ RandomStream stream2 = new MRG31k3p();
+ RandomStream stream3 = new MRG31k3p();
+
+ // Create 3 parallel streams of normal random variates
+ RandomVariateGen gen1 = new NormalGen (stream1);
+ RandomVariateGen gen2 = new NormalGen (stream2);
+ RandomVariateGen gen3 = new NormalGen (stream3);
+
+ final int n = 5;
+ genere (gen1, n);
+ genere (gen2, n);
+ genere (gen3, n);
+ }
+
+ private static void genere (RandomVariateGen gen, int n) {
+ double u;
+ for (int i = 0; i < n; i++) {
+ u = gen.nextDouble();
+ System.out.printf ("%12.6f%n", u);
+ }
+ System.out.println ("----------------------");
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvar/general.tex b/source/umontreal/iro/lecuyer/randvar/general.tex
new file mode 100644
index 0000000..df08c3a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/general.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{General Classes}
diff --git a/source/umontreal/iro/lecuyer/randvar/guiderandvar.bbl b/source/umontreal/iro/lecuyer/randvar/guiderandvar.bbl
new file mode 100644
index 0000000..ca610b8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/guiderandvar.bbl
@@ -0,0 +1,217 @@
+\begin{thebibliography}{10}
+
+\bibitem{rAHR72b}
+J.~H. Ahrens and U.~Dieter.
+\newblock Computer methods for sampling from gamma, beta, poisson and bionomial
+ distributions.
+\newblock {\em Computing}, 12:223--246, 1972.
+
+\bibitem{rAHR82b}
+J.~H. Ahrens and U.~Dieter.
+\newblock Computer generation of poisson deviates from modified normal
+ distributions.
+\newblock {\em ACM Trans. Math. Software}, 8:163--179, 1982.
+
+\bibitem{rAHR82a}
+J.~H. Ahrens and U.~Dieter.
+\newblock Generating gamma variates by a modified rejection technique.
+\newblock {\em Communications of the ACM}, 25:47--54, 1982.
+
+\bibitem{rBAI94a}
+R.~W. Bailey.
+\newblock Polar generation of random variates with the $t$-distribution.
+\newblock {\em Mathematics of Computation}, 62(206):779--781, 1994.
+
+\bibitem{tBER94a}
+A.~Berlinet and L.~Devroye.
+\newblock A comparison of kernel density estimates.
+\newblock {\em Publications de l'Institut de Statistique de l'Universit\'e de
+ {P}aris}, 38(3):3--59, 1994.
+\newblock available at \url{http://cgm.cs.mcgill.ca/~luc/np.html}.
+
+\bibitem{rBES78a}
+D.~J. Best.
+\newblock A simple algorithm for the computer generation of random samples from
+ a {S}tudent's $t$ or symmetric beta distribution.
+\newblock In L.~C.~A. Corsten and J.~Hermans, editors, {\em {COMPSTAT} 1978:
+ Proceedings in Computational statistics}, pages 341--347, Vienna, 1978.
+ Physica-Verlag.
+
+\bibitem{tBIR69a}
+Z.~W. Birnbaum and S.~C. Saunders.
+\newblock A new family of life distributions.
+\newblock {\em Journal of Applied Probability}, 6:319--327, 1969.
+
+\bibitem{rBOX58a}
+G.~E.~P. Box and M.~E. Muller.
+\newblock A note on the generation of random normal deviates.
+\newblock {\em Annals of Mathematical Statistics}, 29:610--611, 1958.
+
+\bibitem{tCAO94a}
+R.~Cao, A.~Cuevas, and W.~Gonz\'alez-Manteiga.
+\newblock A comparative study of several smoothing methods for density
+ estimation.
+\newblock {\em Computational Statistics and Data Analysis}, 17:153--176, 1994.
+
+\bibitem{rCHE77a}
+R.~C.~H. Cheng.
+\newblock The generation of gamma variables with non-integral shape parameter.
+\newblock {\em Applied Statistics}, 26:71--75, 1977.
+
+\bibitem{rCHE78a}
+R.~C.~H. Cheng.
+\newblock Generating beta variates with nonintegral shape parameters.
+\newblock {\em Communications of the ACM}, 21:317--322, 1978.
+
+\bibitem{rDER10a}
+G.~Derflinger, W.~H\"{o}rmann, and J.~Leydold.
+\newblock Random variate generation by numerical inversion when only the
+ density is known.
+\newblock {\em ACM Transactions on Modeling and Computer Simulation},
+ 20(4):Article 18, 2010.
+
+\bibitem{rDEV86a}
+L.~Devroye.
+\newblock {\em Non-Uniform Random Variate Generation}.
+\newblock Springer-Verlag, New York, NY, 1986.
+
+\bibitem{rDEV96a}
+L.~Devroye.
+\newblock Random variate generation in one line of code.
+\newblock In {\em Proceedings of the 1996 Winter Simulation Conference}, pages
+ 265--271. IEEE Press, 1996.
+
+\bibitem{rDEV06a}
+L.~Devroye.
+\newblock Nonuniform random variate generation.
+\newblock In S.~G. Henderson and B.~L. Nelson, editors, {\em Simulation},
+ Handbooks in Operations Research and Management Science, pages 83--121.
+ Elsevier, Amsterdam, The Netherlands, 2006.
+\newblock Chapter 4.
+
+\bibitem{tDEV85a}
+L.~Devroye and L.~Gy\"orfi.
+\newblock {\em Nonparametric Density Estimation: The $L_1$ View}.
+\newblock John Wiley, New York, NY, 1985.
+
+\bibitem{rGEN98a}
+J.~E. Gentle.
+\newblock {\em Random Number Generation and {M}onte {C}arlo Methods}.
+\newblock Springer, New York, NY, 1998.
+
+\bibitem{rHOR90a}
+W.~H{\"o}ermann and G.~Derflinger.
+\newblock The {ACR} method for generating normal random variables.
+\newblock {\em OR Spektrum}, 12:181--185, 1990.
+
+\bibitem{rHOR00a}
+W.~H{\"o}rmann and J.~Leydold.
+\newblock Automatic random variate generation for simulation input.
+\newblock In J.~A. Joines, R.~R. Barton, K.~Kang, and P.~A. Fishwick, editors,
+ {\em Proceedings of the 2000 Winter Simulation Conference}, pages 675--682,
+ Piscataway, NJ, Dec 2000. {IEEE} Press.
+
+\bibitem{rHOR04a}
+W.~H\"ormann, J.~Leydold, and G.~Derflinger.
+\newblock {\em Automatic Nonuniform Random Variate Generation}.
+\newblock Springer-Verlag, Berlin, 2004.
+
+\bibitem{tJOH49a}
+N.~L. Johnson.
+\newblock Systems of frequency curves generated by methods of translation.
+\newblock {\em Biometrika}, 36:149--176, 1949.
+
+\bibitem{tJOH95a}
+N.~L. Johnson, S.~Kotz, and N.~Balakrishnan.
+\newblock {\em Continuous Univariate Distributions}, volume~1.
+\newblock Wiley, 2nd edition, 1994.
+
+\bibitem{tJOH95b}
+N.~L. Johnson, S.~Kotz, and N.~Balakrishnan.
+\newblock {\em Continuous Univariate Distributions}, volume~2.
+\newblock Wiley, 2nd edition, 1995.
+
+\bibitem{sKAC85a}
+V.~Kachitvichyanukul and B.~Schmeiser.
+\newblock Computer generation of hypergeometric random variates.
+\newblock {\em J. Statist. Comput. Simul.}, 22:127--145, 1985.
+
+\bibitem{fKAL07a}
+A.~Kalemanova, B.~Schmid, and R.~Werner.
+\newblock The normal inverse gaussian distribution for synthetic {CDO} pricing.
+\newblock {\em Journal of derivatives}, 14(3):80--93, 2007.
+
+\bibitem{rKEM81a}
+A.~W. Kemp.
+\newblock Efficient generation of logarithmically distributed pseudo-random
+ variables.
+\newblock {\em Applied Statistics}, 30:249--253, 1981.
+
+\bibitem{rKIN76a}
+A.~J. Kinderman and J.~G. Ramage.
+\newblock Computer generation of normal random variables.
+\newblock {\em Journal of the American Statistical Association}, 71:893--898,
+ 1976.
+
+\bibitem{tKRI06a}
+K.~Krishnamoorthy.
+\newblock {\em Handbook of statistical distributions with applications}.
+\newblock Chapman \& Hall/CRC Press, Boca Raton, FL, 2006.
+
+\bibitem{sLAW00a}
+A.~M. Law and W.~D. Kelton.
+\newblock {\em Simulation Modeling and Analysis}.
+\newblock McGraw-Hill, New York, NY, third edition, 2000.
+
+\bibitem{iLEY02a}
+J.~Leydold and W.~H\"ormann.
+\newblock {\em {UNU.RAN}---A Library for Universal Non-Uniform Random Number
+ Generators}, 2002.
+\newblock Available at \url{http://statistik.wu-wien.ac.at/unuran}.
+
+\bibitem{rMAR62a}
+G.~Marsaglia.
+\newblock Improving the polar method for generating a pair of random variables.
+\newblock Technical report, Boeing Scientific Research Laboratory, Seattle,
+ Washington, 1962.
+
+\bibitem{rMIC76a}
+J.~R. Michael, W.~R. Schuchany, and R.~W. Haas.
+\newblock Generating random variates using transformations with multiple roots.
+\newblock {\em The American Statistician}, 30:88--90, 1976.
+
+\bibitem{rSAK83a}
+H.~Sakasegawa.
+\newblock Stratified rejection and squeeze method for generating beta random
+ numbers.
+\newblock {\em Annals of the Institute of Mathematical Statistics},
+ 35B:291--302, 1983.
+
+\bibitem{tSIL86a}
+B.~Silverman.
+\newblock {\em Density Estimation for Statistics and Data Analysis}.
+\newblock Chapman and Hall, London, 1986.
+
+\bibitem{rSTA93a}
+E.~Stadlober and H.~Zechner.
+\newblock Generating beta variates via patchwork rejection.
+\newblock {\em Computing}, 50:1--18, 1993.
+
+\bibitem{rTIR04a}
+G.~Tirler, P.~Dalgaard, W.~H{\"o}rmann, and J.~Leydold.
+\newblock An error in the {K}inderman-{R}amage method and how to fix it.
+\newblock {\em Computational Statistics and Data Analysis}, 47(3):433--440,
+ 2004.
+
+\bibitem{rULR84a}
+G.~Ulrich.
+\newblock Computer generation of distributions on the m-sphere.
+\newblock {\em Applied Statistics}, 33:158--163, 1984.
+
+\bibitem{fWEB03a}
+N.~Webber and C.~Ribeiro.
+\newblock A {M}onte {C}arlo method for the normal inverse gaussian option
+ valuation model using an inverse gaussian bridge.
+\newblock Technical Report~5, Society for Computational Economics, 2003.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/randvar/guiderandvar.tex b/source/umontreal/iro/lecuyer/randvar/guiderandvar.tex
new file mode 100644
index 0000000..88852b7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/guiderandvar.tex
@@ -0,0 +1,174 @@
+\documentclass [12pt]{article}
+\usepackage{amssymb}
+%\usepackage{alltt}
+%\usepackage{html}
+%\usepackage{tcode}
+%begin{latexonly}
+\usepackage{url}
+%end{latexonly}
+\usepackage{ssj}
+\usepackage{color}
+\usepackage{crayola}
+\usepackage[procnames]{listings}
+\usepackage{lstpatch}
+
+\lstloadlanguages{Java}
+\lstset{language=Java,
+float=tbhp,
+captionpos=t,
+frame=trbl,
+abovecaptionskip=1.5em,
+belowskip=2em,
+basicstyle=\small\ttfamily,
+stringstyle=\color{OliveGreen},
+commentstyle=\color{red},
+identifierstyle=\color{Bittersweet},
+basewidth={0.5em},
+showstringspaces=false,
+framerule=0.8pt,
+procnamestyle=\bfseries\color{blue},
+emphstyle=\bfseries\color{Cerulean},
+procnamekeys={class,extends,interface,implements}
+}
+
+
+\mytwoheads
+
+% \includeonly{InverseFromDensityGen}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+
+\title{randvar}{Generating Non-Uniform Random Numbers}
+
+This package implements random number generators from various standard
+distributions.
+It also provides an interface to the C package UNURAN.
+
+\vfill
+\end{titlepage}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+
+\include{overview}
+
+\include{general}
+\include{RandomVariateGen}
+\include{RandomVariateGenInt}
+\include{RandomVariateGenWithCache}
+%\include{RandomVariateGenFactory}
+\include{InverseFromDensityGen}
+
+%%
+\include{discrete}
+\include{BernoulliGen}
+\include{BinomialGen}
+\include{BinomialConvolutionGen}
+\include{GeometricGen}
+\include{HypergeometricGen}
+\include{LogarithmicGen}
+\include{NegativeBinomialGen}
+\include{PascalGen}
+\include{PascalConvolutionGen}
+\include{PoissonGen}
+\include{PoissonTIACGen}
+\include{UniformIntGen}
+
+%%
+\include{continuous}
+\include{BetaGen}
+\include{BetaRejectionLoglogisticGen}
+\include{BetaStratifiedRejectionGen}
+\include{BetaSymmetricalGen}
+\include{BetaSymmetricalPolarGen}
+\include{BetaSymmetricalBestGen}
+\include{CauchyGen}
+\include{ChiGen}
+\include{ChiRatioOfUniformsGen}
+\include{ChiSquareGen}
+\include{ChiSquareNoncentralGen}
+\include{ChiSquareNoncentralGamGen}
+\include{ChiSquareNoncentralPoisGen}
+\include{ConstantGen}
+\include{ErlangGen}
+\include{ErlangConvolutionGen}
+\include{ExponentialGen}
+\include{ExponentialInverseFromDensityGen}
+\include{ExtremeValueGen}
+\include{FatigueLifeGen}
+\include{FisherFGen}
+\include{FNoncentralGen}
+\include{FoldedNormalGen}
+\include{FrechetGen}
+\include{GammaGen}
+\include{GammaAcceptanceRejectionGen}
+\include{GammaRejectionLoglogisticGen}
+\include{GumbelGen}
+\include{HalfNormalGen}
+\include{HyperbolicSecantGen}
+\include{HypoExponentialGen}
+\include{InverseGammaGen}
+\include{InverseGaussianGen}
+\include{InverseGaussianMSHGen}
+\include{JohnsonSystemG}
+\include{JohnsonSLGen}
+\include{JohnsonSBGen}
+\include{JohnsonSUGen}
+\include{KernelDensityGen}
+\include{KernelDensityVarCorrectGen}
+\include{LaplaceGen}
+\include{LogisticGen}
+\include{LoglogisticGen}
+\include{LognormalGen}
+\include{LognormalSpecialGen}
+\include{NakagamiGen}
+\include{NormalGen}
+\include{NormalACRGen}
+\include{NormalBoxMullerGen}
+\include{NormalPolarGen}
+\include{NormalInverseFromDensityGen}
+\include{NormalKindermannRamageGen}
+\include{NormalInverseGaussianGen}
+\include{NormalInverseGaussianIGGen}
+\include{ParetoGen}
+\include{Pearson5Gen}
+\include{Pearson6Gen}
+\include{PowerGen}
+\include{RayleighGen}
+\include{StudentGen}
+\include{StudentPolarGen}
+\include{StudentNoncentralGen}
+\include{TriangularGen}
+\include{UniformGen}
+\include{WeibullGen}
+
+\include{unuran}
+\include{UnuranContinuous}
+\include{UnuranDiscreteInt}
+\include{UnuranEmpirical}
+\include{UnuranException}
+
+%\include{filter}
+%\include{RandomVariateTransform}
+%\include{PositiveTransform}
+% \include{TruncatedTrans}
+
+\setcounter{section}{1}
+\renewcommand{\thesection}{\Alph{section}.}
+%\include{annexeb}
+%\input annexed.tex
+%\input annexee.tex
+%\include{refer}
+
+\bibliography{simul,random,ift,stat,prob,math,fin}
+\bibliographystyle{plain}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/randvar/overview.tex b/source/umontreal/iro/lecuyer/randvar/overview.tex
new file mode 100644
index 0000000..6bdfa5a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/overview.tex
@@ -0,0 +1,177 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+
+This package provides a collection of classes for non-uniform
+random variate generation, primarily from standard distributions.
+
+Each non-uniform random variate generator requires
+at least one \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} object
+(from package \externalclass{umontreal.iro.lecuyer}{rng}), used to
+generate the underlying uniform random numbers.
+%
+
+The generic classes
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} and
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}
+permit one to construct a random variate generator from a random
+stream and an arbitrary distribution
+(see interface \externalclass{umontreal.iro.lecuyer.probdist}{Distribution}).
+To generate random variates by inversion from an arbitrary
+distribution over the real numbers using a given random stream,
+it suffices to construct a
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} object
+with the desired (previously created)
+\externalclass{umontreal.iro.lecuyer.probdist}{Distribution}
+and \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} objects,
+and then call its
+\externalmethod{umontreal.iro.lecuyer.randvar}{RandomVariateGen}{nextDouble}{} method
+as many times as needed.
+For discrete distributions over the integers, one can construct a
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}
+object containing the
+desired \externalclass{umontreal.iro.lecuyer.probdist}{DiscreteDistributionInt}
+and \externalclass{umontreal.iro.lecuyer.rng}{RandomStream},
+and call its
+\externalmethod{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}{nextInt}{}
+method.
+By default, these generators simply call the
+\externalmethod{umontreal.iro.lecuyer.probdist}{ContinuousDistribution}{inverseF}{}
+method from the specified distribution object.
+These two classes suffice as long as we are willing to use inversion.
+Here is a simple example in which we create three parallel streams of
+normal random variates using inversion.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\lstinputlisting[label=lst:normaltest,%
+caption={Using three parallel streams of random normal variates},%
+lineskip=-2pt,%
+emph={genere,main}
+]{exam/normaltest.java}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+To generate random variates by other methods than inversion,
+one can use specialized classes that extend
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen}
+or \externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}.
+Such classes are provided for a variety of standard discrete and
+continuous distributions.
+For example, five different subclasses implement normal random variate generators,
+using five different methods.
+One of them, the class
+\externalclass{umontreal.iro.lecuyer.randvar}{NormalGen},
+extends \externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen}
+directly and provides normal random variate generators based on inversion,
+so it does the same thing as using
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen}
+with the normal distribution.
+% and each of its instances contain a
+% \externalclass{umontreal.iro.lecuyer.probdist}{NormalDist} and a
+% \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} objects.
+The others are subclasses of
+\externalclass{umontreal.iro.lecuyer.randvar}{NormalGen};
+they implement various non-inversion normal variate generation methods.
+To generate random variates with a specific method, it suffices to
+construct an object of the appropriate subclass and then call its
+\texttt{nextDouble} method.
+
+
+
+%
+% Selection methods, with names of the form \texttt{use...Method},
+% permit one to choose which normal variate generation technique is used
+% when invoking
+% \externalmethod{umontreal.iro.lecuyer.randvar}{NormalGen}{nextDouble}{},
+% for each object.
+\hpierre{Apr\`es avoir examin\'e le code, je me rends compte que l'utilisation
+ de \texttt{use...Method} n'est pas la bonne approche, car cela am\`ene
+ plein de \texttt{case} dans l'implantation, ce qui rend le code plus laid, plus
+ compliqu\'e et plus inefficace. Il serait probablement pr\'ef\'erable
+ d'utiliser des sous-classes \`a la place, du moins pour les m\'ethodes
+ non statiques. Une sous-classe pour chaque type de m\'ethode de g\'en\'eration.}
+
+In most cases, the specialized classes maintain local copies of the
+distribution parameters and use them for variate generation.
+If the parameters of the contained distribution objects are later modified,
+this may lead to inconsistencies: the variate generator object will
+keep using the old values.
+In fact, the constructors of the specialized classes often precompute
+constants and tables based on these parameter values, which would have
+to be recomputed if the parameters are changed.
+On the other hand, the generic classes
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} and
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}
+call directly the \texttt{inverseF} method of the contained
+distribution object, so they will always use the new parameter values
+whenever the parameters in the distribution object are changed.
+%
+\hpierre{We must make the \texttt{nextInt()} and \texttt{nextDouble()}
+ methods as quick as possible. For example, it is better to avoid
+ calling methods to access the parameters of the distribution object.
+ We can store local copies of the parameters instead.}
+\hpierre{We must decide exactly what we do with this and
+ explain it clearly in the guide. If we leave it like this, it must be made
+ clear in the documentation of each subclass and method, which parameter
+ values are used.}%
+ %
+\hpierre{It seems to me that in the future, only the constructors of
+ \externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} and
+ \externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}
+ should require a distribution object. In the subclasses, we should
+ directly pass the required parameters and there would not necessarily
+ be a distribution object created. We should examine the implications
+ of such a change.}
+
+
+With some variate generation methods (e.g., the \emph{rejection}
+method), the number of uniforms required to get a single non-uniform
+variate varies from one call to the next.
+In that case, an auxiliary stream is often used to preserve
+the synchronization between random variates when implementing
+variance-reduction methods \cite{sLAW00a}.
+The main random number stream is called a fixed number of times
+per non-uniform variate generation. If more uniform random numbers
+are needed, they are obtained from the auxiliary stream.
+For these types of generators, two
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} objects
+should be passed to the constructor.
+Otherwise, by default, the same stream will be used for all uniforms.
+
+
+\emph{Static} methods in the specialized classes allow the generation
+of random variates from specific distributions without constructing a
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen} object.
+
+This package also provides an interface to the
+\emph{UNURAN} (Universal Non-Uniform RANdom number generators) package,
+a rich library of C functions designed and
+implemented by the ARVAG (Automatic Random VAriate Generation)
+project group in Vienna \cite{iLEY02a}.
+% (for more information see http://statistik.wu-wien.ac.at/unuran/).
+This interface can be used to access distributions or
+generation methods not available directly in SSJ.
+To get a UNURAN generator, it suffices to instantiate one
+of the UNURAN interface classes:
+\externalclass{umontreal.iro.lecuyer.randvar}{UnuranDiscreteInt}
+for discrete random variates,
+\externalclass{umontreal.iro.lecuyer.randvar}{UnuranContinuous}
+for continuous ones (in one dimension), and
+\externalclass{umontreal.iro.lecuyer.randvar}{UnuranEmpirical}
+for quasi-empirical distributions based on experimental data.
+The type of distribution and its parameters are specified to
+UNURAN via its String API (see the UNURAN documentation).
+Only univariate distributions are supported because
+the UNURAN String API does not support the multivariate ones yet.
+
+In the UNURAN interface classes,
+\externalmethod{umontreal.iro.lecuyer.randvar}{RandomVariateGen}{nextDouble}{} and
+\externalmethod{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}{nextInt}{}
+can be invoked as usual to generate variates,
+but these methods are slowed down significantly by the overhead
+in the interactions between code on the native side and on the Java side.
+When several random variates are needed, it is much more efficient to
+generate them in a single call, via the methods
+\externalmethod{umontreal.iro.lecuyer.randvar}{RandomVariateGen}{nextArrayOfDouble}{} and
+\externalmethod{umontreal.iro.lecuyer.randvar}{RandomVariateGenInt}{nextArrayOfInt}{}.
diff --git a/source/umontreal/iro/lecuyer/randvar/umontreal_iro_lecuyer_randvar_RandUnuran.h b/source/umontreal/iro/lecuyer/randvar/umontreal_iro_lecuyer_randvar_RandUnuran.h
new file mode 100644
index 0000000..adfd6f5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/umontreal_iro_lecuyer_randvar_RandUnuran.h
@@ -0,0 +1,109 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class umontreal_iro_lecuyer_randvar_RandUnuran */
+
+#ifndef _Included_umontreal_iro_lecuyer_randvar_RandUnuran
+#define _Included_umontreal_iro_lecuyer_randvar_RandUnuran
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: init
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_init
+ (JNIEnv *, jobject, jstring);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: close
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_close
+ (JNIEnv *, jobject);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: getRandDisc
+ * Signature: (DI)I
+ */
+JNIEXPORT jint JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandDisc
+ (JNIEnv *, jobject, jdouble, jint);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: getRandCont
+ * Signature: (DI)D
+ */
+JNIEXPORT jdouble JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandCont
+ (JNIEnv *, jobject, jdouble, jint);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: getRandVec
+ * Signature: (DI[D)V
+ */
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandVec
+ (JNIEnv *, jobject, jdouble, jint, jdoubleArray);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: getRandDiscArray
+ * Signature: (I[D[D[III)V
+ */
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandDiscArray
+ (JNIEnv *, jobject, jint, jdoubleArray, jdoubleArray, jintArray, jint, jint);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: getRandContArray
+ * Signature: (I[D[D[DII)V
+ */
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_getRandContArray
+ (JNIEnv *, jobject, jint, jdoubleArray, jdoubleArray, jdoubleArray, jint, jint);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: isDiscrete
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isDiscrete
+ (JNIEnv *, jobject);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: isContinuous
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isContinuous
+ (JNIEnv *, jobject);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: isContinuousMultivariate
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isContinuousMultivariate
+ (JNIEnv *, jobject);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: isEmpirical
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isEmpirical
+ (JNIEnv *, jobject);
+
+/*
+ * Class: umontreal_iro_lecuyer_randvar_RandUnuran
+ * Method: isEmpiricalMultivariate
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_umontreal_iro_lecuyer_randvar_RandUnuran_isEmpiricalMultivariate
+ (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/source/umontreal/iro/lecuyer/randvar/unuran.tex b/source/umontreal/iro/lecuyer/randvar/unuran.tex
new file mode 100644
index 0000000..9772d96
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvar/unuran.tex
@@ -0,0 +1 @@
+\addcontentsline{toc}{section}{UNURAN Interface}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/DirichletGen.java b/source/umontreal/iro/lecuyer/randvarmulti/DirichletGen.java
new file mode 100644
index 0000000..e9989a6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/DirichletGen.java
@@ -0,0 +1,153 @@
+
+
+/*
+ * Class: DirichletGen
+ * Description: multivariate generator for a Dirichlet} distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvarmulti;
+
+import umontreal.iro.lecuyer.probdist.GammaDist;
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+import umontreal.iro.lecuyer.randvar.GammaAcceptanceRejectionGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+
+/**
+ * Extends {@link RandomMultivariateGen} for a
+ * <SPAN CLASS="textit">Dirichlet</SPAN> distribution. This distribution uses the
+ * parameters
+ *
+ *
+ * <SPAN CLASS="MATH"><I>α</I><SUB>1</SUB>,..., <I>α</I><SUB>k</SUB></SPAN>, and has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>k</SUB>) = <I>Γ</I>(<I>α</I><SUB>0</SUB>)∏<SUB>i=1</SUB><SUP>k</SUP><I>x</I><SUB>i</SUB><SUP><I>α</I><SUB>i</SUB>-1</SUP> / ∏<SUB>i=1</SUB><SUP>k</SUP><I>Γ</I>(<I>α</I><SUB>i</SUB>))
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>α</I><SUB>0</SUB> = ∑<SUB>i=1</SUB><SUP>k</SUP><I>α</I><SUB>i</SUB></SPAN>.
+ *
+ * <P>
+ * Here, the successive coordinates of the Dirichlet vector are generated
+ *
+ * via the class
+ * {@link umontreal.iro.lecuyer.randvar.GammaAcceptanceRejectionGen GammaAcceptanceRejectionGen}
+ * in package <TT>randvar</TT>, using the same stream for all the uniforms.
+ *
+ */
+public class DirichletGen extends RandomMultivariateGen {
+ private GammaAcceptanceRejectionGen[] ggens;
+
+
+ /**
+ * Constructs a new Dirichlet
+ * generator with parameters
+ * <SPAN CLASS="MATH"><I>α</I><SUB>i+1</SUB> =</SPAN> <TT>alphas[i]</TT>,
+ * for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>k</I> - 1</SPAN>, and the stream <TT>stream</TT>.
+ *
+ * @param stream the random number stream used to generate uniforms.
+ *
+ * @param alphas the <SPAN CLASS="MATH"><I>α</I><SUB>i</SUB></SPAN> parameters of the generated distribution.
+ *
+ * @exception IllegalArgumentException if one <SPAN CLASS="MATH"><I>α</I><SUB>k</SUB></SPAN> is negative or 0.
+ *
+ * @exception NullPointerException if any argument is <TT>null</TT>.
+ *
+ */
+ public DirichletGen (RandomStream stream, double[] alphas) {
+ if (stream == null)
+ throw new NullPointerException ("stream is null");
+ this.stream = stream;
+ dimension = alphas.length;
+ ggens = new GammaAcceptanceRejectionGen[alphas.length];
+ for (int k = 0; k < alphas.length; k++)
+ ggens[k] = new GammaAcceptanceRejectionGen
+ (stream, new GammaDist (alphas[k], 1.0/2.0));
+ }
+
+
+ /**
+ * Returns the
+ * <SPAN CLASS="MATH"><I>α</I><SUB>i+1</SUB></SPAN> parameter for this
+ * Dirichlet generator.
+ *
+ * @param i the index of the parameter.
+ *
+ * @return the value of the parameter.
+ * @exception ArrayIndexOutOfBoundsException if <TT>i</TT> is
+ * negative or greater than or equal to {@link #getDimension(()) getDimension}.
+ *
+ *
+ */
+ public double getAlpha (int i) {
+ return ((GammaDist)ggens[i].getDistribution()).getAlpha();
+ }
+
+
+ /**
+ * Generates a new point from the Dirichlet distribution with
+ * parameters <TT>alphas</TT>, using the stream <TT>stream</TT>.
+ * The generated values are placed into <TT>p</TT>.
+ *
+ * @param stream the random number stream used to generate the uniforms.
+ *
+ * @param alphas the <SPAN CLASS="MATH"><I>α</I><SUB>i</SUB></SPAN> parameters of the distribution, for
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 1,…, <I>k</I></SPAN>.
+ *
+ * @param p the array to be filled with the generated point.
+ *
+ *
+ */
+ public static void nextPoint (RandomStream stream, double[] alphas,
+ double[] p) {
+ double total = 0;
+ for (int i = 0; i < alphas.length; i++) {
+ p[i] = GammaAcceptanceRejectionGen.nextDouble
+ (stream, stream, alphas[i], 1.0/2.0);
+ total += p[i];
+ }
+ for (int i = 0; i < alphas.length; i++)
+ p[i] /= total;
+ }
+
+
+ /**
+ * Generates a point from the Dirichlet distribution.
+ *
+ * @param p the array to be filled with the generated point.
+ *
+ */
+ public void nextPoint (double[] p) {
+ int n = ggens.length;
+ double total = 0;
+ for (int i = 0; i < n; i++) {
+ p[i] = ggens[i].nextDouble();
+ total += p[i];
+ }
+ for (int i = 0; i < n; i++)
+ p[i] /= total;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/DirichletGen.tex b/source/umontreal/iro/lecuyer/randvarmulti/DirichletGen.tex
new file mode 100644
index 0000000..53271db
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/DirichletGen.tex
@@ -0,0 +1,157 @@
+\defmodule{DirichletGen}
+
+Extends \class{RandomMultivariateGen} for a
+\emph{Dirichlet} \cite{tJOH72a} distribution. This distribution uses the
+parameters
+\hrichard{Doit-on utiliser $d$ pour la dimension comme dans
+ \texttt{MultiNormalGen et RandomMultivariateGen}?}
+$\alpha_1,\dots,\alpha_k$, and has density
+\begin{latexonly}%
+\[
+ f(x_1,\ldots,x_k) = \frac{\Gamma(\alpha_0)\prod_{i=1}^k x_i^{\alpha_i - 1}}
+ {\prod_{i=1}^k \Gamma(\alpha_i)}
+\]
+\end{latexonly}%
+\begin{htmlonly}
+\[
+ f(x_1,\ldots,x_k) = \Gamma(\alpha_0)\prod_{i=1}^k x_i^{\alpha_i - 1}\ / \ \
+ ({\prod_{i=1}^k \Gamma(\alpha_i)})
+\]
+\end{htmlonly}%
+where $\alpha_0=\sum_{i=1}^k\alpha_i$.
+
+Here, the successive coordinates of the Dirichlet vector are generated
+\pierre{How? }
+via the class
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaAcceptance\-Rejec\-tion\-Gen}{}
+in package \texttt{randvar}, using the same stream for all the uniforms.
+% with its particular value of $\alpha$.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DirichletGen
+ * Description: multivariate generator for a Dirichlet} distribution
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvarmulti;\begin{hide}
+
+import umontreal.iro.lecuyer.probdist.GammaDist;
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+import umontreal.iro.lecuyer.randvar.GammaAcceptanceRejectionGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+
+public class DirichletGen extends RandomMultivariateGen\begin{hide} {
+ private GammaAcceptanceRejectionGen[] ggens;
+\end{hide}\end{code}
+
+\subsubsection* {Constructor}
+\begin{code}
+
+ public DirichletGen (RandomStream stream, double[] alphas)\begin{hide} {
+ if (stream == null)
+ throw new NullPointerException ("stream is null");
+ this.stream = stream;
+ dimension = alphas.length;
+ ggens = new GammaAcceptanceRejectionGen[alphas.length];
+ for (int k = 0; k < alphas.length; k++)
+ ggens[k] = new GammaAcceptanceRejectionGen
+ (stream, new GammaDist (alphas[k], 1.0/2.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new Dirichlet
+ generator with parameters $\alpha_{i+1}=$~\texttt{alphas[i]},
+ for $i=0,\ldots,k-1$, and the stream \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the random number stream used to generate uniforms.}
+ \param{alphas}{the $\alpha_i$ parameters of the generated distribution.}
+ \exception{IllegalArgumentException}{if one $\alpha_k$ is negative or 0.}
+ \exception{NullPointerException}{if any argument is \texttt{null}.}
+\end{htmlonly}
+
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+
+ public double getAlpha (int i)\begin{hide} {
+ return ((GammaDist)ggens[i].getDistribution()).getAlpha();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $\alpha_{i+1}$ parameter for this
+ Dirichlet generator.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the index of the parameter.}
+ \return{the value of the parameter.}
+ \exception{ArrayIndexOutOfBoundsException}{if \texttt{i} is
+ negative or greater than or equal to \method{getDimension}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public static void nextPoint (RandomStream stream, double[] alphas,
+ double[] p)\begin{hide} {
+ double total = 0;
+ for (int i = 0; i < alphas.length; i++) {
+ p[i] = GammaAcceptanceRejectionGen.nextDouble
+ (stream, stream, alphas[i], 1.0/2.0);
+ total += p[i];
+ }
+ for (int i = 0; i < alphas.length; i++)
+ p[i] /= total;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a new point from the Dirichlet distribution with
+ parameters \texttt{alphas}, using the stream \texttt{stream}.
+ The generated values are placed into \texttt{p}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the random number stream used to generate the uniforms.}
+ \param{alphas}{the $\alpha_i$ parameters of the distribution, for
+ $i=1,\ldots,k$.}
+ \param{p}{the array to be filled with the generated point.}
+\end{htmlonly}
+\begin{code}
+
+ public void nextPoint (double[] p)\begin{hide} {
+ int n = ggens.length;
+ double total = 0;
+ for (int i = 0; i < n; i++) {
+ p[i] = ggens[i].nextDouble();
+ total += p[i];
+ }
+ for (int i = 0; i < n; i++)
+ p[i] /= total;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Generates a point from the Dirichlet distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the array to be filled with the generated point.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/IIDMultivariateGen.java b/source/umontreal/iro/lecuyer/randvarmulti/IIDMultivariateGen.java
new file mode 100644
index 0000000..be5a93b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/IIDMultivariateGen.java
@@ -0,0 +1,109 @@
+
+
+/*
+ * Class: IIDMultivariateGen
+ * Description: vector of independent identically distributed random variables
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvarmulti;
+
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+
+
+/**
+ * Extends {@link RandomMultivariateGen} for a vector of independent identically distributed
+ * (i.i.d.) random variables.
+ *
+ */
+public class IIDMultivariateGen extends RandomMultivariateGen {
+
+
+ /**
+ * Constructs a generator for a <TT>d</TT>-dimensional vector of i.i.d. variates
+ * with a common one-dimensional generator <TT>gen1</TT>.
+ *
+ * @param gen1 the one-dimensional generator
+ *
+ * @param d dimension of the vector (number of i.i.d. variates).
+ *
+ */
+ public IIDMultivariateGen (RandomVariateGen gen1, int d) {
+ setGen1 (gen1);
+ this.stream = gen1.getStream();
+ dimension = d;
+ }
+
+
+ /**
+ * Changes the dimension of the vector to <TT>d</TT>.
+ *
+ */
+ public void setDimension (int d) {
+ dimension = d;
+ }
+
+
+ /**
+ * Generates a vector of i.i.d. variates.
+ *
+ */
+ public void nextPoint (double[] p) {
+ if (p.length != dimension)
+ throw new IllegalArgumentException(String.format(
+ "p's dimension (%d) does not mach dimension (%d)", p.length, dimension));
+
+ for (int i = 0; i < dimension; i++)
+ p[i] = gen1.nextDouble();
+ }
+
+
+ /**
+ * Sets the common one-dimensional generator to <TT>gen1</TT>.
+ *
+ */
+ public void setGen1 (RandomVariateGen gen1) {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+ this.gen1 = gen1;
+ }
+
+
+ /**
+ * Returns the common one-dimensional generator used in this class.
+ *
+ */
+ public RandomVariateGen getGen1() {
+ return gen1;
+ }
+
+
+ /**
+ * Returns a string representation of the generator.
+ *
+ */
+ public String toString() {
+ return dimension + "-dimensional vector of i.i.d. " +
+ gen1.getDistribution().toString();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/IIDMultivariateGen.tex b/source/umontreal/iro/lecuyer/randvarmulti/IIDMultivariateGen.tex
new file mode 100644
index 0000000..122ef8c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/IIDMultivariateGen.tex
@@ -0,0 +1,124 @@
+\defmodule{IIDMultivariateGen}
+
+Extends \class{RandomMultivariateGen} for a vector of independent identically distributed
+(i.i.d.) random variables.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: IIDMultivariateGen
+ * Description: vector of independent identically distributed random variables
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvarmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+\end{hide}
+
+public class IIDMultivariateGen extends RandomMultivariateGen\begin{hide} {
+\end{hide}\end{code}
+
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public IIDMultivariateGen (RandomVariateGen gen1, int d) \begin{hide} {
+ setGen1 (gen1);
+ this.stream = gen1.getStream();
+ dimension = d;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Constructs a generator for a \texttt{d}-dimensional vector of i.i.d.\ variates
+with a common one-dimensional generator \texttt{gen1}.
+\end{tabb}
+\begin{htmlonly}
+ \param{gen1}{the one-dimensional generator}
+ \param{d}{dimension of the vector (number of i.i.d.\ variates).}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public void setDimension (int d) \begin{hide} {
+ dimension = d;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Changes the dimension of the vector to \texttt{d}.
+\end{tabb}
+\begin{code}
+
+ public void nextPoint (double[] p) \begin{hide} {
+ if (p.length != dimension)
+ throw new IllegalArgumentException(String.format(
+ "p's dimension (%d) does not mach dimension (%d)", p.length, dimension));
+
+ for (int i = 0; i < dimension; i++)
+ p[i] = gen1.nextDouble();
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Generates a vector of i.i.d.\ variates.
+\end{tabb}
+\begin{code}
+
+ public void setGen1 (RandomVariateGen gen1)\begin{hide} {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+ this.gen1 = gen1;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Sets the common one-dimensional generator to \texttt{gen1}.
+\end{tabb}
+\begin{code}
+
+ public RandomVariateGen getGen1()\begin{hide} {
+ return gen1;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Returns the common one-dimensional generator used in this class.
+\end{tabb}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ return dimension + "-dimensional vector of i.i.d. " +
+ gen1.getDistribution().toString();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns a string representation of the generator.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/MultinormalCholeskyGen.java b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalCholeskyGen.java
new file mode 100644
index 0000000..8d110e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalCholeskyGen.java
@@ -0,0 +1,267 @@
+
+
+/*
+ * Class: MultinormalCholeskyGen
+ * Description: multivariate normal random variable generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvarmulti;
+
+ import cern.colt.matrix.DoubleMatrix2D;
+ import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+ import cern.colt.matrix.linalg.CholeskyDecomposition;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.randvar.NormalGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * Extends {@link MultinormalGen} for a <SPAN CLASS="textit">multivariate normal</SPAN> distribution, generated via a Cholesky decomposition of the covariance
+ * matrix. The covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> is decomposed (by the constructor)
+ * as
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I> = <B>A</B><B>A</B><SUP>t</SUP></SPAN> where
+ * <SPAN CLASS="MATH"><B>A</B></SPAN> is a lower-triangular matrix
+ * (this is the Cholesky decomposition), and
+ * <SPAN CLASS="MATH"><B>X</B></SPAN> is generated via
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>X</B> = <I><B>μ</B></I> + <B>A</B><B>Z</B>,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN> is a <SPAN CLASS="MATH"><I>d</I></SPAN>-dimensional vector of independent standard normal random
+ * variates, and
+ * <SPAN CLASS="MATH"><B>A</B><SUP>t</SUP></SPAN> is the transpose of
+ * <SPAN CLASS="MATH"><B>A</B></SPAN>.
+ * The covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> must be positive-definite, otherwise the
+ * Cholesky decomposition will fail. The decomposition method
+ * uses the <TT>CholeskyDecomposition</TT> class in <TT>colt</TT>.
+ *
+ */
+public class MultinormalCholeskyGen extends MultinormalGen {
+
+ private void initL() {
+ if (mu.length != sigma.rows() || mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix");
+ CholeskyDecomposition decomp = new CholeskyDecomposition (sigma);
+ //if (!decomp.isSymmetricPositiveDefinite())
+ // throw new IllegalArgumentException
+ // ("The covariance matrix must be symmetric and positive-definite");
+ sqrtSigma = decomp.getL();
+ }
+
+
+ /**
+ * Equivalent to
+ * {@link #MultinormalCholeskyGen((NormalGen, double[], DoubleMatrix2D)) MultinormalCholeskyGen}<TT>(gen1, mu, new DenseDoubleMatrix2D(sigma))</TT>.
+ *
+ * @param gen1 the one-dimensional generator
+ *
+ * @param mu the mean vector.
+ *
+ * @param sigma the covariance matrix.
+ *
+ * @exception NullPointerException if any argument is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of the mean
+ * vector is incompatible with the dimensions of the covariance matrix.
+ *
+ *
+ */
+ public MultinormalCholeskyGen (NormalGen gen1, double[] mu,
+ double[][] sigma) {
+ super(gen1, mu, sigma);
+ initL();
+ }
+
+
+ /**
+ * Constructs a multinormal generator with mean vector <TT>mu</TT>
+ * and covariance matrix <TT>sigma</TT>. The mean vector must have the same
+ * length as the dimensions of the covariance matrix, which must be symmetric
+ * and positive-definite.
+ * If any of the above conditions is violated, an exception is thrown.
+ * The vector
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN> is generated by calling <SPAN CLASS="MATH"><I>d</I></SPAN> times the generator <TT>gen1</TT>,
+ * which must be a <SPAN CLASS="textit">standard normal</SPAN> 1-dimensional generator.
+ *
+ * @param gen1 the one-dimensional generator
+ *
+ * @param mu the mean vector.
+ *
+ * @param sigma the covariance matrix.
+ *
+ * @exception NullPointerException if any argument is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of the mean
+ * vector is incompatible with the dimensions of the covariance matrix.
+ *
+ */
+ public MultinormalCholeskyGen (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma) {
+ super(gen1, mu, sigma);
+ initL();
+ }
+
+
+ /**
+ * Returns the lower-triangular matrix
+ * <SPAN CLASS="MATH"><B>A</B></SPAN> in the
+ * Cholesky decomposition of
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>.
+ *
+ * @return the Cholesky decomposition of the covariance matrix.
+ *
+ */
+ public DoubleMatrix2D getCholeskyDecompSigma() {
+ return sqrtSigma.copy();
+ }
+
+
+ /**
+ * Sets the covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> of this multinormal generator
+ * to <TT>sigma</TT> (and recomputes
+ * <SPAN CLASS="MATH"><B>A</B></SPAN>).
+ *
+ * @param sigma the new covariance matrix.
+ *
+ * @exception IllegalArgumentException if <TT>sigma</TT> has
+ * incorrect dimensions.
+ *
+ *
+ */
+ public void setSigma (DoubleMatrix2D sigma) {
+ if (sigma.rows() != mu.length || sigma.columns() != mu.length)
+ throw new IllegalArgumentException
+ ("Invalid dimensions of covariance matrix");
+ CholeskyDecomposition decomp = new CholeskyDecomposition (sigma);
+ //if (!decomp.isSymmetricPositiveDefinite())
+ // throw new IllegalArgumentException
+ // ("The new covariance matrix must be symmetric and positive-definite");
+ this.sigma.assign (sigma);
+ this.sqrtSigma = decomp.getL();
+ }
+
+
+ /**
+ * Equivalent to
+ * {@link #nextPoint((NormalGen, double[], DoubleMatrix2D, double[])) nextPoint}<TT>(gen1, mu, new DenseDoubleMatrix2D(sigma), p)</TT>.
+ *
+ */
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ double[][] sigma, double[] p) {
+ nextPoint (gen1, mu, new DenseDoubleMatrix2D (sigma), p);
+ }
+
+
+ /**
+ * Generates a <SPAN CLASS="MATH"><I>d</I></SPAN>-dimensional vector from the multinormal
+ * distribution with mean vector <TT>mu</TT> and covariance matrix
+ * <TT>sigma</TT>, using the one-dimensional normal generator <TT>gen1</TT> to
+ * generate the coordinates of
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN>, and using the Cholesky decomposition of
+ *
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>. The resulting vector is put into <TT>p</TT>.
+ * Note that this static method will be very slow for large dimensions, since
+ * it computes the Cholesky decomposition at every call. It is therefore
+ * recommended to use a <TT>MultinormalCholeskyGen</TT> object instead,
+ * if the method is to be called more than once.
+ *
+ * @param p the array to be filled with the generated point.
+ *
+ * @exception IllegalArgumentException if the one-dimensional normal
+ * generator uses a normal distribution with <SPAN CLASS="MATH"><I>μ</I></SPAN> not equal to 0, or
+ * <SPAN CLASS="MATH"><I>σ</I></SPAN> not equal to 1.
+ *
+ * @exception IllegalArgumentException if the length of the mean
+ * vector is different from the dimensions of the covariance matrix,
+ * or if the covariance matrix is not symmetric and positive-definite.
+ *
+ * @exception NullPointerException if any argument is <TT>null</TT>.
+ *
+ *
+ */
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma, double[] p) {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+
+ NormalDist dist = (NormalDist) gen1.getDistribution();
+ if (dist.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (dist.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+
+ if (mu.length != sigma.rows() ||
+ mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix dimensions");
+ CholeskyDecomposition decomp = new CholeskyDecomposition (sigma);
+ double[] temp = new double[mu.length];
+ DoubleMatrix2D sqrtSigma = decomp.getL();
+ for (int i = 0; i < temp.length; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < temp.length; i++) {
+ p[i] = 0;
+ for (int c = 0; c <= i; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }
+
+
+ /**
+ * Generates a point from this multinormal distribution. This is much
+ * faster than the static method as it computes the singular value decomposition
+ * matrix only once in the constructor.
+ *
+ * @param p the array to be filled with the generated point
+ *
+ */
+ public void nextPoint (double[] p) {
+ int n = mu.length;
+ for (int i = 0; i < n; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < n; i++) {
+ p[i] = 0;
+ // Matrix is lower-triangular
+ for (int c = 0; c <= i; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/MultinormalCholeskyGen.tex b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalCholeskyGen.tex
new file mode 100644
index 0000000..d0b56ea
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalCholeskyGen.tex
@@ -0,0 +1,249 @@
+\defmodule{MultinormalCholeskyGen}
+
+Extends \class{MultinormalGen} for a \emph{multivariate normal} distribution
+\cite{tJOH72a}, generated via a Cholesky decomposition of the covariance
+matrix. The covariance matrix $\boldSigma$ is decomposed (by the constructor)
+as $\boldSigma = \bA\bA^{\!\tr}$ where $\bA$ is a lower-triangular matrix
+(this is the Cholesky decomposition), and $\bX$ is generated via
+\[
+ \bX = \bmu + \bA \bZ,
+\]
+where $\bZ$ is a $d$-dimensional vector of independent standard normal random
+variates, and $\bA^{\!\tr}$ is the transpose of $\bA$.
+The covariance matrix $\boldSigma$ must be positive-definite, otherwise the
+Cholesky decomposition will fail. The decomposition method
+uses the \texttt{CholeskyDecom\-po\-sition} class in \texttt{colt}.
+% The constructor specifies how these normal variates are generated.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MultinormalCholeskyGen
+ * Description: multivariate normal random variable generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvarmulti;
+
+ import cern.colt.matrix.DoubleMatrix2D;
+ import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+ import cern.colt.matrix.linalg.CholeskyDecomposition;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.randvar.NormalGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+public class MultinormalCholeskyGen extends MultinormalGen\begin{hide} {
+
+ private void initL() {
+ if (mu.length != sigma.rows() || mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix");
+ CholeskyDecomposition decomp = new CholeskyDecomposition (sigma);
+ //if (!decomp.isSymmetricPositiveDefinite())
+ // throw new IllegalArgumentException
+ // ("The covariance matrix must be symmetric and positive-definite");
+ sqrtSigma = decomp.getL();
+ }\end{hide}
+\end{code}
+
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public MultinormalCholeskyGen (NormalGen gen1, double[] mu,
+ double[][] sigma)\begin{hide} {
+ super(gen1, mu, sigma);
+ initL();
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to
+ \method{MultinormalCholeskyGen}{(NormalGen, double[], DoubleMatrix2D)}\texttt{(gen1, mu, new DenseDoubleMatrix2D(sigma))}.
+\end{tabb}
+\begin{htmlonly}
+ \param{gen1}{the one-dimensional generator}
+ \param{mu}{the mean vector.}
+ \param{sigma}{the covariance matrix.}
+ \exception{NullPointerException}{if any argument is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of the mean
+ vector is incompatible with the dimensions of the covariance matrix.}
+\end{htmlonly}
+\begin{code}
+
+ public MultinormalCholeskyGen (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma)\begin{hide} {
+ super(gen1, mu, sigma);
+ initL();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a multinormal generator with mean vector \texttt{mu}
+ and covariance matrix \texttt{sigma}. The mean vector must have the same
+ length as the dimensions of the covariance matrix, which must be symmetric
+ and positive-definite.
+ If any of the above conditions is violated, an exception is thrown.
+ The vector $\bZ$ is generated by calling $d$ times the generator \texttt{gen1},
+ which must be a \emph{standard normal} 1-dimensional generator.
+\end{tabb}
+\begin{htmlonly}
+ \param{gen1}{the one-dimensional generator}
+ \param{mu}{the mean vector.}
+ \param{sigma}{the covariance matrix.}
+ \exception{NullPointerException}{if any argument is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of the mean
+ vector is incompatible with the dimensions of the covariance matrix.}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public DoubleMatrix2D getCholeskyDecompSigma()\begin{hide} {
+ return sqrtSigma.copy();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the lower-triangular matrix $\bA$ in the
+ Cholesky decomposition of $\bSigma$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the Cholesky decomposition of the covariance matrix.}
+\end{htmlonly}
+\begin{code}
+
+ public void setSigma (DoubleMatrix2D sigma)\begin{hide} {
+ if (sigma.rows() != mu.length || sigma.columns() != mu.length)
+ throw new IllegalArgumentException
+ ("Invalid dimensions of covariance matrix");
+ CholeskyDecomposition decomp = new CholeskyDecomposition (sigma);
+ //if (!decomp.isSymmetricPositiveDefinite())
+ // throw new IllegalArgumentException
+ // ("The new covariance matrix must be symmetric and positive-definite");
+ this.sigma.assign (sigma);
+ this.sqrtSigma = decomp.getL();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the covariance matrix $\bSigma$ of this multinormal generator
+ to \texttt{sigma} (and recomputes $\bA$).
+\end{tabb}
+\begin{htmlonly}
+ \param{sigma}{the new covariance matrix.}
+ \exception{IllegalArgumentException}{if \texttt{sigma} has
+ incorrect dimensions.}
+\end{htmlonly}
+\begin{code}
+
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ double[][] sigma, double[] p)\begin{hide} {
+ nextPoint (gen1, mu, new DenseDoubleMatrix2D (sigma), p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to
+ \method{nextPoint}{(NormalGen, double[], DoubleMatrix2D, double[])}\texttt{(gen1, mu, new DenseDoubleMatrix2D(sigma), p)}.
+\end{tabb}
+\begin{code}
+
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma, double[] p)\begin{hide} {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+
+ NormalDist dist = (NormalDist) gen1.getDistribution();
+ if (dist.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (dist.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+
+ if (mu.length != sigma.rows() ||
+ mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix dimensions");
+ CholeskyDecomposition decomp = new CholeskyDecomposition (sigma);
+ double[] temp = new double[mu.length];
+ DoubleMatrix2D sqrtSigma = decomp.getL();
+ for (int i = 0; i < temp.length; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < temp.length; i++) {
+ p[i] = 0;
+ for (int c = 0; c <= i; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a $d$-dimensional vector from the multinormal
+ distribution with mean vector \texttt{mu} and covariance matrix
+ \texttt{sigma}, using the one-dimensional normal generator \texttt{gen1} to
+ generate the coordinates of $\bZ$, and using the Cholesky decomposition of
+ $\bSigma$. The resulting vector is put into \texttt{p}.
+ Note that this static method will be very slow for large dimensions, since
+ it computes the Cholesky decomposition at every call. It is therefore
+ recommended to use a \texttt{MultinormalCholeskyGen} object instead,
+ if the method is to be called more than once.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the array to be filled with the generated point.}
+ \exception{IllegalArgumentException}{if the one-dimensional normal
+ generator uses a normal distribution with $\mu$ not equal to 0, or
+ $\sigma$ not equal to 1.}
+ \exception{IllegalArgumentException}{if the length of the mean
+ vector is different from the dimensions of the covariance matrix,
+ or if the covariance matrix is not symmetric and positive-definite.}
+ \exception{NullPointerException}{if any argument is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public void nextPoint (double[] p)\begin{hide} {
+ int n = mu.length;
+ for (int i = 0; i < n; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < n; i++) {
+ p[i] = 0;
+ // Matrix is lower-triangular
+ for (int c = 0; c <= i; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Generates a point from this multinormal distribution. This is much
+faster than the static method as it computes the singular value decomposition
+matrix only once in the constructor.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the array to be filled with the generated point}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/MultinormalGen.java b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalGen.java
new file mode 100644
index 0000000..d869bfb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalGen.java
@@ -0,0 +1,298 @@
+
+
+/*
+ * Class: MultinormalGen
+ * Description: multivariate normal random variable generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvarmulti;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.randvar.NormalGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.linalg.CholeskyDecomposition;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+
+
+/**
+ * Extends {@link RandomMultivariateGen} for a
+ * <SPAN CLASS="textit">multivariate normal</SPAN> (or <EM>multinormal</EM>) distribution.
+ * The <SPAN CLASS="MATH"><I>d</I></SPAN>-dimensional multivariate normal distribution
+ * with mean vector
+ * <SPAN CLASS="MATH"><I><B>μ</B></I>∈<B>R</B><SUP>d</SUP></SPAN> and (symmetric positive-definite)
+ * covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>, denoted
+ * <SPAN CLASS="MATH"><I>N</I>(<I><B>μ</B></I>, <I><B>Σ</B></I>)</SPAN>, has density
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>f</I> (<B>X</B>) = exp(- (<B>X</B> - <I><B>μ</B></I>)<SUP>t</SUP><I><B>Σ</B></I><SUP>-1</SUP>(<B>X</B> - <I><B>μ</B></I>)/2)/((2π)^d )<SUP>1/2</SUP>,
+ * </DIV><P></P>
+ * for all
+ * <SPAN CLASS="MATH"><B>X</B>∈<B>R</B><SUP>d</SUP></SPAN>, and
+ * <SPAN CLASS="MATH"><B>X</B><SUP>t</SUP></SPAN> is the transpose vector of
+ * <SPAN CLASS="MATH"><B>X</B></SPAN>.
+ * If
+ * <SPAN CLASS="MATH"><B>Z</B>∼<I>N</I>( 0,<B>I</B>)</SPAN> where
+ * <SPAN CLASS="MATH"><B>I</B></SPAN> is the
+ * identity matrix,
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN> is said to have the <EM>standard multinormal</EM>
+ * distribution.
+ *
+ * <P>
+ * For the special case <SPAN CLASS="MATH"><I>d</I> = 2</SPAN>, if the random vector
+ * <SPAN CLASS="MATH"><B>X</B> = (<I>X</I><SUB>1</SUB>, <I>X</I><SUB>2</SUB>)<SUP>t</SUP></SPAN>
+ * has a bivariate normal distribution, then it has mean
+ *
+ * <SPAN CLASS="MATH"><I><B>μ</B></I> = (<I>μ</I><SUB>1</SUB>, <I>μ</I><SUB>2</SUB>)<SUP>t</SUP></SPAN>, and covariance matrix
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I><B>Σ</B></I> = [1#1]
+ * </DIV><P></P>
+ * if and only if
+ * <SPAN CLASS="MATH">Var[<I>X</I><SUB>1</SUB>] = <I>σ</I><SUB>1</SUB><SUP>2</SUP></SPAN>,
+ * <SPAN CLASS="MATH">Var[<I>X</I><SUB>2</SUB>] = <I>σ</I><SUB>2</SUB><SUP>2</SUP></SPAN>, and the
+ * linear correlation between <SPAN CLASS="MATH"><I>X</I><SUB>1</SUB></SPAN> and <SPAN CLASS="MATH"><I>X</I><SUB>2</SUB></SPAN> is <SPAN CLASS="MATH"><I>ρ</I></SPAN>, where
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>1</SUB> > 0</SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>σ</I><SUB>2</SUB> > 0</SPAN>, and
+ * <SPAN CLASS="MATH">-1 <= <I>ρ</I> <= 1</SPAN>.
+ *
+ */
+public class MultinormalGen extends RandomMultivariateGen {
+ protected double[] mu;
+ protected DoubleMatrix2D sigma;
+ protected DoubleMatrix2D sqrtSigma;
+ protected double[] temp;
+ protected static final double MYINF = 37.54;
+
+
+ private void initMN (NormalGen gen1, double[] mu, int d) {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+
+ if (gen1.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (gen1.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+/*
+ NormalDist dist = (NormalDist) gen1.getDistribution();
+ if (dist != null) {
+ if (dist.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (dist.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+ }
+ dist = null;
+*/
+ this.gen1 = gen1;
+
+ if (mu == null) { // d is the dimension
+ dimension = d;
+ this.mu = new double[d];
+ } else { // d is unused
+ dimension = mu.length;
+ this.mu = (double[])mu.clone();
+ }
+ temp = new double[dimension];
+ }
+
+ /**
+ * Constructs a generator with the standard multinormal distribution
+ * (with
+ * <SPAN CLASS="MATH"><I><B>μ</B></I> = 0</SPAN> and
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I> = <B>I</B></SPAN>) in <SPAN CLASS="MATH"><I>d</I></SPAN> dimensions.
+ * Each vector
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN> will be generated via <SPAN CLASS="MATH"><I>d</I></SPAN> successive calls to
+ * <TT>gen1</TT>, which must be a <SPAN CLASS="textit">standard normal</SPAN> generator.
+ *
+ * @param gen1 the one-dimensional generator
+ *
+ * @param d the dimension of the generated vectors
+ *
+ * @exception IllegalArgumentException if the one-dimensional normal
+ * generator uses a normal distribution with <SPAN CLASS="MATH"><I>μ</I></SPAN> not equal to 0, or
+ * <SPAN CLASS="MATH"><I>σ</I></SPAN> not equal to 1.
+ *
+ * @exception IllegalArgumentException if <TT>d</TT>
+ * is negative.
+ *
+ * @exception NullPointerException if <TT>gen1</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public MultinormalGen (NormalGen gen1, int d) {
+ initMN (gen1, null, d);
+ sigma = new DenseDoubleMatrix2D (d, d);
+ sqrtSigma = new DenseDoubleMatrix2D (d, d);
+ for (int i = 0; i < d; i++) {
+ sigma.setQuick (i, i, 1.0);
+ sqrtSigma.setQuick (i, i, 1.0);
+ }
+ }
+
+
+ /**
+ * Constructs a multinormal generator with mean vector
+ * <TT>mu</TT> and covariance matrix <TT>sigma</TT>.
+ * The mean vector must have the same length as the dimensions
+ * of the covariance matrix, which must be symmetric and positive-definite.
+ * If any of the above conditions is violated, an exception is thrown.
+ * The vector
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN> is generated by calling <SPAN CLASS="MATH"><I>d</I></SPAN> times the generator <TT>gen1</TT>,
+ * which must be <SPAN CLASS="textit">standard normal</SPAN>.
+ *
+ * @param gen1 the one-dimensional generator
+ *
+ * @param mu the mean vector.
+ *
+ * @param sigma the covariance matrix.
+ *
+ * @exception NullPointerException if any argument is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of the mean
+ * vector is incompatible with the dimensions of the covariance matrix.
+ *
+ *
+ */
+ protected MultinormalGen (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma) {
+ initMN (gen1, mu, -1);
+ this.sigma = sigma.copy();
+ }
+
+
+ /**
+ * Equivalent to
+ * {@link #MultinormalGen((NormalGen, double[], DoubleMatrix2D)) MultinormalGen} <TT>(gen1, mu, new DenseDoubleMatrix2D (sigma))</TT>.
+ *
+ */
+ protected MultinormalGen (NormalGen gen1, double[] mu, double[][] sigma) {
+ initMN (gen1, mu, -1);
+ this.sigma = new DenseDoubleMatrix2D (sigma);
+ }
+
+ /**
+ * Returns the mean vector used by this generator.
+ *
+ * @return the current mean vector.
+ *
+ */
+ public double[] getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>i</I></SPAN>-th component of the mean vector
+ * for this generator.
+ *
+ * @param i the index of the required component.
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>μ</I><SUB>i</SUB></SPAN>.
+ * @exception ArrayIndexOutOfBoundsException if
+ * <TT>i</TT> is negative or greater than or equal to {@link #getDimension(()) getDimension}.
+ *
+ *
+ */
+ public double getMu (int i) {
+ return mu[i];
+ }
+
+
+ /**
+ * Sets the mean vector to <TT>mu</TT>.
+ *
+ * @param mu the new mean vector.
+ *
+ * @exception NullPointerException if <TT>mu</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of <TT>mu</TT>
+ * does not correspond to {@link #getDimension(()) getDimension}.
+ *
+ *
+ */
+ public void setMu (double[] mu) {
+ if (mu.length != this.mu.length)
+ throw new IllegalArgumentException
+ ("Incompatible length of mean vector");
+ this.mu = mu;
+ }
+
+
+ /**
+ * Sets the <SPAN CLASS="MATH"><I>i</I></SPAN>-th component of the mean vector to <TT>mui</TT>.
+ *
+ * @param i the index of the modified component.
+ *
+ * @param mui the new value of <SPAN CLASS="MATH"><I>μ</I><SUB>i</SUB></SPAN>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if <TT>i</TT>
+ * is negative or greater than or equal to {@link #getDimension(()) getDimension}.
+ *
+ *
+ */
+ public void setMu (int i, double mui) {
+ mu[i] = mui;
+ }
+
+
+ /**
+ * Returns the covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>
+ * used by this generator.
+ *
+ * @return the used covariance matrix.
+ *
+ */
+ public DoubleMatrix2D getSigma() {
+ return sigma.copy();
+ }
+
+
+ /**
+ * Generates a point from this multinormal distribution.
+ *
+ * @param p the array to be filled with the generated point
+ *
+ *
+ */
+ public void nextPoint (double[] p) {
+ int n = dimension;
+ for (int i = 0; i < n; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < n; i++) {
+ p[i] = 0;
+ for (int c = 0; c < n; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/MultinormalGen.tex b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalGen.tex
new file mode 100644
index 0000000..0d8689b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalGen.tex
@@ -0,0 +1,282 @@
+\defmodule{MultinormalGen}
+
+Extends \class{RandomMultivariateGen} for a
+\emph{multivariate normal} (or {\em multinormal\/}) distribution \cite{tJOH72a}.
+The $d$-dimensional multivariate normal distribution
+with mean vector $\boldmu\in\RR^d$ and (symmetric positive-definite)
+covariance matrix $\boldSigma$, denoted $N(\boldmu, \boldSigma)$, has density
+\begin{latexonly}
+\[
+ f(\bX)=\frac1{\sqrt{(2\pi)^d\det(\boldSigma)}}
+ \exp\left(-(\bX - \boldmu)^{\!\tr}\boldSigma^{-1}(\bX -
+ \boldmu)/2\right),
+\]
+\end{latexonly}
+\begin{htmlonly}
+\[
+ f(\bX)=
+ \exp\left(-(\bX - \boldmu)^{\!\tr}\boldSigma^{-1}(\bX -
+ \boldmu)/2\right) / \sqrt{(2\pi)^d\;\det{\boldSigma}},
+\]
+\end{htmlonly}
+for all $\bX\in\RR^d$, and $\bX^{\tr}$ is the transpose vector of $\bX$.
+If $\bZ \sim N(\bzero, \bI)$ where $\bI$ is the
+identity matrix, $\bZ$ is said to have the {\em standard multinormal\/}
+ distribution.
+
+For the special case $d=2$, if the random vector $\bX = (X_1, X_2)^\tr$
+has a bivariate normal distribution, then it has mean
+$\boldmu = (\mu_1, \mu_2)^\tr$, and covariance matrix
+\[
+\boldSigma =
+\left[\begin{array}{cc}
+\sigma_1^2 & \rho\sigma_1\sigma_2 \\
+\rho\sigma_1\sigma_2 &\sigma_2^2
+\end{array}\right]
+\]
+if and only if $\Var[X_1] = \sigma_1^2$, $\Var[X_2] = \sigma_2^2$, and the
+linear correlation between $X_1$ and $X_2$ is $\rho$, where $\sigma_1 > 0$,
+ $\sigma_2 > 0$, and $-1 \le \rho \le 1$.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MultinormalGen
+ * Description: multivariate normal random variable generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvarmulti;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.randvar.NormalGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.linalg.CholeskyDecomposition;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+\end{hide}
+
+public class MultinormalGen extends RandomMultivariateGen\begin{hide} {
+ protected double[] mu;
+ protected DoubleMatrix2D sigma;
+ protected DoubleMatrix2D sqrtSigma;
+ protected double[] temp;
+ protected static final double MYINF = 37.54;
+
+
+ private void initMN (NormalGen gen1, double[] mu, int d) {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+
+ if (gen1.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (gen1.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+/*
+ NormalDist dist = (NormalDist) gen1.getDistribution();
+ if (dist != null) {
+ if (dist.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (dist.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+ }
+ dist = null;
+*/
+ this.gen1 = gen1;
+
+ if (mu == null) { // d is the dimension
+ dimension = d;
+ this.mu = new double[d];
+ } else { // d is unused
+ dimension = mu.length;
+ this.mu = (double[])mu.clone();
+ }
+ temp = new double[dimension];
+ }\end{hide}\end{code}
+
+\subsubsection* {Constructors}
+\begin{code}
+
+ public MultinormalGen (NormalGen gen1, int d)\begin{hide} {
+ initMN (gen1, null, d);
+ sigma = new DenseDoubleMatrix2D (d, d);
+ sqrtSigma = new DenseDoubleMatrix2D (d, d);
+ for (int i = 0; i < d; i++) {
+ sigma.setQuick (i, i, 1.0);
+ sqrtSigma.setQuick (i, i, 1.0);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a generator with the standard multinormal distribution
+ (with $\bmu=\bzero$ and $\bSigma = \bI$) in $d$ dimensions.
+ Each vector $\bZ$ will be generated via $d$ successive calls to
+ \texttt{gen1}, which must be a \emph{standard normal} generator.
+\end{tabb}
+\begin{htmlonly}
+ \param{gen1}{the one-dimensional generator}
+ \param{d}{the dimension of the generated vectors}
+ \exception{IllegalArgumentException}{if the one-dimensional normal
+ generator uses a normal distribution with $\mu$ not equal to 0, or
+ $\sigma$ not equal to 1.}
+ \exception{IllegalArgumentException}{if \texttt{d}
+ is negative.}
+ \exception{NullPointerException}{if \texttt{gen1} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ protected MultinormalGen (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma)\begin{hide} {
+ initMN (gen1, mu, -1);
+ this.sigma = sigma.copy();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a multinormal generator with mean vector
+ \texttt{mu} and covariance matrix \texttt{sigma}.
+ The mean vector must have the same length as the dimensions
+ of the covariance matrix, which must be symmetric and positive-definite.
+ If any of the above conditions is violated, an exception is thrown.
+ The vector $\bZ$ is generated by calling $d$ times the generator \texttt{gen1},
+ which must be \emph{standard normal}.
+\end{tabb}
+\begin{htmlonly}
+ \param{gen1}{the one-dimensional generator}
+ \param{mu}{the mean vector.}
+ \param{sigma}{the covariance matrix.}
+ \exception{NullPointerException}{if any argument is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of the mean
+ vector is incompatible with the dimensions of the covariance matrix.}
+\end{htmlonly}
+\begin{code}
+
+ protected MultinormalGen (NormalGen gen1, double[] mu, double[][] sigma) \begin{hide} {
+ initMN (gen1, mu, -1);
+ this.sigma = new DenseDoubleMatrix2D (sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to
+ \method{MultinormalGen}{(NormalGen, double[], DoubleMatrix2D)} \texttt{(gen1, mu, new DenseDoubleMatrix2D (sigma))}.
+\end{tabb}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+ public double[] getMu()\begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the mean vector used by this generator.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current mean vector.}
+\end{htmlonly}
+\begin{code}
+
+ public double getMu (int i)\begin{hide} {
+ return mu[i];
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the $i$-th component of the mean vector
+ for this generator.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the index of the required component.}
+ \return{the value of $\mu_i$.}
+ \exception{ArrayIndexOutOfBoundsException}{if
+ \texttt{i} is negative or greater than or equal to \method{getDimension}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public void setMu (double[] mu)\begin{hide} {
+ if (mu.length != this.mu.length)
+ throw new IllegalArgumentException
+ ("Incompatible length of mean vector");
+ this.mu = mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the mean vector to \texttt{mu}.
+\end{tabb}
+\begin{htmlonly}
+ \param{mu}{the new mean vector.}
+ \exception{NullPointerException}{if \texttt{mu} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of \texttt{mu}
+ does not correspond to \method{getDimension}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public void setMu (int i, double mui)\begin{hide} {
+ mu[i] = mui;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the $i$-th component of the mean vector to \texttt{mui}.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the index of the modified component.}
+ \param{mui}{the new value of $\mu_i$.}
+ \exception{ArrayIndexOutOfBoundsException}{if \texttt{i}
+ is negative or greater than or equal to \method{getDimension}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public DoubleMatrix2D getSigma()\begin{hide} {
+ return sigma.copy();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the covariance matrix $\boldSigma$
+ used by this generator.
+\end{tabb}
+\begin{htmlonly}
+ \return{the used covariance matrix.}
+\end{htmlonly}
+\begin{code}
+
+ public void nextPoint (double[] p)\begin{hide} {
+ int n = dimension;
+ for (int i = 0; i < n; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < n; i++) {
+ p[i] = 0;
+ for (int c = 0; c < n; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a point from this multinormal distribution.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the array to be filled with the generated point}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/MultinormalPCAGen.java b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalPCAGen.java
new file mode 100644
index 0000000..bde13e0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalPCAGen.java
@@ -0,0 +1,319 @@
+
+
+/*
+ * Class: MultinormalPCAGen
+ * Description: multivariate normal random variable generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvarmulti;
+
+ import cern.colt.matrix.DoubleMatrix2D;
+ import cern.colt.matrix.linalg.SingularValueDecomposition;
+
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.randvar.NormalGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+
+
+/**
+ * Extends {@link MultinormalGen} for a <SPAN CLASS="textit">multivariate normal</SPAN> distribution, generated via the method of principal components analysis
+ * (PCA) of the covariance matrix. The covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> is
+ * decomposed (by the constructor) as
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I> = <B>V</B><I><B>Λ</B></I><B>V</B><SUP>t</SUP></SPAN> where
+ * <SPAN CLASS="MATH"><B>V</B></SPAN> is an orthogonal matrix and
+ * <SPAN CLASS="MATH"><I><B>Λ</B></I></SPAN> is the diagonal matrix made up
+ * of the eigenvalues of
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>. <SPAN CLASS="MATH"><B>V</B><SUP>t</SUP></SPAN> is the transpose
+ * matrix of <SPAN CLASS="MATH"><B>V</B></SPAN>. The eigenvalues are ordered from the
+ * largest (<SPAN CLASS="MATH"><I>λ</I><SUB>1</SUB></SPAN>) to the smallest (<SPAN CLASS="MATH"><I>λ</I><SUB>d</SUB></SPAN>). The random multinormal
+ * vector
+ * <SPAN CLASS="MATH"><B>X</B></SPAN> is generated via
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <B>X</B> = <I><B>μ</B></I> + <B>A</B><B>Z</B>,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><B>A</B> = <B>V</B>()<SUP>1/2</SUP></SPAN>, and
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN> is a <SPAN CLASS="MATH"><I>d</I></SPAN>-dimensional vector of
+ * independent standard normal random variates. The decomposition method
+ * uses the <TT>SingularValueDecomposition</TT> class in <TT>colt</TT>.
+ *
+ */
+public class MultinormalPCAGen extends MultinormalGen {
+ private double[] lambda;
+
+ private static SingularValueDecomposition getSvd (DoubleMatrix2D sigma) {
+ return (new SingularValueDecomposition (sigma));
+ }
+
+ private DoubleMatrix2D decompPCA (SingularValueDecomposition svd) {
+ DoubleMatrix2D D = svd.getS ();
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); ++i) {
+ lambda[i] = D.getQuick (i, i);
+ D.setQuick (i, i, Math.sqrt (D.getQuick (i, i)));
+ }
+ DoubleMatrix2D P = svd.getV();
+ return P.zMult (D, null);
+ }
+
+ private void initL() {
+ if (mu.length != sigma.rows() || mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix");
+ lambda = new double[mu.length];
+ sqrtSigma = decompPCA (getSvd(sigma));
+ }
+
+
+ /**
+ * Equivalent to
+ * {@link #MultinormalPCAGen((NormalGen, double[], DoubleMatrix2D)) MultinormalPCAGen}<TT>(gen1, mu, new DenseDoubleMatrix2D(sigma))</TT>.
+ *
+ */
+ public MultinormalPCAGen (NormalGen gen1, double[] mu, double[][] sigma) {
+ super(gen1, mu, sigma);
+ initL();
+ }
+
+
+ /**
+ * Constructs a multinormal generator with mean vector <TT>mu</TT>
+ * and covariance matrix <TT>sigma</TT>. The mean vector must have the same
+ * length as the dimensions of the covariance matrix, which must be symmetric
+ * and positive semi-definite.
+ * If any of the above conditions is violated, an exception is thrown.
+ * The vector
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN> is generated by calling <SPAN CLASS="MATH"><I>d</I></SPAN> times the generator <TT>gen1</TT>,
+ * which must be a <SPAN CLASS="textit">standard normal</SPAN> 1-dimensional generator.
+ *
+ * @param gen1 the one-dimensional generator
+ *
+ * @param mu the mean vector.
+ *
+ * @param sigma the covariance matrix.
+ *
+ * @exception NullPointerException if any argument is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of the mean
+ * vector is incompatible with the dimensions of the covariance matrix.
+ *
+ */
+ public MultinormalPCAGen (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma) {
+ super(gen1, mu, sigma);
+ initL();
+ }
+
+ /**
+ * Computes the decomposition <TT>sigma</TT> =
+ *
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I> = <B>V</B><I><B>Λ</B></I><B>V</B><SUP>t</SUP></SPAN>. Returns
+ * <SPAN CLASS="MATH"><B>A</B> = <B>V</B>()<SUP>1/2</SUP></SPAN>.
+ *
+ */
+ public static DoubleMatrix2D decompPCA (double[][] sigma) {
+ return decompPCA (new DenseDoubleMatrix2D (sigma));
+ }
+
+
+ /**
+ * Computes the decomposition <TT>sigma</TT> =
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I> = <B>V</B><I><B>Λ</B></I><B>V</B><SUP>t</SUP></SPAN>. Returns
+ * <SPAN CLASS="MATH"><B>A</B> = <B>V</B>()<SUP>1/2</SUP></SPAN>.
+ *
+ */
+ public static DoubleMatrix2D decompPCA (DoubleMatrix2D sigma) {
+ // L'objet SingularValueDecomposition permet de recuperer la matrice
+ // des valeurs propres en ordre decroissant et celle des vecteurs propres de
+ // sigma (pour une matrice symetrique et definie-positive seulement).
+
+ SingularValueDecomposition sv = getSvd (sigma);
+ // D contient les valeurs propres sur la diagonale
+ DoubleMatrix2D D = sv.getS ();
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); ++i)
+ D.setQuick (i, i, Math.sqrt (D.getQuick (i, i)));
+ DoubleMatrix2D P = sv.getV();
+ // Multiplier par la matrice orthogonale (ici P)
+ return P.zMult (D, null);
+ }
+
+
+ /**
+ * Returns the matrix
+ * <SPAN CLASS="MATH"><B>A</B> = <B>V</B>()<SUP>1/2</SUP></SPAN> of this object.
+ *
+ * @return the PCA square root of the covariance matrix
+ *
+ */
+ public DoubleMatrix2D getPCADecompSigma() {
+ return sqrtSigma.copy();
+ }
+
+
+ /**
+ * Computes and returns the eigenvalues of <TT>sigma</TT> in
+ * decreasing order.
+ *
+ */
+ public static double[] getLambda (DoubleMatrix2D sigma) {
+ SingularValueDecomposition sv = getSvd (sigma);
+ DoubleMatrix2D D = sv.getS ();
+ double[] lambd = new double[D.rows()];
+ for (int i = 0; i < D.rows(); ++i)
+ lambd[i] = D.getQuick (i, i);
+ return lambd;
+ }
+
+
+ /**
+ * Returns the eigenvalues of
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> in decreasing order.
+ *
+ */
+ public double[] getLambda() {
+ return lambda;
+ }
+
+
+ /**
+ * Sets the covariance matrix
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN> of this multinormal generator
+ * to <TT>sigma</TT> (and recomputes
+ * <SPAN CLASS="MATH"><B>A</B></SPAN>).
+ *
+ * @param sigma the new covariance matrix.
+ *
+ * @exception IllegalArgumentException if <TT>sigma</TT> has
+ * incorrect dimensions.
+ *
+ *
+ */
+ public void setSigma (DoubleMatrix2D sigma) {
+ if (sigma.rows() != mu.length || sigma.columns() != mu.length)
+ throw new IllegalArgumentException
+ ("Invalid dimensions of covariance matrix");
+ this.sigma.assign (sigma);
+ this.sqrtSigma = decompPCA(getSvd (sigma));
+ }
+
+
+ /**
+ * Generates a <SPAN CLASS="MATH"><I>d</I></SPAN>-dimensional vector from the multinormal
+ * distribution with mean vector <TT>mu</TT> and covariance matrix
+ * <TT>sigma</TT>, using the one-dimensional normal generator <TT>gen1</TT> to
+ * generate the coordinates of
+ * <SPAN CLASS="MATH"><B>Z</B></SPAN>, and using the PCA decomposition of
+ *
+ * <SPAN CLASS="MATH"><I><B>Σ</B></I></SPAN>. The resulting vector is put into <TT>p</TT>.
+ * Note that this static method will be very slow for large dimensions, because
+ * it recomputes the singular value decomposition at every call. It is therefore
+ * recommended to use a <TT>MultinormalPCAGen</TT> object instead,
+ * if the method is to be called more than once.
+ *
+ * @param p the array to be filled with the generated point.
+ *
+ * @exception IllegalArgumentException if the one-dimensional normal
+ * generator uses a normal distribution with <SPAN CLASS="MATH"><I>μ</I></SPAN> not equal to 0, or
+ * <SPAN CLASS="MATH"><I>σ</I></SPAN> not equal to 1.
+ *
+ * @exception IllegalArgumentException if the length of the mean
+ * vector is different from the dimensions of the covariance matrix,
+ * or if the covariance matrix is not symmetric and positive-definite.
+ *
+ * @exception NullPointerException if any argument is <TT>null</TT>.
+ *
+ *
+ */
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma, double[] p) {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+
+ NormalDist dist = (NormalDist) gen1.getDistribution();
+ if (dist.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (dist.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+
+ if (mu.length != sigma.rows() ||
+ mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix dimensions");
+ double[] temp = new double[mu.length];
+ DoubleMatrix2D sqrtSigma = decompPCA(sigma);
+ for (int i = 0; i < temp.length; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < temp.length; i++) {
+ p[i] = 0;
+ for (int c = 0; c < temp.length; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }
+
+
+ /**
+ * Equivalent to
+ * {@link #nextPoint((NormalGen, double[], DoubleMatrix2D, double[])) nextPoint}<TT>(gen1, mu, new DenseDoubleMatrix2D(sigma), p)</TT>.
+ *
+ */
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ double[][] sigma, double[] p) {
+ nextPoint(gen1, mu, new DenseDoubleMatrix2D(sigma), p);
+ }
+
+
+ /**
+ * Generates a point from this multinormal distribution. This is much
+ * faster than the static method as it computes the singular value decomposition
+ * matrix only once in the constructor.
+ *
+ * @param p the array to be filled with the generated point
+ *
+ */
+ public void nextPoint (double[] p) {
+ int n = mu.length;
+ for (int i = 0; i < n; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < n; i++) {
+ p[i] = 0;
+ for (int c = 0; c < n; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/MultinormalPCAGen.tex b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalPCAGen.tex
new file mode 100644
index 0000000..87afc77
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/MultinormalPCAGen.tex
@@ -0,0 +1,299 @@
+\defmodule{MultinormalPCAGen}
+
+Extends \class{MultinormalGen} for a \emph{multivariate normal} distribution
+\cite{tJOH72a}, generated via the method of principal components analysis
+(PCA) of the covariance matrix. The covariance matrix $\boldSigma$ is
+decomposed (by the constructor) as $\boldSigma = \bV\boldLambda\bV^{\tr}$ where
+ $\bV$ is an orthogonal matrix and $\boldLambda$ is the diagonal matrix made up
+of the eigenvalues of $\boldSigma$. $\bV^{\tr}$ is the transpose
+matrix of $\bV$. The eigenvalues are ordered from the
+largest ($\lambda_1$) to the smallest ($\lambda_d$). The random multinormal
+vector $\bX$ is generated via
+\[
+ \bX = \bmu + \bA \bZ,
+\]
+where $\bA = \bV\sqrt{\boldLambda}$, and $\bZ$ is a $d$-dimensional vector of
+independent standard normal random variates. The decomposition method
+uses the \texttt{SingularValueDecomposition} class in \texttt{colt}.
+% The constructor specifies how these normal variates are generated.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MultinormalPCAGen
+ * Description: multivariate normal random variable generator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvarmulti;
+
+ import cern.colt.matrix.DoubleMatrix2D;
+ import cern.colt.matrix.linalg.SingularValueDecomposition;
+\begin{hide}
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.randvar.NormalGen;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+\end{hide}
+
+public class MultinormalPCAGen extends MultinormalGen\begin{hide} {
+ private double[] lambda;
+
+ private static SingularValueDecomposition getSvd (DoubleMatrix2D sigma) {
+ return (new SingularValueDecomposition (sigma));
+ }
+
+ private DoubleMatrix2D decompPCA (SingularValueDecomposition svd) {
+ DoubleMatrix2D D = svd.getS ();
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); ++i) {
+ lambda[i] = D.getQuick (i, i);
+ D.setQuick (i, i, Math.sqrt (D.getQuick (i, i)));
+ }
+ DoubleMatrix2D P = svd.getV();
+ return P.zMult (D, null);
+ }
+
+ private void initL() {
+ if (mu.length != sigma.rows() || mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix");
+ lambda = new double[mu.length];
+ sqrtSigma = decompPCA (getSvd(sigma));
+ }
+\end{hide}\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public MultinormalPCAGen (NormalGen gen1, double[] mu, double[][] sigma)\begin{hide} {
+ super(gen1, mu, sigma);
+ initL();
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to
+ \method{MultinormalPCAGen}{(NormalGen, double[], DoubleMatrix2D)}\texttt{(gen1, mu, new DenseDoubleMatrix2D(sigma))}.
+\end{tabb}
+\begin{code}
+
+ public MultinormalPCAGen (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma)\begin{hide} {
+ super(gen1, mu, sigma);
+ initL();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a multinormal generator with mean vector \texttt{mu}
+ and covariance matrix \texttt{sigma}. The mean vector must have the same
+ length as the dimensions of the covariance matrix, which must be symmetric
+ and positive semi-definite.
+ If any of the above conditions is violated, an exception is thrown.
+ The vector $\bZ$ is generated by calling $d$ times the generator \texttt{gen1},
+ which must be a \emph{standard normal} 1-dimensional generator.
+\end{tabb}
+\begin{htmlonly}
+ \param{gen1}{the one-dimensional generator}
+ \param{mu}{the mean vector.}
+ \param{sigma}{the covariance matrix.}
+ \exception{NullPointerException}{if any argument is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of the mean
+ vector is incompatible with the dimensions of the covariance matrix.}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
+\subsubsection* {Methods}
+\begin{code}
+ public static DoubleMatrix2D decompPCA (double[][] sigma) \begin{hide} {
+ return decompPCA (new DenseDoubleMatrix2D (sigma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the decomposition \texttt{sigma} =
+$\boldSigma = \bV\boldLambda\bV^{\tr}$. Returns $\bA = \bV\sqrt{\boldLambda}$.
+\end{tabb}
+\begin{code}
+
+ public static DoubleMatrix2D decompPCA (DoubleMatrix2D sigma) \begin{hide} {
+ // L'objet SingularValueDecomposition permet de recuperer la matrice
+ // des valeurs propres en ordre decroissant et celle des vecteurs propres de
+ // sigma (pour une matrice symetrique et definie-positive seulement).
+
+ SingularValueDecomposition sv = getSvd (sigma);
+ // D contient les valeurs propres sur la diagonale
+ DoubleMatrix2D D = sv.getS ();
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); ++i)
+ D.setQuick (i, i, Math.sqrt (D.getQuick (i, i)));
+ DoubleMatrix2D P = sv.getV();
+ // Multiplier par la matrice orthogonale (ici P)
+ return P.zMult (D, null);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the decomposition \texttt{sigma} = $\boldSigma =
+ \bV\boldLambda\bV^{\tr}$. Returns $\bA = \bV\sqrt{\boldLambda}$.
+\end{tabb}
+\begin{code}
+
+ public DoubleMatrix2D getPCADecompSigma()\begin{hide} {
+ return sqrtSigma.copy();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the matrix $\bA = \bV\sqrt{\boldLambda}$ of this object.
+\end{tabb}
+\begin{htmlonly}
+ \return{the PCA square root of the covariance matrix}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] getLambda (DoubleMatrix2D sigma)\begin{hide} {
+ SingularValueDecomposition sv = getSvd (sigma);
+ DoubleMatrix2D D = sv.getS ();
+ double[] lambd = new double[D.rows()];
+ for (int i = 0; i < D.rows(); ++i)
+ lambd[i] = D.getQuick (i, i);
+ return lambd;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes and returns the eigenvalues of \texttt{sigma} in
+ decreasing order.
+\end{tabb}
+\begin{code}
+
+ public double[] getLambda()\begin{hide} {
+ return lambda;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the eigenvalues of $\boldSigma$ in decreasing order.
+\end{tabb}
+\begin{code}
+
+ public void setSigma (DoubleMatrix2D sigma)\begin{hide} {
+ if (sigma.rows() != mu.length || sigma.columns() != mu.length)
+ throw new IllegalArgumentException
+ ("Invalid dimensions of covariance matrix");
+ this.sigma.assign (sigma);
+ this.sqrtSigma = decompPCA(getSvd (sigma));
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the covariance matrix $\bSigma$ of this multinormal generator
+ to \texttt{sigma} (and recomputes $\bA$).
+\end{tabb}
+\begin{htmlonly}
+ \param{sigma}{the new covariance matrix.}
+ \exception{IllegalArgumentException}{if \texttt{sigma} has
+ incorrect dimensions.}
+\end{htmlonly}
+\begin{code}
+
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ DoubleMatrix2D sigma, double[] p)\begin{hide} {
+ if (gen1 == null)
+ throw new NullPointerException ("gen1 is null");
+
+ NormalDist dist = (NormalDist) gen1.getDistribution();
+ if (dist.getMu() != 0.0)
+ throw new IllegalArgumentException ("mu != 0");
+ if (dist.getSigma() != 1.0)
+ throw new IllegalArgumentException ("sigma != 1");
+
+ if (mu.length != sigma.rows() ||
+ mu.length != sigma.columns())
+ throw new IllegalArgumentException
+ ("Incompatible mean vector and covariance matrix dimensions");
+ double[] temp = new double[mu.length];
+ DoubleMatrix2D sqrtSigma = decompPCA(sigma);
+ for (int i = 0; i < temp.length; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < temp.length; i++) {
+ p[i] = 0;
+ for (int c = 0; c < temp.length; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a $d$-dimensional vector from the multinormal
+ distribution with mean vector \texttt{mu} and covariance matrix
+ \texttt{sigma}, using the one-dimensional normal generator \texttt{gen1} to
+ generate the coordinates of $\bZ$, and using the PCA decomposition of
+ $\bSigma$. The resulting vector is put into \texttt{p}.
+ Note that this static method will be very slow for large dimensions, because
+ it recomputes the singular value decomposition at every call. It is therefore
+ recommended to use a \texttt{MultinormalPCAGen} object instead,
+ if the method is to be called more than once.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the array to be filled with the generated point.}
+ \exception{IllegalArgumentException}{if the one-dimensional normal
+ generator uses a normal distribution with $\mu$ not equal to 0, or
+ $\sigma$ not equal to 1.}
+ \exception{IllegalArgumentException}{if the length of the mean
+ vector is different from the dimensions of the covariance matrix,
+ or if the covariance matrix is not symmetric and positive-definite.}
+ \exception{NullPointerException}{if any argument is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public static void nextPoint (NormalGen gen1, double[] mu,
+ double[][] sigma, double[] p)\begin{hide} {
+ nextPoint(gen1, mu, new DenseDoubleMatrix2D(sigma), p);
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to
+ \method{nextPoint}{(NormalGen, double[], DoubleMatrix2D, double[])}\texttt{(gen1, mu, new DenseDoubleMatrix2D(sigma), p)}.
+\end{tabb}
+\begin{code}
+
+ public void nextPoint (double[] p)\begin{hide} {
+ int n = mu.length;
+ for (int i = 0; i < n; i++) {
+ temp[i] = gen1.nextDouble();
+ if (temp[i] == Double.NEGATIVE_INFINITY)
+ temp[i] = -MYINF;
+ if (temp[i] == Double.POSITIVE_INFINITY)
+ temp[i] = MYINF;
+ }
+ for (int i = 0; i < n; i++) {
+ p[i] = 0;
+ for (int c = 0; c < n; c++)
+ p[i] += sqrtSigma.getQuick (i, c)*temp[c];
+ p[i] += mu[i];
+ }
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Generates a point from this multinormal distribution. This is much
+faster than the static method as it computes the singular value decomposition
+matrix only once in the constructor.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the array to be filled with the generated point}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/RandomMultivariateGen.java b/source/umontreal/iro/lecuyer/randvarmulti/RandomMultivariateGen.java
new file mode 100644
index 0000000..8229f15
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/RandomMultivariateGen.java
@@ -0,0 +1,130 @@
+
+
+/*
+ * Class: RandomMultivariateGen
+ * Description: base class for multidimensional random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.randvarmulti;
+
+import umontreal.iro.lecuyer.probdistmulti.ContinuousDistributionMulti;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+
+
+
+/**
+ * This class is the multivariate counterpart of
+ * {@link umontreal.iro.lecuyer.randvar.RandomVariateGen RandomVariateGen}.
+ * It is the base class for general random variate generators over
+ * the <SPAN CLASS="MATH"><I>d</I></SPAN>-dimensional real space <SPAN CLASS="MATH"><B>R</B><SUP>d</SUP></SPAN>.
+ * It specifies the signature of the {@link #nextPoint nextPoint} method, which is
+ * normally called to generate a random vector from a given distribution.
+ * Contrary to univariate distributions and generators, here the inversion method
+ * is not well defined, so we cannot construct a multivariate generator simply
+ * by passing a multivariate distribution and a stream; we must specify a
+ * generating method as well. For this reason, this class is abstract.
+ * Generators can be constructed only by invoking the constructor of a subclass.
+ * This is an important difference with
+ * {@link umontreal.iro.lecuyer.randvar.RandomVariateGen RandomVariateGen}.
+ *
+ */
+public abstract class RandomMultivariateGen {
+ protected int dimension;
+ // Careful here: there is also a RandomStream inside gen1. But only one
+ // of these two is used in a given class.
+ protected RandomStream stream; // stream used to generate random numbers
+ protected RandomVariateGen gen1; // 1-dim generator used to generate random variates
+
+// This constructor is needed for subclasses with no associated distribution.
+// protected RandomMultivariateGen() {}
+
+
+ /**
+ * Generates a random point <SPAN CLASS="MATH"><I>p</I></SPAN> using the
+ * the stream contained in this object.
+ *
+ */
+ abstract public void nextPoint (double[] p);
+
+
+ /**
+ * Generates <SPAN CLASS="MATH"><I>n</I></SPAN> random points. These points are stored in
+ * the array <TT>v</TT>, starting at index <TT>start</TT>. Thus
+ * <TT>v[start][i]</TT> contains
+ * coordinate <SPAN CLASS="MATH"><I>i</I></SPAN> of the first generated point.
+ * By default, this method calls {@link #nextPoint nextPoint} <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * times, but one can override it in subclasses for better efficiency.
+ * The array argument <TT>v[][d]</TT> must have <SPAN CLASS="MATH"><I>d</I></SPAN> elements reserved
+ * for each generated point before calling this method.
+ *
+ * @param v array in which the variates will be stored
+ *
+ * @param start starting index, in <TT>v</TT>, of the new variates
+ *
+ * @param n number of variates to generate
+ *
+ *
+ */
+ public void nextArrayOfPoints (double[][] v, int start, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = 0; i < n; i++)
+ nextPoint(v[start + i]);
+ }
+
+
+ /**
+ * Returns the dimension of this multivariate generator
+ * (the dimension of the random points).
+ *
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+
+ /**
+ * Returns the {@link RandomStream} used by this object.
+ *
+ * @return the stream associated to this object
+ *
+ */
+ public RandomStream getStream() {
+ if (null != gen1)
+ return gen1.getStream();
+ return stream;
+ }
+
+
+ /**
+ * Sets the {@link RandomStream} used by this object to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ if (null != gen1)
+ gen1.setStream(stream);
+ else
+ this.stream = stream;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/RandomMultivariateGen.tex b/source/umontreal/iro/lecuyer/randvarmulti/RandomMultivariateGen.tex
new file mode 100644
index 0000000..b6070dc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/RandomMultivariateGen.tex
@@ -0,0 +1,148 @@
+\defmodule {RandomMultivariateGen}
+
+This class is the multivariate counterpart of
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen}.
+It is the base class for general random variate generators over
+the $d$-dimensional real space $\RR^d$.
+It specifies the signature of the \method{nextPoint}{} method, which is
+normally called to generate a random vector from a given distribution.
+Contrary to univariate distributions and generators, here the inversion method
+is not well defined, so we cannot construct a multivariate generator simply
+by passing a multivariate distribution and a stream; we must specify a
+generating method as well. For this reason, this class is abstract.
+Generators can be constructed only by invoking the constructor of a subclass.
+This is an important difference with
+\externalclass{umontreal.iro.lecuyer.randvar}{RandomVariateGen}.
+
+\hpierre{Je pense qu'il faut enlever les constructeurs de cette classe-ci.}
+\hrichard{Je pense que le \texttt{RandomMultivariateGen(RandomStream)}
+ doit rester.}
+
+%\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} objects.
+%The multi-dimensional generator normally uses one or more one-dimensional
+%generators or a primitive stream to generate the components of the points one
+%at a time.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomMultivariateGen
+ * Description: base class for multidimensional random variate generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.randvarmulti;\begin{hide}
+
+import umontreal.iro.lecuyer.probdistmulti.ContinuousDistributionMulti;
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.randvar.RandomVariateGen;
+\end{hide}
+
+
+public abstract class RandomMultivariateGen \begin{hide} {
+ protected int dimension;
+ // Careful here: there is also a RandomStream inside gen1. But only one
+ // of these two is used in a given class.
+ protected RandomStream stream; // stream used to generate random numbers
+ protected RandomVariateGen gen1; // 1-dim generator used to generate random variates
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection* {Constructor}
+\begin{hide}\begin{code}
+// This constructor is needed for subclasses with no associated distribution.
+// protected RandomMultivariateGen() {}
+\end{code}\end{hide}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ abstract public void nextPoint (double[] p);
+\end{code}
+ \begin{tabb} Generates a random point $p$ using the
+ the stream contained in this object.
+ \end{tabb}
+\begin{code}
+
+ public void nextArrayOfPoints (double[][] v, int start, int n) \begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = 0; i < n; i++)
+ nextPoint(v[start + i]);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Generates $n$ random points. These points are stored in
+ the array \texttt{v}, starting at index \texttt{start}. Thus
+ \texttt{v[start][i]} contains
+ coordinate $i$ of the first generated point.
+ By default, this method calls \method{nextPoint}{} $n$
+ times, but one can override it in subclasses for better efficiency.
+ The array argument \texttt{v[][d]} must have $d$ elements reserved
+ for each generated point before calling this method.
+ \end{tabb}
+\begin{htmlonly}
+ \param{v}{array in which the variates will be stored}
+ \param{start}{starting index, in \texttt{v}, of the new variates}
+ \param{n}{number of variates to generate}
+\end{htmlonly}
+\begin{code}
+
+ public int getDimension()\begin{hide} {
+ return dimension;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the dimension of this multivariate generator
+ (the dimension of the random points).
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ if (null != gen1)
+ return gen1.getStream();
+ return stream;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the \class{RandomStream} used by this object.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the stream associated to this object}
+\end{htmlonly}
+\begin{code}
+
+ public void setStream (RandomStream stream)\begin{hide} {
+ if (null != gen1)
+ gen1.setStream(stream);
+ else
+ this.stream = stream;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the \class{RandomStream} used by this object to \texttt{stream}.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/guiderandvarmulti.bbl b/source/umontreal/iro/lecuyer/randvarmulti/guiderandvarmulti.bbl
new file mode 100644
index 0000000..82a2fbd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/guiderandvarmulti.bbl
@@ -0,0 +1,9 @@
+\begin{thebibliography}{1}
+
+\bibitem{tJOH72a}
+N.~L. Johnson and S.~Kotz.
+\newblock {\em Distributions in Statistics: Continuous Multivariate
+ Distributions}.
+\newblock John Wiley, New York, NY, 1972.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/guiderandvarmulti.tex b/source/umontreal/iro/lecuyer/randvarmulti/guiderandvarmulti.tex
new file mode 100644
index 0000000..cd8817a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/guiderandvarmulti.tex
@@ -0,0 +1,67 @@
+\documentclass [12pt]{article}
+\usepackage{amssymb}
+%\usepackage{myarticle}
+%\usepackage{alltt}
+%\usepackage{html}
+%\usepackage{tcode}
+%begin{latexonly}
+%\usepackage{url}
+%end{latexonly}
+\usepackage{ssj}
+
+\mytwoheads
+
+\def\bX{\textbf{X}}
+\def\bZ{\textbf{Z}}
+\def\bA{\textbf{A}}
+\def\bI{\textbf{I}}
+\def\bmu{\boldmu}
+\def\bzero{\textbf{0}}
+\def\bSigma{\boldSigma}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+
+\title{randvarmulti}{Generating Random Vectors}
+
+This package is a multivariate version of the package \texttt{randvar}.
+It implements random number generators for some
+(nonuniform) multivariate distributions.
+
+
+\vfill
+\end{titlepage}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+
+\include{overview}
+
+%\include{general}
+\include{RandomMultivariateGen}
+\include{IIDMultivariateGen}
+%\include{RandomVariateGenInt}
+
+%%
+%\include{discrete}
+
+%%
+%\include{continuous}
+\include{MultinormalGen}
+\include{MultinormalCholeskyGen}
+\include{MultinormalPCAGen}
+\include{DirichletGen}
+
+
+\bibliography{simul,random,ift,stat,prob,math}
+\bibliographystyle{plain}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/randvarmulti/overview.tex b/source/umontreal/iro/lecuyer/randvarmulti/overview.tex
new file mode 100644
index 0000000..dda1a9a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/randvarmulti/overview.tex
@@ -0,0 +1,7 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+
+This package provides a collection of classes for non-uniform
+random variate generation, very similar to \texttt{randvar},
+but for multivariate distributions.
+
+% \`A compl\'eter.
diff --git a/source/umontreal/iro/lecuyer/rng/AntitheticStream.java b/source/umontreal/iro/lecuyer/rng/AntitheticStream.java
new file mode 100644
index 0000000..8bfa14b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/AntitheticStream.java
@@ -0,0 +1,146 @@
+
+
+/*
+ * Class: AntitheticStream
+ * Description: container class allows the user to force any RandomStream
+ to return antithetic variates
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+/**
+ * This container class allows the user to force any {@link RandomStream} to
+ * return antithetic variates. That is, {@link #nextDouble nextDouble} returns
+ * <SPAN CLASS="MATH">1 - <I>u</I></SPAN> instead of <SPAN CLASS="MATH"><I>u</I></SPAN> and the corresponding change is made in
+ * {@link #nextInt nextInt}.
+ * Any instance of this class behaves exactly like a {@link RandomStream},
+ * except that it depends on another random number generator stream, called the
+ * <SPAN CLASS="textit">base stream</SPAN>, to generate its numbers.
+ * Any call to one of the <TT>next...</TT>
+ * methods of this class will modify the state of the base stream.
+ *
+ */
+public class AntitheticStream implements RandomStream {
+
+ // The base stream.
+ private RandomStream st;
+
+
+
+
+ /**
+ * Constructs a new antithetic stream, using the random numbers
+ * from the base stream <TT>stream</TT>.
+ *
+ */
+ public AntitheticStream (RandomStream stream) {
+ this.st = stream;
+ }
+
+ public void resetStartStream() {
+ st.resetStartStream();
+ }
+
+ public void resetStartSubstream() {
+ st.resetStartSubstream();
+ }
+
+ public void resetNextSubstream() {
+ st.resetNextSubstream();
+ }
+
+ /**
+ * Returns a string starting with <TT>"Antithetic of "</TT>
+ * and finishing with the result of the call to the <TT>toString</TT>
+ * method of the generator.
+ *
+ */
+ public String toString() {
+ return "Antithetic of " + st.toString();
+ }
+
+
+ /**
+ * Returns <TT>1.0 - s.nextDouble()</TT> where <TT>s</TT> is the
+ * base stream.
+ *
+ */
+ public double nextDouble() {
+ return 1.0 - st.nextDouble();
+ }
+
+
+ /**
+ * Returns <TT>j - i - s.nextInt(i, j)</TT> where <TT>s</TT> is the
+ * base stream.
+ *
+ */
+ public int nextInt (int i, int j) {
+ // pas (j - st.nextInt(0,j-i)), au cas ou le resultat varie.
+ return j - i - st.nextInt(i, j);
+ }
+
+
+ /**
+ * Calls <TT>nextArrayOfDouble (u, start, n)</TT> for the base stream,
+ * then replaces each <TT>u[i]</TT> by <TT>1.0 - u[i]</TT>.
+ *
+ * @param u the array in which the numbers will be stored
+ *
+ * @param start the first index of <TT>u</TT> to be used
+ *
+ * @param n the number of random numbers to put in <TT>u</TT>
+ *
+ *
+ */
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ st.nextArrayOfDouble (u, start, n);
+ for (int ii = start; ii < start + n; ii++)
+ u[ii] = 1.0 - u[ii];
+ }
+
+
+ /**
+ * Calls <TT>nextArrayOfInt (i, j, u, start, n)</TT> for the base stream,
+ * then replaces each <TT>u[i]</TT> by <TT>j - i - u[i]</TT>.
+ *
+ * @param i the smallest possible integer to put in <TT>u</TT>
+ *
+ * @param j the largest possible integer to put in <TT>u</TT>
+ *
+ * @param u the array in which the numbers will be stored
+ *
+ * @param start the first index of <TT>u</TT> to be used
+ *
+ * @param n the number of random numbers to put in <TT>u</TT>
+ *
+ *
+ */
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ st.nextArrayOfInt (i, j, u, start, n);
+ for (int ii = start; ii < start + n; ii++)
+ u[ii] = j - i - u[ii];
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/AntitheticStream.tex b/source/umontreal/iro/lecuyer/rng/AntitheticStream.tex
new file mode 100644
index 0000000..333a1d6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/AntitheticStream.tex
@@ -0,0 +1,147 @@
+\defmodule {AntitheticStream}
+
+This container class allows the user to force any \class{RandomStream} to
+return antithetic variates. That is, \method{nextDouble}{} returns
+$1-u$ instead of $u$ and the corresponding change is made in
+\method{nextInt}{}.
+Any instance of this class behaves exactly like a \class{RandomStream},
+except that it depends on another random number generator stream, called the
+\emph{base stream}, to generate its numbers.
+Any call to one of the \texttt{next...}
+methods of this class will modify the state of the base stream.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: AntitheticStream
+ * Description: container class allows the user to force any RandomStream
+ to return antithetic variates
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;
+
+public class AntitheticStream implements RandomStream \begin{hide} {
+
+ // The base stream.
+ private RandomStream st;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public AntitheticStream (RandomStream stream) \begin{hide} {
+ this.st = stream;
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new antithetic stream, using the random numbers
+ from the base stream \texttt{stream}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+ public void resetStartStream() {
+ st.resetStartStream();
+ }
+
+ public void resetStartSubstream() {
+ st.resetStartSubstream();
+ }
+
+ public void resetNextSubstream() {
+ st.resetNextSubstream();
+ }\end{hide}
+
+ public String toString() \begin{hide} {
+ return "Antithetic of " + st.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a string starting with \texttt{"Antithetic of "}
+ and finishing with the result of the call to the \texttt{toString}
+ method of the generator.
+\end{tabb}
+\begin{code}
+
+ public double nextDouble() \begin{hide} {
+ return 1.0 - st.nextDouble();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns \texttt{1.0 - s.nextDouble()} where \texttt{s} is the
+ base stream.
+\end{tabb}
+\begin{code}
+
+ public int nextInt (int i, int j) \begin{hide} {
+ // pas (j - st.nextInt(0,j-i)), au cas ou le resultat varie.
+ return j - i - st.nextInt(i, j);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns \texttt{j - i - s.nextInt(i, j)} where \texttt{s} is the
+ base stream.
+\end{tabb}
+\begin{code}
+
+ public void nextArrayOfDouble (double[] u, int start, int n) \begin{hide} {
+ st.nextArrayOfDouble (u, start, n);
+ for (int ii = start; ii < start + n; ii++)
+ u[ii] = 1.0 - u[ii];
+ }\end{hide}
+\end{code}
+\begin{tabb} Calls \texttt{nextArrayOfDouble (u, start, n)} for the base stream,
+ then replaces each \texttt{u[i]} by \texttt{1.0 - u[i]}.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{the array in which the numbers will be stored}
+ \param{start}{the first index of \texttt{u} to be used}
+ \param{n}{the number of random numbers to put in \texttt{u}}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) \begin{hide} {
+ st.nextArrayOfInt (i, j, u, start, n);
+ for (int ii = start; ii < start + n; ii++)
+ u[ii] = j - i - u[ii];
+ }\end{hide}
+\end{code}
+\begin{tabb} Calls \texttt{nextArrayOfInt (i, j, u, start, n)} for the base stream,
+ then replaces each \texttt{u[i]} by \texttt{j - i - u[i]}.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the smallest possible integer to put in \texttt{u}}
+ \param{j}{the largest possible integer to put in \texttt{u}}
+ \param{u}{the array in which the numbers will be stored}
+ \param{start}{the first index of \texttt{u} to be used}
+ \param{n}{the number of random numbers to put in \texttt{u}}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/BakerTransformedStream.java b/source/umontreal/iro/lecuyer/rng/BakerTransformedStream.java
new file mode 100644
index 0000000..9401629
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/BakerTransformedStream.java
@@ -0,0 +1,155 @@
+
+
+/*
+ * Class: BakerTransformedStream
+ * Description: container class permits one to apply the baker's
+ transformation to the output of any RandomStream
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+/**
+ * This container class permits one to apply the baker's transformation to
+ * the output of any {@link RandomStream}.
+ * It transforms each
+ * <SPAN CLASS="MATH"><I>u</I>∈[0, 1]</SPAN> into <SPAN CLASS="MATH">2<I>u</I></SPAN> if <SPAN CLASS="MATH"><I>u</I> <= 1/2</SPAN> and <SPAN CLASS="MATH">2(1 - <I>u</I>)</SPAN>
+ * if <SPAN CLASS="MATH"><I>u</I> > 1/2</SPAN>.
+ * The {@link #nextDouble nextDouble} method will return the result of this transformation
+ * and the other <TT>next...</TT> methods are affected accordingly.
+ * Any instance of this class contains a {@link RandomStream} called its
+ * <SPAN CLASS="textit">base stream</SPAN>, used to generate its numbers and to which the
+ * transformation is applied.
+ * Any call to one of the <TT>next...</TT>
+ * methods of this class will modify the state of the base stream.
+ *
+ * <P>
+ * The baker transformation is often applied when the {@link RandomStream}
+ * is actually an iterator over a point set used for quasi-Monte Carlo
+ * integration (see the <TT>hups</TT> package).
+ *
+ */
+public class BakerTransformedStream implements RandomStream {
+
+ // The base stream.
+ private RandomStream st;
+
+
+
+
+ /**
+ * Constructs a new baker transformed stream, using the random
+ * numbers from the base stream <TT>stream</TT>.
+ *
+ */
+ public BakerTransformedStream (RandomStream stream) {
+ st = stream;
+ }
+
+ public void resetStartStream() {
+ st.resetStartStream();
+ }
+
+ public void resetStartSubstream() {
+ st.resetStartSubstream();
+ }
+
+ public void resetNextSubstream() {
+ st.resetNextSubstream();
+ }
+
+ /**
+ * Returns a string starting with <TT>"Baker transformation of "</TT>
+ * and finishing with the result of the call to the <TT>toString</TT>
+ * method of the generator.
+ *
+ */
+ public String toString() {
+ return "Baker transformation of " + st.toString();
+ }
+
+
+ /**
+ * Returns the baker transformation of <TT>s.nextDouble()</TT>
+ * where <TT>s</TT> is the base stream.
+ *
+ */
+ public double nextDouble() {
+ double u = st.nextDouble();
+ if (u > 0.5) return 2.0 * (1.0 - u);
+ else return u + u;
+ }
+
+
+ /**
+ * Generates a random integer in
+ * <SPAN CLASS="MATH">{<I>i</I>,..., <I>j</I>}</SPAN> via
+ * {@link #nextDouble nextDouble} (in which the baker transformation is applied).
+ *
+ */
+ public int nextInt (int i, int j) {
+ return i + (int)(nextDouble() * (j - i + 1.0));
+ }
+
+
+ /**
+ * Calls <TT>nextArrayOfDouble (u, start, n)</TT> for the base stream,
+ * then applies the baker transformation.
+ *
+ * @param u the array in which the numbers will be stored
+ *
+ * @param start the first index of <TT>u</TT> to be used
+ *
+ * @param n the number of random numbers to put in <TT>u</TT>
+ *
+ *
+ */
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ st.nextArrayOfDouble (u, start, n);
+ for (int i = start; i < start + n; i++)
+ if (u[i] > 0.5) u[i] = 2.0 * (1.0 - u[i]);
+ else u[i] += u[i];
+ }
+
+
+ /**
+ * Fills up the array by calling <TT>nextInt (i, j)</TT>.
+ *
+ * @param i the smallest possible integer to put in <TT>u</TT>
+ *
+ * @param j the largest possible integer to put in <TT>u</TT>
+ *
+ * @param u the array in which the numbers will be stored
+ *
+ * @param start the first index of <TT>u</TT> to be used
+ *
+ * @param n the number of random numbers to put in <TT>u</TT>
+ *
+ *
+ */
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ for(int ii = start; ii < start + n; ii++)
+ u[ii] = nextInt(i,j);
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/BakerTransformedStream.tex b/source/umontreal/iro/lecuyer/rng/BakerTransformedStream.tex
new file mode 100644
index 0000000..ae3c14a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/BakerTransformedStream.tex
@@ -0,0 +1,153 @@
+\defmodule {BakerTransformedStream}
+
+This container class permits one to apply the baker's transformation to
+the output of any \class{RandomStream}.
+It transforms each $u \in [0,1]$ into $2u$ if $u \le 1/2$ and $2(1-u)$
+if $u > 1/2$.
+The \method{nextDouble}{} method will return the result of this transformation
+and the other \texttt{next...} methods are affected accordingly.
+Any instance of this class contains a \class{RandomStream} called its
+\emph{base stream}, used to generate its numbers and to which the
+transformation is applied.
+Any call to one of the \texttt{next...}
+methods of this class will modify the state of the base stream.
+
+The baker transformation is often applied when the \class{RandomStream}
+is actually an iterator over a point set used for quasi-Monte Carlo
+integration (see the \texttt{hups} package).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BakerTransformedStream
+ * Description: container class permits one to apply the baker's
+ transformation to the output of any RandomStream
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;
+
+public class BakerTransformedStream implements RandomStream \begin{hide} {
+
+ // The base stream.
+ private RandomStream st;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public BakerTransformedStream (RandomStream stream) \begin{hide} {
+ st = stream;
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new baker transformed stream, using the random
+ numbers from the base stream \texttt{stream}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+ public void resetStartStream() {
+ st.resetStartStream();
+ }
+
+ public void resetStartSubstream() {
+ st.resetStartSubstream();
+ }
+
+ public void resetNextSubstream() {
+ st.resetNextSubstream();
+ }\end{hide}
+
+ public String toString() \begin{hide} {
+ return "Baker transformation of " + st.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a string starting with \texttt{"Baker transformation of "}
+ and finishing with the result of the call to the \texttt{toString}
+ method of the generator.
+\end{tabb}
+\begin{code}
+
+ public double nextDouble() \begin{hide} {
+ double u = st.nextDouble();
+ if (u > 0.5) return 2.0 * (1.0 - u);
+ else return u + u;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the baker transformation of \texttt{s.nextDouble()}
+ where \texttt{s} is the base stream.
+\end{tabb}
+\begin{code}
+
+ public int nextInt (int i, int j) \begin{hide} {
+ return i + (int)(nextDouble() * (j - i + 1.0));
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a random integer in $\{i,...,j\}$ via
+ \method{nextDouble}{} (in which the baker transformation is applied).
+\end{tabb}
+\begin{code}
+
+ public void nextArrayOfDouble (double[] u, int start, int n) \begin{hide} {
+ st.nextArrayOfDouble (u, start, n);
+ for (int i = start; i < start + n; i++)
+ if (u[i] > 0.5) u[i] = 2.0 * (1.0 - u[i]);
+ else u[i] += u[i];
+ }\end{hide}
+\end{code}
+\begin{tabb} Calls \texttt{nextArrayOfDouble (u, start, n)} for the base stream,
+ then applies the baker transformation.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{the array in which the numbers will be stored}
+ \param{start}{the first index of \texttt{u} to be used}
+ \param{n}{the number of random numbers to put in \texttt{u}}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) \begin{hide} {
+ for(int ii = start; ii < start + n; ii++)
+ u[ii] = nextInt(i,j);
+ }\end{hide}
+\end{code}
+\begin{tabb} Fills up the array by calling \texttt{nextInt (i, j)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the smallest possible integer to put in \texttt{u}}
+ \param{j}{the largest possible integer to put in \texttt{u}}
+ \param{u}{the array in which the numbers will be stored}
+ \param{start}{the first index of \texttt{u} to be used}
+ \param{n}{the number of random numbers to put in \texttt{u}}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/BasicRandomStreamFactory.java b/source/umontreal/iro/lecuyer/rng/BasicRandomStreamFactory.java
new file mode 100644
index 0000000..028ef6b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/BasicRandomStreamFactory.java
@@ -0,0 +1,147 @@
+
+
+/*
+ * Class: BasicRandomStreamFactory
+ * Description: basic random stream factory
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.rng.MRG32k3a;
+
+
+/**
+ * Represents a basic random stream factory that can constructs new
+ * instances of a given {@link RandomStream} implementation via the
+ * {@link #newInstance(()) newInstance} method.
+ * The class name of the implementation to be used must be passed to
+ * the constructor as a <TT>String</TT>, which must be the name of
+ * a nullary constructor of a {@link RandomStream} object
+ * (i.e., a constructor that has no parameters).
+ * The streams are constructed by the factory by reflection from this
+ * <TT>String</TT>.
+ *
+ */
+public class BasicRandomStreamFactory implements RandomStreamFactory {
+ private Class rsClass;
+
+
+ /**
+ * Constructs a new basic random stream factory with
+ * random stream class <TT>rsClass</TT>.
+ * The supplied class object must represent an implementation
+ * of {@link RandomStream} and must provide a nullary
+ * constructor. For example, to construct a factory
+ * producing {@link MRG32k3a} random streams, this constructor
+ * must be called with <TT>MRG33k3a.class</TT>.
+ *
+ * @param rsClass the random stream class being used.
+ *
+ * @exception NullPointerException if <TT>rsClass</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>rsClass</TT> does
+ * not represent an implementation of {@link RandomStream}, or
+ * does not provide a nullary constructor.
+ *
+ *
+ */
+ public BasicRandomStreamFactory (Class rsClass) {
+ checkRandomStreamClass (rsClass);
+ this.rsClass = rsClass;
+ }
+
+
+ /**
+ * Returns the random stream class associated with this
+ * object.
+ *
+ * @return the associated random stream class.
+ *
+ */
+ public Class getRandomStreamClass() {
+ return rsClass;
+ }
+
+
+ /**
+ * Sets the associated random stream class to
+ * <TT>rsClass</TT>.
+ * The supplied class object must represent an implementation
+ * of {@link RandomStream} and must provide a nullary
+ * constructor.
+ *
+ * @param rsClass the random stream class being used.
+ *
+ * @exception NullPointerException if <TT>rsClass</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>rsClass</TT> does
+ * not represent an implementation of {@link RandomStream}, or
+ * does not provide a nullary constructor.
+ *
+ *
+ */
+ public void setRandomStreamClass (Class rsClass) {
+ checkRandomStreamClass (rsClass);
+ this.rsClass = rsClass;
+ }
+
+
+ private void checkRandomStreamClass (final Class rsClass) {
+ if (!RandomStream.class.isAssignableFrom (rsClass))
+ throw new IllegalArgumentException
+ ("The random class must implement the RandomStream interface");
+ try {
+ rsClass.getConstructor (new Class[0]);
+ }
+ catch (NoSuchMethodException nme) {
+ throw new IllegalArgumentException
+ ("The random stream class " + rsClass.getName() + " does not have a " +
+ "nullary public constructor.");
+ }
+ }
+
+ public RandomStream newInstance() {
+ try {
+ return (RandomStream)rsClass.newInstance();
+ }
+ catch (IllegalAccessException iae) {
+ throw new RandomStreamInstantiationException
+ (this, "Cannot access constructor for random stream class " + rsClass.getName(),
+ iae);
+ }
+ catch (InstantiationException ie) {
+ throw new RandomStreamInstantiationException
+ (this, "Cannot instantiate random stream class "
+ + rsClass.getName(), ie);
+ }
+ catch (Exception e) {
+ throw new RandomStreamInstantiationException
+ (this, "Exception while calling the nullary constructor for random stream class "
+ + rsClass.getName(), e);
+ }
+ }
+
+ public String toString() {
+ return "Basic random stream factory constructing streams of class " + rsClass.getName();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/BasicRandomStreamFactory.tex b/source/umontreal/iro/lecuyer/rng/BasicRandomStreamFactory.tex
new file mode 100644
index 0000000..9afe17c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/BasicRandomStreamFactory.tex
@@ -0,0 +1,145 @@
+\defmodule{BasicRandomStreamFactory}
+
+Represents a basic random stream factory that can constructs new
+instances of a given \class{RandomStream} implementation via the
+\method{newInstance}{()} method.
+The class name of the implementation to be used must be passed to
+the constructor as a \texttt{String}, which must be the name of
+a nullary constructor of a \class{RandomStream} object
+(i.e., a constructor that has no parameters).
+The streams are constructed by the factory by reflection from this
+\texttt{String}.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BasicRandomStreamFactory
+ * Description: basic random stream factory
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.rng.MRG32k3a;
+\end{hide}
+
+public class BasicRandomStreamFactory implements RandomStreamFactory\begin{hide} {
+ private Class rsClass;
+\end{hide}
+
+ public BasicRandomStreamFactory (Class rsClass)\begin{hide} {
+ checkRandomStreamClass (rsClass);
+ this.rsClass = rsClass;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new basic random stream factory with
+ random stream class \texttt{rsClass}.
+ The supplied class object must represent an implementation
+ of \class{RandomStream} and must provide a nullary
+ constructor. For example, to construct a factory
+ producing \class{MRG32k3a} random streams, this constructor
+ must be called with \texttt{MRG33k3a.class}.
+\end{tabb}
+\begin{htmlonly}
+ \param{rsClass}{the random stream class being used.}
+ \exception{NullPointerException}{if \texttt{rsClass} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{rsClass} does
+ not represent an implementation of \class{RandomStream}, or
+ does not provide a nullary constructor.}
+\end{htmlonly}
+\begin{code}
+
+ public Class getRandomStreamClass()\begin{hide} {
+ return rsClass;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the random stream class associated with this
+ object.
+\end{tabb}
+\begin{htmlonly}
+ \return{the associated random stream class.}
+\end{htmlonly}
+\begin{code}
+
+ public void setRandomStreamClass (Class rsClass)\begin{hide} {
+ checkRandomStreamClass (rsClass);
+ this.rsClass = rsClass;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the associated random stream class to
+ \texttt{rsClass}.
+ The supplied class object must represent an implementation
+ of \class{RandomStream} and must provide a nullary
+ constructor.
+\end{tabb}
+\begin{htmlonly}
+ \param{rsClass}{the random stream class being used.}
+ \exception{NullPointerException}{if \texttt{rsClass} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{rsClass} does
+ not represent an implementation of \class{RandomStream}, or
+ does not provide a nullary constructor.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private void checkRandomStreamClass (final Class rsClass) {
+ if (!RandomStream.class.isAssignableFrom (rsClass))
+ throw new IllegalArgumentException
+ ("The random class must implement the RandomStream interface");
+ try {
+ rsClass.getConstructor (new Class[0]);
+ }
+ catch (NoSuchMethodException nme) {
+ throw new IllegalArgumentException
+ ("The random stream class " + rsClass.getName() + " does not have a " +
+ "nullary public constructor.");
+ }
+ }
+
+ public RandomStream newInstance() {
+ try {
+ return (RandomStream)rsClass.newInstance();
+ }
+ catch (IllegalAccessException iae) {
+ throw new RandomStreamInstantiationException
+ (this, "Cannot access constructor for random stream class " + rsClass.getName(),
+ iae);
+ }
+ catch (InstantiationException ie) {
+ throw new RandomStreamInstantiationException
+ (this, "Cannot instantiate random stream class "
+ + rsClass.getName(), ie);
+ }
+ catch (Exception e) {
+ throw new RandomStreamInstantiationException
+ (this, "Exception while calling the nullary constructor for random stream class "
+ + rsClass.getName(), e);
+ }
+ }
+
+ public String toString() {
+ return "Basic random stream factory constructing streams of class " + rsClass.getName();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/CloneableRandomStream.java b/source/umontreal/iro/lecuyer/rng/CloneableRandomStream.java
new file mode 100644
index 0000000..8b76472
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/CloneableRandomStream.java
@@ -0,0 +1,52 @@
+
+
+/*
+ * Class: CloneableRandomStream
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+/**
+ * {@link CloneableRandomStream} extends {@link RandomStream} and {@link Cloneable}.
+ * All classes that implements this interface are able to produce cloned objects.
+ *
+ * <P>
+ * The cloned object is entirely independent of the older odject.
+ * Moreover the cloned object has all the same properties as the older one.
+ * All his seeds are duplicated, and therefore both generators will produce the
+ * same random number sequence.
+ *
+ */
+public interface CloneableRandomStream extends RandomStream, Cloneable {
+
+
+ /**
+ * Clones the current object and returns its copy.
+ *
+ * @return A deep copy of the current object
+ *
+ */
+ public CloneableRandomStream clone();
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/CloneableRandomStream.tex b/source/umontreal/iro/lecuyer/rng/CloneableRandomStream.tex
new file mode 100644
index 0000000..bd767b7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/CloneableRandomStream.tex
@@ -0,0 +1,60 @@
+\defmodule {CloneableRandomStream}
+
+\class{CloneableRandomStream} extends \class{RandomStream} and \class{Cloneable}.
+All classes that implements this interface are able to produce cloned objects.
+
+The cloned object is entirely independent of the older odject.
+Moreover the cloned object has all the same properties as the older one.
+All his seeds are duplicated, and therefore both generators will produce the
+same random number sequence.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: CloneableRandomStream
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;
+
+public interface CloneableRandomStream extends RandomStream, Cloneable \begin{hide} { \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public CloneableRandomStream clone();
+\end{code}
+ \begin{tabb} Clones the current object and returns its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current object}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/F2NL607.java b/source/umontreal/iro/lecuyer/rng/F2NL607.java
new file mode 100644
index 0000000..aa895d3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/F2NL607.java
@@ -0,0 +1,577 @@
+
+
+/*
+ * Class: F2NL607
+ * Description: generator: combination of the WELL607 proposed by
+ Panneton with a nonlinear generator.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+import java.util.Random;
+
+
+/**
+ * Implements the {@link RandomStream} interface by using as a backbone
+ * generator the combination of the WELL607 proposed in
+ * (and implemented in {@link WELL607}) with a nonlinear generator.
+ * This nonlinear generator is made up of a small number of components (say <SPAN CLASS="MATH"><I>n</I></SPAN>)
+ * combined via addition modulo 1. Each component contains an array already
+ * filled with a ``random'' permutation of
+ * <SPAN CLASS="MATH">{0,..., <I>s</I> - 1}</SPAN> where <TT>s</TT> is the
+ * size of the array.
+ * These numbers and the lengths of the components can be changed by the user.
+ * Each call to the generator uses the next number in each array
+ * (or the first one if we are at the end of the array). By default,
+ * there are 3 components of lengths 1019, 1021, and 1031, respectively.
+ * The non-linear generator is combined with the WELL using a bitwise XOR operation.
+ * This ensures that the new generator has at least as much equidistribution
+ * as the WELL607, as shown in.
+ *
+ * <P>
+ * The combined generator has a period length of approximatively
+ * <SPAN CLASS="MATH">2<SUP>637</SUP></SPAN>. The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN>
+ * are <SPAN CLASS="MATH">2<SUP>250</SUP></SPAN>,
+ * <SPAN CLASS="MATH">2<SUP>150</SUP></SPAN>, and <SPAN CLASS="MATH">2<SUP>400</SUP></SPAN>, respectively (see {@link RandomStream} for their
+ * definition). The seed of the RNG has two part: the linear part is a
+ * 19-dimensional vector of 32-bit integers, while the nonlinear part is
+ * made up of a <SPAN CLASS="MATH"><I>n</I></SPAN>-dimensional vector of indices, representing the position
+ * of the generator in each array of the nonlinear components.
+ *
+ */
+public class F2NL607 extends WELL607base {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //stream variable for the WELL607 part
+ private static int[] curr_stream = {0xD6AFB71C, 0x82ADB18E, 0x326E714E,
+ 0xB1EE42B6, 0xF1A834ED, 0x04AE5721,
+ 0xC5EA2843, 0xFA04116B, 0x6ACE14EF,
+ 0xCD5781A0, 0x6B1F731C, 0x7E3B8E3D,
+ 0x8B34DE2A, 0x74EC15F5, 0x84EBC216,
+ 0x83EA2C61, 0xE4A83B1E, 0xA5D82CB9,
+ 0x9E1A6C89};
+
+ //data for the non-linear part
+ private static int[][] nlData;
+ private static int[] nlJumpW, nlJumpZ;
+ private int[] nlState;
+
+ //stream and substream variables for the non-linear part
+ private int[] nlStream;
+ private int[] nlSubstream;
+ private static int[] curr_nlStream;
+
+ //set to true when a instance of F2NL607 is constructed
+ private static boolean constructed = false;
+
+ //initialisation of the generator
+ static
+ {
+ curr_nlStream = new int[]{ 0, 0, 0};
+
+ nlData = new int[][]{ new int[1019],
+ new int[1021],
+ new int[1031]};
+
+ Random rand = new Random(0);
+
+ for(int i = 0; i < nlData.length; i++) {
+ for(int j = 0; j < nlData[i].length; j++)
+ nlData[i][j] = (int)((((long)j) << 32) / nlData[i].length);
+ scramble(nlData[i], rand);
+
+ /*
+ for(int j = 0; j < nlData[i].length; j++)
+ if(nlData[i][j] >= 0)
+ System.out.print(nlData[i][j] + "U, ");
+ else
+ System.out.print(((long)nlData[i][j] + 0x100000000L) + "U, ");
+ System.out.println(PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ */
+ }
+
+ //computing the jumps length
+ //A FAIRE EN PRE-CALCUL
+ nlJumpW = new int[3];
+ nlJumpZ = new int[3];
+ for(int i = 0; i < nlJumpW.length; i++) {
+ int temp = 1;
+ for(int j = 0; j < w; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpW[i] = temp;
+ for(int j = 0; j < v; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpZ[i] = temp;
+ }
+ }
+
+ private static void scramble(int[] data, Random rand) {
+ int buffer;
+ int j;
+
+ for(int i = 0; i < data.length - 1; i++) {
+ //choose a random number between i and data.length - 1
+ j = (int)(rand.nextDouble() * (data.length - i)) + i;
+ //exchange data[i] and data[j]
+ buffer = data[i];
+ data[i] = data[j];
+ data[j] = buffer;
+ }
+ }
+
+ private static void scramble(int[] data, RandomStream rand) {
+ int buffer;
+ int j;
+
+ for(int i = 0; i < data.length - 1; i++) {
+ //choose a random number between i and data.length - 1
+ j = rand.nextInt(i, data.length - 1);
+ //exchange data[i] and data[j]
+ buffer = data[i];
+ data[i] = data[j];
+ data[j] = buffer;
+ }
+ }
+
+
+ /**
+ * Constructs a new stream, initializing it at its beginning.
+ * Also makes sure that the seed of the next constructed stream is
+ * <SPAN CLASS="MATH"><I>Z</I></SPAN> steps away. Sets its antithetic switch to <TT>false</TT> and sets
+ * the stream to normal precision mode (offers 32 bits of precision).
+ *
+ */
+ public F2NL607() {
+ //linear part
+ constructed = true;
+
+ state = new int[BUFFER_SIZE];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+// advanceSeed(curr_stream, Apz);
+ advanceSeed(curr_stream, WELL607.pz);
+
+ //non-linear part
+ nlState = new int[nlData.length];
+ nlStream = new int[nlData.length];
+ nlSubstream = new int[nlData.length];
+
+ for(int i = 0; i < nlData.length; i++) {
+ nlStream[i] = curr_nlStream[i];
+ curr_nlStream[i] += nlJumpZ[i];
+ }
+
+ resetStartStream();
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used in the <TT>toString</TT> method).
+ *
+ * @param name name of the stream
+ *
+ *
+ */
+ public F2NL607 (String name) {
+ this();
+ this.name = name;
+ }
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ for(int i = 0; i < nlSubstream.length; i++)
+ nlSubstream[i] = nlStream[i];
+
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ for(int i = 0; i < nlState.length; i++)
+ nlState[i] = nlSubstream[i];
+ }
+
+ public void resetNextSubstream() {
+// advanceSeed(substream, WELL607.Apw);
+ advanceSeed(substream, WELL607.pw);
+ for(int i = 0; i < nlState.length; i++)
+ nlSubstream[i] = (nlSubstream[i] + nlJumpW[i]) %
+ nlData[i].length;
+
+ resetStartSubstream();
+ }
+
+ /**
+ * Sets the initial seed of the linear part of the class
+ * <TT>F2NL607</TT> to the 19 integers of the vector <TT>seed[0..18]</TT>.
+ * This will be the initial seed (or seed) of the next created stream.
+ * At least one of the integers must be non-zero and if this integer is
+ * the last one, it must not be equal to <TT>0x7FFFFFFF</TT>.
+ *
+ * @param seed array of 19 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageLinearSeed (int seed[]) {
+ verifySeed(seed);
+
+ for(int i = 0; i < R; i++)
+ curr_stream[i] = seed[i];
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial linear
+ * seed <TT>seed[0..18]</TT>. The seed must satisfy the same conditions as
+ * in <TT>setPackageSeed</TT>.
+ * The non-linear seed is not modified; thus the non-linear part of the random
+ * number generator is reset to the beginning of the old stream.
+ * This method only affects the specified stream; the others are not
+ * modified. Hence after calling this method, the beginning of the streams
+ * will no longer be spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very exceptional cases;
+ * proper use of the <TT>reset...</TT> methods and of the stream constructor is
+ * preferable.
+ *
+ * @param seed array of 19 elements representing the seed
+ *
+ *
+ */
+ public void setLinearSeed (int seed[]) {
+ verifySeed(seed);
+
+ for(int i = 0; i < R; i++)
+ stream[i] = seed[i];
+
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the linear part of the stream,
+ * represented as an array of 19 integers.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public int[] getLinearState() {
+ return getState();
+ }
+
+
+ /**
+ * Sets the non-linear part of the initial seed of the class
+ * <TT>F2NL607</TT> to the <SPAN CLASS="MATH"><I>n</I></SPAN> integers of the vector <TT>seed[0..n-1]</TT>,
+ * where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number of components of the non-linear part. The
+ * default is <SPAN CLASS="MATH"><I>n</I> = 3</SPAN>.
+ * Each of the integers must be between 0 and the length of the corresponding
+ * component minus one. By default, the lengths are
+ * <SPAN CLASS="MATH">(1019, 1021, 1031)</SPAN>.
+ *
+ * @param seed array of <SPAN CLASS="MATH"><I>n</I></SPAN> elements representing the non-linear seed
+ *
+ *
+ */
+ public static void setPackageNonLinearSeed (int seed[]) {
+ if (seed.length < nlData.length)
+ throw new IllegalArgumentException("Seed must contain " +
+ nlData.length + " values");
+ for (int i = 0; i < nlData.length; i++)
+ if (seed[i] < 0 || seed[i] >= nlData[i].length)
+ throw new IllegalArgumentException("Seed number " + i +
+ " must be between 0 and " +
+ (nlData[i].length - 1));
+
+ for(int i = 0; i < nlData.length; i++)
+ curr_nlStream[i] = seed[i];
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * non-linear seed <TT>seed[0..n-1]</TT>, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number of components
+ * of the non-linear part of the generator.
+ * The linear seed is not modified so the linear part of the random number
+ * generator is reset to the beginning of the old stream.
+ * This method only affects the specified stream; the others are not
+ * modified. Hence after calling this method, the beginning of the streams
+ * will no longer be spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very exceptional cases;
+ * proper use of the <TT>reset...</TT> methods and of the stream constructor is
+ * preferable.
+ *
+ * @param seed
+ *
+ *
+ */
+ public void setNonLinearSeed (int seed[]) {
+ if(seed.length != nlData.length)
+ throw new IllegalArgumentException("Seed must contain " +
+ nlData.length + " values");
+ for(int i = 0; i < nlData.length; i++)
+ if(seed[i] < 0 || seed[i] >= nlData[i].length)
+ throw new IllegalArgumentException("Seed number " + i +
+ " must be between 0 and " +
+ (nlData[i].length - 1));
+
+ for(int i = 0; i < nlData.length; i++)
+ nlStream[i] = seed[i];
+
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the non-linear part of the stream,
+ * represented as an array of <SPAN CLASS="MATH"><I>n</I></SPAN> integers, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number
+ * of components in the non-linear generator.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public int[] getNonLinearState() {
+ //we must prevent the user to change the state, so we give him a copy
+ int[] state = new int[nlState.length];
+ for(int i = 0; i < nlState.length; i++)
+ state[i] = nlState[i];
+ return state;
+ }
+
+
+ /**
+ * Return the data of all the components of the non-linear
+ * part of the random number generator. This data is explained in the
+ * introduction.
+ *
+ * @return the data of the components
+ *
+ */
+ public static int[][] getNonLinearData() {
+ //we must prevent the user to change the data, so we give him a copy
+ int[][] data = new int[nlData.length][];
+ for(int i = 0; i < nlData.length; i++) {
+ data[i] = new int[nlData[i].length];
+ for(int j = 0; j < nlData[i].length; j++)
+ data[i][j] = nlData[i][j];
+ }
+ return data;
+ }
+
+
+ /**
+ * Selects new data for the components of the non-linear generator.
+ * The number of arrays in <TT>data</TT> will decide the number of components.
+ * Each of the arrays will be assigned to one of the components. The period
+ * of the resulting non-linear generator will be equal to the lowest common
+ * multiple of the lengths of the arrays. It is thus recommended to choose
+ * only prime length for the best results.
+ *
+ * <P>
+ * NOTE : This method cannot be called if at least one instance of
+ * <TT>F2NL607</TT> has been constructed. In that case, it will throw
+ * an {@link IllegalStateException}.
+ *
+ * @param data the new data of the components
+ *
+ * @exception IllegalStateException if an instance of the class was
+ * constructed before
+ *
+ *
+ */
+ public static void setNonLinearData (int[][] data) {
+ if(constructed)
+ throw new IllegalStateException("setNonLinearData can only be " +
+ "called before the creation of " +
+ "any F2NL607");
+
+ nlData = new int[data.length][];
+ curr_nlStream = new int[data.length];
+ for(int i = 0; i < data.length; i++) {
+ nlData[i] = new int[data[i].length];
+ for(int j = 0; j < data[i].length; j++)
+ nlData[i][j] = data[i][j];
+ curr_nlStream[i] = 0;
+ }
+
+ //recomputing the jumps length (an inefficient method is used)
+ nlJumpW = new int[data.length];
+ nlJumpZ = new int[data.length];
+ for(int i = 0; i < nlJumpW.length; i++) {
+ int temp = 1;
+ for(int j = 0; j < w; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpW[i] = temp;
+ for(int j = 0; j < v; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpZ[i] = temp;
+ }
+ }
+
+
+ /**
+ * Selects new data for the components of the non-linear generator.
+ * The number of arrays in <TT>data</TT> will decide the number of components.
+ * Each of the arrays will be assigned to one of the components. The period
+ * of the resulting non-linear generator will be equal to the lowest common
+ * multiple of the lengths of the arrays. It is thus recommended to choose
+ * only prime length for the best results.
+ *
+ * <P>
+ * NOTE : This method cannot be called if at least one instance of
+ * <TT>F2NL607</TT> has been constructed. In that case, it will throw
+ * an {@link IllegalStateException}.
+ *
+ * @param rand the random numbers source to do the scrambling
+ *
+ * @param steps number of time to do the scrambling
+ *
+ * @param size size of each components
+ *
+ * @exception IllegalStateException if an instance of the class was
+ * constructed before
+ *
+ *
+ */
+ public static void setScrambleData (RandomStream rand, int steps,
+ int[] size) {
+ if (constructed)
+ throw new IllegalStateException("setScrambleData can only be " +
+ "called before the creation of " +
+ "any F2NL607");
+
+ curr_nlStream = new int[size.length];
+ for(int i = 0; i < size.length; i++)
+ curr_nlStream[i] = 0;
+
+ nlData = new int[size.length][];
+ for(int i = 0; i < size.length; i++)
+ nlData[i] = new int[size[i]];
+
+ for(int i = 0; i < nlData.length; i++) {
+ for(int j = 0; j < nlData[i].length; j++)
+ nlData[i][j] = (int)((((long)j) << 32) / nlData[i].length);
+ for(int j = 0; j < steps; j++)
+ scramble(nlData[i], rand);
+ }
+
+ //computing the jumps length
+ //inefficient algorithm alert!
+ nlJumpW = new int[size.length];
+ nlJumpZ = new int[size.length];
+ for(int i = 0; i < nlJumpW.length; i++) {
+ int temp = 1;
+ for(int j = 0; j < w; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpW[i] = temp;
+ for(int j = 0; j < v; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpZ[i] = temp;
+ }
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public F2NL607 clone() {
+ F2NL607 retour = null;
+ retour = (F2NL607)super.clone();
+ retour.state = new int[BUFFER_SIZE];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+ retour.nlState = new int[nlData.length];
+ retour.nlStream = new int[nlData.length];
+ retour.nlSubstream = new int[nlData.length];
+
+ for (int i = 0; i<R; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ for (int i = 0; i<BUFFER_SIZE; i++) {
+ retour.state[i] = state[i];
+ }
+ for (int i = 0; i<nlData.length; i++) {
+ retour.nlState[i] = nlState[i];
+ retour.nlStream[i] = nlStream[i];
+ retour.nlSubstream[i] = nlSubstream[i];
+ }
+ return retour;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ if(name == null)
+ sb.append("The state of this F2NL607 is : " + PrintfFormat.NEWLINE);
+ else
+ sb.append("The state of " + name + " is : " + PrintfFormat.NEWLINE);
+ sb.append(" Linear part : { ");
+ sb.append(super.stringState());
+ sb.append(PrintfFormat.NEWLINE + " Non-linear part : { ");
+ for(int i = 0; i < nlState.length; i++)
+ sb.append(nlState[i] + " ");
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+
+
+ protected double nextValue() {
+ for (int i = 0; i < nlData.length; i++)
+ if (nlState[i] >= nlData[i].length - 1)
+ nlState[i] = 0;
+ else
+ nlState[i]++;
+
+ int nonLin = 0;
+ for (int i = 0; i < nlData.length; i++)
+ nonLin += nlData[i][nlState[i]];
+
+ long result = (nextInt() ^ nonLin);
+ if(result <= 0)
+ result += 0x100000000L;
+ return result * NORM;
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/F2NL607.tex b/source/umontreal/iro/lecuyer/rng/F2NL607.tex
new file mode 100644
index 0000000..4ce70bd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/F2NL607.tex
@@ -0,0 +1,580 @@
+\defmodule {F2NL607}
+
+Implements the \class{RandomStream} interface by using as a backbone
+generator the combination of the WELL607 proposed in \cite{rPAN04t,rPAN06b}
+(and implemented in \class{WELL607}) with a nonlinear generator.
+This nonlinear generator is made up of a small number of components (say $n$)
+combined via addition modulo 1. Each component contains an array already
+filled with a ``random'' permutation of $\{0,...,s-1\}$ where \texttt{s} is the
+size of the array.
+These numbers and the lengths of the components can be changed by the user.
+Each call to the generator uses the next number in each array
+(or the first one if we are at the end of the array). By default,
+there are 3 components of lengths 1019, 1021, and 1031, respectively.
+The non-linear generator is combined with the WELL using a bitwise XOR operation.
+This ensures that the new generator has at least as much equidistribution
+as the WELL607, as shown in \cite{rLEC03c}.
+
+The combined generator has a period length of \html{approximatively}
+\latex{$\rho\approx$ } $2^{637}$. The values of $V$, $W$ and $Z$
+ are $2^{250}$,
+$2^{150}$, and $2^{400}$, respectively (see \class{RandomStream} for their
+definition). The seed of the RNG has two part: the linear part is a
+19-dimensional vector of 32-bit integers, while the nonlinear part is
+made up of a $n$-dimensional vector of indices, representing the position
+of the generator in each array of the nonlinear components.
+%The default initial seed of the linear part is $(0xD6AFB71C,$ $ 0x82ADB18E,$ $
+%0x326E714E,$ $ 0xB1EE42B6,$ $ 0xF1A834ED,$ $ 0x04AE5721,$ $ 0xC5EA2843,$ $
+%0xFA04116B,$ $ 0x6ACE14EF,$ $ 0xCD5781A0,$ $ 0x6B1F731C,$ $ 0x7E3B8E3D,$ $
+%0x8B34DE2A,$ $ 0x74EC15F5,$ $ 0x84EBC216,$ $ 0x83EA2C61,$ $ 0xE4A83B1E,$ $
+%0xA5D82CB9,$ $ 0x9E1A6C89)$,
+%while the default initial seed for the non-linear part is $(0, 0, 0)$.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: F2NL607
+ * Description: generator: combination of the WELL607 proposed by
+ Panneton with a nonlinear generator.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+import java.util.Random;
+\end{hide}
+
+public class F2NL607 \begin{hide} extends WELL607base {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //stream variable for the WELL607 part
+ private static int[] curr_stream = {0xD6AFB71C, 0x82ADB18E, 0x326E714E,
+ 0xB1EE42B6, 0xF1A834ED, 0x04AE5721,
+ 0xC5EA2843, 0xFA04116B, 0x6ACE14EF,
+ 0xCD5781A0, 0x6B1F731C, 0x7E3B8E3D,
+ 0x8B34DE2A, 0x74EC15F5, 0x84EBC216,
+ 0x83EA2C61, 0xE4A83B1E, 0xA5D82CB9,
+ 0x9E1A6C89};
+
+ //data for the non-linear part
+ private static int[][] nlData;
+ private static int[] nlJumpW, nlJumpZ;
+ private int[] nlState;
+
+ //stream and substream variables for the non-linear part
+ private int[] nlStream;
+ private int[] nlSubstream;
+ private static int[] curr_nlStream;
+
+ //set to true when a instance of F2NL607 is constructed
+ private static boolean constructed = false;
+
+ //initialisation of the generator
+ static
+ {
+ curr_nlStream = new int[]{ 0, 0, 0};
+
+ nlData = new int[][]{ new int[1019],
+ new int[1021],
+ new int[1031]};
+
+ Random rand = new Random(0);
+
+ for(int i = 0; i < nlData.length; i++) {
+ for(int j = 0; j < nlData[i].length; j++)
+ nlData[i][j] = (int)((((long)j) << 32) / nlData[i].length);
+ scramble(nlData[i], rand);
+
+ /*
+ for(int j = 0; j < nlData[i].length; j++)
+ if(nlData[i][j] >= 0)
+ System.out.print(nlData[i][j] + "U, ");
+ else
+ System.out.print(((long)nlData[i][j] + 0x100000000L) + "U, ");
+ System.out.println(PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ */
+ }
+
+ //computing the jumps length
+ //A FAIRE EN PRE-CALCUL
+ nlJumpW = new int[3];
+ nlJumpZ = new int[3];
+ for(int i = 0; i < nlJumpW.length; i++) {
+ int temp = 1;
+ for(int j = 0; j < w; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpW[i] = temp;
+ for(int j = 0; j < v; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpZ[i] = temp;
+ }
+ }
+
+ private static void scramble(int[] data, Random rand) {
+ int buffer;
+ int j;
+
+ for(int i = 0; i < data.length - 1; i++) {
+ //choose a random number between i and data.length - 1
+ j = (int)(rand.nextDouble() * (data.length - i)) + i;
+ //exchange data[i] and data[j]
+ buffer = data[i];
+ data[i] = data[j];
+ data[j] = buffer;
+ }
+ }
+
+ private static void scramble(int[] data, RandomStream rand) {
+ int buffer;
+ int j;
+
+ for(int i = 0; i < data.length - 1; i++) {
+ //choose a random number between i and data.length - 1
+ j = rand.nextInt(i, data.length - 1);
+ //exchange data[i] and data[j]
+ buffer = data[i];
+ data[i] = data[j];
+ data[j] = buffer;
+ }
+ }
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public F2NL607() \begin{hide} {
+ //linear part
+ constructed = true;
+
+ state = new int[BUFFER_SIZE];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+// advanceSeed(curr_stream, Apz);
+ advanceSeed(curr_stream, WELL607.pz);
+
+ //non-linear part
+ nlState = new int[nlData.length];
+ nlStream = new int[nlData.length];
+ nlSubstream = new int[nlData.length];
+
+ for(int i = 0; i < nlData.length; i++) {
+ nlStream[i] = curr_nlStream[i];
+ curr_nlStream[i] += nlJumpZ[i];
+ }
+
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream, initializing it at its beginning.
+ Also makes sure that the seed of the next constructed stream is
+ $Z$ steps away. Sets its antithetic switch to \texttt{false} and sets
+ the stream to normal precision mode (offers 32 bits of precision).
+\end{tabb}
+\begin{code}
+
+ public F2NL607 (String name) \begin{hide} {
+ this();
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used in the \texttt{toString} method).
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ for(int i = 0; i < nlSubstream.length; i++)
+ nlSubstream[i] = nlStream[i];
+
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ for(int i = 0; i < nlState.length; i++)
+ nlState[i] = nlSubstream[i];
+ }
+
+ public void resetNextSubstream() {
+// advanceSeed(substream, WELL607.Apw);
+ advanceSeed(substream, WELL607.pw);
+ for(int i = 0; i < nlState.length; i++)
+ nlSubstream[i] = (nlSubstream[i] + nlJumpW[i]) %
+ nlData[i].length;
+
+ resetStartSubstream();
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageLinearSeed (int seed[]) \begin{hide} {
+ verifySeed(seed);
+
+ for(int i = 0; i < R; i++)
+ curr_stream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed of the linear part of the class
+ \texttt{F2NL607} to the 19 integers of the vector \texttt{seed[0..18]}.
+ This will be the initial seed (or seed) of the next created stream.
+ At least one of the integers must be non-zero and if this integer is
+ the last one, it must not be equal to \texttt{0x7FFFFFFF}.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 19 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setLinearSeed (int seed[]) \begin{hide} {
+ verifySeed(seed);
+
+ for(int i = 0; i < R; i++)
+ stream[i] = seed[i];
+
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial linear
+ seed \texttt{seed[0..18]}. The seed must satisfy the same conditions as
+ in \texttt{setPackageSeed}.
+ The non-linear seed is not modified; thus the non-linear part of the random
+ number generator is reset to the beginning of the old stream.
+ This method only affects the specified stream; the others are not
+ modified. Hence after calling this method, the beginning of the streams
+ will no longer be spaced $Z$ values apart.
+ For this reason, this method should only be used in very exceptional cases;
+ proper use of the \texttt{reset...} methods and of the stream constructor is
+ preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 19 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getLinearState() \begin{hide} {
+ return getState();
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the linear part of the stream,
+ represented as an array of 19 integers.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}
+
+ public static void setPackageNonLinearSeed (int seed[]) \begin{hide} {
+ if (seed.length < nlData.length)
+ throw new IllegalArgumentException("Seed must contain " +
+ nlData.length + " values");
+ for (int i = 0; i < nlData.length; i++)
+ if (seed[i] < 0 || seed[i] >= nlData[i].length)
+ throw new IllegalArgumentException("Seed number " + i +
+ " must be between 0 and " +
+ (nlData[i].length - 1));
+
+ for(int i = 0; i < nlData.length; i++)
+ curr_nlStream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the non-linear part of the initial seed of the class
+ \texttt{F2NL607} to the $n$ integers of the vector \texttt{seed[0..n-1]},
+ where $n$ is the number of components of the non-linear part. The
+ default is $n = 3$.
+ Each of the integers must be between 0 and the length of the corresponding
+ component minus one. By default, the lengths are $(1019, 1021, 1031)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of $n$ elements representing the non-linear seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setNonLinearSeed (int seed[]) \begin{hide} {
+ if(seed.length != nlData.length)
+ throw new IllegalArgumentException("Seed must contain " +
+ nlData.length + " values");
+ for(int i = 0; i < nlData.length; i++)
+ if(seed[i] < 0 || seed[i] >= nlData[i].length)
+ throw new IllegalArgumentException("Seed number " + i +
+ " must be between 0 and " +
+ (nlData[i].length - 1));
+
+ for(int i = 0; i < nlData.length; i++)
+ nlStream[i] = seed[i];
+
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ non-linear seed \texttt{seed[0..n-1]}, where $n$ is the number of components
+ of the non-linear part of the generator.
+ The linear seed is not modified so the linear part of the random number
+ generator is reset to the beginning of the old stream.
+ This method only affects the specified stream; the others are not
+ modified. Hence after calling this method, the beginning of the streams
+ will no longer be spaced $Z$ values apart.
+ For this reason, this method should only be used in very exceptional cases;
+ proper use of the \texttt{reset...} methods and of the stream constructor is
+ preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getNonLinearState() \begin{hide} {
+ //we must prevent the user to change the state, so we give him a copy
+ int[] state = new int[nlState.length];
+ for(int i = 0; i < nlState.length; i++)
+ state[i] = nlState[i];
+ return state;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the non-linear part of the stream,
+ represented as an array of $n$ integers, where $n$ is the number
+ of components in the non-linear generator.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}
+
+ public static int[][] getNonLinearData() \begin{hide} {
+ //we must prevent the user to change the data, so we give him a copy
+ int[][] data = new int[nlData.length][];
+ for(int i = 0; i < nlData.length; i++) {
+ data[i] = new int[nlData[i].length];
+ for(int j = 0; j < nlData[i].length; j++)
+ data[i][j] = nlData[i][j];
+ }
+ return data;
+ } \end{hide}
+\end{code}
+\begin{tabb} Return the data of all the components of the non-linear
+ part of the random number generator. This data is explained in the
+ introduction.
+\end{tabb}
+\begin{htmlonly}
+ \return{the data of the components}
+\end{htmlonly}
+\begin{code}
+
+ public static void setNonLinearData (int[][] data) \begin{hide} {
+ if(constructed)
+ throw new IllegalStateException("setNonLinearData can only be " +
+ "called before the creation of " +
+ "any F2NL607");
+
+ nlData = new int[data.length][];
+ curr_nlStream = new int[data.length];
+ for(int i = 0; i < data.length; i++) {
+ nlData[i] = new int[data[i].length];
+ for(int j = 0; j < data[i].length; j++)
+ nlData[i][j] = data[i][j];
+ curr_nlStream[i] = 0;
+ }
+
+ //recomputing the jumps length (an inefficient method is used)
+ nlJumpW = new int[data.length];
+ nlJumpZ = new int[data.length];
+ for(int i = 0; i < nlJumpW.length; i++) {
+ int temp = 1;
+ for(int j = 0; j < w; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpW[i] = temp;
+ for(int j = 0; j < v; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpZ[i] = temp;
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Selects new data for the components of the non-linear generator.
+ The number of arrays in \texttt{data} will decide the number of components.
+ Each of the arrays will be assigned to one of the components. The period
+ of the resulting non-linear generator will be equal to the lowest common
+ multiple of the lengths of the arrays. It is thus recommended to choose
+ only prime length for the best results.
+
+ NOTE : This method cannot be called if at least one instance of
+ \texttt{F2NL607} has been constructed. In that case, it will throw
+ an \class{IllegalStateException}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{the new data of the components}
+ \exception{IllegalStateException}{if an instance of the class was
+ constructed before}
+\end{htmlonly}
+\begin{code}
+
+ public static void setScrambleData (RandomStream rand, int steps,
+ int[] size) \begin{hide} {
+ if (constructed)
+ throw new IllegalStateException("setScrambleData can only be " +
+ "called before the creation of " +
+ "any F2NL607");
+
+ curr_nlStream = new int[size.length];
+ for(int i = 0; i < size.length; i++)
+ curr_nlStream[i] = 0;
+
+ nlData = new int[size.length][];
+ for(int i = 0; i < size.length; i++)
+ nlData[i] = new int[size[i]];
+
+ for(int i = 0; i < nlData.length; i++) {
+ for(int j = 0; j < nlData[i].length; j++)
+ nlData[i][j] = (int)((((long)j) << 32) / nlData[i].length);
+ for(int j = 0; j < steps; j++)
+ scramble(nlData[i], rand);
+ }
+
+ //computing the jumps length
+ //inefficient algorithm alert!
+ nlJumpW = new int[size.length];
+ nlJumpZ = new int[size.length];
+ for(int i = 0; i < nlJumpW.length; i++) {
+ int temp = 1;
+ for(int j = 0; j < w; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpW[i] = temp;
+ for(int j = 0; j < v; j++)
+ temp = (2 * temp) % nlData[i].length;
+ nlJumpZ[i] = temp;
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Selects new data for the components of the non-linear generator.
+ The number of arrays in \texttt{data} will decide the number of components.
+ Each of the arrays will be assigned to one of the components. The period
+ of the resulting non-linear generator will be equal to the lowest common
+ multiple of the lengths of the arrays. It is thus recommended to choose
+ only prime length for the best results.
+
+ NOTE : This method cannot be called if at least one instance of
+ \texttt{F2NL607} has been constructed. In that case, it will throw
+ an \class{IllegalStateException}.
+\end{tabb}
+\begin{htmlonly}
+ \param{rand}{the random numbers source to do the scrambling}
+ \param{steps}{number of time to do the scrambling}
+ \param{size}{size of each components}
+ \exception{IllegalStateException}{if an instance of the class was
+ constructed before}
+\end{htmlonly}
+\begin{code}
+
+ public F2NL607 clone() \begin{hide} {
+ F2NL607 retour = null;
+ retour = (F2NL607)super.clone();
+ retour.state = new int[BUFFER_SIZE];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+ retour.nlState = new int[nlData.length];
+ retour.nlStream = new int[nlData.length];
+ retour.nlSubstream = new int[nlData.length];
+
+ for (int i = 0; i<R; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ for (int i = 0; i<BUFFER_SIZE; i++) {
+ retour.state[i] = state[i];
+ }
+ for (int i = 0; i<nlData.length; i++) {
+ retour.nlState[i] = nlState[i];
+ retour.nlStream[i] = nlStream[i];
+ retour.nlSubstream[i] = nlSubstream[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ if(name == null)
+ sb.append("The state of this F2NL607 is : " + PrintfFormat.NEWLINE);
+ else
+ sb.append("The state of " + name + " is : " + PrintfFormat.NEWLINE);
+ sb.append(" Linear part : { ");
+ sb.append(super.stringState());
+ sb.append(PrintfFormat.NEWLINE + " Non-linear part : { ");
+ for(int i = 0; i < nlState.length; i++)
+ sb.append(nlState[i] + " ");
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+
+
+ protected double nextValue() {
+ for (int i = 0; i < nlData.length; i++)
+ if (nlState[i] >= nlData[i].length - 1)
+ nlState[i] = 0;
+ else
+ nlState[i]++;
+
+ int nonLin = 0;
+ for (int i = 0; i < nlData.length; i++)
+ nonLin += nlData[i][nlState[i]];
+
+ long result = (nextInt() ^ nonLin);
+ if(result <= 0)
+ result += 0x100000000L;
+ return result * NORM;
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/F2wPoly.java b/source/umontreal/iro/lecuyer/rng/F2wPoly.java
new file mode 100644
index 0000000..2094bc6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/F2wPoly.java
@@ -0,0 +1,425 @@
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+/*
+ * Class: F2wPoly
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author:
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+
+class F2wPoly {
+ private int r; //dimension
+ private F2w.F2wElem coeff[]; //coefficients non-nuls
+ private int noCoeff[]; //position des coefficients non-nuls
+ private F2w f2wBase; //l'espace des F2w
+
+ private F2wPolyElem z_i[]; //resultats du calculs des z^i dans F2wPoly
+
+
+ /*
+ Constructeur :
+ Les coefficients coeff sont ceux du polynome P(z).
+ Ils doivent avoir ete cree par la base f2wBase.
+ */
+ public F2wPoly(int r, F2w.F2wElem coeff[], int noCoeff[], F2w f2wBase) {
+ this.r = r;
+
+ this.coeff = new F2w.F2wElem[coeff.length];
+ this.noCoeff = new int[noCoeff.length];
+ for(int i = 0; i < coeff.length; i++) {
+ this.coeff[i] = coeff[i];
+ this.noCoeff[i] = noCoeff[i];
+ }
+
+ this.f2wBase = f2wBase;
+
+
+ //initialisation des z (pre-calculs)
+ z_i = new F2wPolyElem[2 * r];
+ z_i[0] = new F2wPolyElem();
+ for(int i = 0; i < r; i++)
+ z_i[0].value[i].value = 0;
+ z_i[0].value[0].value = 0x80000000;
+
+ for(int i = 1; i < 2*r; i++)
+ z_i[i] = z_i[i-1].multiplyZ();
+ }
+
+
+ /*
+ Differentes methodes permettant d'acceder aux constructeurs de
+ F2wPolyElem (on ne peut y acceder directement puisque F2wPolyElem
+ a besoin de connaitre a quel F2wPoly il appartient).
+ */
+ public F2wPolyElem createElem() {
+ return new F2wPolyElem();
+ }
+ public F2wPolyElem createElem(F2wPolyElem that) {
+ return new F2wPolyElem(that);
+ }
+ public F2wPolyElem createElem(int[] value) {
+ return new F2wPolyElem(value);
+ }
+
+ /*
+ Retourne le F2wPolyElem qui est egal a au polynome "z"
+ */
+ public F2wPolyElem createZ() {
+ int[] val = new int[r];
+ for(int i = 0; i < r; i++)
+ val[i] = 0;
+ val[1] = 0x80000000;
+ return createElem(val);
+ }
+
+
+
+ /*
+ Represente l'espace (F2w) dont font partie les coefficients des
+ polynomes.
+
+ F2w est definie comme etant F2[zeta]\Q(zeta).
+
+ Note : la classe a ete concue pour que w = 32.
+ */
+ public static class F2w {
+ private int w; //dimension
+ private int modQ; //modulo
+
+ private F2wElem[] zeta_i;
+
+
+ public F2w(int modQ) {
+ this(32, modQ);
+ }
+
+ private F2w(int w, int modQ) {
+ this.w = w;
+ this.modQ = modQ;
+
+ // initialisation des zetas
+ zeta_i = new F2wElem[w];
+ zeta_i[0] = new F2wElem(0x80000000);
+ for(int i = 1; i < w; i++)
+ zeta_i[i] = zeta_i[i-1].multiplyZeta(2);
+ }
+
+
+ /*
+ Methodes permettant d'acceder aux constructeurs de F2wElem.
+
+
+ */
+ public F2wElem createElem() {
+ return new F2wElem();
+ }
+ public F2wElem createElem(F2wElem that) {
+ return new F2wElem(that);
+ }
+ public F2wElem createElem(int val) {
+ return new F2wElem(val);
+ }
+
+ public int getDim() {
+ return w;
+ }
+ public int getModulo() {
+ return modQ;
+ }
+
+
+ /*
+ Classe representant les elements de F2w.
+ Le bit le plus significatif (celui en 0x80000000) represente
+ zeta^0 et le moins significatif (celui en 0x00000001) represente
+ zeta^31. La multiplication par zeta se fait donc par un rigth-shift,
+ suivis d'un modulo si le resultat depasse zeta^32.
+ */
+ public class F2wElem {
+ private int value;
+
+ //constructeurs
+ private F2wElem() {
+ value = 0;
+ }
+ private F2wElem(F2wElem that) {
+ if(this.getBase() != that.getBase())
+ throw new IllegalArgumentException
+ ("The copied F2wElem must originate from the same F2w.");
+ this.value = that.value;
+ }
+ private F2wElem(int val) {
+ value = val;
+ }
+
+ public F2w getBase() {
+ return F2w.this;
+ }
+ public int getValue() {
+ return value;
+ }
+
+ // calcule this * zeta^k dans F2w
+ public F2wElem multiplyZeta(int k) {
+ int res = value;
+
+ if(k == 0)
+ return new F2wElem(res);
+ else {
+ for(int i = 0; i < k; i++)
+ if((1 & res) != 0)
+ res = (res >>> 1) ^ modQ;
+ else
+ res >>>= 1;
+ return new F2wElem(res);
+ }
+ }
+
+ // calcule this * that dans F2w
+ public F2wElem multiply(F2wElem that) {
+ if(this.getBase() != that.getBase())
+ throw new IllegalArgumentException
+ ("Both F2wElem must originate from the same F2w.");
+
+ int res = 0;
+ int verif = 1;
+
+ for(int i = w - 1; i >= 0; i--) {
+ if((that.value & verif) != 0)
+ res ^= this.multiplyZeta(i).value;
+ verif <<= 1;
+ }
+
+ return new F2wElem(res);
+ }
+
+ // calcule (this)^2 dans F2w
+ public F2wElem square() {
+ int res = 0;
+
+ for(int i = 0; i < w; i++)
+ if((value & (0x80000000 >>> i)) != 0)
+ res ^= zeta_i[i].value;
+
+ return new F2wElem(res);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ int temp = value;
+
+ for(int i = 0; i < 32; i++) {
+ sb.append((temp & 1) == 1 ? '1' : '0');
+ temp >>>= 1;
+ }
+
+ return sb.reverse().toString();
+ }
+ }
+
+ }
+
+
+
+ /*
+ Represente un polynome faisant partie de F2wPoly.
+
+ Le premier element de value est le coefficient de z^0, tandis que
+ le dernier est le coefficient de z^(r-1).
+ */
+ public class F2wPolyElem {
+ private F2w.F2wElem[] value;
+
+ private F2wPolyElem() {
+ value = new F2w.F2wElem[r];
+ for(int i = 0; i < r; i++)
+ value[i] = f2wBase.createElem();
+ }
+
+ private F2wPolyElem(F2wPolyElem that) {
+ if(this.getBase() != that.getBase())
+ throw new IllegalArgumentException
+ ("The copied F2wPolyElem must come from the same F2wPoly.");
+
+ value = new F2w.F2wElem[r];
+ for(int i = 0; i < r; i++)
+ this.value[i] = f2wBase.createElem(that.value[i]);
+ }
+
+ private F2wPolyElem(int[] value) {
+ if(r != value.length)
+ throw new IllegalArgumentException
+ ("Array length must be equal to r (" + r + ")");
+
+ this.value = new F2w.F2wElem[r];
+ for(int i = 0; i < r; i++)
+ this.value[i] = f2wBase.createElem(value[i]);
+ }
+
+
+ public F2wPoly getBase() {
+ return F2wPoly.this;
+ }
+
+
+ //multiplie par this par z^1 dans F2w[z]
+ public F2wPolyElem multiplyZ() {
+ F2wPolyElem res = new F2wPolyElem();
+
+ res.value[0].value = 0;
+ for(int i = 1; i < r; i++)
+ res.value[i].value = this.value[i-1].value;
+
+ for(int i = 0; i < noCoeff.length; i++)
+ res.value[noCoeff[i]].value ^=
+ this.value[r-1].multiply(coeff[i]).value;
+
+ return res;
+ }
+
+ //calcule this * that dans F2w[z]
+ public F2wPolyElem multiply(F2wPolyElem that) {
+ if(this.getBase() != that.getBase())
+ throw new IllegalArgumentException
+ ("Both F2wPolyElem must originate from the same F2wPoly.");
+
+ F2wPolyElem res = new F2wPolyElem();
+
+ for(int i = 0; i < r; i++)
+ for(int j = 0; j < r; j++) {
+ F2w.F2wElem temp = this.value[i].multiply(that.value[j]);
+ for(int k = 0; k < r; k++)
+ res.value[k].value ^=
+ z_i[i+j].value[k].multiply(temp).value;
+ }
+ return res;
+ }
+
+
+ //calcule this^(2^d)
+ public F2wPolyElem exponentiateBase2 (int d) {
+ F2wPolyElem res = new F2wPolyElem(this);
+ F2wPolyElem temp= new F2wPolyElem();
+
+ for (int i = 0; i < d; i++) {
+ for(int j = 0; j < r; j++)
+ temp.value[j].value = 0;
+
+ for(int j = 0; j < r; j++) {
+ F2w.F2wElem coeff = res.value[j].square();
+ for(int k = 0; k < r; k++)
+ temp.value[k].value ^=
+ z_i[2*j].value[k].multiply(coeff).value;
+ }
+
+ for(int j = 0; j < r; j++)
+ res.value[j].value = temp.value[j].value;
+ }
+
+ return res;
+ }
+
+
+ public void copyFrom(F2wPolyElem that) {
+ if(this.getBase() != that.getBase())
+ throw new IllegalArgumentException
+ ("Both F2wPolyElem must originate from the same F2wPoly.");
+
+ for(int i = 0; i < r; i++)
+ this.value[i].value = that.value[i].value;
+ }
+ public void copyFrom(int[] val) {
+ if(r != value.length)
+ throw new IllegalArgumentException
+ ("Array length must be equal to r (" + r + ")");
+
+ for(int i = 0; i < r; i++)
+ this.value[i].value = val[i];
+ }
+ public void copyTo(int[] val) {
+ if(r != value.length)
+ throw new IllegalArgumentException
+ ("Array length must be equal to r (" + r + ")");
+
+ for(int i = 0; i < r; i++)
+ val[i] = this.value[i].value;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer("{");
+
+ for(int i = 0; i < r - 1; i++)
+ sb.append(value[i].toString() + ", " +
+ PrintfFormat.NEWLINE + " ");
+ if(r > 0)
+ sb.append(value[r-1].toString());
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ }
+
+
+ public static void main(String[] args) {
+ F2w f1 = new F2w(32, 0x00010002);
+ F2w f2 = new F2w(32, 0x10204080);
+
+ F2w.F2wElem e1 = f1.createElem(0x12345678);
+ F2w.F2wElem e2 = f2.createElem(0x19414111);
+ F2w.F2wElem e3 = e1.multiply(e2);
+
+ /*
+ F2w f2w = new F2w(32, 0xFA4F9B3F);
+
+ F2wPoly poly = new F2wPoly(25,
+ new F2w.F2wElem[]{f2w.createElem(0xE6A68D20),
+ f2w.createElem(0x287AB842)},
+ new int[]{7,
+ 0},
+ f2w);
+
+ F2wPolyElem gen = poly.createElem
+ (new int[]{0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239,
+ 0x715FAD23, 0x24A590AD, 0x69E4B5EF, 0xBF456141,
+ 0x96BC1B7B, 0xA7BDF825, 0xC1DE75B7, 0x8858A9C9,
+ 0x2DA87693, 0xB657F9DD, 0xFFDC8A8F, 0x8121DA71,
+ 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
+ 0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1,
+ 0xA6B7AADB});
+
+
+ for(int i = 0; i < poly.z_i.length; i++)
+ {
+ System.out.println(i + " :");
+ System.out.println(poly.z_i[i]);
+ System.out.println();
+ }
+ */
+
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/GenF2w32.java b/source/umontreal/iro/lecuyer/rng/GenF2w32.java
new file mode 100644
index 0000000..4bc1d1d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/GenF2w32.java
@@ -0,0 +1,584 @@
+
+
+/*
+ * Class: GenF2w32
+ * Description: generator proposed by Panneton
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.BitVector;
+import umontreal.iro.lecuyer.util.BitMatrix;
+import java.io.*;
+
+/**
+ * Implements the {@link RandomStream} interface via inheritance
+ * from {@link RandomStreamBase}. The backbone generator is a Linear Congruential
+ * Generator (LCG) in the finite field
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2<SUP>w</SUP></SUB></SPAN> instead of
+ *
+ * <SPAN CLASS="MATH"><B>F</B><SUB>2</SUB></SPAN>.
+ * The implemented generator is the <TT>GenF2w2_32</TT> proposed by
+ * Panneton. Its state is 25 32-bit words and it has
+ * a period length of
+ * <SPAN CLASS="MATH">2<SUP>800</SUP> - 1</SPAN>.
+ * The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>200</SUP></SPAN>, <SPAN CLASS="MATH">2<SUP>300</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>500</SUP></SPAN>
+ * respectively (see {@link RandomStream} for their definition). The seed of the
+ * RNG, and the state of a stream at any given step, is a 25-dimensional vector
+ * of 32-bits integers.
+ * Its <TT>nextValue</TT> method returns numbers with 32 bits of precision.
+ *
+ */
+public class GenF2w32 extends RandomStreamBase {
+
+ private static final long serialVersionUID = 120307L;
+ //La date de modification a l'envers, lire 07/03/2012
+
+ //tas de constantes
+ private final static double NORM = 1.0 / (0x100000001L);
+
+ private final static int Z0 = 0x80000000;
+
+ private final static int W = 32;
+
+ private final static int R = 25;
+ private final static int T = 7;
+
+ private final static int W1 = 11, W2 = 11, W3 = 10;
+ private final static int Wsplit1 = W - W1;
+ private final static int Wsplit2 = Wsplit1 - W2;
+ private final static int MASK1 = 0xFFFFFFFF << Wsplit1;
+ private final static int MASK2 = (0xFFFFFFFF << Wsplit2) ^ MASK1;
+ private final static int MASK3 = 0xFFFFFFFF ^ MASK1 ^ MASK2;
+
+ private static int[] BrmT1, BrmT2, BrmT3;
+ private static int[] Br1, Br2, Br3;
+
+
+ private final static int BrmT = 0xE6A68D20;
+ private final static int Br = 0x287AB842;
+
+ private final static int modQ = 0xFA4F9B3F;
+
+ //stream variables
+ private final static int w = 300;
+ private final static int v = 200;
+ //private static F2wPoly.F2wPolyElem jumpW;
+ //private static F2wPoly.F2wPolyElem jumpZ;
+ private static BitMatrix Apw;
+ private static BitMatrix Apz;
+
+ //private static F2wPoly polyBase;
+
+ /*
+ private static F2wPoly.F2wPolyElem curr_stream;
+
+ private F2wPoly.F2wPolyElem stream;
+ private F2wPoly.F2wPolyElem substream;
+ */
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream;
+
+ //if the generator was initialised
+ private static boolean initialised = false;
+
+ private int[] state;
+ private int state_i;
+
+
+ static
+ {
+ initTables();
+
+ /*
+ F2wPoly.F2w f2wBase = new F2wPoly.F2w(modQ);
+
+ polyBase = new F2wPoly
+ (R, new F2wPoly.F2w.F2wElem[]{f2wBase.createElem(Br),
+ f2wBase.createElem(BrmT)},
+ new int[]{0,T}, f2wBase);
+
+
+ curr_stream = polyBase.createElem
+ (new int[]{0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239,
+ 0x715FAD23, 0x24A590AD, 0x69E4B5EF, 0xBF456141,
+ 0x96BC1B7B, 0xA7BDF825, 0xC1DE75B7, 0x8858A9C9,
+ 0x2DA87693, 0xB657F9DD, 0xFFDC8A8F, 0x8121DA71,
+ 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
+ 0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1,
+ 0xA6B7AADB});
+
+ curr_stream = new int[]{0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239,
+ 0x715FAD23, 0x24A590AD, 0x69E4B5EF, 0xBF456141,
+ 0x96BC1B7B, 0xA7BDF825, 0xC1DE75B7, 0x8858A9C9,
+ 0x2DA87693, 0xB657F9DD, 0xFFDC8A8F, 0x8121DA71,
+ 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
+ 0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1,
+ 0xA6B7AADB};
+
+ // Il n'est pas utile de fournir les resultats des exponentiation
+ // puisqu'elles ne prennent qu'un temps negligeable par rapport
+ // a la creation des tables de pre-calculs.
+
+
+ jumpW = polyBase.createElem(new int[]
+ {0, 1960343840, 1516339037, -333505122, -1976104464, -482584330,
+ -1279385510, -1806644808, -1348555459, -588715441, 2019804456,
+ 533392567, 1830622053, -348374534, 335303887, 193005475, 2020690292,
+ 1810924850, -1106874017, 988574120, -581662300, 1744525859,
+ 697086516, 1542714170, -916619518});
+
+ jumpZ = polyBase.createElem(new int[]
+ {0, -946485540, 1238370284, -1390769524, -1573619753, -524893823,
+ -873333569, 600407275, 631551291, 1484250168, 81475604, 721883080,
+ 767883194, 1119863295, -948128525, -2061810380, 631461516,
+ 351243833, 417985878, 828823193, 1965856614, -673630318,
+ -2078590119, -1319538924, 838929999});
+
+
+ jumpW = polyBase.createZ().exponentiateBase2(w);
+ jumpZ = jumpW.exponentiateBase2(v);
+
+
+
+ //transfert des vecteurs dans des matrices
+
+ BitVector[] bv = new BitVector[800];
+ int[] vect = new int[R];
+
+ for(int i = 0; i < 800; i++)
+ {
+ for(int j = 0; j < vect.length; j++)
+ vect[j] = 0;
+ vect[i / 32] = 1 << (i % 32);
+ polyBase.createElem(vect).multiply(jumpW).copyTo(vect);
+ bv[i] = new BitVector(vect, 800);
+ }
+ Apw = (new BitMatrix(bv)).transpose();
+
+
+ for(int i = 0; i < 800; i++)
+ {
+ for(int j = 0; j < vect.length; j++)
+ vect[j] = 0;
+ vect[i / 32] = 1 << (i % 32);
+ polyBase.createElem(vect).multiply(jumpZ).copyTo(vect);
+ bv[i] = new BitVector(vect, 800);
+ }
+ Apz = (new BitMatrix(bv)).transpose();
+ */
+ }
+
+
+
+ static private void initialisation() {
+ //initialise all of the state variables
+
+ curr_stream = new int[]{0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239,
+ 0x715FAD23, 0x24A590AD, 0x69E4B5EF, 0xBF456141,
+ 0x96BC1B7B, 0xA7BDF825, 0xC1DE75B7, 0x8858A9C9,
+ 0x2DA87693, 0xB657F9DD, 0xFFDC8A8F, 0x8121DA71,
+ 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
+ 0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1,
+ 0xA6B7AADB};
+
+ //reading the state transition matrices
+
+ try {
+ InputStream is = GenF2w32.class.getClassLoader().
+ getResourceAsStream ("umontreal/iro/lecuyer/rng/GenF2w32.dat");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ Apw = (BitMatrix)ois.readObject();
+ Apz = (BitMatrix)ois.readObject();
+ ois.close();
+
+ } catch(FileNotFoundException e) {
+ System.err.println("Couldn't find GenF2w32.dat");
+ e.printStackTrace();
+ throw new RuntimeException(" initialisation of GenF2w32");
+ } catch(IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(" initialisation of GenF2w32");
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ throw new RuntimeException(" initialisation of GenF2w32");
+ }
+
+ initialised = true;
+ }
+
+
+
+ /*
+ Calcule a*z^k dans GF(2^32)
+ */
+ private static int multiplyZ (int a, int k, int modPoly) {
+ for(int i = 0; i < k; i++)
+ if((a & 1) != 0)
+ a = (a >>> 1) ^ modPoly;
+ else
+ a >>>= 1;
+
+ return a;
+ }
+
+ /*
+ Calcule a * b dans GF(2^32)
+ */
+ private static int multiply (int a, int b, int modPoly) {
+ int res = 0;
+ int verif = 1;
+ for(int i = 0; i < W; i++) {
+ if((b & verif) != 0)
+ res ^= multiplyZ(a, W - 1 - i, modPoly);
+ verif <<= 1;
+ }
+
+ return res;
+ }
+
+ /*
+ Initialise les tables de pre-calculs
+ */
+ private static void initTables() {
+ BrmT1 = new int[1 << W1];
+ Br1 = new int[1 << W1];
+ BrmT2 = new int[1 << W2];
+ Br2 = new int[1 << W2];
+ BrmT3 = new int[1 << W3];
+ Br3 = new int[1 << W3];
+
+ for(int i = 0; i < Br1.length; i++) {
+ BrmT1[i] = multiply(BrmT, i << Wsplit1, modQ);
+ Br1[i] = multiply(Br, i << Wsplit1, modQ);
+ }
+ for(int i = 0; i < Br2.length; i++) {
+ BrmT2[i] = multiply(BrmT, i << Wsplit2, modQ);
+ Br2[i] = multiply(Br, i << Wsplit2, modQ);
+ }
+ for(int i = 0; i < Br3.length; i++) {
+ BrmT3[i] = multiply(BrmT, i, modQ);
+ Br3[i] = multiply(Br, i, modQ);
+ }
+
+ }
+
+ private void advanceSeed(int[] seed, BitMatrix bm) {
+ BitVector bv = new BitVector(seed, 800);
+
+ bv = bm.multiply(bv);
+
+ for(int i = 0; i < R; i++)
+ seed[i] = bv.getInt(i);
+ }
+
+ private GenF2w32 (int i) {
+ //unit vector (to build the state transition matrice)
+ state = new int[R];
+ for(int j = 0; j < R; j++)
+ state[j] = 0;
+ state[i / W] = 1 << (i % W);
+ state_i = R - 1;
+ }
+
+ /**
+ * Constructs a new stream.
+ *
+ */
+ public GenF2w32() {
+ if (!initialised)
+ initialisation();
+
+ //stream = polyBase.createElem();
+ //substream = polyBase.createElem();
+ stream = new int[R];
+ substream = new int[R];
+ state = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+ //stream.copyFrom(curr_stream);
+
+ advanceSeed(curr_stream, Apz);
+ // curr_stream = curr_stream.multiply(jumpZ);
+
+ resetStartStream();
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used in the <TT>toString</TT> method).
+ *
+ * @param name name of the stream
+ *
+ */
+ public GenF2w32 (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed of the class <TT>GenF2w2r32</TT> to the 25
+ * integers of the vector <TT>seed[0..24]</TT>.
+ * This will be the initial seed of the class for the next created stream.
+ * At least one of the integers must be non-zero.
+ *
+ * @param seed array of 25 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (int seed[]) {
+ if (!initialised)
+ initialisation();
+ if (seed.length < R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ "values.");
+ boolean goodSeed = false;
+ for(int i = 0; i < R; i++)
+ if(seed[i] != 0)
+ goodSeed = true;
+ if(!goodSeed)
+ throw new IllegalArgumentException("At least one part of the seed" +
+ " must be non-zero.");
+
+ for(int i = 0 ; i < R; i++)
+ curr_stream[i] = seed[i];
+ //curr_stream = polyBase.createElem(seed);
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * seed <TT>seed[0..24]</TT>. The seed must satisfy the same conditions as in
+ * <TT>setPackageSeed</TT>.
+ * This method only affects the specified stream; the others are not modified.
+ * Hence after calling this method, the beginning of the streams will no
+ * longer be spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very exceptional cases;
+ * proper use of the <TT>reset...</TT> methods and of the stream constructor
+ * is preferable.
+ *
+ * @param seed array of 25 elements representing the seed
+ *
+ *
+ */
+ public void setSeed (int seed[]) {
+ if(seed.length != R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ "values.");
+ boolean goodSeed = false;
+ for(int i = 0; i < R; i++)
+ if(seed[i] != 0)
+ goodSeed = true;
+ if(!goodSeed)
+ throw new IllegalArgumentException("At least one part of the seed" +
+ " must be non-zero.");
+
+ for(int i = 0 ; i < R; i++)
+ stream[i] = seed[i];
+ //stream = polyBase.createElem(seed);
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the stream, represented as an
+ * array of 25 integers.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public int[] getState() {
+ int res[] = new int[R];
+ for(int i = 0; i < R; i++)
+ res[i] = state[(state_i + i) % R];
+ return res;
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public GenF2w32 clone() {
+ GenF2w32 retour = null;
+ retour = (GenF2w32)super.clone();
+ retour.state = new int[R];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+ for (int i = 0; i<R; i++) {
+ retour.state[i] = state[i];
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }
+
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ //substream.copyFrom(stream);
+
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = R - 1;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ //substream.copyTo(state);
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, Apw);
+ // substream = substream.multiply(jumpW);
+
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("The state of the ");
+ sb.append(name == null ? "GenF2w32" : name);
+ sb.append(" is : {");
+ for(int i = 0; i < R - 1; i++)
+ sb.append(state[(state_i + i) % R] + ", ");
+ sb.append(state[(state_i + R - 1) % R] + "}");
+
+ return sb.toString();
+ }
+
+ /*
+ //LFSR (plus rapide d'environ 15% sur le LCG)
+ protected double nextValue()
+ {
+ if(state_i >= R)
+ state_i = 0;
+
+
+ if(state_i < R - T)
+ state[state_i] = ((BrmT1[(state[state_i + T] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i + T] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i + T] & MASK3)]) ^
+ (Br1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ Br2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ Br3[(state[state_i] & MASK3)]));
+ else
+ state[state_i] = ((BrmT1[(state[state_i +T-R] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i +T-R] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i +T-R] & MASK3)]) ^
+ (Br1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ Br2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ Br3[(state[state_i] & MASK3)]));
+
+ long result = state[state_i++];
+
+ return (result <= 0 ? result + 0x100000000L : result) * NORM;
+ }
+ */
+
+
+ //LCG (plus lent)
+ protected double nextValue() {
+ if(state_i < 0)
+ state_i = R - 1;
+
+ if(state_i + T < R)
+ state[state_i + T] ^= (BrmT1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i] & MASK3)]);
+ else
+ state[state_i +T-R] ^= (BrmT1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i] & MASK3)]);
+
+ state[state_i] = (Br1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ Br2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ Br3[(state[state_i] & MASK3)]);
+
+ long result = state[state_i--];
+
+ return (result <= 0 ? result + 0x100000000L : result) * NORM;
+ }
+
+ /**
+ * This method is only meant to be used during the compilation process.
+ * It is used to create the resource file the class need in order to
+ * run.
+ *
+ */
+
+ public static void main(String[] args) {
+ if(args.length < 1) {
+ System.err.println("Must provide the output file.");
+ System.exit(1);
+ }
+
+ //computes the state transition matrices
+
+ System.out.println("Creating the GenF2w32 state transition matrices.");
+
+ //the state transition matrices
+ BitMatrix STp0, STpw, STpz;
+
+ BitVector[] bv = new BitVector[800];
+ GenF2w32 gen;
+ int[] vect = new int[R];
+
+ for(int i = 0; i < 800; i++) {
+ gen = new GenF2w32(i);
+
+ gen.nextValue();
+ for(int j = 0; j < R; j++)
+ vect[j] = gen.state[(j + R - 1) % R];
+
+ bv[i] = new BitVector(vect, 800);
+ }
+
+ STp0 = (new BitMatrix(bv)).transpose();
+
+ STpw = STp0.power2e(w);
+ STpz = STpw.power2e(v);
+
+
+ try {
+ FileOutputStream fos = new FileOutputStream(args[0]);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(STpw);
+ oos.writeObject(STpz);
+ oos.close();
+ } catch(FileNotFoundException e) {
+ System.err.println("Couldn't create " + args[0]);
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/GenF2w32.tex b/source/umontreal/iro/lecuyer/rng/GenF2w32.tex
new file mode 100644
index 0000000..3d5e075
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/GenF2w32.tex
@@ -0,0 +1,591 @@
+\defmodule {GenF2w32}
+
+Implements the \class{RandomStream} interface via inheritance
+from \class{RandomStreamBase}. The backbone generator is a Linear Congruential
+Generator (LCG) in the finite field
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_{2^w}$ instead of
+ $\latex{\mathbb{F}}\html{\mathbf{F}}_2$.
+The implemented generator is the \texttt{GenF2w2\_32} proposed by
+Panneton \cite{rPAN04a,rPAN04t}. Its state is 25 32-bit words and it has
+a period length of $2^{800} - 1$.
+The values of $V$, $W$ and $Z$ are $2^{200}$, $2^{300}$ and $2^{500}$
+respectively (see \class{RandomStream} for their definition). The seed of the
+RNG, and the state of a stream at any given step, is a 25-dimensional vector
+of 32-bits integers.
+Its \texttt{nextValue} method returns numbers with 32 bits of precision.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: GenF2w32
+ * Description: generator proposed by Panneton
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import umontreal.iro.lecuyer.util.BitVector;
+import umontreal.iro.lecuyer.util.BitMatrix;
+import java.io.*;\end{hide}
+
+public class GenF2w32 extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 120307L;
+ //La date de modification a l'envers, lire 07/03/2012
+
+ //tas de constantes
+ private final static double NORM = 1.0 / (0x100000001L);
+
+ private final static int Z0 = 0x80000000;
+
+ private final static int W = 32;
+
+ private final static int R = 25;
+ private final static int T = 7;
+
+ private final static int W1 = 11, W2 = 11, W3 = 10;
+ private final static int Wsplit1 = W - W1;
+ private final static int Wsplit2 = Wsplit1 - W2;
+ private final static int MASK1 = 0xFFFFFFFF << Wsplit1;
+ private final static int MASK2 = (0xFFFFFFFF << Wsplit2) ^ MASK1;
+ private final static int MASK3 = 0xFFFFFFFF ^ MASK1 ^ MASK2;
+
+ private static int[] BrmT1, BrmT2, BrmT3;
+ private static int[] Br1, Br2, Br3;
+
+
+ private final static int BrmT = 0xE6A68D20;
+ private final static int Br = 0x287AB842;
+
+ private final static int modQ = 0xFA4F9B3F;
+
+ //stream variables
+ private final static int w = 300;
+ private final static int v = 200;
+ //private static F2wPoly.F2wPolyElem jumpW;
+ //private static F2wPoly.F2wPolyElem jumpZ;
+ private static BitMatrix Apw;
+ private static BitMatrix Apz;
+
+ //private static F2wPoly polyBase;
+
+ /*
+ private static F2wPoly.F2wPolyElem curr_stream;
+
+ private F2wPoly.F2wPolyElem stream;
+ private F2wPoly.F2wPolyElem substream;
+ */
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream;
+
+ //if the generator was initialised
+ private static boolean initialised = false;
+
+ private int[] state;
+ private int state_i;
+
+
+ static
+ {
+ initTables();
+
+ /*
+ F2wPoly.F2w f2wBase = new F2wPoly.F2w(modQ);
+
+ polyBase = new F2wPoly
+ (R, new F2wPoly.F2w.F2wElem[]{f2wBase.createElem(Br),
+ f2wBase.createElem(BrmT)},
+ new int[]{0,T}, f2wBase);
+
+
+ curr_stream = polyBase.createElem
+ (new int[]{0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239,
+ 0x715FAD23, 0x24A590AD, 0x69E4B5EF, 0xBF456141,
+ 0x96BC1B7B, 0xA7BDF825, 0xC1DE75B7, 0x8858A9C9,
+ 0x2DA87693, 0xB657F9DD, 0xFFDC8A8F, 0x8121DA71,
+ 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
+ 0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1,
+ 0xA6B7AADB});
+
+ curr_stream = new int[]{0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239,
+ 0x715FAD23, 0x24A590AD, 0x69E4B5EF, 0xBF456141,
+ 0x96BC1B7B, 0xA7BDF825, 0xC1DE75B7, 0x8858A9C9,
+ 0x2DA87693, 0xB657F9DD, 0xFFDC8A8F, 0x8121DA71,
+ 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
+ 0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1,
+ 0xA6B7AADB};
+
+ // Il n'est pas utile de fournir les resultats des exponentiation
+ // puisqu'elles ne prennent qu'un temps negligeable par rapport
+ // a la creation des tables de pre-calculs.
+
+
+ jumpW = polyBase.createElem(new int[]
+ {0, 1960343840, 1516339037, -333505122, -1976104464, -482584330,
+ -1279385510, -1806644808, -1348555459, -588715441, 2019804456,
+ 533392567, 1830622053, -348374534, 335303887, 193005475, 2020690292,
+ 1810924850, -1106874017, 988574120, -581662300, 1744525859,
+ 697086516, 1542714170, -916619518});
+
+ jumpZ = polyBase.createElem(new int[]
+ {0, -946485540, 1238370284, -1390769524, -1573619753, -524893823,
+ -873333569, 600407275, 631551291, 1484250168, 81475604, 721883080,
+ 767883194, 1119863295, -948128525, -2061810380, 631461516,
+ 351243833, 417985878, 828823193, 1965856614, -673630318,
+ -2078590119, -1319538924, 838929999});
+
+
+ jumpW = polyBase.createZ().exponentiateBase2(w);
+ jumpZ = jumpW.exponentiateBase2(v);
+
+
+
+ //transfert des vecteurs dans des matrices
+
+ BitVector[] bv = new BitVector[800];
+ int[] vect = new int[R];
+
+ for(int i = 0; i < 800; i++)
+ {
+ for(int j = 0; j < vect.length; j++)
+ vect[j] = 0;
+ vect[i / 32] = 1 << (i % 32);
+ polyBase.createElem(vect).multiply(jumpW).copyTo(vect);
+ bv[i] = new BitVector(vect, 800);
+ }
+ Apw = (new BitMatrix(bv)).transpose();
+
+
+ for(int i = 0; i < 800; i++)
+ {
+ for(int j = 0; j < vect.length; j++)
+ vect[j] = 0;
+ vect[i / 32] = 1 << (i % 32);
+ polyBase.createElem(vect).multiply(jumpZ).copyTo(vect);
+ bv[i] = new BitVector(vect, 800);
+ }
+ Apz = (new BitMatrix(bv)).transpose();
+ */
+ }
+
+
+
+ static private void initialisation() {
+ //initialise all of the state variables
+
+ curr_stream = new int[]{0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239,
+ 0x715FAD23, 0x24A590AD, 0x69E4B5EF, 0xBF456141,
+ 0x96BC1B7B, 0xA7BDF825, 0xC1DE75B7, 0x8858A9C9,
+ 0x2DA87693, 0xB657F9DD, 0xFFDC8A8F, 0x8121DA71,
+ 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
+ 0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1,
+ 0xA6B7AADB};
+
+ //reading the state transition matrices
+
+ try {
+ InputStream is = GenF2w32.class.getClassLoader().
+ getResourceAsStream ("umontreal/iro/lecuyer/rng/GenF2w32.dat");
+ ObjectInputStream ois = new ObjectInputStream(is);
+ Apw = (BitMatrix)ois.readObject();
+ Apz = (BitMatrix)ois.readObject();
+ ois.close();
+
+ } catch(FileNotFoundException e) {
+ System.err.println("Couldn't find GenF2w32.dat");
+ e.printStackTrace();
+ throw new RuntimeException(" initialisation of GenF2w32");
+ } catch(IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(" initialisation of GenF2w32");
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ throw new RuntimeException(" initialisation of GenF2w32");
+ }
+
+ initialised = true;
+ }
+
+
+
+ /*
+ Calcule a*z^k dans GF(2^32)
+ */
+ private static int multiplyZ (int a, int k, int modPoly) {
+ for(int i = 0; i < k; i++)
+ if((a & 1) != 0)
+ a = (a >>> 1) ^ modPoly;
+ else
+ a >>>= 1;
+
+ return a;
+ }
+
+ /*
+ Calcule a * b dans GF(2^32)
+ */
+ private static int multiply (int a, int b, int modPoly) {
+ int res = 0;
+ int verif = 1;
+ for(int i = 0; i < W; i++) {
+ if((b & verif) != 0)
+ res ^= multiplyZ(a, W - 1 - i, modPoly);
+ verif <<= 1;
+ }
+
+ return res;
+ }
+
+ /*
+ Initialise les tables de pre-calculs
+ */
+ private static void initTables() {
+ BrmT1 = new int[1 << W1];
+ Br1 = new int[1 << W1];
+ BrmT2 = new int[1 << W2];
+ Br2 = new int[1 << W2];
+ BrmT3 = new int[1 << W3];
+ Br3 = new int[1 << W3];
+
+ for(int i = 0; i < Br1.length; i++) {
+ BrmT1[i] = multiply(BrmT, i << Wsplit1, modQ);
+ Br1[i] = multiply(Br, i << Wsplit1, modQ);
+ }
+ for(int i = 0; i < Br2.length; i++) {
+ BrmT2[i] = multiply(BrmT, i << Wsplit2, modQ);
+ Br2[i] = multiply(Br, i << Wsplit2, modQ);
+ }
+ for(int i = 0; i < Br3.length; i++) {
+ BrmT3[i] = multiply(BrmT, i, modQ);
+ Br3[i] = multiply(Br, i, modQ);
+ }
+
+ }
+
+ private void advanceSeed(int[] seed, BitMatrix bm) {
+ BitVector bv = new BitVector(seed, 800);
+
+ bv = bm.multiply(bv);
+
+ for(int i = 0; i < R; i++)
+ seed[i] = bv.getInt(i);
+ }
+
+ private GenF2w32 (int i) {
+ //unit vector (to build the state transition matrice)
+ state = new int[R];
+ for(int j = 0; j < R; j++)
+ state[j] = 0;
+ state[i / W] = 1 << (i % W);
+ state_i = R - 1;
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public GenF2w32() \begin{hide} {
+ if (!initialised)
+ initialisation();
+
+ //stream = polyBase.createElem();
+ //substream = polyBase.createElem();
+ stream = new int[R];
+ substream = new int[R];
+ state = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+ //stream.copyFrom(curr_stream);
+
+ advanceSeed(curr_stream, Apz);
+ // curr_stream = curr_stream.multiply(jumpZ);
+
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream.
+\end{tabb}
+\begin{code}
+
+ public GenF2w32 (String name) \begin{hide} {
+ this();
+ this.name = name;
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used in the \texttt{toString} method).
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (int seed[]) \begin{hide} {
+ if (!initialised)
+ initialisation();
+ if (seed.length < R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ "values.");
+ boolean goodSeed = false;
+ for(int i = 0; i < R; i++)
+ if(seed[i] != 0)
+ goodSeed = true;
+ if(!goodSeed)
+ throw new IllegalArgumentException("At least one part of the seed" +
+ " must be non-zero.");
+
+ for(int i = 0 ; i < R; i++)
+ curr_stream[i] = seed[i];
+ //curr_stream = polyBase.createElem(seed);
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed of the class \texttt{GenF2w2r32} to the 25
+ integers of the vector \texttt{seed[0..24]}.
+ This will be the initial seed of the class for the next created stream.
+ At least one of the integers must be non-zero.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 25 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (int seed[]) \begin{hide} {
+ if(seed.length != R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ "values.");
+ boolean goodSeed = false;
+ for(int i = 0; i < R; i++)
+ if(seed[i] != 0)
+ goodSeed = true;
+ if(!goodSeed)
+ throw new IllegalArgumentException("At least one part of the seed" +
+ " must be non-zero.");
+
+ for(int i = 0 ; i < R; i++)
+ stream[i] = seed[i];
+ //stream = polyBase.createElem(seed);
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ seed \texttt{seed[0..24]}. The seed must satisfy the same conditions as in
+ \texttt{setPackageSeed}.
+ This method only affects the specified stream; the others are not modified.
+ Hence after calling this method, the beginning of the streams will no
+ longer be spaced $Z$ values apart.
+ For this reason, this method should only be used in very exceptional cases;
+ proper use of the \texttt{reset...} methods and of the stream constructor
+ is preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 25 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getState() \begin{hide} {
+ int res[] = new int[R];
+ for(int i = 0; i < R; i++)
+ res[i] = state[(state_i + i) % R];
+ return res;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the stream, represented as an
+ array of 25 integers.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}
+
+ public GenF2w32 clone() \begin{hide} {
+ GenF2w32 retour = null;
+ retour = (GenF2w32)super.clone();
+ retour.state = new int[R];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+ for (int i = 0; i<R; i++) {
+ retour.state[i] = state[i];
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}\begin{hide}
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ //substream.copyFrom(stream);
+
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = R - 1;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ //substream.copyTo(state);
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, Apw);
+ // substream = substream.multiply(jumpW);
+
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("The state of the ");
+ sb.append(name == null ? "GenF2w32" : name);
+ sb.append(" is : {");
+ for(int i = 0; i < R - 1; i++)
+ sb.append(state[(state_i + i) % R] + ", ");
+ sb.append(state[(state_i + R - 1) % R] + "}");
+
+ return sb.toString();
+ }
+
+ /*
+ //LFSR (plus rapide d'environ 15% sur le LCG)
+ protected double nextValue()
+ {
+ if(state_i >= R)
+ state_i = 0;
+
+
+ if(state_i < R - T)
+ state[state_i] = ((BrmT1[(state[state_i + T] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i + T] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i + T] & MASK3)]) ^
+ (Br1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ Br2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ Br3[(state[state_i] & MASK3)]));
+ else
+ state[state_i] = ((BrmT1[(state[state_i +T-R] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i +T-R] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i +T-R] & MASK3)]) ^
+ (Br1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ Br2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ Br3[(state[state_i] & MASK3)]));
+
+ long result = state[state_i++];
+
+ return (result <= 0 ? result + 0x100000000L : result) * NORM;
+ }
+ */
+
+
+ //LCG (plus lent)
+ protected double nextValue() {
+ if(state_i < 0)
+ state_i = R - 1;
+
+ if(state_i + T < R)
+ state[state_i + T] ^= (BrmT1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i] & MASK3)]);
+ else
+ state[state_i +T-R] ^= (BrmT1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ BrmT2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ BrmT3[(state[state_i] & MASK3)]);
+
+ state[state_i] = (Br1[(state[state_i] & MASK1) >>> Wsplit1] ^
+ Br2[(state[state_i] & MASK2) >>> Wsplit2] ^
+ Br3[(state[state_i] & MASK3)]);
+
+ long result = state[state_i--];
+
+ return (result <= 0 ? result + 0x100000000L : result) * NORM;
+ }
+ \end{hide}
+\end{code}
+\unmoved\begin{htmlonly}
+ This method is only meant to be used during the compilation process.
+ It is used to create the resource file the class need in order to
+ run.
+\end{htmlonly}
+\begin{code}\begin{hide}
+ public static void main(String[] args) {
+ if(args.length < 1) {
+ System.err.println("Must provide the output file.");
+ System.exit(1);
+ }
+
+ //computes the state transition matrices
+
+ System.out.println("Creating the GenF2w32 state transition matrices.");
+
+ //the state transition matrices
+ BitMatrix STp0, STpw, STpz;
+
+ BitVector[] bv = new BitVector[800];
+ GenF2w32 gen;
+ int[] vect = new int[R];
+
+ for(int i = 0; i < 800; i++) {
+ gen = new GenF2w32(i);
+
+ gen.nextValue();
+ for(int j = 0; j < R; j++)
+ vect[j] = gen.state[(j + R - 1) % R];
+
+ bv[i] = new BitVector(vect, 800);
+ }
+
+ STp0 = (new BitMatrix(bv)).transpose();
+
+ STpw = STp0.power2e(w);
+ STpz = STpw.power2e(v);
+
+
+ try {
+ FileOutputStream fos = new FileOutputStream(args[0]);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(STpw);
+ oos.writeObject(STpz);
+ oos.close();
+ } catch(FileNotFoundException e) {
+ System.err.println("Couldn't create " + args[0]);
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/rng/LFSR113.java b/source/umontreal/iro/lecuyer/rng/LFSR113.java
new file mode 100644
index 0000000..aed2d48
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/LFSR113.java
@@ -0,0 +1,388 @@
+
+
+/*
+ * Class: LFSR113
+ * Description: 32-bit composite linear feedback shift register proposed by L'Ecuyer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.io.Serializable;
+
+
+/**
+ * Extends {@link RandomStreamBase} using a composite linear feedback
+ * shift register (LFSR) (or Tausworthe) RNG as defined in.
+ * This generator is the <TT>LFSR113</TT> proposed by.
+ * It has four 32-bit components combined by a bitwise xor.
+ * Its period length is approximatively 2^113
+ * . The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>35</SUP></SPAN>,
+ * <SPAN CLASS="MATH">2<SUP>55</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>90</SUP></SPAN> respectively (see {@link RandomStream} for their
+ * definition). The seed of the RNG, and the state of a stream at any given
+ * step, are four-dimensional vectors of 32-bit integers.
+ * The default initial seed of the RNG is
+ * <A NAME="tex2html1"
+ * HREF="#foot16"><SUP><SPAN CLASS="arabic">1</SPAN></SUP></A>(987654321, 987654321, 987654321, 987654321).
+ * The <TT>nextValue</TT> method returns numbers with 32 bits of precision.
+ *
+ */
+public class LFSR113 extends RandomStreamBase {
+
+ private static final long serialVersionUID = 70510L;
+ // La date de modification a l'envers, lire 10/05/2007
+
+ // generator constant: make sure that double values 0 and 1 never occur
+ private static final double NORM = 1.0 / 0x100000001L; // 2^32 + 1
+
+
+ // state variables:
+ private int z0;
+ private int z1;
+ private int z2;
+ private int z3;
+
+ //stream and substream variables :
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream = {987654321, 987654321, 987654321, 987654321};
+
+
+
+ /**
+ * Constructs a new stream.
+ *
+ */
+ public LFSR113() {
+ name = null;
+
+ stream = new int[4];
+ substream = new int[4];
+
+ for(int i = 0; i < 4; i++)
+ stream[i] = curr_stream[i];
+
+ resetStartStream();
+
+
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^90 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^90 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ int z, b;
+
+ z = curr_stream[0] & -2;
+ b = (z << 6) ^ z;
+ z = (z) ^ (z << 2) ^ (z << 3) ^ (z << 10) ^ (z << 13) ^
+ (z << 16) ^ (z << 19) ^ (z << 22) ^ (z << 25) ^
+ (z << 27) ^ (z << 28) ^
+ (b >>> 3) ^ (b >>> 4) ^ (b >>> 6) ^ (b >>> 9) ^ (b >>> 12) ^
+ (b >>> 15) ^ (b >>> 18) ^ (b >>> 21);
+ curr_stream[0] = z;
+
+
+ z = curr_stream[1] & -8;
+ b = (z << 2) ^ z;
+ z = (b >>> 13) ^ (z << 16);
+ curr_stream[1] = z;
+
+
+ z = curr_stream[2] & -16;
+ b = (z << 13) ^ z;
+ z = (z << 2) ^ (z << 4) ^ (z << 10) ^ (z << 12) ^ (z << 13) ^
+ (z << 17) ^ (z << 25) ^
+ (b >>> 3) ^ (b >>> 11) ^ (b >>> 15) ^ (b >>> 16) ^ (b >>> 24);
+ curr_stream[2] = z;
+
+
+ z = curr_stream[3] & -128;
+ b = (z << 3) ^ z;
+ z = (z << 9) ^ (z << 10) ^ (z << 11) ^ (z << 14) ^ (z << 16) ^
+ (z << 18) ^ (z << 23) ^ (z << 24) ^
+ (b >>> 1) ^ (b >>> 2) ^ (b >>> 7) ^ (b >>> 9) ^ (b >>> 11) ^
+ (b >>> 14) ^ (b >>> 15) ^ (b >>> 16) ^ (b >>> 23) ^ (b >>> 24);
+ curr_stream[3] = z;
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>.
+ *
+ * @param name name of the stream
+ *
+ */
+ public LFSR113 (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed for the class <TT>LFSR113</TT> to the four
+ * integers of the vector <TT>seed[0..3]</TT>.
+ * This will be the initial state of the next created stream.
+ * The default seed for the first stream is <A NAME="tex2html2"
+ * HREF="#foot30"><SUP><SPAN CLASS="arabic">2</SPAN></SUP></A> (987654321, 987654321, 987654321, 987654321).
+ * The first, second, third and fourth integers of <TT>seed</TT>
+ * must be either negative, or greater than or equal to
+ * 2, 8, 16 and 128 respectively.
+ *
+ * @param seed array of 4 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (int[] seed) {
+ checkSeed (seed);
+ for(int i = 0; i < 4; i++)
+ curr_stream[i] = seed[i];
+ }
+
+ private static void checkSeed (int[] seed) {
+ if (seed.length < 4)
+ throw new IllegalArgumentException("Seed must contain 4 values");
+ if ((seed[0] >= 0 && seed[0] < 2) ||
+ (seed[1] >= 0 && seed[1] < 8) ||
+ (seed[2] >= 0 && seed[2] < 16) ||
+ (seed[3] >= 0 && seed[3] < 128))
+ throw new IllegalArgumentException
+ ("The seed elements must be either negative or greater than 1, 7, 15 and 127 respectively");
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * seed <TT>seed[0..3]</TT>. The seed must satisfy the same conditions
+ * as in <TT>setPackageSeed</TT>.
+ * This method only affects the specified stream; the others are not
+ * modified, so the beginning of the streams will not be
+ * spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very
+ * exceptional cases; proper use of the <TT>reset...</TT> methods
+ * and of the stream constructor is preferable.
+ *
+ * @param seed array of 4 elements representing the seed
+ *
+ *
+ */
+ public void setSeed (int[] seed) {
+ checkSeed (seed);
+ for(int i = 0; i < 4; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the stream, represented as
+ * an array of four integers.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public int[] getState() {
+ return new int[]{z0, z1, z2, z3};
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public LFSR113 clone() {
+ LFSR113 retour = null;
+ retour = (LFSR113)super.clone();
+ retour.stream = new int[4];
+ retour.substream = new int[4];
+ for (int i = 0; i<4; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }
+
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < 4; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ z0 = substream[0];
+ z1 = substream[1];
+ z2 = substream[2];
+ z3 = substream[3];
+ }
+/*
+ // La version de Mario: beaucoup plus lent que l'ancienne version: on garde
+ // l'ancienne version.
+ public void resetNextSubstream() {
+ byte [] c0 = new byte[] {0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1};
+ byte [] c1 = new byte[] {1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0};
+ byte [] c2 = new byte[] {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0};
+ byte [] c3 = new byte[] {1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int x0 = 0;
+ int x1 = 0;
+ int x2 = 0;
+ int x3 = 0;
+ resetStartSubstream();
+ for (int i = 0; i < 31; ++i) {
+ if (c0[i] == 1) x0 ^= z0;
+ if (c1[i] == 1) x1 ^= z1;
+ if (c2[i] == 1) x2 ^= z2;
+ if (c3[i] == 1) x3 ^= z3;
+
+ int b;
+ b = (((z0 << 6) ^ z0) >>> 13);
+ z0 = (((z0 & -2) << 18) ^ b);
+ b = (((z1 << 2) ^ z1) >>> 27);
+ z1 = (((z1 & -8) << 2) ^ b);
+ b = (((z2 << 13) ^ z2) >>> 21);
+ z2 = (((z2 & -16) << 7) ^ b);
+ b = (((z3 << 3) ^ z3) >>> 12);
+ z3 = (((z3 & -128) << 13) ^ b);
+ }
+ substream[0] = x0;
+ substream[1] = x1;
+ substream[2] = x2;
+ substream[3] = x3;
+ resetStartSubstream();
+ }
+*/
+
+ public void resetNextSubstream() {
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^55 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^55 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ int z, b;
+
+ z = substream[0] & -2;
+ b = (z << 6) ^ z;
+ z = (z) ^ (z << 3) ^ (z << 4) ^ (z << 6) ^ (z << 7) ^
+ (z << 8) ^ (z << 10) ^ (z << 11) ^ (z << 13) ^ (z << 14) ^
+ (z << 16) ^ (z << 17) ^ (z << 18) ^ (z << 22) ^
+ (z << 24) ^ (z << 25) ^ (z << 26) ^ (z << 28) ^ (z << 30);
+ z ^= (b >>> 1) ^ (b >>> 3) ^ (b >>> 5) ^ (b >>> 6) ^
+ (b >>> 7) ^ (b >>> 9) ^ (b >>> 13) ^ (b >>> 14) ^
+ (b >>> 15) ^ (b >>> 17) ^ (b >>> 18) ^ (b >>> 20) ^
+ (b >>> 21) ^ (b >>> 23) ^ (b >>> 24) ^ (b >>> 25) ^
+ (b >>> 26) ^ (b >>> 27) ^ (b >>> 30);
+ substream[0] = z;
+
+
+ z = substream[1] & -8;
+ b = z ^ (z << 1);
+ b ^= (b << 2);
+ b ^= (b << 4);
+ b ^= (b << 8);
+
+ b <<= 8;
+ b ^= (z << 22) ^ (z << 25) ^ (z << 27);
+ if((z & 0x80000000) != 0) b ^= 0xABFFF000;
+ if((z & 0x40000000) != 0) b ^= 0x55FFF800;
+ z = b ^ (z >>> 7) ^ (z >>> 20) ^ (z >>> 21);
+ substream[1] = z;
+
+
+ z = substream[2] & -16;
+ b = (z << 13) ^ z;
+ z = (b >>> 3) ^ (b >>> 17) ^
+ (z << 10) ^ (z << 11) ^ (z << 25);
+ substream[2] = z;
+
+
+ z = substream[3] & -128;
+ b = (z << 3) ^ z;
+ z = (z << 14) ^ (z << 16) ^ (z << 20) ^
+ (b >>> 5) ^ (b >>> 9) ^ (b >>> 11);
+ substream[3] = z;
+
+ resetStartSubstream();
+ }
+
+
+ public String toString() {
+ if (name == null)
+ return "The state of the LFSR113 is: { " +
+ z0 + ", " + z1 + ", " + z2 + ", " + z3 + " }";
+ else
+ return "The state of " + name + " is: { " +
+ z0 + ", " + z1 + ", " + z2 + ", " + z3 + " }";
+ }
+
+ private long nextNumber() {
+ int b;
+ b = (((z0 << 6) ^ z0) >>> 13);
+ z0 = (((z0 & -2) << 18) ^ b);
+ b = (((z1 << 2) ^ z1) >>> 27);
+ z1 = (((z1 & -8) << 2) ^ b);
+ b = (((z2 << 13) ^ z2) >>> 21);
+ z2 = (((z2 & -16) << 7) ^ b);
+ b = (((z3 << 3) ^ z3) >>> 12);
+ z3 = (((z3 & -128) << 13) ^ b);
+
+ long r = (z0 ^ z1 ^ z2 ^ z3);
+
+ if (r <= 0)
+ r += 0x100000000L; //2^32
+
+ return r;
+ }
+
+ protected double nextValue() {
+ // Make sure that double values 0 and 1 never occur
+ return nextNumber() * NORM;
+ }
+
+ public int nextInt (int i, int j) {
+ if (i > j)
+ throw new IllegalArgumentException(i + " is larger than " + j + ".");
+ long d = j-i+1L;
+ long q = 0x100000000L / d;
+ long r = 0x100000000L % d;
+ long res;
+
+ do {
+ res = nextNumber();
+ } while (res >= 0x100000000L - r);
+
+ return (int) (res / q) + i;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/LFSR113.tex b/source/umontreal/iro/lecuyer/rng/LFSR113.tex
new file mode 100644
index 0000000..55f6456
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/LFSR113.tex
@@ -0,0 +1,400 @@
+\defmodule {LFSR113}
+
+Extends \class{RandomStreamBase} using a composite linear feedback
+shift register (LFSR) (or Tausworthe) RNG as defined in
+\cite{rLEC96a,rTEZ91b}.
+This generator is the \texttt{LFSR113} proposed by \cite{rLEC99a}.
+It has four 32-bit components combined by a bitwise xor.
+Its period length is \html{approximatively 2^{113}}
+\latex{$\rho\approx 2^{113}$}. The values of $V$, $W$ and $Z$ are $2^{35}$,
+$2^{55}$ and $2^{90}$ respectively (see \class{RandomStream} for their
+definition). The seed of the RNG, and the state of a stream at any given
+step, are four-dimensional vectors of 32-bit integers.
+The default initial seed of the RNG is
+\footnote{In previous versions, it was $(12345, 12345, 12345, 12345)$.}
+(987654321, 987654321, 987654321, 987654321).
+The \texttt{nextValue} method returns numbers with 32 bits of precision.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LFSR113
+ * Description: 32-bit composite linear feedback shift register proposed by L'Ecuyer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import java.io.Serializable;
+\end{hide}
+
+public class LFSR113 extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ // La date de modification a l'envers, lire 10/05/2007
+
+ // generator constant: make sure that double values 0 and 1 never occur
+ private static final double NORM = 1.0 / 0x100000001L; // 2^32 + 1
+
+
+ // state variables:
+ private int z0;
+ private int z1;
+ private int z2;
+ private int z3;
+
+ //stream and substream variables :
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream = {987654321, 987654321, 987654321, 987654321};
+
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public LFSR113() \begin{hide} {
+ name = null;
+
+ stream = new int[4];
+ substream = new int[4];
+
+ for(int i = 0; i < 4; i++)
+ stream[i] = curr_stream[i];
+
+ resetStartStream();
+
+
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^90 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^90 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ int z, b;
+
+ z = curr_stream[0] & -2;
+ b = (z << 6) ^ z;
+ z = (z) ^ (z << 2) ^ (z << 3) ^ (z << 10) ^ (z << 13) ^
+ (z << 16) ^ (z << 19) ^ (z << 22) ^ (z << 25) ^
+ (z << 27) ^ (z << 28) ^
+ (b >>> 3) ^ (b >>> 4) ^ (b >>> 6) ^ (b >>> 9) ^ (b >>> 12) ^
+ (b >>> 15) ^ (b >>> 18) ^ (b >>> 21);
+ curr_stream[0] = z;
+
+
+ z = curr_stream[1] & -8;
+ b = (z << 2) ^ z;
+ z = (b >>> 13) ^ (z << 16);
+ curr_stream[1] = z;
+
+
+ z = curr_stream[2] & -16;
+ b = (z << 13) ^ z;
+ z = (z << 2) ^ (z << 4) ^ (z << 10) ^ (z << 12) ^ (z << 13) ^
+ (z << 17) ^ (z << 25) ^
+ (b >>> 3) ^ (b >>> 11) ^ (b >>> 15) ^ (b >>> 16) ^ (b >>> 24);
+ curr_stream[2] = z;
+
+
+ z = curr_stream[3] & -128;
+ b = (z << 3) ^ z;
+ z = (z << 9) ^ (z << 10) ^ (z << 11) ^ (z << 14) ^ (z << 16) ^
+ (z << 18) ^ (z << 23) ^ (z << 24) ^
+ (b >>> 1) ^ (b >>> 2) ^ (b >>> 7) ^ (b >>> 9) ^ (b >>> 11) ^
+ (b >>> 14) ^ (b >>> 15) ^ (b >>> 16) ^ (b >>> 23) ^ (b >>> 24);
+ curr_stream[3] = z;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream.
+\end{tabb}
+\begin{code}
+
+ public LFSR113 (String name) \begin{hide} {
+ this();
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (int[] seed) \begin{hide} {
+ checkSeed (seed);
+ for(int i = 0; i < 4; i++)
+ curr_stream[i] = seed[i];
+ }
+
+ private static void checkSeed (int[] seed) {
+ if (seed.length < 4)
+ throw new IllegalArgumentException("Seed must contain 4 values");
+ if ((seed[0] >= 0 && seed[0] < 2) ||
+ (seed[1] >= 0 && seed[1] < 8) ||
+ (seed[2] >= 0 && seed[2] < 16) ||
+ (seed[3] >= 0 && seed[3] < 128))
+ throw new IllegalArgumentException
+ ("The seed elements must be either negative or greater than 1, 7, 15 and 127 respectively");
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed for the class \texttt{LFSR113} to the four
+ integers of the vector \texttt{seed[0..3]}.
+ This will be the initial state of the next created stream.
+ The default seed for the first stream is \footnote{In previous
+ versions, it was $(12345, 12345, 12345, 12345)$.}
+ (987654321, 987654321, 987654321, 987654321).
+ The first, second, third and fourth integers of \texttt{seed}
+ must be either negative, or greater than or equal to
+ 2, 8, 16 and 128 respectively.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 4 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (int[] seed) \begin{hide} {
+ checkSeed (seed);
+ for(int i = 0; i < 4; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ seed \texttt{seed[0..3]}. The seed must satisfy the same conditions
+ as in \texttt{setPackageSeed}.
+ This method only affects the specified stream; the others are not
+ modified, so the beginning of the streams will not be
+ spaced $Z$ values apart.
+ For this reason, this method should only be used in very
+ exceptional cases; proper use of the \texttt{reset...} methods
+ and of the stream constructor is preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 4 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getState() \begin{hide} {
+ return new int[]{z0, z1, z2, z3};
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the stream, represented as
+ an array of four integers.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}
+
+ public LFSR113 clone() \begin{hide} {
+ LFSR113 retour = null;
+ retour = (LFSR113)super.clone();
+ retour.stream = new int[4];
+ retour.substream = new int[4];
+ for (int i = 0; i<4; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+
+ public void resetStartStream() {
+ for(int i = 0; i < 4; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ z0 = substream[0];
+ z1 = substream[1];
+ z2 = substream[2];
+ z3 = substream[3];
+ }
+/*
+ // La version de Mario: beaucoup plus lent que l'ancienne version: on garde
+ // l'ancienne version.
+ public void resetNextSubstream() {
+ byte [] c0 = new byte[] {0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1};
+ byte [] c1 = new byte[] {1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0};
+ byte [] c2 = new byte[] {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0};
+ byte [] c3 = new byte[] {1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int x0 = 0;
+ int x1 = 0;
+ int x2 = 0;
+ int x3 = 0;
+ resetStartSubstream();
+ for (int i = 0; i < 31; ++i) {
+ if (c0[i] == 1) x0 ^= z0;
+ if (c1[i] == 1) x1 ^= z1;
+ if (c2[i] == 1) x2 ^= z2;
+ if (c3[i] == 1) x3 ^= z3;
+
+ int b;
+ b = (((z0 << 6) ^ z0) >>> 13);
+ z0 = (((z0 & -2) << 18) ^ b);
+ b = (((z1 << 2) ^ z1) >>> 27);
+ z1 = (((z1 & -8) << 2) ^ b);
+ b = (((z2 << 13) ^ z2) >>> 21);
+ z2 = (((z2 & -16) << 7) ^ b);
+ b = (((z3 << 3) ^ z3) >>> 12);
+ z3 = (((z3 & -128) << 13) ^ b);
+ }
+ substream[0] = x0;
+ substream[1] = x1;
+ substream[2] = x2;
+ substream[3] = x3;
+ resetStartSubstream();
+ }
+*/
+
+ public void resetNextSubstream() {
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^55 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^55 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ int z, b;
+
+ z = substream[0] & -2;
+ b = (z << 6) ^ z;
+ z = (z) ^ (z << 3) ^ (z << 4) ^ (z << 6) ^ (z << 7) ^
+ (z << 8) ^ (z << 10) ^ (z << 11) ^ (z << 13) ^ (z << 14) ^
+ (z << 16) ^ (z << 17) ^ (z << 18) ^ (z << 22) ^
+ (z << 24) ^ (z << 25) ^ (z << 26) ^ (z << 28) ^ (z << 30);
+ z ^= (b >>> 1) ^ (b >>> 3) ^ (b >>> 5) ^ (b >>> 6) ^
+ (b >>> 7) ^ (b >>> 9) ^ (b >>> 13) ^ (b >>> 14) ^
+ (b >>> 15) ^ (b >>> 17) ^ (b >>> 18) ^ (b >>> 20) ^
+ (b >>> 21) ^ (b >>> 23) ^ (b >>> 24) ^ (b >>> 25) ^
+ (b >>> 26) ^ (b >>> 27) ^ (b >>> 30);
+ substream[0] = z;
+
+
+ z = substream[1] & -8;
+ b = z ^ (z << 1);
+ b ^= (b << 2);
+ b ^= (b << 4);
+ b ^= (b << 8);
+
+ b <<= 8;
+ b ^= (z << 22) ^ (z << 25) ^ (z << 27);
+ if((z & 0x80000000) != 0) b ^= 0xABFFF000;
+ if((z & 0x40000000) != 0) b ^= 0x55FFF800;
+ z = b ^ (z >>> 7) ^ (z >>> 20) ^ (z >>> 21);
+ substream[1] = z;
+
+
+ z = substream[2] & -16;
+ b = (z << 13) ^ z;
+ z = (b >>> 3) ^ (b >>> 17) ^
+ (z << 10) ^ (z << 11) ^ (z << 25);
+ substream[2] = z;
+
+
+ z = substream[3] & -128;
+ b = (z << 3) ^ z;
+ z = (z << 14) ^ (z << 16) ^ (z << 20) ^
+ (b >>> 5) ^ (b >>> 9) ^ (b >>> 11);
+ substream[3] = z;
+
+ resetStartSubstream();
+ }
+
+
+ public String toString() {
+ if (name == null)
+ return "The state of the LFSR113 is: { " +
+ z0 + ", " + z1 + ", " + z2 + ", " + z3 + " }";
+ else
+ return "The state of " + name + " is: { " +
+ z0 + ", " + z1 + ", " + z2 + ", " + z3 + " }";
+ }
+
+ private long nextNumber() {
+ int b;
+ b = (((z0 << 6) ^ z0) >>> 13);
+ z0 = (((z0 & -2) << 18) ^ b);
+ b = (((z1 << 2) ^ z1) >>> 27);
+ z1 = (((z1 & -8) << 2) ^ b);
+ b = (((z2 << 13) ^ z2) >>> 21);
+ z2 = (((z2 & -16) << 7) ^ b);
+ b = (((z3 << 3) ^ z3) >>> 12);
+ z3 = (((z3 & -128) << 13) ^ b);
+
+ long r = (z0 ^ z1 ^ z2 ^ z3);
+
+ if (r <= 0)
+ r += 0x100000000L; //2^32
+
+ return r;
+ }
+
+ protected double nextValue() {
+ // Make sure that double values 0 and 1 never occur
+ return nextNumber() * NORM;
+ }
+
+ public int nextInt (int i, int j) {
+ if (i > j)
+ throw new IllegalArgumentException(i + " is larger than " + j + ".");
+ long d = j-i+1L;
+ long q = 0x100000000L / d;
+ long r = 0x100000000L % d;
+ long res;
+
+ do {
+ res = nextNumber();
+ } while (res >= 0x100000000L - r);
+
+ return (int) (res / q) + i;
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/LFSR258.java b/source/umontreal/iro/lecuyer/rng/LFSR258.java
new file mode 100644
index 0000000..7902c4d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/LFSR258.java
@@ -0,0 +1,516 @@
+
+
+/*
+ * Class: LFSR258
+ * Description: 64-bit composite linear feedback shift register proposed by L'Ecuyer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.io.Serializable;
+/*
+import umontreal.iro.lecuyer.util.BitVector;
+import umontreal.iro.lecuyer.util.BitMatrix;
+*/
+
+
+/**
+ * Extends {@link RandomStreamBase} using a 64-bit composite linear feedback
+ * shift register (LFSR) (or Tausworthe) RNG as defined in.
+ * This generator is the <TT>LFSR258</TT> proposed in.
+ * It has five components combined by a bitwise xor.
+ * Its period length is approximatively 2^258
+ * . The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>100</SUP></SPAN>,
+ * <SPAN CLASS="MATH">2<SUP>100</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>200</SUP></SPAN> respectively (see {@link RandomStream} for their
+ * definition). The seed of the RNG, and the state of a stream at any given
+ * step, are five-dimensional vectors of 64-bit integers.
+ * The default initial seed <A NAME="tex2html1"
+ * HREF="#foot16"><SUP><SPAN CLASS="arabic">1</SPAN></SUP></A> of the RNG is (123456789123456789, 123456789123456789, 123456789123456789,
+ * 123456789123456789, 123456789123456789).
+ * The <TT>nextValue</TT> method returns numbers with 53 bits of precision.
+ * This generator is fast for 64-bit machines.
+ *
+ */
+public class LFSR258 extends RandomStreamBase {
+
+ private static final long serialVersionUID = 140406L;
+ //La date de modification a l'envers, lire 06/04/2014
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //private static final double NORM = 5.4210108624275221e-20;
+
+ //equivalent a NORM = 1.0 / 0xFFFFFFFFFFFFF800L
+ private static final double NORM = 0.5 / 0x7FFFFFFFFFFFFC00L;
+ private static final double MAX = 0xFFFFFFFFFFFFF800L * NORM + 1.0;
+ //MAX = plus grand double < 1.0
+
+ private static final long GERME = 123456789123456789L;
+
+ private long z0, z1, z2, z3, z4; // l'etat
+
+ //stream and substream variables :
+ private long[] stream;
+ private long[] substream;
+ private static long[] curr_stream = {GERME, GERME, GERME, GERME, GERME};
+
+
+
+ /**
+ * Constructs a new stream.
+ *
+ */
+ public LFSR258() {
+ name = null;
+
+ stream = new long[5];
+ substream = new long[5];
+
+ for(int i = 0; i < 5; i++)
+ stream[i] = curr_stream[i];
+
+ resetStartStream();
+
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^200 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^200 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ long z, b;
+
+ z = curr_stream[0] & 0xfffffffffffffffeL;
+ b = z ^ (z << 1);
+ z = (b >>> 58) ^ (b >>> 55) ^ (b >>> 46) ^ (b >>> 43) ^ (z << 5) ^
+ (z << 8) ^ (z << 17) ^ (z << 20);
+ curr_stream[0] = z;
+
+
+ z = curr_stream[1] & 0xfffffffffffffe00L;
+ b = z ^ (z << 24);
+ z = (b >>> 54) ^ (b >>> 53) ^ (b >>> 52) ^ (b >>> 50) ^ (b >>> 49) ^
+ (b >>> 48) ^ (b >>> 43) ^ (b >>> 41) ^ (b >>> 38) ^ (b >>> 37) ^
+ (b >>> 30) ^ (b >>> 25) ^ (b >>> 24) ^ (b >>> 23) ^ (b >>> 19) ^
+ (b >>> 16) ^ (b >>> 15) ^ (b >>> 14) ^ (b >>> 13) ^ (b >>> 11) ^
+ (b >>> 8) ^ (b >>> 7) ^ (b >>> 5) ^ (b >>> 3) ^ (z << 0) ^
+ (z << 2) ^ (z << 3) ^ (z << 6) ^ (z << 7) ^ (z << 8) ^ (z << 9) ^
+ (z << 10) ^ (z << 11) ^ (z << 12) ^ (z << 13) ^ (z << 14) ^
+ (z << 16) ^ (z << 18) ^ (z << 19) ^ (z << 21) ^ (z << 25) ^
+ (z << 30) ^ (z << 31) ^ (z << 32) ^ (z << 36) ^ (z << 39) ^
+ (z << 40) ^ (z << 41) ^ (z << 42) ^ (z << 44) ^ (z << 47) ^
+ (z << 48) ^ (z << 50) ^ (z << 52);
+ curr_stream[1] = z;
+
+
+ z = curr_stream[2] & 0xfffffffffffff000L;
+ b = z ^ (z << 3);
+ z = (b >>> 50) ^ (b >>> 49) ^ (b >>> 46) ^ (b >>> 42) ^ (b >>> 40) ^
+ (b >>> 39) ^ (b >>> 38) ^ (b >>> 37) ^ (b >>> 36) ^ (b >>> 32) ^
+ (b >>> 29) ^ (b >>> 28) ^ (b >>> 27) ^ (b >>> 25) ^ (b >>> 23) ^
+ (b >>> 20) ^ (b >>> 19) ^ (b >>> 15) ^ (b >>> 12) ^ (b >>> 11) ^
+ (b >>> 2) ^ (z << 1) ^ (z << 2) ^ (z << 3) ^ (z << 6) ^ (z << 10) ^
+ (z << 12) ^ (z << 13) ^ (z << 14) ^ (z << 15) ^ (z << 16) ^
+ (z << 20) ^ (z << 23) ^ (z << 24) ^ (z << 25) ^ (z << 27) ^
+ (z << 29) ^ (z << 32) ^ (z << 33) ^ (z << 37) ^ (z << 40) ^
+ (z << 41) ^ (z << 50);
+ curr_stream[2] = z;
+
+
+ z = curr_stream[3] & 0xfffffffffffe0000L;
+ b = z ^ (z << 5);
+ z = (b >>> 46) ^ (b >>> 44) ^ (b >>> 42) ^ (b >>> 41) ^ (b >>> 40) ^
+ (b >>> 38) ^ (b >>> 36) ^ (b >>> 32) ^ (b >>> 30) ^ (b >>> 25) ^
+ (b >>> 18) ^ (b >>> 16) ^ (b >>> 15) ^ (b >>> 14) ^ (b >>> 12) ^
+ (b >>> 11) ^ (b >>> 10) ^ (b >>> 9) ^ (b >>> 8) ^ (b >>> 6) ^
+ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^ (z << 2) ^
+ (z << 5) ^ (z << 6) ^ (z << 7) ^ (z << 9) ^ (z << 11) ^ (z << 15) ^
+ (z << 17) ^ (z << 22) ^ (z << 29) ^ (z << 31) ^ (z << 32) ^
+ (z << 33) ^ (z << 35) ^ (z << 36) ^ (z << 37) ^ (z << 38) ^
+ (z << 39) ^ (z << 41) ^ (z << 42) ^ (z << 43) ^ (z << 44) ^
+ (z << 45);
+ curr_stream[3] = z;
+
+
+ z = curr_stream[4] & 0xffffffffff800000L;
+ b = z ^ (z << 3);
+ z = (b >>> 40) ^ (b >>> 29) ^ (b >>> 10) ^ (z << 1) ^ (z << 12) ^
+ (z << 31);
+ curr_stream[4] = z;
+
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>.
+ *
+ * @param name name of the stream
+ *
+ */
+ public LFSR258 (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed for the class <TT>LFSR258</TT> to the five
+ * integers of array <TT>seed[0..4]</TT>.
+ * This will be the initial state of the next created stream.
+ * The default seed <A NAME="tex2html2"
+ * HREF="#foot30"><SUP><SPAN CLASS="arabic">2</SPAN></SUP></A> for the first stream is (123456789123456789, 123456789123456789, 123456789123456789,
+ * 123456789123456789, 123456789123456789).
+ * The first, second, third, fourth and fifth integers of <TT>seed</TT>
+ * must be either negative,
+ * or greater than or equal to 2, 512, 4096, 131072 and 8388608 respectively.
+ *
+ * @param seed array of 5 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (long seed[]) {
+ checkSeed (seed);
+ for(int i = 0; i < 5; i++)
+ curr_stream[i] = seed[i];
+ }
+
+ private static void checkSeed (long seed[]) {
+ if (seed.length < 5)
+ throw new IllegalArgumentException("Seed must contain 5 values");
+ if ((seed[0] >= 0 && seed[0] < 2) ||
+ (seed[1] >= 0 && seed[1] < 512) ||
+ (seed[2] >= 0 && seed[2] < 4096) ||
+ (seed[3] >= 0 && seed[3] < 131072) ||
+ (seed[4] >= 0 && seed[4] < 8388608))
+ throw new IllegalArgumentException
+ ("The seed elements must be either negative or greater than 1, 511, 4095, 131071 and 8388607 respectively");
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * seed <TT>seed[0..4]</TT>. The seed must satisfy the same conditions
+ * as in <TT>setPackageSeed</TT>.
+ * This method only affects the specified stream; the others are not
+ * modified, so the beginning of the streams will not be
+ * spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very
+ * exceptional cases; proper use of the <TT>reset...</TT> methods
+ * and of the stream constructor is preferable.
+ *
+ * @param seed array of 5 elements representing the seed
+ *
+ *
+ */
+ public void setSeed (long seed[]) {
+ checkSeed (seed);
+ for(int i = 0; i < 5; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the stream, represented as
+ * an array of five integers.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public long[] getState() {
+ return new long[]{z0, z1, z2, z3, z4};
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public LFSR258 clone() {
+ LFSR258 retour = null;
+ retour = (LFSR258)super.clone();
+ retour.stream = new long[5];
+ retour.substream = new long[5];
+ for (int i = 0; i<5; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }
+
+
+
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < 5; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ z0 = substream[0];
+ z1 = substream[1];
+ z2 = substream[2];
+ z3 = substream[3];
+ z4 = substream[4];
+ }
+
+ public void resetNextSubstream() {
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^100 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^100 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ long z, b;
+
+ z = substream[0] & 0xfffffffffffffffeL;
+ b = z ^ (z << 1);
+ z = (b >>> 61) ^ (b >>> 59) ^ (b >>> 58) ^ (b >>> 57) ^ (b >>> 51) ^
+ (b >>> 47) ^ (b >>> 46) ^ (b >>> 45) ^ (b >>> 43) ^ (b >>> 39) ^
+ (b >>> 30) ^ (b >>> 29) ^ (b >>> 23) ^ (b >>> 15) ^ (z << 2) ^
+ (z << 4) ^ (z << 5) ^ (z << 6) ^ (z << 12) ^ (z << 16) ^
+ (z << 17) ^ (z << 18) ^ (z << 20) ^ (z << 24) ^ (z << 33) ^
+ (z << 34) ^ (z << 40) ^ (z << 48);
+ substream[0] = z;
+
+
+ z = substream[1] & 0xfffffffffffffe00L;
+ b = z ^ (z << 24);
+ z = (b >>> 52) ^ (b >>> 50) ^ (b >>> 49) ^ (b >>> 46) ^ (b >>> 43) ^
+ (b >>> 40) ^ (b >>> 37) ^ (b >>> 34) ^ (b >>> 30) ^ (b >>> 28) ^
+ (b >>> 26) ^ (b >>> 25) ^ (b >>> 23) ^ (b >>> 21) ^ (b >>> 20) ^
+ (b >>> 19) ^ (b >>> 17) ^ (b >>> 15) ^ (b >>> 13) ^ (b >>> 12) ^
+ (b >>> 10) ^ (b >>> 8) ^ (b >>> 7) ^ (b >>> 6) ^ (b >>> 2) ^
+ (z << 1) ^ (z << 4) ^ (z << 6) ^ (z << 7) ^ (z << 11) ^ (z << 14) ^
+ (z << 15) ^ (z << 16) ^ (z << 17) ^ (z << 21) ^ (z << 22) ^
+ (z << 25) ^ (z << 27) ^ (z << 29) ^ (z << 30) ^ (z << 32) ^
+ (z << 34) ^ (z << 35) ^ (z << 36) ^ (z << 38) ^ (z << 40) ^
+ (z << 42) ^ (z << 43) ^ (z << 45) ^ (z << 47) ^ (z << 48) ^
+ (z << 49) ^ (z << 53);
+ substream[1] = z;
+
+
+ z = substream[2] & 0xfffffffffffff000L;
+ b = z ^ (z << 3);
+ z = (b >>> 49) ^ (b >>> 45) ^ (b >>> 41) ^ (b >>> 40) ^ (b >>> 32) ^
+ (b >>> 27) ^ (b >>> 23) ^ (b >>> 14) ^ (b >>> 1) ^ (z << 2) ^
+ (z << 3) ^ (z << 7) ^ (z << 11) ^ (z << 12) ^ (z << 20) ^
+ (z << 25) ^ (z << 29) ^ (z << 38) ^ (z << 51);
+ substream[2] = z;
+
+
+
+ z = substream[3] & 0xfffffffffffe0000L;
+ b = z ^ (z << 5);
+ z = (b >>> 45) ^ (b >>> 32) ^ (b >>> 27) ^ (b >>> 22) ^ (b >>> 17) ^
+ (b >>> 13) ^ (b >>> 12) ^ (b >>> 7) ^ (b >>> 3) ^ (b >>> 2) ^
+ (z << 3) ^ (z << 15) ^ (z << 20) ^ (z << 25) ^ (z << 30) ^
+ (z << 34) ^ (z << 35) ^ (z << 40) ^ (z << 44) ^ (z << 45);
+ substream[3] = z;
+
+
+ z = substream[4] & 0xffffffffff800000L;
+ b = z ^ (z << 3);
+ z = (b >>> 40) ^ (b >>> 39) ^ (b >>> 38) ^ (b >>> 37) ^ (b >>> 35) ^
+ (b >>> 34) ^ (b >>> 31) ^ (b >>> 30) ^ (b >>> 29) ^ (b >>> 28) ^
+ (b >>> 27) ^ (b >>> 26) ^ (b >>> 24) ^ (b >>> 23) ^ (b >>> 21) ^
+ (b >>> 20) ^ (b >>> 18) ^ (b >>> 15) ^ (b >>> 12) ^ (b >>> 10) ^
+ (b >>> 9) ^ (b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^
+ (b >>> 3) ^ (z << 1) ^ (z << 2) ^ (z << 3) ^ (z << 4) ^ (z << 6) ^
+ (z << 7) ^ (z << 10) ^ (z << 11) ^ (z << 12) ^ (z << 13) ^
+ (z << 14) ^ (z << 15) ^ (z << 17) ^ (z << 18) ^ (z << 20) ^
+ (z << 21) ^ (z << 23) ^ (z << 26) ^ (z << 29) ^ (z << 31) ^
+ (z << 32) ^ (z << 34) ^ (z << 35) ^ (z << 36) ^ (z << 37) ^
+ (z << 38);
+ substream[4] = z;
+
+ resetStartSubstream();
+ }
+
+
+ public String toString() {
+ if (name == null)
+ return "The state of the LFSR258 is: " +
+ z0 + "L, " + z1 + "L, " + z2 + "L, " + z3 + "L, " + z4 + "L";
+ else
+ return "The state of " + name + " is: " +
+ z0 + "L, " + z1 + "L, " + z2 + "L, " + z3 + "L, " + z4 + "L";
+ }
+
+
+ private long nextNumber() {
+ long b;
+ b = (((z0 << 1) ^ z0) >>> 53);
+ z0 = (((z0 & 0xFFFFFFFFFFFFFFFEL) << 10) ^ b);
+ b = (((z1 << 24) ^ z1) >>> 50);
+ z1 = (((z1 & 0xFFFFFFFFFFFFFE00L) << 5) ^ b);
+ b = (((z2 << 3) ^ z2) >>> 23);
+ z2 = (((z2 & 0xFFFFFFFFFFFFF000L) << 29) ^ b);
+ b = (((z3 << 5) ^ z3) >>> 24);
+ z3 = (((z3 & 0xFFFFFFFFFFFE0000L) << 23) ^ b);
+ b = (((z4 << 3) ^ z4) >>> 33);
+ z4 = (((z4 & 0xFFFFFFFFFF800000L) << 8) ^ b);
+
+ return (z0 ^ z1 ^ z2 ^ z3 ^ z4);
+ }
+
+
+ protected double nextValue() {
+
+ long res = nextNumber();
+ if (res <= 0)
+ return (res * NORM + MAX);
+ else
+ return res * NORM;
+ }
+
+
+ public int nextInt (int i, int j) {
+ if (i > j)
+ throw new IllegalArgumentException(i + " is larger than " + j + ".");
+ long d = j-i+1;
+ long q = 0x4000000000000000L / d;
+ long r = 0x4000000000000000L % d;
+ long res;
+
+ do {
+ res = nextNumber() >>> 2;
+ } while (res >= 0x4000000000000000L - r);
+
+ return i + (int) (res / q);
+ }
+
+
+ /*
+ Methodes qui permettent de generer les series de shifts des
+ methodes de sauts en avant.
+
+
+ Preuve que la fonction resultante est bien equivalente a la matrice
+ fournie :
+
+ Soit M la matrice de transition pour une iteration simple du Tausworthe
+ d'une seule composante. (Il est a noter que chaque appel de
+ nextValue fait s iterations simples pour chaque composantes.)
+
+ On prend I la matrice identite. (I = M^0)
+ Chaque colonne de I represente un vecteur d'etat pour le generateur.
+ Puisque les (64 - k) bits les moins significatifs du vecteur d'etat
+ ne sont pas repris par la recurrence a long terme, c'est-a-dire que
+ la correlation entre leurs valeurs initiales et la valeur de l'etat
+ (64 - k) iterations simples plus loin est nulle. Donc, a partir de
+ M^(64 - k), les (64 - k) premieres colonnes de la matrice sont nulles.
+ Puisque l'on traite qu'avec de tres larges exposants, on peut
+ supposer que ces colonnes sont nulles, ce qui est equivalent a
+ mettre a 0 les (64 - k) premiers bits.
+
+ Les colonnes qui suivent dans I, jusqu'aux q dernieres, sont toutes
+ a une iteration simple d'ecart. Lorsque la matrice I va etre multipliee
+ un certain nombre de fois par M pour donner M^x, puisque cette operation
+ est equivalente a faire x iterations simples, ces colonnes resteront
+ a une iteration simple d'ecart. Puisque l'effet d'une iteration simple
+ consiste simplement de faire un shift deplacant tous les bits d'une
+ position ainsi que de remplir le bit vide ainsi cree par une nouvelle
+ valeur, alors les differentes colonnes ne different que ce shift et
+ leurs extremites. La consequence est que, pour ces colonnes, tous les
+ bits sur la meme diagonale sont egaux. Il suffit alors de prendre les
+ valeurs aux debuts des diagonales (dernieres rangee et colonne) et
+ d'en ressortir les shifts puisqu'un shift est equivalent a une
+ translation des bits de la matrice identite, donc equivalent a une
+ diagonale dans la matrice.
+
+ Le meme argument peut etre applique aux q dernieres colonnes de la
+ matrice. Il y a cependant un fracture entre la q-ieme derniere colonne
+ et la (q+1)-ieme derniere. Cet ecart vient du fait que, dans I, ces
+ deux colonnes ne sont pas a une iteration simple d'ecart. Puisque
+ la recurrence qui definit l'iteration simple est
+ x_n = x_(n-(k-q)) ^ x_(n-k), le nouveau bit qui s'ajoute au debut
+ de la partie significative (k premiers bits) du vecteur d'etat est
+ la somme binaire du dernier bit et de (q+1)-ieme dernier bit. Donc,
+ la (q+1)-ieme derniere colonne est equivalente a la combinaison
+ lineaire de la derniere et du resultat d'une iteration simple sur la
+ q-ieme derniere colonne. La consequence est premierement que
+ toutes diagonales (donc shift) partant de la derniere colonne continue
+ apres la (q+1)-ieme colonne. Secondement, pour chacune de ces diagonales,
+ une autre diagonale commence a la meme rangee, mais a la (q+1)-ieme
+ colonne, ce qui est equivalent a un autre shift, mais avec un masque
+ pour couper les q derniers bits du shift. La combinaison de ces deux
+ diagonales est b = z ^ (z << q).
+
+
+
+ private static void analyseMat(BitMatrix bm, int decal, int signif) {
+ //note : decal = q, signif = k (selon la notation de l'article)
+
+ System.out.println("z = z & 0x" +
+ Long.toHexString(-1 << (64 - signif)) + "L;");
+ System.out.println("b = z ^ (z << " + decal + ");");
+
+ StringBuffer sb = new StringBuffer("z =");
+ for(int i = (64 - signif); i < 63; i++)
+ if(bm.getBool(i, 63))
+ sb.append(" (b >>> " + (63 - i) + ") ^");
+
+ for(int i = 0; i < 64; i++)
+ if(bm.getBool(63, 63 - i))
+ sb.append(" (z << " + i + ") ^");
+ sb.setCharAt(sb.length() - 1, ';');
+
+
+ System.out.println(sb);
+ }
+
+
+ public static void main(String[] args) {
+ BitMatrix bm;
+ BitVector[] bv = new BitVector[64];
+ LFSR258 gen = new LFSR258();
+
+ for(int i = 0; i < 64; i++) {
+ gen.z0 = 1L << i;
+ gen.nextValue();
+ bv[i] = new BitVector(new int[]{(int)gen.z0,
+ (int)(gen.z0 >>> 32)});
+ }
+
+ bm = (new BitMatrix(bv)).transpose();
+
+ int W = 100;
+ int Z = 200;
+
+ BitMatrix bmPw = bm.power2e(W);
+ BitMatrix bmPz = bm.power2e(Z);
+
+ analyseMat(bmPw, 1, 63);
+ analyseMat(bmPz, 1, 63);
+ }
+ */
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/LFSR258.tex b/source/umontreal/iro/lecuyer/rng/LFSR258.tex
new file mode 100644
index 0000000..eb36ddf
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/LFSR258.tex
@@ -0,0 +1,532 @@
+\defmodule {LFSR258}
+
+Extends \class{RandomStreamBase} using a 64-bit composite linear feedback
+shift register (LFSR) (or Tausworthe) RNG as defined in
+\cite{rLEC96a,rTEZ91b}.
+This generator is the \texttt{LFSR258} proposed in \cite{rLEC99a}.
+It has five components combined by a bitwise xor.
+Its period length is \html{approximatively 2^{258}}
+\latex{$\rho\approx 2^{258}$}. The values of $V$, $W$ and $Z$ are $2^{100}$,
+$2^{100}$ and $2^{200}$ respectively (see \class{RandomStream} for their
+definition). The seed of the RNG, and the state of a stream at any given
+step, are five-dimensional vectors of 64-bit integers.
+The default initial seed \footnote{In previous versions, it was
+(1234567890, 1234567890, 1234567890, 1234567890, 1234567890).}
+ of the RNG is (123456789123456789, 123456789123456789, 123456789123456789,
+ 123456789123456789, 123456789123456789).
+The \texttt{nextValue} method returns numbers with 53 bits of precision.
+This generator is fast for 64-bit machines.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LFSR258
+ * Description: 64-bit composite linear feedback shift register proposed by L'Ecuyer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import java.io.Serializable;
+/*
+import umontreal.iro.lecuyer.util.BitVector;
+import umontreal.iro.lecuyer.util.BitMatrix;
+*/
+\end{hide}
+
+public class LFSR258 extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 140406L;
+ //La date de modification a l'envers, lire 06/04/2014
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //private static final double NORM = 5.4210108624275221e-20;
+
+ //equivalent a NORM = 1.0 / 0xFFFFFFFFFFFFF800L
+ private static final double NORM = 0.5 / 0x7FFFFFFFFFFFFC00L;
+ private static final double MAX = 0xFFFFFFFFFFFFF800L * NORM + 1.0;
+ //MAX = plus grand double < 1.0
+
+ private static final long GERME = 123456789123456789L;
+
+ private long z0, z1, z2, z3, z4; // l'etat
+
+ //stream and substream variables :
+ private long[] stream;
+ private long[] substream;
+ private static long[] curr_stream = {GERME, GERME, GERME, GERME, GERME};
+
+ \end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public LFSR258() \begin{hide} {
+ name = null;
+
+ stream = new long[5];
+ substream = new long[5];
+
+ for(int i = 0; i < 5; i++)
+ stream[i] = curr_stream[i];
+
+ resetStartStream();
+
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^200 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^200 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ long z, b;
+
+ z = curr_stream[0] & 0xfffffffffffffffeL;
+ b = z ^ (z << 1);
+ z = (b >>> 58) ^ (b >>> 55) ^ (b >>> 46) ^ (b >>> 43) ^ (z << 5) ^
+ (z << 8) ^ (z << 17) ^ (z << 20);
+ curr_stream[0] = z;
+
+
+ z = curr_stream[1] & 0xfffffffffffffe00L;
+ b = z ^ (z << 24);
+ z = (b >>> 54) ^ (b >>> 53) ^ (b >>> 52) ^ (b >>> 50) ^ (b >>> 49) ^
+ (b >>> 48) ^ (b >>> 43) ^ (b >>> 41) ^ (b >>> 38) ^ (b >>> 37) ^
+ (b >>> 30) ^ (b >>> 25) ^ (b >>> 24) ^ (b >>> 23) ^ (b >>> 19) ^
+ (b >>> 16) ^ (b >>> 15) ^ (b >>> 14) ^ (b >>> 13) ^ (b >>> 11) ^
+ (b >>> 8) ^ (b >>> 7) ^ (b >>> 5) ^ (b >>> 3) ^ (z << 0) ^
+ (z << 2) ^ (z << 3) ^ (z << 6) ^ (z << 7) ^ (z << 8) ^ (z << 9) ^
+ (z << 10) ^ (z << 11) ^ (z << 12) ^ (z << 13) ^ (z << 14) ^
+ (z << 16) ^ (z << 18) ^ (z << 19) ^ (z << 21) ^ (z << 25) ^
+ (z << 30) ^ (z << 31) ^ (z << 32) ^ (z << 36) ^ (z << 39) ^
+ (z << 40) ^ (z << 41) ^ (z << 42) ^ (z << 44) ^ (z << 47) ^
+ (z << 48) ^ (z << 50) ^ (z << 52);
+ curr_stream[1] = z;
+
+
+ z = curr_stream[2] & 0xfffffffffffff000L;
+ b = z ^ (z << 3);
+ z = (b >>> 50) ^ (b >>> 49) ^ (b >>> 46) ^ (b >>> 42) ^ (b >>> 40) ^
+ (b >>> 39) ^ (b >>> 38) ^ (b >>> 37) ^ (b >>> 36) ^ (b >>> 32) ^
+ (b >>> 29) ^ (b >>> 28) ^ (b >>> 27) ^ (b >>> 25) ^ (b >>> 23) ^
+ (b >>> 20) ^ (b >>> 19) ^ (b >>> 15) ^ (b >>> 12) ^ (b >>> 11) ^
+ (b >>> 2) ^ (z << 1) ^ (z << 2) ^ (z << 3) ^ (z << 6) ^ (z << 10) ^
+ (z << 12) ^ (z << 13) ^ (z << 14) ^ (z << 15) ^ (z << 16) ^
+ (z << 20) ^ (z << 23) ^ (z << 24) ^ (z << 25) ^ (z << 27) ^
+ (z << 29) ^ (z << 32) ^ (z << 33) ^ (z << 37) ^ (z << 40) ^
+ (z << 41) ^ (z << 50);
+ curr_stream[2] = z;
+
+
+ z = curr_stream[3] & 0xfffffffffffe0000L;
+ b = z ^ (z << 5);
+ z = (b >>> 46) ^ (b >>> 44) ^ (b >>> 42) ^ (b >>> 41) ^ (b >>> 40) ^
+ (b >>> 38) ^ (b >>> 36) ^ (b >>> 32) ^ (b >>> 30) ^ (b >>> 25) ^
+ (b >>> 18) ^ (b >>> 16) ^ (b >>> 15) ^ (b >>> 14) ^ (b >>> 12) ^
+ (b >>> 11) ^ (b >>> 10) ^ (b >>> 9) ^ (b >>> 8) ^ (b >>> 6) ^
+ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^ (z << 2) ^
+ (z << 5) ^ (z << 6) ^ (z << 7) ^ (z << 9) ^ (z << 11) ^ (z << 15) ^
+ (z << 17) ^ (z << 22) ^ (z << 29) ^ (z << 31) ^ (z << 32) ^
+ (z << 33) ^ (z << 35) ^ (z << 36) ^ (z << 37) ^ (z << 38) ^
+ (z << 39) ^ (z << 41) ^ (z << 42) ^ (z << 43) ^ (z << 44) ^
+ (z << 45);
+ curr_stream[3] = z;
+
+
+ z = curr_stream[4] & 0xffffffffff800000L;
+ b = z ^ (z << 3);
+ z = (b >>> 40) ^ (b >>> 29) ^ (b >>> 10) ^ (z << 1) ^ (z << 12) ^
+ (z << 31);
+ curr_stream[4] = z;
+
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream.
+\end{tabb}
+\begin{code}
+
+ public LFSR258 (String name) \begin{hide} {
+ this();
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (long seed[]) \begin{hide} {
+ checkSeed (seed);
+ for(int i = 0; i < 5; i++)
+ curr_stream[i] = seed[i];
+ }
+
+ private static void checkSeed (long seed[]) {
+ if (seed.length < 5)
+ throw new IllegalArgumentException("Seed must contain 5 values");
+ if ((seed[0] >= 0 && seed[0] < 2) ||
+ (seed[1] >= 0 && seed[1] < 512) ||
+ (seed[2] >= 0 && seed[2] < 4096) ||
+ (seed[3] >= 0 && seed[3] < 131072) ||
+ (seed[4] >= 0 && seed[4] < 8388608))
+ throw new IllegalArgumentException
+ ("The seed elements must be either negative or greater than 1, 511, 4095, 131071 and 8388607 respectively");
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed for the class \texttt{LFSR258} to the five
+ integers of array \texttt{seed[0..4]}.
+ This will be the initial state of the next created stream.
+ The default seed \footnote{In previous versions, it was
+ (1234567890, 1234567890, 1234567890, 1234567890, 1234567890).}
+ for the first stream is (123456789123456789, 123456789123456789, 123456789123456789,
+ 123456789123456789, 123456789123456789).
+ The first, second, third, fourth and fifth integers of \texttt{seed}
+ must be either negative,
+ or greater than or equal to 2, 512, 4096, 131072 and 8388608 respectively.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 5 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (long seed[]) \begin{hide} {
+ checkSeed (seed);
+ for(int i = 0; i < 5; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ seed \texttt{seed[0..4]}. The seed must satisfy the same conditions
+ as in \texttt{setPackageSeed}.
+ This method only affects the specified stream; the others are not
+ modified, so the beginning of the streams will not be
+ spaced $Z$ values apart.
+ For this reason, this method should only be used in very
+ exceptional cases; proper use of the \texttt{reset...} methods
+ and of the stream constructor is preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 5 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public long[] getState() \begin{hide} {
+ return new long[]{z0, z1, z2, z3, z4};
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the stream, represented as
+ an array of five integers.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}
+
+ public LFSR258 clone() \begin{hide} {
+ LFSR258 retour = null;
+ retour = (LFSR258)super.clone();
+ retour.stream = new long[5];
+ retour.substream = new long[5];
+ for (int i = 0; i<5; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < 5; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ z0 = substream[0];
+ z1 = substream[1];
+ z2 = substream[2];
+ z3 = substream[3];
+ z4 = substream[4];
+ }
+
+ public void resetNextSubstream() {
+ // Les operations qui suivent permettent de faire sauter en avant
+ // de 2^100 iterations chacunes des composantes du generateur.
+ // L'etat interne apres le saut est cependant legerement different
+ // de celui apres 2^100 iterations puisqu'il ignore l'etat dans
+ // lequel se retrouvent les premiers bits de chaque composantes,
+ // puisqu'ils sont ignores dans la recurrence. L'etat redevient
+ // identique a ce que l'on aurait avec des iterations normales
+ // apres un appel a nextValue().
+
+ long z, b;
+
+ z = substream[0] & 0xfffffffffffffffeL;
+ b = z ^ (z << 1);
+ z = (b >>> 61) ^ (b >>> 59) ^ (b >>> 58) ^ (b >>> 57) ^ (b >>> 51) ^
+ (b >>> 47) ^ (b >>> 46) ^ (b >>> 45) ^ (b >>> 43) ^ (b >>> 39) ^
+ (b >>> 30) ^ (b >>> 29) ^ (b >>> 23) ^ (b >>> 15) ^ (z << 2) ^
+ (z << 4) ^ (z << 5) ^ (z << 6) ^ (z << 12) ^ (z << 16) ^
+ (z << 17) ^ (z << 18) ^ (z << 20) ^ (z << 24) ^ (z << 33) ^
+ (z << 34) ^ (z << 40) ^ (z << 48);
+ substream[0] = z;
+
+
+ z = substream[1] & 0xfffffffffffffe00L;
+ b = z ^ (z << 24);
+ z = (b >>> 52) ^ (b >>> 50) ^ (b >>> 49) ^ (b >>> 46) ^ (b >>> 43) ^
+ (b >>> 40) ^ (b >>> 37) ^ (b >>> 34) ^ (b >>> 30) ^ (b >>> 28) ^
+ (b >>> 26) ^ (b >>> 25) ^ (b >>> 23) ^ (b >>> 21) ^ (b >>> 20) ^
+ (b >>> 19) ^ (b >>> 17) ^ (b >>> 15) ^ (b >>> 13) ^ (b >>> 12) ^
+ (b >>> 10) ^ (b >>> 8) ^ (b >>> 7) ^ (b >>> 6) ^ (b >>> 2) ^
+ (z << 1) ^ (z << 4) ^ (z << 6) ^ (z << 7) ^ (z << 11) ^ (z << 14) ^
+ (z << 15) ^ (z << 16) ^ (z << 17) ^ (z << 21) ^ (z << 22) ^
+ (z << 25) ^ (z << 27) ^ (z << 29) ^ (z << 30) ^ (z << 32) ^
+ (z << 34) ^ (z << 35) ^ (z << 36) ^ (z << 38) ^ (z << 40) ^
+ (z << 42) ^ (z << 43) ^ (z << 45) ^ (z << 47) ^ (z << 48) ^
+ (z << 49) ^ (z << 53);
+ substream[1] = z;
+
+
+ z = substream[2] & 0xfffffffffffff000L;
+ b = z ^ (z << 3);
+ z = (b >>> 49) ^ (b >>> 45) ^ (b >>> 41) ^ (b >>> 40) ^ (b >>> 32) ^
+ (b >>> 27) ^ (b >>> 23) ^ (b >>> 14) ^ (b >>> 1) ^ (z << 2) ^
+ (z << 3) ^ (z << 7) ^ (z << 11) ^ (z << 12) ^ (z << 20) ^
+ (z << 25) ^ (z << 29) ^ (z << 38) ^ (z << 51);
+ substream[2] = z;
+
+
+
+ z = substream[3] & 0xfffffffffffe0000L;
+ b = z ^ (z << 5);
+ z = (b >>> 45) ^ (b >>> 32) ^ (b >>> 27) ^ (b >>> 22) ^ (b >>> 17) ^
+ (b >>> 13) ^ (b >>> 12) ^ (b >>> 7) ^ (b >>> 3) ^ (b >>> 2) ^
+ (z << 3) ^ (z << 15) ^ (z << 20) ^ (z << 25) ^ (z << 30) ^
+ (z << 34) ^ (z << 35) ^ (z << 40) ^ (z << 44) ^ (z << 45);
+ substream[3] = z;
+
+
+ z = substream[4] & 0xffffffffff800000L;
+ b = z ^ (z << 3);
+ z = (b >>> 40) ^ (b >>> 39) ^ (b >>> 38) ^ (b >>> 37) ^ (b >>> 35) ^
+ (b >>> 34) ^ (b >>> 31) ^ (b >>> 30) ^ (b >>> 29) ^ (b >>> 28) ^
+ (b >>> 27) ^ (b >>> 26) ^ (b >>> 24) ^ (b >>> 23) ^ (b >>> 21) ^
+ (b >>> 20) ^ (b >>> 18) ^ (b >>> 15) ^ (b >>> 12) ^ (b >>> 10) ^
+ (b >>> 9) ^ (b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^
+ (b >>> 3) ^ (z << 1) ^ (z << 2) ^ (z << 3) ^ (z << 4) ^ (z << 6) ^
+ (z << 7) ^ (z << 10) ^ (z << 11) ^ (z << 12) ^ (z << 13) ^
+ (z << 14) ^ (z << 15) ^ (z << 17) ^ (z << 18) ^ (z << 20) ^
+ (z << 21) ^ (z << 23) ^ (z << 26) ^ (z << 29) ^ (z << 31) ^
+ (z << 32) ^ (z << 34) ^ (z << 35) ^ (z << 36) ^ (z << 37) ^
+ (z << 38);
+ substream[4] = z;
+
+ resetStartSubstream();
+ }
+
+
+ public String toString() {
+ if (name == null)
+ return "The state of the LFSR258 is: " +
+ z0 + "L, " + z1 + "L, " + z2 + "L, " + z3 + "L, " + z4 + "L";
+ else
+ return "The state of " + name + " is: " +
+ z0 + "L, " + z1 + "L, " + z2 + "L, " + z3 + "L, " + z4 + "L";
+ }
+
+
+ private long nextNumber() {
+ long b;
+ b = (((z0 << 1) ^ z0) >>> 53);
+ z0 = (((z0 & 0xFFFFFFFFFFFFFFFEL) << 10) ^ b);
+ b = (((z1 << 24) ^ z1) >>> 50);
+ z1 = (((z1 & 0xFFFFFFFFFFFFFE00L) << 5) ^ b);
+ b = (((z2 << 3) ^ z2) >>> 23);
+ z2 = (((z2 & 0xFFFFFFFFFFFFF000L) << 29) ^ b);
+ b = (((z3 << 5) ^ z3) >>> 24);
+ z3 = (((z3 & 0xFFFFFFFFFFFE0000L) << 23) ^ b);
+ b = (((z4 << 3) ^ z4) >>> 33);
+ z4 = (((z4 & 0xFFFFFFFFFF800000L) << 8) ^ b);
+
+ return (z0 ^ z1 ^ z2 ^ z3 ^ z4);
+ }
+
+
+ protected double nextValue() {
+
+ long res = nextNumber();
+ if (res <= 0)
+ return (res * NORM + MAX);
+ else
+ return res * NORM;
+ }
+
+
+ public int nextInt (int i, int j) {
+ if (i > j)
+ throw new IllegalArgumentException(i + " is larger than " + j + ".");
+ long d = j-i+1;
+ long q = 0x4000000000000000L / d;
+ long r = 0x4000000000000000L % d;
+ long res;
+
+ do {
+ res = nextNumber() >>> 2;
+ } while (res >= 0x4000000000000000L - r);
+
+ return i + (int) (res / q);
+ }
+
+
+ /*
+ Methodes qui permettent de generer les series de shifts des
+ methodes de sauts en avant.
+
+
+ Preuve que la fonction resultante est bien equivalente a la matrice
+ fournie :
+
+ Soit M la matrice de transition pour une iteration simple du Tausworthe
+ d'une seule composante. (Il est a noter que chaque appel de
+ nextValue fait s iterations simples pour chaque composantes.)
+
+ On prend I la matrice identite. (I = M^0)
+ Chaque colonne de I represente un vecteur d'etat pour le generateur.
+ Puisque les (64 - k) bits les moins significatifs du vecteur d'etat
+ ne sont pas repris par la recurrence a long terme, c'est-a-dire que
+ la correlation entre leurs valeurs initiales et la valeur de l'etat
+ (64 - k) iterations simples plus loin est nulle. Donc, a partir de
+ M^(64 - k), les (64 - k) premieres colonnes de la matrice sont nulles.
+ Puisque l'on traite qu'avec de tres larges exposants, on peut
+ supposer que ces colonnes sont nulles, ce qui est equivalent a
+ mettre a 0 les (64 - k) premiers bits.
+
+ Les colonnes qui suivent dans I, jusqu'aux q dernieres, sont toutes
+ a une iteration simple d'ecart. Lorsque la matrice I va etre multipliee
+ un certain nombre de fois par M pour donner M^x, puisque cette operation
+ est equivalente a faire x iterations simples, ces colonnes resteront
+ a une iteration simple d'ecart. Puisque l'effet d'une iteration simple
+ consiste simplement de faire un shift deplacant tous les bits d'une
+ position ainsi que de remplir le bit vide ainsi cree par une nouvelle
+ valeur, alors les differentes colonnes ne different que ce shift et
+ leurs extremites. La consequence est que, pour ces colonnes, tous les
+ bits sur la meme diagonale sont egaux. Il suffit alors de prendre les
+ valeurs aux debuts des diagonales (dernieres rangee et colonne) et
+ d'en ressortir les shifts puisqu'un shift est equivalent a une
+ translation des bits de la matrice identite, donc equivalent a une
+ diagonale dans la matrice.
+
+ Le meme argument peut etre applique aux q dernieres colonnes de la
+ matrice. Il y a cependant un fracture entre la q-ieme derniere colonne
+ et la (q+1)-ieme derniere. Cet ecart vient du fait que, dans I, ces
+ deux colonnes ne sont pas a une iteration simple d'ecart. Puisque
+ la recurrence qui definit l'iteration simple est
+ x_n = x_(n-(k-q)) ^ x_(n-k), le nouveau bit qui s'ajoute au debut
+ de la partie significative (k premiers bits) du vecteur d'etat est
+ la somme binaire du dernier bit et de (q+1)-ieme dernier bit. Donc,
+ la (q+1)-ieme derniere colonne est equivalente a la combinaison
+ lineaire de la derniere et du resultat d'une iteration simple sur la
+ q-ieme derniere colonne. La consequence est premierement que
+ toutes diagonales (donc shift) partant de la derniere colonne continue
+ apres la (q+1)-ieme colonne. Secondement, pour chacune de ces diagonales,
+ une autre diagonale commence a la meme rangee, mais a la (q+1)-ieme
+ colonne, ce qui est equivalent a un autre shift, mais avec un masque
+ pour couper les q derniers bits du shift. La combinaison de ces deux
+ diagonales est b = z ^ (z << q).
+
+
+
+ private static void analyseMat(BitMatrix bm, int decal, int signif) {
+ //note : decal = q, signif = k (selon la notation de l'article)
+
+ System.out.println("z = z & 0x" +
+ Long.toHexString(-1 << (64 - signif)) + "L;");
+ System.out.println("b = z ^ (z << " + decal + ");");
+
+ StringBuffer sb = new StringBuffer("z =");
+ for(int i = (64 - signif); i < 63; i++)
+ if(bm.getBool(i, 63))
+ sb.append(" (b >>> " + (63 - i) + ") ^");
+
+ for(int i = 0; i < 64; i++)
+ if(bm.getBool(63, 63 - i))
+ sb.append(" (z << " + i + ") ^");
+ sb.setCharAt(sb.length() - 1, ';');
+
+
+ System.out.println(sb);
+ }
+
+
+ public static void main(String[] args) {
+ BitMatrix bm;
+ BitVector[] bv = new BitVector[64];
+ LFSR258 gen = new LFSR258();
+
+ for(int i = 0; i < 64; i++) {
+ gen.z0 = 1L << i;
+ gen.nextValue();
+ bv[i] = new BitVector(new int[]{(int)gen.z0,
+ (int)(gen.z0 >>> 32)});
+ }
+
+ bm = (new BitMatrix(bv)).transpose();
+
+ int W = 100;
+ int Z = 200;
+
+ BitMatrix bmPw = bm.power2e(W);
+ BitMatrix bmPz = bm.power2e(Z);
+
+ analyseMat(bmPw, 1, 63);
+ analyseMat(bmPz, 1, 63);
+ }
+ */
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/MRG31k3p.java b/source/umontreal/iro/lecuyer/rng/MRG31k3p.java
new file mode 100644
index 0000000..95ffdb3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MRG31k3p.java
@@ -0,0 +1,349 @@
+
+
+/*
+ * Class: MRG31k3p
+ * Description: combined multiple recursive generator proposed by L'Ecuyer and Touzin
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.rng.RandomStreamBase;
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import java.io.Serializable;
+
+
+/**
+ * Extends the abstract class {@link RandomStreamBase}, thus
+ * implementing the {@link RandomStream} interface indirectly. The backbone
+ * generator is the combined multiple recursive generator (CMRG) <TT>MRG31k3p</TT>
+ * proposed by L'Ecuyer and Touzin,
+ * implemented in 32-bit integer arithmetic.
+ * This RNG has a period length of approximately
+ * <SPAN CLASS="MATH">2<SUP>185</SUP></SPAN>. The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>62</SUP></SPAN>,
+ * <SPAN CLASS="MATH">2<SUP>72</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>134</SUP></SPAN> respectively. (See {@link RandomStream} for their
+ * definition.) The seed and the state of a stream at any given step
+ * are six-dimensional vectors of 32-bit integers.
+ * The default initial seed is
+ * <SPAN CLASS="MATH">(12345, 12345, 12345, 12345, 12345, 12345)</SPAN>.
+ * The method <TT>nextValue</TT> provides 31 bits of precision.
+ *
+ * <P>
+ * The difference between the RNG of class {@link MRG32k3a} and this one is
+ * that this one has all its coefficients of the form
+ *
+ * <SPAN CLASS="MATH"><I>a</I> = ±2<SUP>q</SUP>±2<SUP>r</SUP></SPAN>. This permits a faster implementation than for
+ * arbitrary coefficients. On a 32-bit computer, <TT>MRG31k3p</TT> is about twice
+ * as fast as <TT>MRG32k3a</TT>.
+ * On the other hand, the latter does a little better in the spectral test
+ * and has been more extensively tested.
+ *
+ */
+public class MRG31k3p extends RandomStreamBase {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //generator constants :
+ private static final int M1 = 2147483647; //2^31 - 1
+ private static final int M2 = 2147462579; //2^31 - 21069
+ private static final int MASK12 = 511; //2^9 - 1
+ private static final int MASK13 = 16777215; //2^24 - 1
+ private static final int MASK2 = 65535; //2^16 - 1
+ private static final int MULT2 = 21069;
+ private static final double NORM = 4.656612873077392578125e-10;
+ //private static final double NORM = 1.0 / (M1 + 1.0);
+
+ //state variables
+ private int x11;
+ private int x12;
+ private int x13;
+ private int x21;
+ private int x22;
+ private int x23;
+
+ //stream and substream variables
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream = {12345, 12345, 12345,
+ 12345, 12345, 12345};
+
+ //streams constants :
+ private static final int[][] A1p0 =
+ {{0, 4194304, 129},
+ {1, 0, 0},
+ {0, 1, 0}};
+ private static final int[][] A2p0 =
+ {{32768, 0, 32769},
+ {1, 0, 0},
+ {0, 1, 0}};
+
+ private static final int[][] A1p72 =
+ {{1516919229, 758510237, 499121365},
+ {1884998244, 1516919229, 335398200},
+ {601897748, 1884998244, 358115744}};
+ private static final int[][] A2p72 =
+ {{1228857673, 1496414766, 954677935},
+ {1133297478, 1407477216, 1496414766},
+ {2002613992, 1639496704, 1407477216}};
+
+ private static final int[][] A1p134 =
+ {{1702500920, 1849582496, 1656874625},
+ {828554832, 1702500920, 1512419905},
+ {1143731069, 828554832, 102237247}};
+ private static final int[][] A2p134 =
+ {{796789021, 1464208080, 607337906},
+ {1241679051, 1431130166, 1464208080},
+ {1401213391, 1178684362, 1431130166}};
+
+
+ //multiply the first half of v by A with a modulo of m1
+ //and the second half by B with a modulo of m2
+ private static void multMatVect(int[] v, int[][] A, int m1,
+ int[][] B, int m2) {
+ int[] vv = new int[3];
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i];
+ ArithmeticMod.matVecModM(A, vv, vv, m1);
+ for(int i = 0; i < 3; i++)
+ v[i] = vv[i];
+
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i + 3];
+ ArithmeticMod.matVecModM(B, vv, vv, m2);
+ for(int i = 0; i < 3; i++)
+ v[i + 3] = vv[i];
+
+ }
+
+
+
+ /**
+ * Constructs a new stream, initialized at its beginning.
+ * Its seed is
+ * <SPAN CLASS="MATH"><I>Z</I> = 2<SUP>134</SUP></SPAN> steps away from the previous seed.
+ *
+ */
+ public MRG31k3p() {
+ name = null;
+
+ prec53 = false;
+ anti = false;
+
+ stream = new int[6];
+ substream = new int[6];
+ for(int i = 0; i < 6; i++)
+ stream[i] = curr_stream[i];
+
+ resetStartStream();
+
+ multMatVect(curr_stream, A1p134, M1, A2p134, M2);
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used when formatting the stream state).
+ *
+ * @param name name of the stream
+ *
+ */
+ public MRG31k3p (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed for the class <TT>MRG31k3p</TT> to the six
+ * integers of the vector <TT>seed[0..5]</TT>.
+ * This will be the initial state (or seed) of the next created stream.
+ * By default, if this method is not called, the first stream is created with
+ * the seed
+ * <SPAN CLASS="MATH">(12345, 12345, 12345, 12345, 12345, 12345)</SPAN>. If it is called,
+ * the first 3 values of the seed must all be less than
+ * <SPAN CLASS="MATH"><I>m</I><SUB>1</SUB> = 2147483647</SPAN>,
+ * and not all 0; and the last 3 values must all be less than
+ * <SPAN CLASS="MATH"><I>m</I><SUB>2</SUB> = 2147462579</SPAN>, and not all 0.
+ *
+ * @param seed array of 6 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (int seed[]) {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[5] == 0 && seed[3] == 0 && seed[4] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ if (seed[0] >= M1 || seed[1] >= M1 || seed[2] >= M1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + M1);
+
+ if (seed[5] >= M2 || seed[3] >= M2 || seed[4] >= M2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + M2);
+ for (int i = 0; i < 6; ++i)
+ curr_stream[i] = seed[i];
+ }
+
+
+ /**
+ * <SPAN CLASS="textit">Use of this method is strongly discouraged</SPAN>.
+ * Initializes the stream at the beginning of a stream with the
+ * initial seed <TT>seed[0..5]</TT>. This vector must satisfy the same conditions
+ * as in <TT>setPackageSeed</TT>.
+ * This method only affects the specified stream, all the others are not
+ * modified, so the beginning of the streams are no longer spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values
+ * apart.
+ * For this reason, this method should be used only in very
+ * exceptional situations; proper use of <TT>reset...</TT>
+ * and of the stream constructor is preferable.
+ *
+ * @param seed array of 6 integers representing the new seed
+ *
+ *
+ */
+ public void setSeed (int seed[]) {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ if (seed[0] >= M1 || seed[1] >= M1 || seed[2] >= M1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + M1);
+ if (seed[3] >= M2 || seed[4] >= M2 || seed[5] >= M2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + M2);
+ for (int i = 0; i < 6; ++i)
+ stream[i] = seed[i];
+ resetStartStream();
+ }
+
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < 6; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ x11 = substream[0];
+ x12 = substream[1];
+ x13 = substream[2];
+ x21 = substream[3];
+ x22 = substream[4];
+ x23 = substream[5];
+ }
+
+ public void resetNextSubstream() {
+ multMatVect(substream, A1p72, M1, A2p72, M2);
+ resetStartSubstream();
+ }
+
+
+ /**
+ * Returns the current state <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> of this stream.
+ * This is a vector of 6 integers represented.
+ * This method is convenient if we want to save the state for
+ * subsequent use.
+ *
+ * @return the current state of the generator
+ *
+ */
+ public int[] getState() {
+ return new int[]{x11, x12, x13, x21, x22, x23};
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public MRG31k3p clone() {
+ MRG31k3p retour = null;
+ retour = (MRG31k3p)super.clone();
+ retour.substream = new int[6];
+ retour.stream = new int[6];
+ for (int i = 0; i<6; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }
+
+
+ public String toString() {
+ if(name == null)
+ return "The state of the MRG31k3p is: " +
+ x11 + ", " + x12 + ", " + x13 + "; " +
+ x21 + ", " + x22 + ", " + x23;
+ else
+ return "The state of " + name + " is: " +
+ x11 + ", " + x12 + ", " + x13 + "; " +
+ x21 + ", " + x22 + ", " + x23;
+ }
+
+ protected double nextValue() {
+ int y1, y2;
+
+ //first component
+ y1 = ((x12 & MASK12) << 22) + (x12 >>> 9)
+ + ((x13 & MASK13) << 7) + (x13 >>> 24);
+ if(y1 < 0 || y1 >= M1) //must also check overflow
+ y1 -= M1;
+ y1 += x13;
+ if(y1 < 0 || y1 >= M1)
+ y1 -= M1;
+
+ x13 = x12;
+ x12 = x11;
+ x11 = y1;
+
+ //second component
+ y1 = ((x21 & MASK2) << 15) + (MULT2 * (x21 >>> 16));
+ if(y1 < 0 || y1 >= M2)
+ y1 -= M2;
+ y2 = ((x23 & MASK2) << 15) + (MULT2 * (x23 >>> 16));
+ if(y2 < 0 || y2 >= M2)
+ y2 -= M2;
+ y2 += x23;
+ if(y2 < 0 || y2 >= M2)
+ y2 -= M2;
+ y2 += y1;
+ if(y2 < 0 || y2 >= M2)
+ y2 -= M2;
+
+ x23 = x22;
+ x22 = x21;
+ x21 = y2;
+
+ //Must never return either 0 or 1
+ if(x11 <= x21)
+ return (x11 - x21 + M1) * NORM;
+ else
+ return (x11 - x21) * NORM;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/MRG31k3p.tex b/source/umontreal/iro/lecuyer/rng/MRG31k3p.tex
new file mode 100644
index 0000000..58b94c1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MRG31k3p.tex
@@ -0,0 +1,356 @@
+\defmodule {MRG31k3p}
+
+Extends the abstract class \class{RandomStreamBase}, thus
+implementing the \class{RandomStream} interface indirectly. The backbone
+generator is the combined multiple recursive generator (CMRG) \texttt{MRG31k3p}
+proposed by L'Ecuyer and Touzin \cite{rLEC00b},
+implemented in 32-bit integer arithmetic.
+This RNG has a period length of \html{approximately}
+\latex{$\rho\approx$} $2^{185}$. The values of $V$, $W$ and $Z$ are $2^{62}$,
+$2^{72}$ and $2^{134}$ respectively. (See \class{RandomStream} for their
+definition.) The seed and the state of a stream at any given step
+are six-dimensional vectors of 32-bit integers.
+The default initial seed is $(12345, 12345, 12345, 12345, 12345, 12345)$.
+The method \texttt{nextValue} provides 31 bits of precision.
+
+The difference between the RNG of class \class{MRG32k3a} and this one is
+that this one has all its coefficients of the form
+$a = \pm 2^{q} \pm 2^{r}$. This permits a faster implementation than for
+arbitrary coefficients. On a 32-bit computer, \texttt{MRG31k3p} is about twice
+as fast as \texttt{MRG32k3a}.
+On the other hand, the latter does a little better in the spectral test
+and has been more extensively tested.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MRG31k3p
+ * Description: combined multiple recursive generator proposed by L'Ecuyer and Touzin
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.rng.RandomStreamBase;
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import java.io.Serializable;
+\end{hide}
+
+public class MRG31k3p extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //generator constants :
+ private static final int M1 = 2147483647; //2^31 - 1
+ private static final int M2 = 2147462579; //2^31 - 21069
+ private static final int MASK12 = 511; //2^9 - 1
+ private static final int MASK13 = 16777215; //2^24 - 1
+ private static final int MASK2 = 65535; //2^16 - 1
+ private static final int MULT2 = 21069;
+ private static final double NORM = 4.656612873077392578125e-10;
+ //private static final double NORM = 1.0 / (M1 + 1.0);
+
+ //state variables
+ private int x11;
+ private int x12;
+ private int x13;
+ private int x21;
+ private int x22;
+ private int x23;
+
+ //stream and substream variables
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream = {12345, 12345, 12345,
+ 12345, 12345, 12345};
+
+ //streams constants :
+ private static final int[][] A1p0 =
+ {{0, 4194304, 129},
+ {1, 0, 0},
+ {0, 1, 0}};
+ private static final int[][] A2p0 =
+ {{32768, 0, 32769},
+ {1, 0, 0},
+ {0, 1, 0}};
+
+ private static final int[][] A1p72 =
+ {{1516919229, 758510237, 499121365},
+ {1884998244, 1516919229, 335398200},
+ {601897748, 1884998244, 358115744}};
+ private static final int[][] A2p72 =
+ {{1228857673, 1496414766, 954677935},
+ {1133297478, 1407477216, 1496414766},
+ {2002613992, 1639496704, 1407477216}};
+
+ private static final int[][] A1p134 =
+ {{1702500920, 1849582496, 1656874625},
+ {828554832, 1702500920, 1512419905},
+ {1143731069, 828554832, 102237247}};
+ private static final int[][] A2p134 =
+ {{796789021, 1464208080, 607337906},
+ {1241679051, 1431130166, 1464208080},
+ {1401213391, 1178684362, 1431130166}};
+
+
+ //multiply the first half of v by A with a modulo of m1
+ //and the second half by B with a modulo of m2
+ private static void multMatVect(int[] v, int[][] A, int m1,
+ int[][] B, int m2) {
+ int[] vv = new int[3];
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i];
+ ArithmeticMod.matVecModM(A, vv, vv, m1);
+ for(int i = 0; i < 3; i++)
+ v[i] = vv[i];
+
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i + 3];
+ ArithmeticMod.matVecModM(B, vv, vv, m2);
+ for(int i = 0; i < 3; i++)
+ v[i + 3] = vv[i];
+
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public MRG31k3p() \begin{hide} {
+ name = null;
+
+ prec53 = false;
+ anti = false;
+
+ stream = new int[6];
+ substream = new int[6];
+ for(int i = 0; i < 6; i++)
+ stream[i] = curr_stream[i];
+
+ resetStartStream();
+
+ multMatVect(curr_stream, A1p134, M1, A2p134, M2);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream, initialized at its beginning.
+ Its seed is $Z = 2^{134}$ steps away from the previous seed.
+\end{tabb}
+\begin{code}
+
+ public MRG31k3p (String name) \begin{hide} {
+ this();
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used when formatting the stream state).
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (int seed[]) \begin{hide} {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[5] == 0 && seed[3] == 0 && seed[4] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ if (seed[0] >= M1 || seed[1] >= M1 || seed[2] >= M1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + M1);
+
+ if (seed[5] >= M2 || seed[3] >= M2 || seed[4] >= M2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + M2);
+ for (int i = 0; i < 6; ++i)
+ curr_stream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed for the class \texttt{MRG31k3p} to the six
+ integers of the vector \texttt{seed[0..5]}.
+ This will be the initial state (or seed) of the next created stream.
+ By default, if this method is not called, the first stream is created with
+ the seed $(12345, 12345, 12345, 12345, 12345, 12345)$. If it is called,
+ the first 3 values of the seed must all be less than $m_1 = 2147483647$,
+ and not all 0; and the last 3 values must all be less than $m_2 =
+ 2147462579$, and not all 0.
+%% Note that a call to this method does not affect in any way the streams
+%% already created. So the distance between streams that are created
+%% before and after the call to this method is not guaranted to be $2^{134}$.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 6 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (int seed[]) \begin{hide} {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ if (seed[0] >= M1 || seed[1] >= M1 || seed[2] >= M1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + M1);
+ if (seed[3] >= M2 || seed[4] >= M2 || seed[5] >= M2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + M2);
+ for (int i = 0; i < 6; ++i)
+ stream[i] = seed[i];
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} \emph{Use of this method is strongly discouraged}.
+ Initializes the stream at the beginning of a stream with the
+ initial seed \texttt{seed[0..5]}. This vector must satisfy the same conditions
+ as in \texttt{setPackageSeed}.
+ This method only affects the specified stream, all the others are not
+ modified, so the beginning of the streams are no longer spaced $Z$ values
+ apart.
+ For this reason, this method should be used only in very
+ exceptional situations; proper use of \texttt{reset...}
+ and of the stream constructor is preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 6 integers representing the new seed}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+
+ public void resetStartStream() {
+ for(int i = 0; i < 6; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ x11 = substream[0];
+ x12 = substream[1];
+ x13 = substream[2];
+ x21 = substream[3];
+ x22 = substream[4];
+ x23 = substream[5];
+ }
+
+ public void resetNextSubstream() {
+ multMatVect(substream, A1p72, M1, A2p72, M2);
+ resetStartSubstream();
+ }
+
+\end{hide}
+ public int[] getState() \begin{hide} {
+ return new int[]{x11, x12, x13, x21, x22, x23};
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the current state $C_g$ of this stream.
+ This is a vector of 6 integers represented.
+ This method is convenient if we want to save the state for
+ subsequent use.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the current state of the generator}
+\end{htmlonly}
+\begin{code}
+
+ public MRG31k3p clone() \begin{hide} {
+ MRG31k3p retour = null;
+ retour = (MRG31k3p)super.clone();
+ retour.substream = new int[6];
+ retour.stream = new int[6];
+ for (int i = 0; i<6; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ public String toString() {
+ if(name == null)
+ return "The state of the MRG31k3p is: " +
+ x11 + ", " + x12 + ", " + x13 + "; " +
+ x21 + ", " + x22 + ", " + x23;
+ else
+ return "The state of " + name + " is: " +
+ x11 + ", " + x12 + ", " + x13 + "; " +
+ x21 + ", " + x22 + ", " + x23;
+ }
+
+ protected double nextValue() {
+ int y1, y2;
+
+ //first component
+ y1 = ((x12 & MASK12) << 22) + (x12 >>> 9)
+ + ((x13 & MASK13) << 7) + (x13 >>> 24);
+ if(y1 < 0 || y1 >= M1) //must also check overflow
+ y1 -= M1;
+ y1 += x13;
+ if(y1 < 0 || y1 >= M1)
+ y1 -= M1;
+
+ x13 = x12;
+ x12 = x11;
+ x11 = y1;
+
+ //second component
+ y1 = ((x21 & MASK2) << 15) + (MULT2 * (x21 >>> 16));
+ if(y1 < 0 || y1 >= M2)
+ y1 -= M2;
+ y2 = ((x23 & MASK2) << 15) + (MULT2 * (x23 >>> 16));
+ if(y2 < 0 || y2 >= M2)
+ y2 -= M2;
+ y2 += x23;
+ if(y2 < 0 || y2 >= M2)
+ y2 -= M2;
+ y2 += y1;
+ if(y2 < 0 || y2 >= M2)
+ y2 -= M2;
+
+ x23 = x22;
+ x22 = x21;
+ x21 = y2;
+
+ //Must never return either 0 or 1
+ if(x11 <= x21)
+ return (x11 - x21 + M1) * NORM;
+ else
+ return (x11 - x21) * NORM;
+ }
+
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/rng/MRG32k3a.java b/source/umontreal/iro/lecuyer/rng/MRG32k3a.java
new file mode 100644
index 0000000..8dc6348
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MRG32k3a.java
@@ -0,0 +1,401 @@
+
+
+/*
+ * Class: MRG32k3a
+ * Description: combined multiple recursive generator proposed by L'Ecuyer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.Serializable;
+
+
+/**
+ * Extends the abstract class {@link RandomStreamBase} by using as a
+ * backbone (or main) generator the combined multiple recursive
+ * generator (CMRG) <TT>MRG32k3a</TT> proposed by L'Ecuyer,
+ * implemented in 64-bit floating-point arithmetic.
+ * This backbone generator has a period length of
+ *
+ * <SPAN CLASS="MATH"><I>ρ</I> = 2<SUP>191</SUP></SPAN>.
+ * The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN>, and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>51</SUP></SPAN>, <SPAN CLASS="MATH">2<SUP>76</SUP></SPAN>, and <SPAN CLASS="MATH">2<SUP>127</SUP></SPAN>,
+ * respectively. (See {@link RandomStream} for their definition.)
+ * The seed of the RNG, and the state of a stream at any given step,
+ * are six-dimensional vectors of 32-bit integers, stored in <TT>double</TT>.
+ * The default initial seed of the RNG is
+ *
+ * <SPAN CLASS="MATH">(12345, 12345, 12345, 12345, 12345, 12345)</SPAN>.
+ *
+ */
+public class MRG32k3a extends RandomStreamBase {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ // Private constants %%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ private static final double m1 = 4294967087.0;
+ private static final double m2 = 4294944443.0;
+ private static final double a12 = 1403580.0;
+ private static final double a13n = 810728.0;
+ private static final double a21 = 527612.0;
+ private static final double a23n = 1370589.0;
+ private static final double two17 = 131072.0;
+ private static final double two53 = 9007199254740992.0;
+ private static final double invtwo24 = 5.9604644775390625e-8;
+ private static final double norm = 2.328306549295727688e-10;
+ // private static final double norm = 1.0 / (m1 + 1.0);
+
+
+ /* Unused
+ private static final double InvA1[][] = { // Inverse of A1p0
+ { 184888585.0, 0.0, 1945170933.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double InvA2[][] = { // Inverse of A2p0
+ { 0.0, 360363334.0, 4225571728.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ */
+
+ private static final double A1p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -810728.0, 1403580.0, 0.0 }
+ };
+ private static final double A2p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -1370589.0, 0.0, 527612.0 }
+ };
+ private static final double A1p76[][] = {
+ { 82758667.0, 1871391091.0, 4127413238.0 },
+ { 3672831523.0, 69195019.0, 1871391091.0 },
+ { 3672091415.0, 3528743235.0, 69195019.0 }
+ };
+ private static final double A2p76[][] = {
+ { 1511326704.0, 3759209742.0, 1610795712.0 },
+ { 4292754251.0, 1511326704.0, 3889917532.0 },
+ { 3859662829.0, 4292754251.0, 3708466080.0 }
+ };
+ private static final double A1p127[][] = {
+ { 2427906178.0, 3580155704.0, 949770784.0 },
+ { 226153695.0, 1230515664.0, 3580155704.0 },
+ { 1988835001.0, 986791581.0, 1230515664.0 }
+ };
+ private static final double A2p127[][] = {
+ { 1464411153.0, 277697599.0, 1610723613.0 },
+ { 32183930.0, 1464411153.0, 1022607788.0 },
+ { 2824425944.0, 32183930.0, 2093834863.0 }
+ };
+
+
+ // Private variables for each stream %%%%%%%%%%%%%%%%%%%%%%%%
+
+ // Default seed of the package for the first stream
+ private static double nextSeed[] = {12345, 12345, 12345,
+ 12345, 12345, 12345};
+ private double Cg0, Cg1, Cg2, Cg3, Cg4, Cg5;
+ private double Bg[] = new double[6];
+ private double Ig[] = new double[6];
+ // The arrays Cg, Bg and Ig contain the current state,
+ // the starting point of the current substream,
+ // and the starting point of the stream, respectively.
+
+
+ //multiply the first half of v by A with a modulo of m1
+ //and the second half by B with a modulo of m2
+ private static void multMatVect(double[] v, double[][] A, double m1,
+ double[][] B, double m2) {
+ double[] vv = new double[3];
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i];
+ ArithmeticMod.matVecModM(A, vv, vv, m1);
+ for(int i = 0; i < 3; i++)
+ v[i] = vv[i];
+
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i + 3];
+ ArithmeticMod.matVecModM(B, vv, vv, m2);
+ for(int i = 0; i < 3; i++)
+ v[i + 3] = vv[i];
+ }
+
+
+
+ /**
+ * Constructs a new stream, initializes its seed <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN>,
+ * sets <SPAN CLASS="MATH"><I>B</I><SUB>g</SUB></SPAN> and <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> equal to <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN>, and sets its antithetic switch
+ * to <TT>false</TT>.
+ * The seed <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN> is equal to the initial seed of the package given by
+ * {@link #setPackageSeed(long[]) setPackageSeed} if this is the first stream created,
+ * otherwise it is <SPAN CLASS="MATH"><I>Z</I></SPAN> steps ahead of that of the stream most recently
+ * created in this class.
+ *
+ */
+ public MRG32k3a() {
+ name = null;
+ anti = false;
+ prec53 = false;
+ for(int i = 0; i < 6; i++)
+ Ig[i] = nextSeed[i];
+ resetStartStream();
+ multMatVect(nextSeed, A1p127, m1, A2p127, m2);
+ }
+
+
+ /**
+ * Constructs a new stream with an identifier <TT>name</TT>
+ * (used when printing the stream state).
+ *
+ * @param name name of the stream
+ *
+ */
+ public MRG32k3a (String name) {
+ this();
+ this.name = name;
+ }
+
+
+ /**
+ * Sets the initial seed for the class <TT>MRG32k3a</TT> to the
+ * six integers in the vector <TT>seed[0..5]</TT>.
+ * This will be the seed (initial state) of the first stream.
+ * If this method is not called, the default initial seed
+ * is
+ * <SPAN CLASS="MATH">(12345, 12345, 12345, 12345, 12345, 12345)</SPAN>.
+ * If it is called, the first 3 values of the seed must all be
+ * less than
+ * <SPAN CLASS="MATH"><I>m</I><SUB>1</SUB> = 4294967087</SPAN>, and not all 0;
+ * and the last 3 values
+ * must all be less than
+ * <SPAN CLASS="MATH"><I>m</I><SUB>2</SUB> = 4294944443</SPAN>, and not all 0.
+ *
+ * @param seed array of 6 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (long seed[]) {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ nextSeed[i] = seed[i];
+ }
+
+
+ /**
+ * Sets the initial seed <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN> of this stream
+ * to the vector <TT>seed[0..5]</TT>. This vector must satisfy the same
+ * conditions as in <TT>setPackageSeed</TT>.
+ * The stream is then reset to this initial seed.
+ * The states and seeds of the other streams are not modified.
+ * As a result, after calling this method, the initial seeds
+ * of the streams are no longer spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, <SPAN CLASS="textit">this method should be used only in very
+ * exceptional situations</SPAN> (I have never used it myself!);
+ * proper use of <TT>reset...</TT>
+ * and of the stream constructor is preferable.
+ *
+ * @param seed array of 6 integers representing the new seed
+ *
+ *
+ */
+ public void setSeed (long seed[]) {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ Ig[i] = seed[i];
+ resetStartStream();
+ }
+
+
+ public void resetStartStream() {
+ for (int i = 0; i < 6; ++i)
+ Bg[i] = Ig[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ Cg0 = Bg[0];
+ Cg1 = Bg[1];
+ Cg2 = Bg[2];
+ Cg3 = Bg[3];
+ Cg4 = Bg[4];
+ Cg5 = Bg[5];
+ }
+
+ public void resetNextSubstream() {
+ multMatVect(Bg, A1p76, m1, A2p76, m2);
+ resetStartSubstream();
+ }
+
+
+ /**
+ * Returns the current state <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> of this stream.
+ * This is a vector of 6 integers. This method is convenient if we want to save the state for
+ * subsequent use.
+ *
+ * @return the current state of the generator
+ *
+ */
+ public long[] getState() {
+ return new long[] {(long)Cg0, (long)Cg1, (long)Cg2,
+ (long)Cg3, (long)Cg4, (long)Cg5};
+ }
+
+
+ /**
+ * Returns a string containing the name and the current state <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN>
+ * of this stream.
+ *
+ * @return the state of the generator, formated as a string
+ *
+ */
+ public String toString() {
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("The current state of the MRG32k3a");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " Cg = { ");
+ str.append ((long) Cg0 + ", ");
+ str.append ((long) Cg1 + ", ");
+ str.append ((long) Cg2 + ", ");
+ str.append ((long) Cg3 + ", ");
+ str.append ((long) Cg4 + ", ");
+ str.append ((long) Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+
+ /**
+ * Returns a string containing the name of this stream and the
+ * values of all its internal variables.
+ *
+ * @return the detailed state of the generator, formatted as a string
+ *
+ */
+ public String toStringFull() {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("The MRG32k3a stream");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " anti = " +
+ (anti ? "true" : "false")).append(PrintfFormat.NEWLINE);
+ str.append (" prec53 = " + (prec53 ? "true" : "false")).append(PrintfFormat.NEWLINE);
+
+ str.append (" Ig = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Ig[i] + ", ");
+ str.append ((long) Ig[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Bg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Bg[i] + ", ");
+ str.append ((long) Bg[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Cg = { ");
+ str.append ((long) Cg0 + ", ");
+ str.append ((long) Cg1 + ", ");
+ str.append ((long) Cg2 + ", ");
+ str.append ((long) Cg3 + ", ");
+ str.append ((long) Cg4 + ", ");
+ str.append ((long) Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public MRG32k3a clone() {
+ MRG32k3a retour = null;
+
+ retour = (MRG32k3a)super.clone();
+ retour.Bg = new double[6];
+ retour.Ig = new double[6];
+ for (int i = 0; i<6; i++) {
+ retour.Bg[i] = Bg[i];
+ retour.Ig[i] = Ig[i];
+ }
+ return retour;
+ }
+
+
+
+ protected double nextValue() {
+ int k;
+ double p1, p2;
+ /* Component 1 */
+ p1 = a12 * Cg1 - a13n * Cg0;
+ k = (int)(p1 / m1);
+ p1 -= k * m1;
+ if (p1 < 0.0)
+ p1 += m1;
+ Cg0 = Cg1;
+ Cg1 = Cg2;
+ Cg2 = p1;
+ /* Component 2 */
+ p2 = a21 * Cg5 - a23n * Cg3;
+ k = (int)(p2 / m2);
+ p2 -= k * m2;
+ if (p2 < 0.0)
+ p2 += m2;
+ Cg3 = Cg4;
+ Cg4 = Cg5;
+ Cg5 = p2;
+ /* Combination */
+ return ((p1 > p2) ? (p1 - p2) * norm : (p1 - p2 + m1) * norm);
+ }
+
+
+ private static void validateSeed (long seed[]) {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException
+ ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException
+ ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException
+ ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[3] >= m2 || seed[4] >= m2 || seed[5] >= m2)
+ throw new IllegalArgumentException
+ ("The last 3 values must be less than " + m2);
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/MRG32k3a.tex b/source/umontreal/iro/lecuyer/rng/MRG32k3a.tex
new file mode 100644
index 0000000..10817b9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MRG32k3a.tex
@@ -0,0 +1,407 @@
+\defmodule {MRG32k3a}
+
+Extends the abstract class \class{RandomStreamBase} by using as a
+backbone (or main) generator the combined multiple recursive
+generator (CMRG) \texttt{MRG32k3a} proposed by L'Ecuyer \cite{rLEC99b},
+implemented in 64-bit floating-point arithmetic.
+This backbone generator has a period length of
+ \html{$\rho = 2^{191}$.} \latex{$\rho\approx 2^{191}$.}
+The values of $V$, $W$, and $Z$ are $2^{51}$, $2^{76}$, and $2^{127}$,
+respectively. (See \class{RandomStream} for their definition.)
+The seed of the RNG, and the state of a stream at any given step,
+are six-dimensional vectors of 32-bit integers, stored in \texttt{double}.
+The default initial seed of the RNG is
+$(12345, 12345, 12345, 12345, 12345, 12345)$.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MRG32k3a
+ * Description: combined multiple recursive generator proposed by L'Ecuyer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.Serializable;
+\end{hide}
+
+public class MRG32k3a extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ // Private constants %%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ private static final double m1 = 4294967087.0;
+ private static final double m2 = 4294944443.0;
+ private static final double a12 = 1403580.0;
+ private static final double a13n = 810728.0;
+ private static final double a21 = 527612.0;
+ private static final double a23n = 1370589.0;
+ private static final double two17 = 131072.0;
+ private static final double two53 = 9007199254740992.0;
+ private static final double invtwo24 = 5.9604644775390625e-8;
+ private static final double norm = 2.328306549295727688e-10;
+ // private static final double norm = 1.0 / (m1 + 1.0);
+
+
+ /* Unused
+ private static final double InvA1[][] = { // Inverse of A1p0
+ { 184888585.0, 0.0, 1945170933.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double InvA2[][] = { // Inverse of A2p0
+ { 0.0, 360363334.0, 4225571728.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ */
+
+ private static final double A1p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -810728.0, 1403580.0, 0.0 }
+ };
+ private static final double A2p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -1370589.0, 0.0, 527612.0 }
+ };
+ private static final double A1p76[][] = {
+ { 82758667.0, 1871391091.0, 4127413238.0 },
+ { 3672831523.0, 69195019.0, 1871391091.0 },
+ { 3672091415.0, 3528743235.0, 69195019.0 }
+ };
+ private static final double A2p76[][] = {
+ { 1511326704.0, 3759209742.0, 1610795712.0 },
+ { 4292754251.0, 1511326704.0, 3889917532.0 },
+ { 3859662829.0, 4292754251.0, 3708466080.0 }
+ };
+ private static final double A1p127[][] = {
+ { 2427906178.0, 3580155704.0, 949770784.0 },
+ { 226153695.0, 1230515664.0, 3580155704.0 },
+ { 1988835001.0, 986791581.0, 1230515664.0 }
+ };
+ private static final double A2p127[][] = {
+ { 1464411153.0, 277697599.0, 1610723613.0 },
+ { 32183930.0, 1464411153.0, 1022607788.0 },
+ { 2824425944.0, 32183930.0, 2093834863.0 }
+ };
+
+
+ // Private variables for each stream %%%%%%%%%%%%%%%%%%%%%%%%
+
+ // Default seed of the package for the first stream
+ private static double nextSeed[] = {12345, 12345, 12345,
+ 12345, 12345, 12345};
+ private double Cg0, Cg1, Cg2, Cg3, Cg4, Cg5;
+ private double Bg[] = new double[6];
+ private double Ig[] = new double[6];
+ // The arrays Cg, Bg and Ig contain the current state,
+ // the starting point of the current substream,
+ // and the starting point of the stream, respectively.
+
+
+ //multiply the first half of v by A with a modulo of m1
+ //and the second half by B with a modulo of m2
+ private static void multMatVect(double[] v, double[][] A, double m1,
+ double[][] B, double m2) {
+ double[] vv = new double[3];
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i];
+ ArithmeticMod.matVecModM(A, vv, vv, m1);
+ for(int i = 0; i < 3; i++)
+ v[i] = vv[i];
+
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i + 3];
+ ArithmeticMod.matVecModM(B, vv, vv, m2);
+ for(int i = 0; i < 3; i++)
+ v[i + 3] = vv[i];
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public MRG32k3a() \begin{hide} {
+ name = null;
+ anti = false;
+ prec53 = false;
+ for(int i = 0; i < 6; i++)
+ Ig[i] = nextSeed[i];
+ resetStartStream();
+ multMatVect(nextSeed, A1p127, m1, A2p127, m2);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new stream, initializes its seed $I_g$,
+ sets $B_g$ and $C_g$ equal to $I_g$, and sets its antithetic switch
+ to \texttt{false}.
+ The seed $I_g$ is equal to the initial seed of the package given by
+ \method{setPackageSeed}{long[]} if this is the first stream created,
+ otherwise it is $Z$ steps ahead of that of the stream most recently
+ created in this class.
+ \end{tabb}
+\begin{code}
+
+ public MRG32k3a (String name) \begin{hide} {
+ this();
+ this.name = name;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new stream with an identifier \texttt{name}
+ (used when printing the stream state).
+ \end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static void setPackageSeed (long seed[]) \begin{hide} {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ nextSeed[i] = seed[i];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the initial seed for the class \texttt{MRG32k3a} to the
+ six integers in the vector \texttt{seed[0..5]}.
+ This will be the seed (initial state) of the first stream.
+ If this method is not called, the default initial seed
+ is $(12345, 12345, 12345, 12345, 12345, 12345)$.
+ If it is called, the first 3 values of the seed must all be
+ less than $m_1 = 4294967087$, and not all 0;
+ and the last 3 values
+ must all be less than $m_2 = 4294944443$, and not all 0.
+ \end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 6 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (long seed[]) \begin{hide} {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ Ig[i] = seed[i];
+ resetStartStream();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the initial seed $I_g$ of this stream
+ to the vector \texttt{seed[0..5]}. This vector must satisfy the same
+ conditions as in \texttt{setPackageSeed}.
+ The stream is then reset to this initial seed.
+ The states and seeds of the other streams are not modified.
+ As a result, after calling this method, the initial seeds
+ of the streams are no longer spaced $Z$ values apart.
+ For this reason, \emph{this method should be used only in very
+ exceptional situations} (I have never used it myself!);
+ proper use of \texttt{reset...}
+ and of the stream constructor is preferable.
+ \end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 6 integers representing the new seed}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ public void resetStartStream() {
+ for (int i = 0; i < 6; ++i)
+ Bg[i] = Ig[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ Cg0 = Bg[0];
+ Cg1 = Bg[1];
+ Cg2 = Bg[2];
+ Cg3 = Bg[3];
+ Cg4 = Bg[4];
+ Cg5 = Bg[5];
+ }
+
+ public void resetNextSubstream() {
+ multMatVect(Bg, A1p76, m1, A2p76, m2);
+ resetStartSubstream();
+ }
+
+\end{hide}
+ public long[] getState() \begin{hide} {
+ return new long[] {(long)Cg0, (long)Cg1, (long)Cg2,
+ (long)Cg3, (long)Cg4, (long)Cg5};
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the current state $C_g$ of this stream.
+ This is a vector of 6 integers. % represented in floating-point format.
+ This method is convenient if we want to save the state for
+ subsequent use.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the current state of the generator}
+\end{htmlonly}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("The current state of the MRG32k3a");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " Cg = { ");
+ str.append ((long) Cg0 + ", ");
+ str.append ((long) Cg1 + ", ");
+ str.append ((long) Cg2 + ", ");
+ str.append ((long) Cg3 + ", ");
+ str.append ((long) Cg4 + ", ");
+ str.append ((long) Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a string containing the name and the current state $C_g$
+ of this stream.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the state of the generator, formated as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String toStringFull() \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("The MRG32k3a stream");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " anti = " +
+ (anti ? "true" : "false")).append(PrintfFormat.NEWLINE);
+ str.append (" prec53 = " + (prec53 ? "true" : "false")).append(PrintfFormat.NEWLINE);
+
+ str.append (" Ig = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Ig[i] + ", ");
+ str.append ((long) Ig[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Bg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Bg[i] + ", ");
+ str.append ((long) Bg[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Cg = { ");
+ str.append ((long) Cg0 + ", ");
+ str.append ((long) Cg1 + ", ");
+ str.append ((long) Cg2 + ", ");
+ str.append ((long) Cg3 + ", ");
+ str.append ((long) Cg4 + ", ");
+ str.append ((long) Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a string containing the name of this stream and the
+ values of all its internal variables.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the detailed state of the generator, formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public MRG32k3a clone() \begin{hide} {
+ MRG32k3a retour = null;
+
+ retour = (MRG32k3a)super.clone();
+ retour.Bg = new double[6];
+ retour.Ig = new double[6];
+ for (int i = 0; i<6; i++) {
+ retour.Bg[i] = Bg[i];
+ retour.Ig[i] = Ig[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+
+ protected double nextValue() {
+ int k;
+ double p1, p2;
+ /* Component 1 */
+ p1 = a12 * Cg1 - a13n * Cg0;
+ k = (int)(p1 / m1);
+ p1 -= k * m1;
+ if (p1 < 0.0)
+ p1 += m1;
+ Cg0 = Cg1;
+ Cg1 = Cg2;
+ Cg2 = p1;
+ /* Component 2 */
+ p2 = a21 * Cg5 - a23n * Cg3;
+ k = (int)(p2 / m2);
+ p2 -= k * m2;
+ if (p2 < 0.0)
+ p2 += m2;
+ Cg3 = Cg4;
+ Cg4 = Cg5;
+ Cg5 = p2;
+ /* Combination */
+ return ((p1 > p2) ? (p1 - p2) * norm : (p1 - p2 + m1) * norm);
+ }
+
+
+ private static void validateSeed (long seed[]) {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException
+ ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException
+ ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException
+ ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[3] >= m2 || seed[4] >= m2 || seed[5] >= m2)
+ throw new IllegalArgumentException
+ ("The last 3 values must be less than " + m2);
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/MRG32k3aL.java b/source/umontreal/iro/lecuyer/rng/MRG32k3aL.java
new file mode 100644
index 0000000..7439ac4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MRG32k3aL.java
@@ -0,0 +1,337 @@
+
+
+/*
+ * Class: MRG32k3aL
+ * Description: Long version of MRG32k3a
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.Serializable;
+
+
+/**
+ * The same generator as {@link MRG32k3a}, except here it is implemented
+ * with type <TT>long</TT> instead of <TT>double</TT>.
+ * (See {@link MRG32k3a} for more information.)
+ *
+ */
+public class MRG32k3aL extends RandomStreamBase {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ // Private constants %%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ private static final long m1 = 4294967087L;
+ private static final long m2 = 4294944443L;
+ private static final long a12 = 1403580L;
+ private static final long a13n = 810728L;
+ private static final long a21 = 527612L;
+ private static final long a23n = 1370589L;
+ private static final long two17 = 131072L;
+ private static final long two53 = 9007199254740992L;
+ private static final double invtwo24 = 5.9604644775390625e-8;
+ private static final double norm = 2.328306549295727688e-10;
+ // private static final double norm = 1.0 / (m1 + 1.0);
+
+
+ /* Unused
+ private static final double InvA1[][] = { // Inverse of A1p0
+ { 184888585.0, 0.0, 1945170933.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double InvA2[][] = { // Inverse of A2p0
+ { 0.0, 360363334.0, 4225571728.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ */
+
+ private static final long A1p0[][] = {
+ { 0L, 1L, 0L },
+ { 0L, 0L, 1L },
+ { -810728L, 1403580L, 0L }
+ };
+ private static final long A2p0[][] = {
+ { 0L, 1L, 0L },
+ { 0L, 0L, 1L },
+ { -1370589L, 0L, 527612L }
+ };
+ private static final long A1p76[][] = {
+ { 82758667L, 1871391091L, 4127413238L },
+ { 3672831523L, 69195019L, 1871391091L },
+ { 3672091415L, 3528743235L, 69195019L }
+ };
+ private static final long A2p76[][] = {
+ { 1511326704L, 3759209742L, 1610795712L },
+ { 4292754251L, 1511326704L, 3889917532L },
+ { 3859662829L, 4292754251L, 3708466080L }
+ };
+ private static final long A1p127[][] = {
+ { 2427906178L, 3580155704L, 949770784L },
+ { 226153695L, 1230515664L, 3580155704L },
+ { 1988835001L, 986791581L, 1230515664L }
+ };
+ private static final long A2p127[][] = {
+ { 1464411153L, 277697599L, 1610723613L },
+ { 32183930L, 1464411153L, 1022607788L },
+ { 2824425944L, 32183930L, 2093834863L }
+ };
+
+
+ // Private variables for each stream %%%%%%%%%%%%%%%%%%%%%%%%
+
+ // Default seed of the package for the first stream
+ private static long nextSeed[] = {12345, 12345, 12345,
+ 12345, 12345, 12345};
+ private long Cg0, Cg1, Cg2, Cg3, Cg4, Cg5;
+ private long Bg[] = new long[6];
+ private long Ig[] = new long[6];
+ // The arrays Cg, Bg and Ig contain the current state,
+ // the starting point of the current substream,
+ // and the starting point of the stream, respectively.
+
+
+ //multiply the first half of v by A with a modulo of m1
+ //and the second half by B with a modulo of m2
+ private static void multMatVect(long[] v, long[][] A, long m1,
+ long[][] B, long m2) {
+ long[] vv = new long[3];
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i];
+ ArithmeticMod.matVecModM(A, vv, vv, m1);
+ for(int i = 0; i < 3; i++)
+ v[i] = vv[i];
+
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i + 3];
+ ArithmeticMod.matVecModM(B, vv, vv, m2);
+ for(int i = 0; i < 3; i++)
+ v[i + 3] = vv[i];
+ }
+
+
+
+ public MRG32k3aL() {
+ name = null;
+ anti = false;
+ prec53 = false;
+ for(int i = 0; i < 6; i++)
+ Ig[i] = nextSeed[i];
+ resetStartStream();
+ multMatVect(nextSeed, A1p127, m1, A2p127, m2);
+ }
+
+
+ /**
+ * @param name name of the stream
+ *
+ */
+ public MRG32k3aL (String name) {
+ this();
+ this.name = name;
+ }
+
+
+ /**
+ * @param seed array of 6 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (long seed[]) {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ nextSeed[i] = seed[i];
+ }
+
+
+ /**
+ * @param seed array of 6 integers representing the new seed
+ *
+ *
+ */
+ public void setSeed (long seed[]) {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ Ig[i] = seed[i];
+ resetStartStream();
+ }
+
+
+ public void resetStartStream() {
+ for (int i = 0; i < 6; ++i)
+ Bg[i] = Ig[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ Cg0 = Bg[0];
+ Cg1 = Bg[1];
+ Cg2 = Bg[2];
+ Cg3 = Bg[3];
+ Cg4 = Bg[4];
+ Cg5 = Bg[5];
+ }
+
+ public void resetNextSubstream() {
+ multMatVect(Bg, A1p76, m1, A2p76, m2);
+ resetStartSubstream();
+ }
+
+
+ /**
+ * @return the current state of the generator
+ *
+ */
+ public long[] getState() {
+ return new long[]{Cg0, Cg1, Cg2, Cg3, Cg4, Cg5};
+ }
+
+
+ /**
+ * @return the state of the generator, formated as a string
+ *
+ */
+ public String toString() {
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("The current state of the MRG32k3aL");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " Cg = { ");
+ str.append ( Cg0 + ", ");
+ str.append ( Cg1 + ", ");
+ str.append ( Cg2 + ", ");
+ str.append ( Cg3 + ", ");
+ str.append ( Cg4 + ", ");
+ str.append ( Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+
+ /**
+ * @return the detailed state of the generator, formatted as a string
+ *
+ */
+ public String toStringFull() {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("The MRG32k3aL stream");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " anti = " +
+ (anti ? "true" : "false")).append(PrintfFormat.NEWLINE);
+ str.append (" prec53 = " + (prec53 ? "true" : "false")).append(PrintfFormat.NEWLINE);
+
+ str.append (" Ig = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ( Ig[i] + ", ");
+ str.append ( Ig[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Bg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ( Bg[i] + ", ");
+ str.append ( Bg[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Cg = { ");
+ str.append ( Cg0 + ", ");
+ str.append ( Cg1 + ", ");
+ str.append ( Cg2 + ", ");
+ str.append ( Cg3 + ", ");
+ str.append ( Cg4 + ", ");
+ str.append ( Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+
+ /**
+ * @return A deep copy of the current generator
+ *
+ */
+ public MRG32k3aL clone() {
+ MRG32k3aL retour = null;
+
+ retour = (MRG32k3aL)super.clone();
+ retour.Bg = new long[6];
+ retour.Ig = new long[6];
+ for (int i = 0; i<6; i++) {
+ retour.Bg[i] = Bg[i];
+ retour.Ig[i] = Ig[i];
+ }
+ return retour;
+ }
+
+
+ protected double nextValue() {
+ int k;
+ long p1, p2;
+
+ /* Component 1 */
+ p1 = (a12 * Cg1 - a13n * Cg0) % m1;
+ if (p1 < 0)
+ p1 += m1;
+ Cg0 = Cg1;
+ Cg1 = Cg2;
+ Cg2 = p1;
+
+ /* Component 2 */
+ p2 = (a21 * Cg5 - a23n * Cg3) % m2;
+ if (p2 < 0)
+ p2 += m2;
+ Cg3 = Cg4;
+ Cg4 = Cg5;
+ Cg5 = p2;
+
+ /* Combination */
+ return (double)((p1 > p2) ? (p1 - p2) * norm : (p1 - p2 + m1) * norm);
+ }
+
+
+ private static void validateSeed (long seed[]) {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException
+ ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException
+ ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException
+ ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[3] >= m2 || seed[4] >= m2 || seed[5] >= m2)
+ throw new IllegalArgumentException
+ ("The last 3 values must be less than " + m2);
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/MRG32k3aL.tex b/source/umontreal/iro/lecuyer/rng/MRG32k3aL.tex
new file mode 100644
index 0000000..6613bbc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MRG32k3aL.tex
@@ -0,0 +1,413 @@
+\defmodule {MRG32k3aL}
+
+
+The same generator as \class{MRG32k3a}, except here it is implemented
+with type \texttt{long} instead of \texttt{double}.
+(See \class{MRG32k3a} for more information.)
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MRG32k3aL
+ * Description: Long version of MRG32k3a
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import umontreal.iro.lecuyer.util.ArithmeticMod;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.Serializable;
+\end{hide}
+
+public class MRG32k3aL extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ // Private constants %%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ private static final long m1 = 4294967087L;
+ private static final long m2 = 4294944443L;
+ private static final long a12 = 1403580L;
+ private static final long a13n = 810728L;
+ private static final long a21 = 527612L;
+ private static final long a23n = 1370589L;
+ private static final long two17 = 131072L;
+ private static final long two53 = 9007199254740992L;
+ private static final double invtwo24 = 5.9604644775390625e-8;
+ private static final double norm = 2.328306549295727688e-10;
+ // private static final double norm = 1.0 / (m1 + 1.0);
+
+
+ /* Unused
+ private static final double InvA1[][] = { // Inverse of A1p0
+ { 184888585.0, 0.0, 1945170933.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double InvA2[][] = { // Inverse of A2p0
+ { 0.0, 360363334.0, 4225571728.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ */
+
+ private static final long A1p0[][] = {
+ { 0L, 1L, 0L },
+ { 0L, 0L, 1L },
+ { -810728L, 1403580L, 0L }
+ };
+ private static final long A2p0[][] = {
+ { 0L, 1L, 0L },
+ { 0L, 0L, 1L },
+ { -1370589L, 0L, 527612L }
+ };
+ private static final long A1p76[][] = {
+ { 82758667L, 1871391091L, 4127413238L },
+ { 3672831523L, 69195019L, 1871391091L },
+ { 3672091415L, 3528743235L, 69195019L }
+ };
+ private static final long A2p76[][] = {
+ { 1511326704L, 3759209742L, 1610795712L },
+ { 4292754251L, 1511326704L, 3889917532L },
+ { 3859662829L, 4292754251L, 3708466080L }
+ };
+ private static final long A1p127[][] = {
+ { 2427906178L, 3580155704L, 949770784L },
+ { 226153695L, 1230515664L, 3580155704L },
+ { 1988835001L, 986791581L, 1230515664L }
+ };
+ private static final long A2p127[][] = {
+ { 1464411153L, 277697599L, 1610723613L },
+ { 32183930L, 1464411153L, 1022607788L },
+ { 2824425944L, 32183930L, 2093834863L }
+ };
+
+
+ // Private variables for each stream %%%%%%%%%%%%%%%%%%%%%%%%
+
+ // Default seed of the package for the first stream
+ private static long nextSeed[] = {12345, 12345, 12345,
+ 12345, 12345, 12345};
+ private long Cg0, Cg1, Cg2, Cg3, Cg4, Cg5;
+ private long Bg[] = new long[6];
+ private long Ig[] = new long[6];
+ // The arrays Cg, Bg and Ig contain the current state,
+ // the starting point of the current substream,
+ // and the starting point of the stream, respectively.
+
+
+ //multiply the first half of v by A with a modulo of m1
+ //and the second half by B with a modulo of m2
+ private static void multMatVect(long[] v, long[][] A, long m1,
+ long[][] B, long m2) {
+ long[] vv = new long[3];
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i];
+ ArithmeticMod.matVecModM(A, vv, vv, m1);
+ for(int i = 0; i < 3; i++)
+ v[i] = vv[i];
+
+ for(int i = 0; i < 3; i++)
+ vv[i] = v[i + 3];
+ ArithmeticMod.matVecModM(B, vv, vv, m2);
+ for(int i = 0; i < 3; i++)
+ v[i + 3] = vv[i];
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public MRG32k3aL() \begin{hide} {
+ name = null;
+ anti = false;
+ prec53 = false;
+ for(int i = 0; i < 6; i++)
+ Ig[i] = nextSeed[i];
+ resetStartStream();
+ multMatVect(nextSeed, A1p127, m1, A2p127, m2);
+ } \end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Constructs a new stream, initializes its seed $I_g$,
+ sets $B_g$ and $C_g$ equal to $I_g$, and sets its antithetic switch
+ to \texttt{false}.
+ The seed $I_g$ is equal to the initial seed of the package given by
+ \method{setPackageSeed}{long[]} if this is the first stream created,
+ otherwise it is $Z$ steps ahead of that of the stream most recently
+ created in this class.
+ \end{tabb}
+\fi
+\begin{code}
+
+ public MRG32k3aL (String name) \begin{hide} {
+ this();
+ this.name = name;
+ } \end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Constructs a new stream with an identifier \texttt{name}
+ (used when printing the stream state).
+ \end{tabb}
+\fi
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+See the description of the same methods in class \class{MRG32k3a}.
+\begin{code}
+
+ public static void setPackageSeed (long seed[]) \begin{hide} {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ nextSeed[i] = seed[i];
+ }\end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Sets the initial seed for the class \texttt{MRG32k3aL} to the
+ six integers in the vector \texttt{seed[0..5]}.
+ This will be the seed (initial state) of the first stream.
+ If this method is not called, the default initial seed
+ is $(12345, 12345, 12345, 12345, 12345, 12345)$.
+ If it is called, the first 3 values of the seed must all be
+ less than $m_1 = 4294967087$, and not all 0;
+ and the last 3 values
+ must all be less than $m_2 = 4294944443$, and not all 0.
+ \end{tabb}
+\fi
+\begin{htmlonly}
+ \param{seed}{array of 6 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (long seed[]) \begin{hide} {
+ // Must use long because there is no unsigned int type.
+ validateSeed (seed);
+ for (int i = 0; i < 6; ++i)
+ Ig[i] = seed[i];
+ resetStartStream();
+ }\end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Sets the initial seed $I_g$ of this stream
+ to the vector \texttt{seed[0..5]}. This vector must satisfy the same
+ conditions as in \texttt{setPackageSeed}.
+ The stream is then reset to this initial seed.
+ The states and seeds of the other streams are not modified.
+ As a result, after calling this method, the initial seeds
+ of the streams are no longer spaced $Z$ values apart.
+ For this reason, \emph{this method should be used only in very
+ exceptional situations} (I have never used it myself!);
+ proper use of \texttt{reset...}
+ and of the stream constructor is preferable.
+ \end{tabb}
+\fi
+\begin{htmlonly}
+ \param{seed}{array of 6 integers representing the new seed}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ public void resetStartStream() {
+ for (int i = 0; i < 6; ++i)
+ Bg[i] = Ig[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ Cg0 = Bg[0];
+ Cg1 = Bg[1];
+ Cg2 = Bg[2];
+ Cg3 = Bg[3];
+ Cg4 = Bg[4];
+ Cg5 = Bg[5];
+ }
+
+ public void resetNextSubstream() {
+ multMatVect(Bg, A1p76, m1, A2p76, m2);
+ resetStartSubstream();
+ }
+
+\end{hide}
+ public long[] getState() \begin{hide} {
+ return new long[]{Cg0, Cg1, Cg2, Cg3, Cg4, Cg5};
+ } \end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Returns the current state $C_g$ of this stream.
+ This is a vector of 6 integers represented in floating-point format.
+ This method is convenient if we want to save the state for
+ subsequent use.
+ \end{tabb}
+\fi
+\begin{htmlonly}
+ \return{the current state of the generator}
+\end{htmlonly}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("The current state of the MRG32k3aL");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " Cg = { ");
+ str.append ( Cg0 + ", ");
+ str.append ( Cg1 + ", ");
+ str.append ( Cg2 + ", ");
+ str.append ( Cg3 + ", ");
+ str.append ( Cg4 + ", ");
+ str.append ( Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Returns a string containing the name and the current state $C_g$
+ of this stream.
+ \end{tabb}
+\fi
+\begin{htmlonly}
+ \return{the state of the generator, formated as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String toStringFull() \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("The MRG32k3aL stream");
+ if (name != null && name.length() > 0)
+ str.append (" " + name);
+ str.append (":" + PrintfFormat.NEWLINE + " anti = " +
+ (anti ? "true" : "false")).append(PrintfFormat.NEWLINE);
+ str.append (" prec53 = " + (prec53 ? "true" : "false")).append(PrintfFormat.NEWLINE);
+
+ str.append (" Ig = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ( Ig[i] + ", ");
+ str.append ( Ig[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Bg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ( Bg[i] + ", ");
+ str.append ( Bg[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Cg = { ");
+ str.append ( Cg0 + ", ");
+ str.append ( Cg1 + ", ");
+ str.append ( Cg2 + ", ");
+ str.append ( Cg3 + ", ");
+ str.append ( Cg4 + ", ");
+ str.append ( Cg5 + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Returns a string containing the name of this stream and the
+ values of all its internal variables.
+ \end{tabb}
+\fi
+\begin{htmlonly}
+ \return{the detailed state of the generator, formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public MRG32k3aL clone() \begin{hide} {
+ MRG32k3aL retour = null;
+
+ retour = (MRG32k3aL)super.clone();
+ retour.Bg = new long[6];
+ retour.Ig = new long[6];
+ for (int i = 0; i<6; i++) {
+ retour.Bg[i] = Bg[i];
+ retour.Ig[i] = Ig[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+\iffalse
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+\fi
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ protected double nextValue() {
+ int k;
+ long p1, p2;
+
+ /* Component 1 */
+ p1 = (a12 * Cg1 - a13n * Cg0) % m1;
+ if (p1 < 0)
+ p1 += m1;
+ Cg0 = Cg1;
+ Cg1 = Cg2;
+ Cg2 = p1;
+
+ /* Component 2 */
+ p2 = (a21 * Cg5 - a23n * Cg3) % m2;
+ if (p2 < 0)
+ p2 += m2;
+ Cg3 = Cg4;
+ Cg4 = Cg5;
+ Cg5 = p2;
+
+ /* Combination */
+ return (double)((p1 > p2) ? (p1 - p2) * norm : (p1 - p2 + m1) * norm);
+ }
+
+
+ private static void validateSeed (long seed[]) {
+ if (seed.length < 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException
+ ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException
+ ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException
+ ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[3] >= m2 || seed[4] >= m2 || seed[5] >= m2)
+ throw new IllegalArgumentException
+ ("The last 3 values must be less than " + m2);
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/MT19937.java b/source/umontreal/iro/lecuyer/rng/MT19937.java
new file mode 100644
index 0000000..d198783
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MT19937.java
@@ -0,0 +1,199 @@
+
+
+/*
+ * Class: MT19937
+ * Description: Mersenne Twister proposed by Matsumoto and Nishimura
+ with a state size of 19937 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.Serializable;
+
+/**
+ * Implements the {@link RandomStream} interface via inheritance from
+ * {@link RandomStreamBase}. The backbone generator is the MT19937
+ * Mersenne Twister, proposed by Matsumoto and Nishimura, which has a state size of 19937 bits and a period length of
+ * approximatively <SPAN CLASS="MATH">2<SUP>19937</SUP></SPAN>.
+ * Each instance uses another {@link CloneableRandomStream} to fill its initial state.
+ * With this design, the initial states of successive streams are not
+ * spaced by an equal number of steps, and there is no guarantee that
+ * different streams do not overlap, but damaging overlap is unlikely
+ * because of the huge size of the state space.
+ * The seed of the RNG, and the state of a stream at any given
+ * step, is a 624-dimensional vector of 32-bit integers.
+ * The output of <TT>nextValue</TT> has 32 bits of precision.
+ *
+ */
+public class MT19937 extends RandomStreamBase {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ private static final double NORM = 1.0 / 0x100000001L; // 1/(2^32 + 1)
+
+ private static final int N = 624;
+ private static final int M = 397;
+ private static final int[] MULT_MATRIX_A = {0x0, 0x9908B0DF};
+ private static final int UPPER_MASK = 0x80000000;
+ private static final int LOWER_MASK = 0x7FFFFFFF;
+
+ private int[] state;
+ private int state_i;
+
+ private CloneableRandomStream seedRng;
+
+ private void fillSeed() {
+ state_i = N;
+
+ for(int i = 0; i < N; i++)
+ state[i] = (int)((long)(seedRng.nextDouble() * 0x100000000L));
+ }
+
+
+ /**
+ * Constructs a new stream, using <TT>rng</TT> to fill its initial state.
+ *
+ * @param rng used to build the seed
+ *
+ *
+ */
+ public MT19937 (CloneableRandomStream rng) {
+ seedRng = rng;
+ name = null;
+
+ state = new int[N];
+
+ //note : on pourrait directement appeler fillSeed, sauf qu'il y aurait
+ // des incoherences si le rng a deja ete appele
+ resetStartStream();
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used in the <TT>toString</TT> method).
+ *
+ * @param rng used to build the seed
+ *
+ * @param name name of the stream
+ *
+ */
+ public MT19937 (CloneableRandomStream rng, String name) {
+ this(rng);
+ this.name = name;
+ }
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public MT19937 clone() {
+ MT19937 retour = null;
+ retour = (MT19937)super.clone();
+ retour.state = new int[N];
+ for (int i = 0; i<N; i++) {
+ retour.state[i] = state[i];
+ }
+ retour.seedRng = seedRng.clone();
+ return retour;
+ }
+
+ public void resetStartStream() {
+ seedRng.resetStartStream();
+ fillSeed();
+ }
+
+ public void resetStartSubstream() {
+ seedRng.resetStartSubstream();
+ fillSeed();
+ }
+
+ public void resetNextSubstream() {
+ seedRng.resetNextSubstream();
+ fillSeed();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if(name == null)
+ sb.append("This MT19937 ");
+ else
+ sb.append(name + " ");
+ sb.append("has a " + seedRng.getClass() + " for its seed." +
+ PrintfFormat.NEWLINE);
+
+ sb.append("State = [");
+ for(int i = 0; i < N-1; i++)
+ sb.append(state[i] + ",");
+ sb.append(state[N-1] + "]. ");
+ sb.append("State index = " + state_i);
+
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ int y;
+
+ if(state_i >= N) {
+ int kk;
+
+ for(kk=0; kk < N - M; kk++) {
+ y = (state[kk] & UPPER_MASK) | (state[kk+1] & LOWER_MASK);
+ state[kk] = state[kk + M] ^ (y >>> 1) ^
+ MULT_MATRIX_A[y & 0x1];
+ }
+ for(; kk < N - 1; kk++) {
+ y = (state[kk] & UPPER_MASK) | (state[kk+1] & LOWER_MASK);
+ state[kk] = state[kk + (M - N)] ^ (y >>> 1) ^
+ MULT_MATRIX_A[y & 0x1];
+ }
+ y = (state[N-1] & UPPER_MASK) | (state[0] & LOWER_MASK);
+ state[N-1] = state[M-1] ^ (y >>> 1) ^
+ MULT_MATRIX_A[y & 0x1];
+
+ state_i = 0;
+ }
+
+ y = state[state_i++];
+
+ // Tempering */
+ y ^= (y >>> 11);
+ y ^= (y << 7) & 0x9d2c5680;
+ y ^= (y << 15) & 0xefc60000;
+ y ^= (y >>> 18);
+
+ long r = (y <= 0) ? y + 0x100000000L : y;
+
+ return r * NORM;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/MT19937.tex b/source/umontreal/iro/lecuyer/rng/MT19937.tex
new file mode 100644
index 0000000..7dd843c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/MT19937.tex
@@ -0,0 +1,212 @@
+\defmodule {MT19937}
+
+Implements the \class{RandomStream} interface via inheritance from
+\class{RandomStreamBase}. The backbone generator is the MT19937
+Mersenne Twister, proposed by Matsumoto and Nishimura
+\cite{rMAT98a}, which has a state size of 19937 bits and a period length of
+\html{approximatively} \latex{$\rho\approx$} $2^{19937}$.
+Each instance uses another \class{CloneableRandomStream} to fill its initial state.
+With this design, the initial states of successive streams are not
+spaced by an equal number of steps, and there is no guarantee that
+different streams do not overlap, but damaging overlap is unlikely
+because of the huge size of the state space.
+The seed of the RNG, and the state of a stream at any given
+step, is a 624-dimensional vector of 32-bit integers.
+The output of \texttt{nextValue} has 32 bits of precision.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: MT19937
+ * Description: Mersenne Twister proposed by Matsumoto and Nishimura
+ with a state size of 19937 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.Serializable;\end{hide}
+
+public class MT19937 extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ private static final double NORM = 1.0 / 0x100000001L; // 1/(2^32 + 1)
+
+ private static final int N = 624;
+ private static final int M = 397;
+ private static final int[] MULT_MATRIX_A = {0x0, 0x9908B0DF};
+ private static final int UPPER_MASK = 0x80000000;
+ private static final int LOWER_MASK = 0x7FFFFFFF;
+
+ private int[] state;
+ private int state_i;
+
+ private CloneableRandomStream seedRng;
+
+ private void fillSeed() {
+ state_i = N;
+
+ for(int i = 0; i < N; i++)
+ state[i] = (int)((long)(seedRng.nextDouble() * 0x100000000L));
+ }
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public MT19937 (CloneableRandomStream rng) \begin{hide} {
+ seedRng = rng;
+ name = null;
+
+ state = new int[N];
+
+ //note : on pourrait directement appeler fillSeed, sauf qu'il y aurait
+ // des incoherences si le rng a deja ete appele
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream, using \texttt{rng} to fill its initial state.
+\end{tabb}
+\begin{htmlonly}
+ \param{rng}{used to build the seed}
+\end{htmlonly}
+\begin{code}
+
+ public MT19937 (CloneableRandomStream rng, String name) \begin{hide} {
+ this(rng);
+ this.name = name;
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used in the \texttt{toString} method).
+\end{tabb}
+\begin{htmlonly}
+ \param{rng}{used to build the seed}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public MT19937 clone() \begin{hide} {
+ MT19937 retour = null;
+ retour = (MT19937)super.clone();
+ retour.state = new int[N];
+ for (int i = 0; i<N; i++) {
+ retour.state[i] = state[i];
+ }
+ retour.seedRng = seedRng.clone();
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}\begin{hide}
+ public void resetStartStream() {
+ seedRng.resetStartStream();
+ fillSeed();
+ }
+
+ public void resetStartSubstream() {
+ seedRng.resetStartSubstream();
+ fillSeed();
+ }
+
+ public void resetNextSubstream() {
+ seedRng.resetNextSubstream();
+ fillSeed();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if(name == null)
+ sb.append("This MT19937 ");
+ else
+ sb.append(name + " ");
+ sb.append("has a " + seedRng.getClass() + " for its seed." +
+ PrintfFormat.NEWLINE);
+
+ sb.append("State = [");
+ for(int i = 0; i < N-1; i++)
+ sb.append(state[i] + ",");
+ sb.append(state[N-1] + "]. ");
+ sb.append("State index = " + state_i);
+
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ int y;
+
+ if(state_i >= N) {
+ int kk;
+
+ for(kk=0; kk < N - M; kk++) {
+ y = (state[kk] & UPPER_MASK) | (state[kk+1] & LOWER_MASK);
+ state[kk] = state[kk + M] ^ (y >>> 1) ^
+ MULT_MATRIX_A[y & 0x1];
+ }
+ for(; kk < N - 1; kk++) {
+ y = (state[kk] & UPPER_MASK) | (state[kk+1] & LOWER_MASK);
+ state[kk] = state[kk + (M - N)] ^ (y >>> 1) ^
+ MULT_MATRIX_A[y & 0x1];
+ }
+ y = (state[N-1] & UPPER_MASK) | (state[0] & LOWER_MASK);
+ state[N-1] = state[M-1] ^ (y >>> 1) ^
+ MULT_MATRIX_A[y & 0x1];
+
+ state_i = 0;
+ }
+
+ y = state[state_i++];
+
+ // Tempering */
+ y ^= (y >>> 11);
+ y ^= (y << 7) & 0x9d2c5680;
+ y ^= (y << 15) & 0xefc60000;
+ y ^= (y >>> 18);
+
+ long r = (y <= 0) ? y + 0x100000000L : y;
+
+ return r * NORM;
+ }\end{hide}
+\end{code}
+
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/RandMrg.java b/source/umontreal/iro/lecuyer/rng/RandMrg.java
new file mode 100644
index 0000000..b1a01fe
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandMrg.java
@@ -0,0 +1,603 @@
+
+
+/*
+ * Class: RandMrg
+ * Description: Old class replaced by MRG32k3a
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.Serializable;
+
+
+ at Deprecated
+/**
+ * <SPAN CLASS="textit">USE</SPAN> {@link MRG32k3a} <SPAN CLASS="textit">INSTEAD of this class</SPAN>.
+ * This class implements the interface {@link RandomStream} directly, with a few
+ * additional tools. It uses the same backbone (or main) generator as
+ * {@link MRG32k3a}, but it is an older
+ * implementation that does not extend {@link RandomStreamBase},
+ * and it is about 10% slower.
+ *
+ */
+public class RandMrg implements CloneableRandomStream, Serializable {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Private constants.
+
+ private static final double m1 = 4294967087.0;
+ private static final double m2 = 4294944443.0;
+ private static final double a12 = 1403580.0;
+ private static final double a13n = 810728.0;
+ private static final double a21 = 527612.0;
+ private static final double a23n = 1370589.0;
+ private static final double two17 = 131072.0;
+ private static final double two53 = 9007199254740992.0;
+ private static final double invtwo24 = 5.9604644775390625e-8;
+// private static final double norm = 2.328306549295727688e-10;
+ private static final double norm = 1.0 / (m1 + 1.0);
+
+ private static final double InvA1[][] = { // Inverse of A1p0
+ { 184888585.0, 0.0, 1945170933.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double InvA2[][] = { // Inverse of A2p0
+ { 0.0, 360363334.0, 4225571728.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double A1p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -810728.0, 1403580.0, 0.0 }
+ };
+ private static final double A2p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -1370589.0, 0.0, 527612.0 }
+ };
+ private static final double A1p76[][] = {
+ { 82758667.0, 1871391091.0, 4127413238.0 },
+ { 3672831523.0, 69195019.0, 1871391091.0 },
+ { 3672091415.0, 3528743235.0, 69195019.0 }
+ };
+ private static final double A2p76[][] = {
+ { 1511326704.0, 3759209742.0, 1610795712.0 },
+ { 4292754251.0, 1511326704.0, 3889917532.0 },
+ { 3859662829.0, 4292754251.0, 3708466080.0 }
+ };
+ private static final double A1p127[][] = {
+ { 2427906178.0, 3580155704.0, 949770784.0 },
+ { 226153695.0, 1230515664.0, 3580155704.0 },
+ { 1988835001.0, 986791581.0, 1230515664.0 }
+ };
+ private static final double A2p127[][] = {
+ { 1464411153.0, 277697599.0, 1610723613.0 },
+ { 32183930.0, 1464411153.0, 1022607788.0 },
+ { 2824425944.0, 32183930.0, 2093834863.0 }
+ };
+
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Private variables (fields) for each stream.
+
+ private static double nextSeed[] = {12345, 12345, 12345, 12345, 12345, 12345};
+ // Default seed of the package and seed for the next stream to be created.
+
+ private double Cg[] = new double[6];
+ private double Bg[] = new double[6];
+ private double Ig[] = new double[6];
+ // The arrays \texttt{Cg}, \texttt{Bg}, and \texttt{Ig} contain the current state,
+ // the starting point of the current substream,
+ // and the starting point of the stream, respectively.
+
+ private boolean anti;
+ // This stream generates antithetic variates if
+ // and only if \texttt{anti = true}.
+
+ private boolean prec53;
+ // The precision of the output numbers is ``increased'' (see
+ // \texttt{increasedPrecis}) if and only if \texttt{prec53 = true}.
+
+ private String descriptor;
+ // Describes the stream (for writing the state, error messages, etc.).
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Private methods
+
+ //--------------------------------------------------------------
+ /* Compute (a*s + c) MOD m ; m must be < 2^35 */
+ /* Works also for s, c < 0. */
+ private static double multModM
+ (double a, double s, double c, double m) {
+ double v;
+ int a1;
+ v = a * s + c;
+ if (v >= two53 || v <= -two53 ) {
+ a1 = (int)(a / two17); a -= a1 * two17;
+ v = a1 * s;
+ a1 = (int)(v / m); v -= a1 * m;
+ v = v * two17 + a * s + c;
+ }
+ a1 = (int)(v / m);
+ if ((v -= a1 * m) < 0.0) return v += m; else return v;
+ }
+
+
+ //-----------------------------------------------------------
+ /* Returns v = A*s MOD m. Assumes that -m < s[i] < m. */
+ /* Works even if v = s. */
+ private static void matVecModM (double A[][], double s[],
+ double v[], double m) {
+ int i;
+ double x[] = new double[3];
+ for (i = 0; i < 3; ++i) {
+ x[i] = multModM (A[i][0], s[0], 0.0, m);
+ x[i] = multModM (A[i][1], s[1], x[i], m);
+ x[i] = multModM (A[i][2], s[2], x[i], m);
+ }
+ for (i = 0; i < 3; ++i) v[i] = x[i];
+ }
+
+ //------------------------------------------------------------
+ /* Returns C = A*B MOD m */
+ /* Note: work even if A = C or B = C or A = B = C. */
+ private static void matMatModM (double A[][], double B[][],
+ double C[][], double m){
+ int i, j;
+ double V[] = new double[3], W[][] = new double[3][3];
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j) V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < 3; ++j) W[j][i] = V[j];
+ }
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j)
+ C[i][j] = W[i][j];
+ }
+ }
+
+ //-------------------------------------------------------------
+ /* Compute matrix B = (A^(2^e) Mod m); works even if A = B */
+ private static void matTwoPowModM (double A[][], double B[][],
+ double m, int e) {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; ++j) B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++) matMatModM (B, B, B, m);
+ }
+
+ //-------------------------------------------------------------
+ /* Compute matrix D = A^c Mod m ; works even if A = B */
+ private static void matPowModM (double A[][], double B[][],
+ double m, int c){
+ int i, j;
+ int n = c;
+ double W[][] = new double[3][3];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0.0;
+ }
+ }
+ for (j = 0; j < 3; ++j) B[j][j] = 1.0;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1) matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ }
+
+ //-------------------------------------------------------------
+ // Generate a uniform random number, with 32 bits of resolution.
+ private double U01() {
+ int k;
+ double p1, p2, u;
+ /* Component 1 */
+ p1 = a12 * Cg[1] - a13n * Cg[0];
+ k = (int)(p1 / m1);
+ p1 -= k * m1;
+ if (p1 < 0.0) p1 += m1;
+ Cg[0] = Cg[1]; Cg[1] = Cg[2]; Cg[2] = p1;
+ /* Component 2 */
+ p2 = a21 * Cg[5] - a23n * Cg[3];
+ k = (int)(p2 / m2);
+ p2 -= k * m2;
+ if (p2 < 0.0) p2 += m2;
+ Cg[3] = Cg[4]; Cg[4] = Cg[5]; Cg[5] = p2;
+ /* Combination */
+ u = ((p1 > p2) ? (p1 - p2) * norm : (p1 - p2 + m1) * norm);
+ return (anti) ? (1 - u) : u;
+ }
+
+ //-------------------------------------------------------------
+ // Generate a uniform random number, with 52 bits of resolution.
+ private double U01d() {
+ double u = U01();
+ if (anti) {
+ // Antithetic case: note that U01 already returns 1-u.
+ u += (U01() - 1.0) * invtwo24;
+ return (u < 0.0) ? u + 1.0 : u;
+ } else {
+ u += U01() * invtwo24;
+ return (u < 1.0) ? u : (u - 1.0);
+ }
+ }
+
+
+
+ /**
+ * Constructs a new stream, initializes its seed <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN>,
+ * sets <SPAN CLASS="MATH"><I>B</I><SUB>g</SUB></SPAN> and <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> equal to <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN>, and sets its antithetic switch
+ * to <TT>false</TT>.
+ * The seed <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN> is equal to the initial seed of the package given by
+ * {@link #setPackageSeed(long[]) setPackageSeed} if this is the first stream created,
+ * otherwise it is <SPAN CLASS="MATH"><I>Z</I></SPAN> steps ahead of that of the stream most recently
+ * created in this class.
+ *
+ */
+ public RandMrg() {
+ anti = false;
+ prec53 = false;
+ for (int i = 0; i < 6; ++i)
+ Bg[i] = Cg[i] = Ig[i] = nextSeed[i];
+ matVecModM (A1p127, nextSeed, nextSeed, m1);
+ double temp[] = new double[3];
+ for (int i = 0; i < 3; ++i)
+ temp[i] = nextSeed[i + 3];
+ matVecModM (A2p127, temp, temp, m2);
+ for (int i = 0; i < 3; ++i)
+ nextSeed[i + 3] = temp[i];
+ }
+
+
+ /**
+ * Constructs a new stream with an identifier <TT>name</TT>
+ * (can be used when printing the stream state, in error messages, etc.).
+ *
+ * @param name name of the stream
+ *
+ */
+ public RandMrg (String name) {
+ this();
+ descriptor = name;
+ }
+
+
+ /**
+ * Sets the initial seed for the class <TT>RandMrg</TT> to the
+ * six integers in the vector <TT>seed[0..5]</TT>.
+ * This will be the seed (initial state) of the first stream.
+ * If this method is not called, the default initial seed
+ * is
+ * <SPAN CLASS="MATH">(12345, 12345, 12345, 12345, 12345, 12345)</SPAN>.
+ * If it is called, the first 3 values of the seed must all be
+ * less than
+ * <SPAN CLASS="MATH"><I>m</I><SUB>1</SUB> = 4294967087</SPAN>, and not all 0;
+ * and the last 3 values
+ * must all be less than
+ * <SPAN CLASS="MATH"><I>m</I><SUB>2</SUB> = 4294944443</SPAN>, and not all 0.
+ *
+ * @param seed array of 6 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (long seed[]) {
+ // Must use long because there is no unsigned int type.
+ if (seed.length != 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[5] == 0 && seed[3] == 0 && seed[4] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[5] >= m2 || seed[3] >= m2 || seed[4] >= m2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + m2);
+ for (int i = 0; i < 6; ++i) nextSeed[i] = seed[i];
+ }
+
+
+ public void resetStartStream() {
+ for (int i = 0; i < 6; ++i) Cg[i] = Bg[i] = Ig[i];
+ }
+
+ public void resetStartSubstream() {
+ for (int i = 0; i < 6; ++i) Cg[i] = Bg[i];
+ }
+
+ public void resetNextSubstream() {
+ int i;
+ matVecModM (A1p76, Bg, Bg, m1);
+ double temp[] = new double[3];
+ for (i = 0; i < 3; ++i) temp[i] = Bg[i + 3];
+ matVecModM (A2p76, temp, temp, m2);
+ for (i = 0; i < 3; ++i) Bg[i + 3] = temp[i];
+ for (i = 0; i < 6; ++i) Cg[i] = Bg[i];
+ }
+
+
+ /**
+ * After calling this method with <TT>incp = true</TT>, each call to
+ * the generator (direct or indirect) for this stream
+ * will return a uniform random number with (roughly) 53 bits of resolution
+ * instead of 32 bits,
+ * and will advance the state of the stream by 2 steps instead of 1.
+ * More precisely, if <TT>s</TT> is a stream of the class <TT>RandMrg</TT>,
+ * in the non-antithetic case, the instruction
+ * ``<TT>u = s.nextDouble()</TT>'', when the resolution has been increased,
+ * is equivalent to
+ * ``<TT>u = (s.nextDouble() + s.nextDouble()*fact) % 1.0</TT>''
+ * where the constant <TT>fact</TT> is equal to <SPAN CLASS="MATH">2<SUP>-24</SUP></SPAN>.
+ * This also applies when calling <TT>nextDouble</TT> indirectly
+ * (e.g., via <TT>nextInt</TT>, etc.).
+ *
+ * <P>
+ * By default, or if this method is called again with <TT>incp = false</TT>,
+ * each call to <TT>nextDouble</TT> for this stream advances the state by 1 step
+ * and returns a number with 32 bits of resolution.
+ *
+ * @param incp <TT>true</TT> if increased precision is desired, <TT>false</TT> otherwise
+ *
+ *
+ */
+ public void increasedPrecis (boolean incp) {
+ prec53 = incp;
+ }
+
+
+ public void setAntithetic (boolean anti) {
+ this.anti = anti;
+ }
+
+ /**
+ * Advances the state of this stream by <SPAN CLASS="MATH"><I>k</I></SPAN> values,
+ * without modifying the states of other streams (as in <TT>setSeed</TT>),
+ * nor the values of <SPAN CLASS="MATH"><I>B</I><SUB>g</SUB></SPAN> and <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN> associated with this stream.
+ * If <SPAN CLASS="MATH"><I>e</I> > 0</SPAN>, then <SPAN CLASS="MATH"><I>k</I> = 2<SUP>e</SUP> + <I>c</I></SPAN>;
+ * if <SPAN CLASS="MATH"><I>e</I> < 0</SPAN>, then
+ * <SPAN CLASS="MATH"><I>k</I> = - 2<SUP>-e</SUP> + <I>c</I></SPAN>; and if <SPAN CLASS="MATH"><I>e</I> = 0</SPAN>, then <SPAN CLASS="MATH"><I>k</I> = <I>c</I></SPAN>.
+ * Note: <SPAN CLASS="MATH"><I>c</I></SPAN> is allowed to take negative values.
+ * This method should be used only in very
+ * exceptional cases; proper use of the <TT>reset...</TT> methods
+ * and of the stream constructor cover most reasonable situations.
+ *
+ * @param e an exponent
+ *
+ * @param c a constant
+ *
+ *
+ */
+ public void advanceState (int e, int c) {
+ double B1[][]= new double[3][3], C1[][]=new double[3][3];
+ double B2[][]= new double[3][3], C2[][]=new double[3][3];
+
+ if (e > 0) {
+ matTwoPowModM (A1p0, B1, m1, e);
+ matTwoPowModM (A2p0, B2, m2, e);
+ } else if (e < 0) {
+ matTwoPowModM (InvA1, B1, m1, -e);
+ matTwoPowModM (InvA2, B2, m2, -e);
+ }
+
+ if (c >= 0) {
+ matPowModM (A1p0, C1, m1, c);
+ matPowModM (A2p0, C2, m2, c);
+ } else {
+ matPowModM (InvA1, C1, m1, -c);
+ matPowModM (InvA2, C2, m2, -c);
+ }
+
+ if (e != 0) {
+ matMatModM (B1, C1, C1, m1);
+ matMatModM (B2, C2, C2, m2);
+ }
+
+ matVecModM (C1, Cg, Cg, m1);
+ double[] cg3 = new double[3];
+ for (int i = 0; i < 3; i++) cg3[i] = Cg[i+3];
+ matVecModM (C2, cg3, cg3, m2);
+ for (int i = 0; i < 3; i++) Cg[i+3] = cg3[i];
+ }
+
+
+ /**
+ * Sets the initial seed <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN> of this stream
+ * to the vector <TT>seed[0..5]</TT>. This vector must satisfy the same
+ * conditions as in <TT>setPackageSeed</TT>.
+ * The stream is then reset to this initial seed.
+ * The states and seeds of the other streams are not modified.
+ * As a result, after calling this method, the initial seeds
+ * of the streams are no longer spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should be used only in very
+ * exceptional situations; proper use of <TT>reset...</TT>
+ * and of the stream constructor is preferable.
+ *
+ * @param seed array of 6 integers representing the new seed
+ *
+ *
+ */
+ public void setSeed (long seed[]) {
+ // Must use long because there is no unsigned int type.
+ if (seed.length != 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[3] >= m2 || seed[4] >= m2 || seed[5] >= m2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + m2);
+ for (int i = 0; i < 6; ++i)
+ Cg[i] = Bg[i] = Ig[i] = seed[i];
+ }
+
+
+ /**
+ * Returns the current state <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> of this stream.
+ * This is a vector of 6 integers represented in floating-point format.
+ * This method is convenient if we want to save the state for
+ * subsequent use.
+ *
+ * @return the current state of the generator
+ *
+ */
+ public double[] getState() {
+ return Cg;
+ }
+
+ public String toString() {
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("The current state of the RandMrg");
+ if (descriptor != null && descriptor.length() > 0)
+ str.append (" " + descriptor);
+ str.append (":" + PrintfFormat.NEWLINE + " Cg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Cg[i] + ", ");
+ str.append ((long) Cg[5] + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+ /**
+ * Returns a string containing the name of this stream and the
+ * values of all its internal variables.
+ *
+ * @return the detailed state of the generator, formatted as a string
+ *
+ */
+ public String toStringFull() {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("The RandMrg");
+ if (descriptor != null && descriptor.length() > 0)
+ str.append (" " + descriptor);
+ str.append (":" + PrintfFormat.NEWLINE + " anti = " +
+ (anti ? "true" : "false")).append(PrintfFormat.NEWLINE);
+
+ str.append (" Ig = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Ig[i] + ", ");
+ str.append ((long) Ig[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Bg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Bg[i] + ", ");
+ str.append ((long) Bg[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Cg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Cg[i] + ", ");
+ str.append ((long) Cg[5] + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+
+ /**
+ * Returns a (pseudo)random number from the uniform distribution
+ * over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>, using this stream,
+ * after advancing its state by one step.
+ * Normally, the returned number has 32 bits of resolution,
+ * in the sense that it is always a multiple of
+ * <SPAN CLASS="MATH">1/(2<SUP>32</SUP> - 208)</SPAN>.
+ * However, if the precision has been increased by calling
+ * <TT>increasedPrecis</TT> for this stream, the resolution is higher
+ * and the stream state advances by two steps.
+ *
+ *
+ */
+ public double nextDouble() {
+ if (prec53) return this.U01d();
+ else return this.U01();
+ }
+
+
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = start; i < start+n; i++)
+ u[i] = nextDouble();
+ }
+
+ public int nextInt (int i, int j) {
+ // This works even for an interval [0, 2^31 - 1].
+ // It would not with u*(j - i + 1)
+ return (i + (int)(nextDouble() * (j - i + 1.0)));
+ }
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int k = start; k < start+n; k++)
+ u[k] = nextInt (i, j);
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public RandMrg clone() {
+ RandMrg retour = null;
+ try {
+ retour = (RandMrg)super.clone();
+ retour.Cg = new double[6];
+ retour.Bg = new double[6];
+ retour.Ig = new double[6];
+ for (int i = 0; i<6; i++) {
+ retour.Cg[i] = Cg[i];
+ retour.Bg[i] = Bg[i];
+ retour.Ig[i] = Ig[i];
+ }
+ }
+ catch (CloneNotSupportedException cnse) {
+ cnse.printStackTrace(System.err);
+ }
+ return retour;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/RandMrg.tex b/source/umontreal/iro/lecuyer/rng/RandMrg.tex
new file mode 100644
index 0000000..b63a306
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandMrg.tex
@@ -0,0 +1,622 @@
+\defmodule {RandMrg}
+
+\textit{USE} \class{MRG32k3a} \textit{INSTEAD of this class}.
+This class implements the interface \class{RandomStream} directly, with a few
+additional tools. It uses the same backbone (or main) generator as
+\class{MRG32k3a}, but it is an older
+ implementation that does not extend \class{RandomStreamBase},
+ and it is about 10\%{} slower.
+
+\iffalse %%%%%%%%
+The method \texttt{getState} returns the state of a stream.
+One can change the state of a given stream, without modifying
+the state of the other streams, by calling \texttt{setSeed}
+or \texttt{advanceState}.
+However, after calling \texttt{setSeed} for a given stream,
+the initial states of the different streams are no longer spaced
+$Z$ values apart. Therefore, this method should be called
+very sparingly, if at all. The methods \texttt{reset...}
+suffices for almost all applications.
+\fi %%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandMrg
+ * Description: Old class replaced by MRG32k3a
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.io.Serializable;
+\end{hide}
+
+ at Deprecated
+public class RandMrg implements CloneableRandomStream, Serializable\begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Private constants.
+
+ private static final double m1 = 4294967087.0;
+ private static final double m2 = 4294944443.0;
+ private static final double a12 = 1403580.0;
+ private static final double a13n = 810728.0;
+ private static final double a21 = 527612.0;
+ private static final double a23n = 1370589.0;
+ private static final double two17 = 131072.0;
+ private static final double two53 = 9007199254740992.0;
+ private static final double invtwo24 = 5.9604644775390625e-8;
+// private static final double norm = 2.328306549295727688e-10;
+ private static final double norm = 1.0 / (m1 + 1.0);
+
+ private static final double InvA1[][] = { // Inverse of A1p0
+ { 184888585.0, 0.0, 1945170933.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double InvA2[][] = { // Inverse of A2p0
+ { 0.0, 360363334.0, 4225571728.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }
+ };
+ private static final double A1p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -810728.0, 1403580.0, 0.0 }
+ };
+ private static final double A2p0[][] = {
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 },
+ { -1370589.0, 0.0, 527612.0 }
+ };
+ private static final double A1p76[][] = {
+ { 82758667.0, 1871391091.0, 4127413238.0 },
+ { 3672831523.0, 69195019.0, 1871391091.0 },
+ { 3672091415.0, 3528743235.0, 69195019.0 }
+ };
+ private static final double A2p76[][] = {
+ { 1511326704.0, 3759209742.0, 1610795712.0 },
+ { 4292754251.0, 1511326704.0, 3889917532.0 },
+ { 3859662829.0, 4292754251.0, 3708466080.0 }
+ };
+ private static final double A1p127[][] = {
+ { 2427906178.0, 3580155704.0, 949770784.0 },
+ { 226153695.0, 1230515664.0, 3580155704.0 },
+ { 1988835001.0, 986791581.0, 1230515664.0 }
+ };
+ private static final double A2p127[][] = {
+ { 1464411153.0, 277697599.0, 1610723613.0 },
+ { 32183930.0, 1464411153.0, 1022607788.0 },
+ { 2824425944.0, 32183930.0, 2093834863.0 }
+ };
+
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Private variables (fields) for each stream.
+
+ private static double nextSeed[] = {12345, 12345, 12345, 12345, 12345, 12345};
+ // Default seed of the package and seed for the next stream to be created.
+
+ private double Cg[] = new double[6];
+ private double Bg[] = new double[6];
+ private double Ig[] = new double[6];
+ // The arrays \texttt{Cg}, \texttt{Bg}, and \texttt{Ig} contain the current state,
+ // the starting point of the current substream,
+ // and the starting point of the stream, respectively.
+
+ private boolean anti;
+ // This stream generates antithetic variates if
+ // and only if \texttt{anti = true}.
+
+ private boolean prec53;
+ // The precision of the output numbers is ``increased'' (see
+ // \texttt{increasedPrecis}) if and only if \texttt{prec53 = true}.
+
+ private String descriptor;
+ // Describes the stream (for writing the state, error messages, etc.).
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Private methods
+
+ //--------------------------------------------------------------
+ /* Compute (a*s + c) MOD m ; m must be < 2^35 */
+ /* Works also for s, c < 0. */
+ private static double multModM
+ (double a, double s, double c, double m) {
+ double v;
+ int a1;
+ v = a * s + c;
+ if (v >= two53 || v <= -two53 ) {
+ a1 = (int)(a / two17); a -= a1 * two17;
+ v = a1 * s;
+ a1 = (int)(v / m); v -= a1 * m;
+ v = v * two17 + a * s + c;
+ }
+ a1 = (int)(v / m);
+ if ((v -= a1 * m) < 0.0) return v += m; else return v;
+ }
+
+
+ //-----------------------------------------------------------
+ /* Returns v = A*s MOD m. Assumes that -m < s[i] < m. */
+ /* Works even if v = s. */
+ private static void matVecModM (double A[][], double s[],
+ double v[], double m) {
+ int i;
+ double x[] = new double[3];
+ for (i = 0; i < 3; ++i) {
+ x[i] = multModM (A[i][0], s[0], 0.0, m);
+ x[i] = multModM (A[i][1], s[1], x[i], m);
+ x[i] = multModM (A[i][2], s[2], x[i], m);
+ }
+ for (i = 0; i < 3; ++i) v[i] = x[i];
+ }
+
+ //------------------------------------------------------------
+ /* Returns C = A*B MOD m */
+ /* Note: work even if A = C or B = C or A = B = C. */
+ private static void matMatModM (double A[][], double B[][],
+ double C[][], double m){
+ int i, j;
+ double V[] = new double[3], W[][] = new double[3][3];
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j) V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < 3; ++j) W[j][i] = V[j];
+ }
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j)
+ C[i][j] = W[i][j];
+ }
+ }
+
+ //-------------------------------------------------------------
+ /* Compute matrix B = (A^(2^e) Mod m); works even if A = B */
+ private static void matTwoPowModM (double A[][], double B[][],
+ double m, int e) {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; ++j) B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++) matMatModM (B, B, B, m);
+ }
+
+ //-------------------------------------------------------------
+ /* Compute matrix D = A^c Mod m ; works even if A = B */
+ private static void matPowModM (double A[][], double B[][],
+ double m, int c){
+ int i, j;
+ int n = c;
+ double W[][] = new double[3][3];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0.0;
+ }
+ }
+ for (j = 0; j < 3; ++j) B[j][j] = 1.0;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1) matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ }
+
+ //-------------------------------------------------------------
+ // Generate a uniform random number, with 32 bits of resolution.
+ private double U01() {
+ int k;
+ double p1, p2, u;
+ /* Component 1 */
+ p1 = a12 * Cg[1] - a13n * Cg[0];
+ k = (int)(p1 / m1);
+ p1 -= k * m1;
+ if (p1 < 0.0) p1 += m1;
+ Cg[0] = Cg[1]; Cg[1] = Cg[2]; Cg[2] = p1;
+ /* Component 2 */
+ p2 = a21 * Cg[5] - a23n * Cg[3];
+ k = (int)(p2 / m2);
+ p2 -= k * m2;
+ if (p2 < 0.0) p2 += m2;
+ Cg[3] = Cg[4]; Cg[4] = Cg[5]; Cg[5] = p2;
+ /* Combination */
+ u = ((p1 > p2) ? (p1 - p2) * norm : (p1 - p2 + m1) * norm);
+ return (anti) ? (1 - u) : u;
+ }
+
+ //-------------------------------------------------------------
+ // Generate a uniform random number, with 52 bits of resolution.
+ private double U01d() {
+ double u = U01();
+ if (anti) {
+ // Antithetic case: note that U01 already returns 1-u.
+ u += (U01() - 1.0) * invtwo24;
+ return (u < 0.0) ? u + 1.0 : u;
+ } else {
+ u += U01() * invtwo24;
+ return (u < 1.0) ? u : (u - 1.0);
+ }
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public RandMrg() \begin{hide} {
+ anti = false;
+ prec53 = false;
+ for (int i = 0; i < 6; ++i)
+ Bg[i] = Cg[i] = Ig[i] = nextSeed[i];
+ matVecModM (A1p127, nextSeed, nextSeed, m1);
+ double temp[] = new double[3];
+ for (int i = 0; i < 3; ++i)
+ temp[i] = nextSeed[i + 3];
+ matVecModM (A2p127, temp, temp, m2);
+ for (int i = 0; i < 3; ++i)
+ nextSeed[i + 3] = temp[i];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new stream, initializes its seed $I_g$,
+ sets $B_g$ and $C_g$ equal to $I_g$, and sets its antithetic switch
+ to \texttt{false}.
+ The seed $I_g$ is equal to the initial seed of the package given by
+ \method{setPackageSeed}{long[]} if this is the first stream created,
+ otherwise it is $Z$ steps ahead of that of the stream most recently
+ created in this class.
+ \end{tabb}
+\begin{code}
+
+ public RandMrg (String name) \begin{hide} {
+ this();
+ descriptor = name;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new stream with an identifier \texttt{name}
+ (can be used when printing the stream state, in error messages, etc.).
+ \end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static void setPackageSeed (long seed[]) \begin{hide} {
+ // Must use long because there is no unsigned int type.
+ if (seed.length != 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[5] == 0 && seed[3] == 0 && seed[4] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[5] >= m2 || seed[3] >= m2 || seed[4] >= m2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + m2);
+ for (int i = 0; i < 6; ++i) nextSeed[i] = seed[i];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the initial seed for the class \texttt{RandMrg} to the
+ six integers in the vector \texttt{seed[0..5]}.
+ This will be the seed (initial state) of the first stream.
+ If this method is not called, the default initial seed
+ is $(12345, 12345, 12345, 12345, 12345, 12345)$.
+ If it is called, the first 3 values of the seed must all be
+ less than $m_1 = 4294967087$, and not all 0;
+ and the last 3 values
+ must all be less than $m_2 = 4294944443$, and not all 0.
+ \end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 6 elements representing the seed}
+\end{htmlonly}
+\begin{hide}
+\begin{code}
+
+ public void resetStartStream() \begin{hide} {
+ for (int i = 0; i < 6; ++i) Cg[i] = Bg[i] = Ig[i];
+ } \end{hide}
+
+ public void resetStartSubstream() \begin{hide} {
+ for (int i = 0; i < 6; ++i) Cg[i] = Bg[i];
+ } \end{hide}
+
+ public void resetNextSubstream() \begin{hide} {
+ int i;
+ matVecModM (A1p76, Bg, Bg, m1);
+ double temp[] = new double[3];
+ for (i = 0; i < 3; ++i) temp[i] = Bg[i + 3];
+ matVecModM (A2p76, temp, temp, m2);
+ for (i = 0; i < 3; ++i) Bg[i + 3] = temp[i];
+ for (i = 0; i < 6; ++i) Cg[i] = Bg[i];
+ } \end{hide}
+\end{code}
+\end{hide}
+\begin{code}
+
+ public void increasedPrecis (boolean incp) \begin{hide} {
+ prec53 = incp;
+ }\end{hide}
+\end{code}
+ \begin{tabb} After calling this method with \texttt{incp = true}, each call to
+ the generator (direct or indirect) for this stream
+ will return a uniform random number with (roughly) 53 bits of resolution
+ instead of 32 bits,
+ and will advance the state of the stream by 2 steps instead of 1.
+ More precisely, if \texttt{s} is a stream of the class \texttt{RandMrg},
+ in the non-antithetic case, the instruction
+ ``\texttt{u = s.nextDouble()}'', when the resolution has been increased,
+ is equivalent to
+ ``\texttt{u = (s.nextDouble() + s.nextDouble()*fact) \%\ 1.0}''
+ where the constant \texttt{fact} is equal to $2^{-24}$.
+ This also applies when calling \texttt{nextDouble} indirectly
+ (e.g., via \texttt{nextInt}, etc.).
+
+ By default, or if this method is called again with \texttt{incp = false},
+ each call to \texttt{nextDouble} for this stream advances the state by 1 step
+ and returns a number with 32 bits of resolution.
+ \end{tabb}
+\begin{htmlonly}
+ \param{incp}{\texttt{true} if increased precision is desired, \texttt{false} otherwise}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void setAntithetic (boolean anti) {
+ this.anti = anti;
+ }\end{hide}
+
+ public void advanceState (int e, int c) \begin{hide} {
+ double B1[][]= new double[3][3], C1[][]=new double[3][3];
+ double B2[][]= new double[3][3], C2[][]=new double[3][3];
+
+ if (e > 0) {
+ matTwoPowModM (A1p0, B1, m1, e);
+ matTwoPowModM (A2p0, B2, m2, e);
+ } else if (e < 0) {
+ matTwoPowModM (InvA1, B1, m1, -e);
+ matTwoPowModM (InvA2, B2, m2, -e);
+ }
+
+ if (c >= 0) {
+ matPowModM (A1p0, C1, m1, c);
+ matPowModM (A2p0, C2, m2, c);
+ } else {
+ matPowModM (InvA1, C1, m1, -c);
+ matPowModM (InvA2, C2, m2, -c);
+ }
+
+ if (e != 0) {
+ matMatModM (B1, C1, C1, m1);
+ matMatModM (B2, C2, C2, m2);
+ }
+
+ matVecModM (C1, Cg, Cg, m1);
+ double[] cg3 = new double[3];
+ for (int i = 0; i < 3; i++) cg3[i] = Cg[i+3];
+ matVecModM (C2, cg3, cg3, m2);
+ for (int i = 0; i < 3; i++) Cg[i+3] = cg3[i];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Advances the state of this stream by $k$ values,
+ without modifying the states of other streams (as in \texttt{setSeed}),
+ nor the values of $B_g$ and $I_g$ associated with this stream.
+ If $e > 0$, then $k=2^e + c$;
+ if $e < 0$, then $k=-2^{-e} + c$; and if $e = 0$, then $k=c$.
+ Note: $c$ is allowed to take negative values.
+ This method should be used only in very
+ exceptional cases; proper use of the \texttt{reset...} methods
+ and of the stream constructor cover most reasonable situations.
+ \end{tabb}
+\begin{htmlonly}
+ \param{e}{an exponent}
+ \param{c}{a constant}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (long seed[]) \begin{hide} {
+ // Must use long because there is no unsigned int type.
+ if (seed.length != 6)
+ throw new IllegalArgumentException ("Seed must contain 6 values");
+ if (seed[0] == 0 && seed[1] == 0 && seed[2] == 0)
+ throw new IllegalArgumentException ("The first 3 values must not be 0");
+ if (seed[3] == 0 && seed[4] == 0 && seed[5] == 0)
+ throw new IllegalArgumentException ("The last 3 values must not be 0");
+ final long m1 = 4294967087L;
+ if (seed[0] >= m1 || seed[1] >= m1 || seed[2] >= m1)
+ throw new IllegalArgumentException ("The first 3 values must be less than " + m1);
+ final long m2 = 4294944443L;
+ if (seed[3] >= m2 || seed[4] >= m2 || seed[5] >= m2)
+ throw new IllegalArgumentException ("The last 3 values must be less than " + m2);
+ for (int i = 0; i < 6; ++i)
+ Cg[i] = Bg[i] = Ig[i] = seed[i];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the initial seed $I_g$ of this stream
+ to the vector \texttt{seed[0..5]}. This vector must satisfy the same
+ conditions as in \texttt{setPackageSeed}.
+ The stream is then reset to this initial seed.
+ The states and seeds of the other streams are not modified.
+ As a result, after calling this method, the initial seeds
+ of the streams are no longer spaced $Z$ values apart.
+ For this reason, this method should be used only in very
+ exceptional situations; proper use of \texttt{reset...}
+ and of the stream constructor is preferable.
+ \end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 6 integers representing the new seed}
+\end{htmlonly}
+\begin{code}
+
+ public double[] getState() \begin{hide} {
+ return Cg;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the current state $C_g$ of this stream.
+ This is a vector of 6 integers represented in floating-point format.
+ This method is convenient if we want to save the state for
+ subsequent use.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the current state of the generator}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+ public String toString() {
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("The current state of the RandMrg");
+ if (descriptor != null && descriptor.length() > 0)
+ str.append (" " + descriptor);
+ str.append (":" + PrintfFormat.NEWLINE + " Cg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Cg[i] + ", ");
+ str.append ((long) Cg[5] + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+
+ public String toStringFull() \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("The RandMrg");
+ if (descriptor != null && descriptor.length() > 0)
+ str.append (" " + descriptor);
+ str.append (":" + PrintfFormat.NEWLINE + " anti = " +
+ (anti ? "true" : "false")).append(PrintfFormat.NEWLINE);
+
+ str.append (" Ig = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Ig[i] + ", ");
+ str.append ((long) Ig[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Bg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Bg[i] + ", ");
+ str.append ((long) Bg[5] + " }" + PrintfFormat.NEWLINE);
+
+ str.append (" Cg = { ");
+ for (int i = 0; i < 5; i++)
+ str.append ((long) Cg[i] + ", ");
+ str.append ((long) Cg[5] + " }" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a string containing the name of this stream and the
+ values of all its internal variables.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the detailed state of the generator, formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public double nextDouble() \begin{hide} {
+ if (prec53) return this.U01d();
+ else return this.U01();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a (pseudo)random number from the uniform distribution
+ over the interval $(0,1)$, using this stream,
+ after advancing its state by one step.
+ Normally, the returned number has 32 bits of resolution,
+ in the sense that it is always a multiple of $1/(2^{32}-208)$.
+ However, if the precision has been increased by calling
+ \texttt{increasedPrecis} for this stream, the resolution is higher
+ and the stream state advances by two steps.
+ \end{tabb}
+\begin{hide}
+\begin{code}
+
+ public void nextArrayOfDouble (double[] u, int start, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int i = start; i < start+n; i++)
+ u[i] = nextDouble();
+ }\end{hide}
+
+ public int nextInt (int i, int j) \begin{hide} {
+ // This works even for an interval [0, 2^31 - 1].
+ // It would not with u*(j - i + 1)
+ return (i + (int)(nextDouble() * (j - i + 1.0)));
+ }\end{hide}
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n must be positive.");
+ for (int k = start; k < start+n; k++)
+ u[k] = nextInt (i, j);
+ }\end{hide}
+\end{code}
+\end{hide}
+\begin{code}
+
+ public RandMrg clone() \begin{hide} {
+ RandMrg retour = null;
+ try {
+ retour = (RandMrg)super.clone();
+ retour.Cg = new double[6];
+ retour.Bg = new double[6];
+ retour.Ig = new double[6];
+ for (int i = 0; i<6; i++) {
+ retour.Cg[i] = Cg[i];
+ retour.Bg[i] = Bg[i];
+ retour.Ig[i] = Ig[i];
+ }
+ }
+ catch (CloneNotSupportedException cnse) {
+ cnse.printStackTrace(System.err);
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/RandRijndael.java b/source/umontreal/iro/lecuyer/rng/RandRijndael.java
new file mode 100644
index 0000000..b2a676b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandRijndael.java
@@ -0,0 +1,299 @@
+
+
+/*
+ * Class: RandRijndael
+ * Description: RNG using the Rijndael block cipher algorithm (AES) with
+ key and block lengths of 128 bits
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.io.Serializable;
+
+/**
+ * Implements a RNG using the Rijndael block cipher algorithm
+ * (AES) with key and block lengths of 128 bits. A block of 128 bits is
+ * encrypted by the Rijndael algorithm to generate 128
+ * pseudo-random bits. Those bits are split into four words of 32 bits which are
+ * returned successively by the method <TT>nextValue</TT>.
+ * The unencrypted block is the state of the generator.
+ * It is incremented by 1 at every four calls to <TT>nextValue</TT>.
+ * Thus, the period is <SPAN CLASS="MATH">2<SUP>130</SUP></SPAN> and jumping ahead is easy.
+ * The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>40</SUP></SPAN>, <SPAN CLASS="MATH">2<SUP>42</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>82</SUP></SPAN>,
+ * respectively (see {@link RandomStream} for their definition).
+ * Seeds/states must be given as 16-dimensional
+ * vectors of bytes (8-bit integers).
+ * The default initial seed is a vector filled with zeros.
+ *
+ * <P>
+ * The Rijndael implementation used here is that of the
+ * <SPAN CLASS="textit">Cryptix Development Team</SPAN>, which can be found on the
+ * <A NAME="tex2html1"
+ * HREF="http://www.esat.kuleuven.ac.be/~rijmen/rijndael/">Rijndael creators' page</A>
+ * .
+ *
+ */
+public class RandRijndael extends RandomStreamBase {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+
+ private static final int BLOCK_SIZE = 16;
+ private static final int JUMP_STREAM = 10;
+ private static final int JUMP_SUBSTREAM = 5;
+
+ //actually a Object[] containing 2 int[][]
+ private static Object key;
+
+ private static byte[] curr_stream;
+ private byte[] stream;
+ private byte[] substream;
+
+ private byte[] state;
+ private byte[] output;
+ private int outputPos;
+
+ static
+ {
+ try {
+ key = Rijndael_Algorithm.makeKey(new byte[]{1,2,3,4,5,6,7,8,
+ 9,10,11,12,13,14,15,16},
+ BLOCK_SIZE);
+ } catch(Exception e) {
+ //pour que Java soit certain que la clef est initialisee
+ key = new Object[0];
+ e.printStackTrace();
+ throw new RuntimeException(" cannot create RandRijndael key");
+ }
+
+ curr_stream = new byte[BLOCK_SIZE];
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ curr_stream[i] = 0;
+ }
+
+ private static void iterate (byte[] b, int pos) {
+ while((pos < b.length) && (++b[pos++] == 0));
+ }
+
+
+
+ /**
+ * Constructs a new stream.
+ *
+ */
+ public RandRijndael() {
+ stream = new byte[BLOCK_SIZE];
+ substream = new byte[BLOCK_SIZE];
+
+ state = new byte[BLOCK_SIZE];
+
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ stream[i] = curr_stream[i];
+
+ iterate(curr_stream, JUMP_STREAM);
+
+ resetStartStream();
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used in the <TT>toString</TT> method).
+ *
+ * @param name name of the stream
+ *
+ */
+ public RandRijndael (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed for the class <TT>RandRijndael</TT> to the
+ * 16 bytes of the vector <TT>seed[0..15]</TT>.
+ * This will be the initial state (or seed) of the next created stream.
+ * The default seed for the first stream is
+ * <SPAN CLASS="MATH">(0, 0,…, 0, 0)</SPAN>.
+ *
+ * @param seed array of 16 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (byte seed[]) {
+ if(seed.length != BLOCK_SIZE)
+ throw new IllegalArgumentException("Seed must contain " +
+ BLOCK_SIZE + " values");
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ curr_stream[i] = seed[i];
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * seed <TT>seed[0..15]</TT>.
+ * This method only affects the specified stream; the others are not modified,
+ * so the beginning of the streams will not be spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very
+ * exceptional cases; proper use of the <TT>reset...</TT> methods
+ * and of the stream constructor is preferable.
+ *
+ * @param seed array of 16 elements representing the seed
+ *
+ *
+ */
+ public void setSeed (byte seed[]) {
+ if(seed.length != BLOCK_SIZE)
+ throw new IllegalArgumentException("Seed must contain " +
+ BLOCK_SIZE + " values");
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ stream[i] = seed[i];
+ }
+
+
+ /**
+ * Returns the current state of the stream, represented as an
+ * array of four integers.
+ * It should be noted that each state of this generator returns 4 successive
+ * values. The particular value of these 4 which will be returned next is not
+ * given by this method.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public byte[] getState() {
+ byte[] stateCopy = new byte[BLOCK_SIZE];
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ stateCopy[i] = state[i];
+ return stateCopy;
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public RandRijndael clone() {
+ RandRijndael retour = null;
+
+ retour = (RandRijndael)super.clone();
+ retour.stream = new byte[BLOCK_SIZE];
+ retour.substream = new byte[BLOCK_SIZE];
+ retour.state = new byte[BLOCK_SIZE];
+ retour.output = new byte[output.length];
+ for (int i = 0; i<BLOCK_SIZE; i++) {
+ retour.stream[i] = stream[i];
+ retour.substream[i] = substream[i];
+ retour.state[i] = state[i];
+ }
+ for (int i=0; i<output.length; i++) {
+ retour.output[i] = output[i];
+ }
+
+ return retour;
+ }
+
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ substream[i] = stream[i];
+
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ state[i] = substream[i];
+ nextOutput();
+ }
+
+ public void resetNextSubstream() {
+ iterate(substream, JUMP_SUBSTREAM);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if(name == null)
+ sb.append("The state of the RandRijn is : [");
+ else
+ sb.append("The state of the " + name + " is : [");
+
+ for(int i = 0; i < BLOCK_SIZE - 1; i++)
+ sb.append(state[i] + ", ");
+ sb.append(state[BLOCK_SIZE - 1] + "] ");
+
+ sb.append("position : " + outputPos);
+
+ return sb.toString();
+ }
+
+ private void nextOutput() {
+ output = Rijndael_Algorithm.blockEncrypt(state, 0, key, BLOCK_SIZE);
+ outputPos = 0;
+ iterate(state,0);
+ }
+
+ protected double nextValue() {
+ if(outputPos > BLOCK_SIZE - 4)
+ nextOutput();
+
+
+ long val = output[outputPos++] & 0xFF;
+ val <<= 8;
+ val |= output[outputPos++] & 0xFF;
+ val <<= 8;
+ val |= output[outputPos++] & 0xFF;
+ val <<= 8;
+ val |= output[outputPos++] & 0xFF;
+
+
+ /*
+ long val = ((output[outputPos] & 0xFF) << 24) |
+ ((output[outputPos + 1] & 0xFF) << 16) |
+ ((output[outputPos + 2] & 0xFF) << 8) |
+ ((output[outputPos + 3] & 0xFF));
+ outputPos += 4;
+ */
+
+ return ((double)val + 1) / 0x100000001L;
+ }
+
+ /*
+ public static void main(String args[]) {
+ int num = Integer.parseInt(args[0]);
+
+ RandomStream rng = new RandRijn();
+
+ for(int i = 0; i < num; i++) {
+ rng.nextDouble();
+ //System.out.println(rng.nextDouble());
+ }
+
+ System.out.println(rng.toString());
+ }
+ */
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/RandRijndael.tex b/source/umontreal/iro/lecuyer/rng/RandRijndael.tex
new file mode 100644
index 0000000..11a13fd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandRijndael.tex
@@ -0,0 +1,307 @@
+\defmodule {RandRijndael}
+
+Implements a RNG using the Rijndael block cipher algorithm
+(AES) with key and block lengths of 128 bits. A block of 128 bits is
+encrypted by the Rijndael algorithm to generate 128
+pseudo-random bits. Those bits are split into four words of 32 bits which are
+returned successively by the method \texttt{nextValue}.
+The unencrypted block is the state of the generator.
+It is incremented by 1 at every four calls to \texttt{nextValue}.
+Thus, the period is $2^{130}$ and jumping ahead is easy.
+The values of $V$, $W$ and $Z$ are $2^{40}$, $2^{42}$ and $2^{82}$,
+respectively (see \class{RandomStream} for their definition).
+Seeds/states must be given as 16-dimensional
+vectors of bytes (8-bit integers).
+The default initial seed is a vector filled with zeros.
+
+The Rijndael implementation used here is that of the
+\emph{Cryptix Development Team}, which can be found on the
+\htmladdnormallink{Rijndael creators' page}
+{http://www.esat.kuleuven.ac.be/~rijmen/rijndael/}
+\latex{\url{http://www.esat.kuleuven.ac.be/~rijmen/rijndael/}}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandRijndael
+ * Description: RNG using the Rijndael block cipher algorithm (AES) with
+ key and block lengths of 128 bits
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import java.io.Serializable; \end{hide}
+
+public class RandRijndael extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+
+ private static final int BLOCK_SIZE = 16;
+ private static final int JUMP_STREAM = 10;
+ private static final int JUMP_SUBSTREAM = 5;
+
+ //actually a Object[] containing 2 int[][]
+ private static Object key;
+
+ private static byte[] curr_stream;
+ private byte[] stream;
+ private byte[] substream;
+
+ private byte[] state;
+ private byte[] output;
+ private int outputPos;
+
+ static
+ {
+ try {
+ key = Rijndael_Algorithm.makeKey(new byte[]{1,2,3,4,5,6,7,8,
+ 9,10,11,12,13,14,15,16},
+ BLOCK_SIZE);
+ } catch(Exception e) {
+ //pour que Java soit certain que la clef est initialisee
+ key = new Object[0];
+ e.printStackTrace();
+ throw new RuntimeException(" cannot create RandRijndael key");
+ }
+
+ curr_stream = new byte[BLOCK_SIZE];
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ curr_stream[i] = 0;
+ }
+
+ private static void iterate (byte[] b, int pos) {
+ while((pos < b.length) && (++b[pos++] == 0));
+ }
+
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public RandRijndael() \begin{hide} {
+ stream = new byte[BLOCK_SIZE];
+ substream = new byte[BLOCK_SIZE];
+
+ state = new byte[BLOCK_SIZE];
+
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ stream[i] = curr_stream[i];
+
+ iterate(curr_stream, JUMP_STREAM);
+
+ resetStartStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream.
+\end{tabb}
+\begin{code}
+
+ public RandRijndael (String name) \begin{hide} {
+ this();
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used in the \texttt{toString} method).
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (byte seed[]) \begin{hide} {
+ if(seed.length != BLOCK_SIZE)
+ throw new IllegalArgumentException("Seed must contain " +
+ BLOCK_SIZE + " values");
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ curr_stream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed for the class \texttt{RandRijndael} to the
+ 16 bytes of the vector \texttt{seed[0..15]}.
+ This will be the initial state (or seed) of the next created stream.
+ The default seed for the first stream is $(0, 0, \ldots, 0, 0)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 16 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (byte seed[]) \begin{hide} {
+ if(seed.length != BLOCK_SIZE)
+ throw new IllegalArgumentException("Seed must contain " +
+ BLOCK_SIZE + " values");
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ stream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ seed \texttt{seed[0..15]}.
+ This method only affects the specified stream; the others are not modified,
+ so the beginning of the streams will not be spaced $Z$ values apart.
+ For this reason, this method should only be used in very
+ exceptional cases; proper use of the \texttt{reset...} methods
+ and of the stream constructor is preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 16 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public byte[] getState() \begin{hide} {
+ byte[] stateCopy = new byte[BLOCK_SIZE];
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ stateCopy[i] = state[i];
+ return stateCopy;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the stream, represented as an
+ array of four integers.
+ It should be noted that each state of this generator returns 4 successive
+ values. The particular value of these 4 which will be returned next is not
+ given by this method.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}
+
+ public RandRijndael clone() \begin{hide} {
+ RandRijndael retour = null;
+
+ retour = (RandRijndael)super.clone();
+ retour.stream = new byte[BLOCK_SIZE];
+ retour.substream = new byte[BLOCK_SIZE];
+ retour.state = new byte[BLOCK_SIZE];
+ retour.output = new byte[output.length];
+ for (int i = 0; i<BLOCK_SIZE; i++) {
+ retour.stream[i] = stream[i];
+ retour.substream[i] = substream[i];
+ retour.state[i] = state[i];
+ }
+ for (int i=0; i<output.length; i++) {
+ retour.output[i] = output[i];
+ }
+
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+
+ public void resetStartStream() {
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ substream[i] = stream[i];
+
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ for(int i = 0; i < BLOCK_SIZE; i++)
+ state[i] = substream[i];
+ nextOutput();
+ }
+
+ public void resetNextSubstream() {
+ iterate(substream, JUMP_SUBSTREAM);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if(name == null)
+ sb.append("The state of the RandRijn is : [");
+ else
+ sb.append("The state of the " + name + " is : [");
+
+ for(int i = 0; i < BLOCK_SIZE - 1; i++)
+ sb.append(state[i] + ", ");
+ sb.append(state[BLOCK_SIZE - 1] + "] ");
+
+ sb.append("position : " + outputPos);
+
+ return sb.toString();
+ }
+
+ private void nextOutput() {
+ output = Rijndael_Algorithm.blockEncrypt(state, 0, key, BLOCK_SIZE);
+ outputPos = 0;
+ iterate(state,0);
+ }
+
+ protected double nextValue() {
+ if(outputPos > BLOCK_SIZE - 4)
+ nextOutput();
+
+
+ long val = output[outputPos++] & 0xFF;
+ val <<= 8;
+ val |= output[outputPos++] & 0xFF;
+ val <<= 8;
+ val |= output[outputPos++] & 0xFF;
+ val <<= 8;
+ val |= output[outputPos++] & 0xFF;
+
+
+ /*
+ long val = ((output[outputPos] & 0xFF) << 24) |
+ ((output[outputPos + 1] & 0xFF) << 16) |
+ ((output[outputPos + 2] & 0xFF) << 8) |
+ ((output[outputPos + 3] & 0xFF));
+ outputPos += 4;
+ */
+
+ return ((double)val + 1) / 0x100000001L;
+ }
+
+ /*
+ public static void main(String args[]) {
+ int num = Integer.parseInt(args[0]);
+
+ RandomStream rng = new RandRijn();
+
+ for(int i = 0; i < num; i++) {
+ rng.nextDouble();
+ //System.out.println(rng.nextDouble());
+ }
+
+ System.out.println(rng.toString());
+ }
+ */
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomPermutation.java b/source/umontreal/iro/lecuyer/rng/RandomPermutation.java
new file mode 100644
index 0000000..ae6c6fd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomPermutation.java
@@ -0,0 +1,673 @@
+
+
+/*
+ * Class: RandomPermutation
+ * Description: Provides methods to randomly shuffle arrays or lists
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.RandomAccess;
+
+
+/**
+ * Provides methods to randomly shuffle arrays
+ * or lists using a random stream.
+ *
+ */
+public class RandomPermutation {
+ private static final int SHUFFLE_THRESHOLD = 5;
+
+
+ /**
+ * Initializes <TT>array</TT> with the first <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * positive integers in natural order as <TT>array</TT><SPAN CLASS="MATH">[<I>i</I> - 1] = <I>i</I></SPAN>, for
+ * <SPAN CLASS="MATH"><I>i</I> = 1,..., <I>n</I></SPAN>. The size of <TT>array</TT> must be at least <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @param array the array to initialize.
+ *
+ * @param n number of elements initialized.
+ *
+ *
+ */
+ public static void init (byte[] array, int n) {
+ for (byte k = 1; k <= n; k++)
+ array[k-1] = k;
+ }
+
+
+ /**
+ * Similar to {@link #init init}<TT>(byte[], int)</TT>.
+ *
+ * @param array the array to initialize.
+ *
+ * @param n number of elements initialized.
+ *
+ *
+ */
+ public static void init (short[] array, int n) {
+ for (short k = 1; k <= n; k++)
+ array[k-1] = k;
+ }
+
+
+ /**
+ * Similar to {@link #init init}<TT>(byte[], int)</TT>.
+ *
+ * @param array the array to initialize.
+ *
+ * @param n number of elements initialized.
+ *
+ *
+ */
+ public static void init (int[] array, int n) {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }
+
+
+ /**
+ * Similar to {@link #init init}<TT>(byte[], int)</TT>.
+ *
+ * @param array the array to initialize.
+ *
+ * @param n number of elements initialized.
+ *
+ *
+ */
+ public static void init (long[] array, int n) {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }
+
+
+ /**
+ * Similar to {@link #init init}<TT>(byte[], int)</TT>.
+ *
+ * @param array the array to initialize.
+ *
+ * @param n number of elements initialized.
+ *
+ *
+ */
+ public static void init (float[] array, int n) {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }
+
+
+ /**
+ * Similar to {@link #init init}<TT>(byte[], int)</TT>.
+ *
+ * <P><P>
+ * <BR>
+ *
+ * @param array the array to initialize.
+ *
+ * @param n number of elements initialized.
+ *
+ *
+ */
+ public static void init (double[] array, int n) {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }
+
+ at SuppressWarnings("unchecked")
+ /**
+ * Same as <TT>java.util.Collections.shuffle(List<?>, Random)</TT>,
+ * but uses a {@link RandomStream} instead of <TT>java.util.Random</TT>.
+ *
+ * @param list the list being shuffled.
+ *
+ * @param stream the random stream used to generate integers.
+ *
+ *
+ */
+ public static void shuffle (List<?> list, RandomStream stream) {
+ // The implementation is inspired from Sun's Collections.shuffle
+ final int size = list.size ();
+ if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
+ for (int i = size; i > 1; i--)
+ Collections.swap (list, i - 1, stream.nextInt (0, i - 1));
+
+ } else {
+ final Object arr[] = list.toArray ();
+
+ // Shuffle array<
+ shuffle (arr, stream);
+
+ // Dump array back into list
+ final ListIterator it = list.listIterator ();
+ for (Object element : arr) {
+ it.next ();
+ it.set (element);
+ }
+ }
+ }
+
+
+ /**
+ * Randomly permutes <TT>array</TT> using <TT>stream</TT>.
+ * This method permutes the whole array.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (Object[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final Object tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Randomly permutes <TT>array</TT> using <TT>stream</TT>.
+ * This method permutes the whole array.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (byte[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final byte tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Similar to {@link #shuffle shuffle}<TT>(byte[], RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (short[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final short tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Similar to {@link #shuffle shuffle}<TT>(byte[], RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (int[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final int tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Similar to {@link #shuffle shuffle}<TT>(byte[], RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (long[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final long tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Similar to {@link #shuffle shuffle}<TT>(byte[], RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (char[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final char tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Similar to {@link #shuffle shuffle}<TT>(byte[], RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (boolean[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final boolean tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Similar to {@link #shuffle shuffle}<TT>(byte[], RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (float[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final float tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+
+ /**
+ * Similar to {@link #shuffle shuffle}<TT>(byte[], RandomStream)</TT>.
+ *
+ * <P><P>
+ * <BR>
+ *
+ * @param array the array being shuffled.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (double[] array, RandomStream stream) {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final double tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }
+
+ at SuppressWarnings("unchecked")
+ /**
+ * Partially permutes <TT>list</TT> as follows using <TT>stream</TT>:
+ * draws the first <SPAN CLASS="MATH"><I>k</I></SPAN> new elements of <TT>list</TT> randomly among the <SPAN CLASS="MATH"><I>n</I></SPAN> old
+ * elements of <TT>list</TT>, assuming that <SPAN CLASS="MATH"><I>k</I> <= <I>n</I> =</SPAN> <TT>list.size()</TT>.
+ * In other words, <SPAN CLASS="MATH"><I>k</I></SPAN> elements are selected at random without replacement from
+ * the <SPAN CLASS="MATH"><I>n</I></SPAN> <TT>list</TT> entries and are placed in the first <SPAN CLASS="MATH"><I>k</I></SPAN> positions,
+ * in random order.
+ *
+ * @param list the list being shuffled.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate integers.
+ *
+ *
+ */
+ public static void shuffle (List<?> list, int k, RandomStream stream) {
+ // @precondition 0 <= k <= n <= size.
+
+ // The implementation is inspired from Sun's Collections.shuffle
+ final int size = list.size ();
+ if (k < 0 || k > size)
+ throw new IllegalArgumentException("k must be 0 <= k <= list.size()");
+ if (0 == k) return;
+ if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, size-1);
+ Collections.swap (list, i, j);
+ }
+
+ } else {
+ final Object arr[] = list.toArray ();
+
+ // Shuffle array<
+ shuffle (arr, size, k, stream);
+
+ // Dump array back into list
+ final ListIterator it = list.listIterator ();
+ for (Object element : arr) {
+ it.next ();
+ it.set (element);
+ }
+ }
+ }
+
+
+ /**
+ * Partially permutes <TT>array</TT> as follows
+ * using <TT>stream</TT>: draws the new <SPAN CLASS="MATH"><I>k</I></SPAN> elements, <TT>array[0]</TT> to
+ * <TT>array[k-1]</TT>, randomly among the old <SPAN CLASS="MATH"><I>n</I></SPAN> elements, <TT>array[0]</TT>
+ * to <TT>array[n-1]</TT>, assuming that
+ * <SPAN CLASS="MATH"><I>k</I> <= <I>n</I> <= </SPAN> <TT>array.length</TT>.
+ * In other words, <SPAN CLASS="MATH"><I>k</I></SPAN> elements are selected at random without replacement
+ * from the first <SPAN CLASS="MATH"><I>n</I></SPAN> array elements and are placed in the first
+ * <SPAN CLASS="MATH"><I>k</I></SPAN> positions, in random order.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (Object[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ // Replace by
+ // if (k < 0 || k > n) throw new IllegalArgumentException();
+ // or at least assert 0 <= k && k <= n;
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ Object temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (byte[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ byte temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (short[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ short temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (int[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ int temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (long[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ long temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (char[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ char temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (boolean[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ boolean temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (float[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ float temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+
+ /**
+ * Similar to
+ * {@link #shuffle shuffle}<TT>(Object[], n, k, RandomStream)</TT>.
+ *
+ * @param array the array being shuffled.
+ *
+ * @param n selection amongst the first n elements.
+ *
+ * @param k number of elements selected.
+ *
+ * @param stream the random stream used to generate random numbers.
+ *
+ *
+ */
+ public static void shuffle (double[] array, int n, int k,
+ RandomStream stream) {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ double temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomPermutation.tex b/source/umontreal/iro/lecuyer/rng/RandomPermutation.tex
new file mode 100644
index 0000000..d6c136d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomPermutation.tex
@@ -0,0 +1,604 @@
+\defmodule{RandomPermutation}
+
+Provides methods to randomly shuffle arrays
+or lists using a random stream.
+% These methods shuffle the entire array.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomPermutation
+ * Description: Provides methods to randomly shuffle arrays or lists
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.RandomAccess;
+\end{hide}
+
+public class RandomPermutation\begin{hide} {
+ private static final int SHUFFLE_THRESHOLD = 5;
+\end{hide}
+
+ public static void init (byte[] array, int n)\begin{hide} {
+ for (byte k = 1; k <= n; k++)
+ array[k-1] = k;
+ }\end{hide}
+\end{code}
+\begin{tabb} Initializes \texttt{array} with the first $n$
+ positive integers in natural order as \texttt{array}$[i-1] = i$, for
+ $i=1,...,n$. The size of \texttt{array} must be at least $n$.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array to initialize.}
+ \param{n}{number of elements initialized.}
+\end{htmlonly}
+\begin{code}
+
+ public static void init (short[] array, int n)\begin{hide} {
+ for (short k = 1; k <= n; k++)
+ array[k-1] = k;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{init}{}{\texttt{(byte[], int)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array to initialize.}
+ \param{n}{number of elements initialized.}
+\end{htmlonly}
+\begin{code}
+
+ public static void init (int[] array, int n)\begin{hide} {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{init}{}{\texttt{(byte[], int)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array to initialize.}
+ \param{n}{number of elements initialized.}
+\end{htmlonly}
+\begin{code}
+
+ public static void init (long[] array, int n)\begin{hide} {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{init}{}{\texttt{(byte[], int)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array to initialize.}
+ \param{n}{number of elements initialized.}
+\end{htmlonly}
+\begin{code}
+
+ public static void init (float[] array, int n)\begin{hide} {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{init}{}{\texttt{(byte[], int)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array to initialize.}
+ \param{n}{number of elements initialized.}
+\end{htmlonly}
+\begin{code}
+
+ public static void init (double[] array, int n)\begin{hide} {
+ for (int k = 1; k <= n; k++)
+ array[k-1] = k;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{init}{}{\texttt{(byte[], int)}}.
+\bigskip\hrule
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array to initialize.}
+ \param{n}{number of elements initialized.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}@SuppressWarnings("unchecked")\end{hide}
+ public static void shuffle (List<?> list, RandomStream stream)\begin{hide} {
+ // The implementation is inspired from Sun's Collections.shuffle
+ final int size = list.size ();
+ if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
+ for (int i = size; i > 1; i--)
+ Collections.swap (list, i - 1, stream.nextInt (0, i - 1));
+
+ } else {
+ final Object arr[] = list.toArray ();
+
+ // Shuffle array<
+ shuffle (arr, stream);
+
+ // Dump array back into list
+ final ListIterator it = list.listIterator ();
+ for (Object element : arr) {
+ it.next ();
+ it.set (element);
+ }
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \texttt{java.util.Collections.shuffle(List<?>, Random)},
+ but uses a \class{RandomStream} instead of \texttt{java.util.Random}.
+\end{tabb}
+\begin{htmlonly}
+ \param{list}{the list being shuffled.}
+ \param{stream}{the random stream used to generate integers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (Object[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final Object tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Randomly permutes \texttt{array} using \texttt{stream}.
+ This method permutes the whole array.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (byte[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final byte tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Randomly permutes \texttt{array} using \texttt{stream}.
+ This method permutes the whole array.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (short[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final short tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{shuffle}{}{\texttt{(byte[], RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (int[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final int tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{shuffle}{}{\texttt{(byte[], RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (long[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final long tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{shuffle}{}{\texttt{(byte[], RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (char[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final char tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{shuffle}{}{\texttt{(byte[], RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (boolean[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final boolean tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{shuffle}{}{\texttt{(byte[], RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (float[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final float tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{shuffle}{}{\texttt{(byte[], RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (double[] array, RandomStream stream)\begin{hide} {
+ final int size = array.length;
+ for (int i = size - 1; i > 0; i--) {
+ final int j = stream.nextInt (0, i);
+ final double tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{shuffle}{}{\texttt{(byte[], RandomStream)}}.
+\bigskip\hrule
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}@SuppressWarnings("unchecked")\end{hide}
+ public static void shuffle (List<?> list, int k, RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= size.
+
+ // The implementation is inspired from Sun's Collections.shuffle
+ final int size = list.size ();
+ if (k < 0 || k > size)
+ throw new IllegalArgumentException("k must be 0 <= k <= list.size()");
+ if (0 == k) return;
+ if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, size-1);
+ Collections.swap (list, i, j);
+ }
+
+ } else {
+ final Object arr[] = list.toArray ();
+
+ // Shuffle array<
+ shuffle (arr, size, k, stream);
+
+ // Dump array back into list
+ final ListIterator it = list.listIterator ();
+ for (Object element : arr) {
+ it.next ();
+ it.set (element);
+ }
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Partially permutes \texttt{list} as follows using \texttt{stream}:
+ draws the first $k$ new elements of \texttt{list} randomly among the $n$ old
+ elements of \texttt{list}, assuming that $k \le n = $ \texttt{list.size()}.
+ In other words, $k$ elements are selected at random without replacement from
+ the $n$ \texttt{list} entries and are placed in the first $k$ positions,
+ in random order.
+\end{tabb}
+\begin{htmlonly}
+ \param{list}{the list being shuffled.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate integers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (Object[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ // Replace by
+ // if (k < 0 || k > n) throw new IllegalArgumentException();
+ // or at least assert 0 <= k && k <= n;
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ Object temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Partially permutes \texttt{array} as follows
+ using \texttt{stream}: draws the new $k$ elements, \texttt{array[0]} to
+ \texttt{array[k-1]}, randomly among the old $n$ elements, \texttt{array[0]}
+ to \texttt{array[n-1]}, assuming that $k \le n \le$ \texttt{array.length}.
+ In other words, $k$ elements are selected at random without replacement
+ from the first $n$ array elements and are placed in the first
+ $k$ positions, in random order.
+% Invoking shuffleArray (a, a.length, a.length, stream) randomly shuffles
+% the entire array.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (byte[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ byte temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (short[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ short temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (int[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ int temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (long[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ long temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (char[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ char temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (boolean[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ boolean temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (float[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ float temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}
+
+ public static void shuffle (double[] array, int n, int k,
+ RandomStream stream)\begin{hide} {
+ // @precondition 0 <= k <= n <= a.length.
+ if (k < 0 || k > n)
+ throw new IllegalArgumentException("k must be 0 <= k <= n");
+ for (int i = 0; i < k; i++) {
+ // Get random j in {i,...,n-1} and interchange a[i] with a[j].
+ int j = stream.nextInt (i, n-1);
+ double temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to
+ \method{shuffle}{}{\texttt{(Object[], n, k, RandomStream)}}.
+\end{tabb}
+\begin{htmlonly}
+ \param{array}{the array being shuffled.}
+ \param{n}{selection amongst the first n elements.}
+ \param{k}{number of elements selected.}
+ \param{stream}{the random stream used to generate random numbers.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStream.java b/source/umontreal/iro/lecuyer/rng/RandomStream.java
new file mode 100644
index 0000000..9e6a41c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStream.java
@@ -0,0 +1,192 @@
+
+
+/*
+ * Class: RandomStream
+ * Description: basic structures to handle multiple streams of uniform
+ (pseudo)-random numbers and tools to move around within
+ and across these streams
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre L'Ecuyer
+ * @since 2000
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+/**
+ * This interface defines the basic structures to handle multiple streams
+ * of uniform (pseudo)random numbers and convenient
+ * tools to move around within and across these streams.
+ * The actual random number generators (RNGs) are provided in classes
+ * that implement this <TT>RandomStream</TT> interface.
+ * Each stream of random numbers is an object of the class that implements
+ * this interface, and can be viewed as a virtual random number generator.
+ *
+ * <P>
+ * For each type of base RNG (i.e., each implementation of the
+ * <TT>RandomStream</TT> interface), the full period of the generator
+ * is cut into adjacent <EM>streams</EM> (or segments) of length <SPAN CLASS="MATH"><I>Z</I></SPAN>,
+ * and each of these streams is partitioned into <SPAN CLASS="MATH"><I>V</I></SPAN> <EM>substreams</EM>
+ * of length <SPAN CLASS="MATH"><I>W</I></SPAN>, where <SPAN CLASS="MATH"><I>Z</I> = <I>VW</I></SPAN>.
+ * The values of <SPAN CLASS="MATH"><I>V</I></SPAN> and <SPAN CLASS="MATH"><I>W</I></SPAN> depend on the specific RNG, but are usually
+ * larger than <SPAN CLASS="MATH">2<SUP>50</SUP></SPAN>.
+ * Thus, the distance <SPAN CLASS="MATH"><I>Z</I></SPAN> between the starting points of two successive
+ * streams provided by an RNG usually exceeds <SPAN CLASS="MATH">2<SUP>100</SUP></SPAN>.
+ * The initial seed of the RNG is the starting point of the first stream.
+ * It has a default value for each type of RNG,
+ * but this initial value can be changed by calling <TT>setPackageSeed</TT>
+ * for the corresponding class.
+ * Each time a new <TT>RandomStream</TT> is created, its starting point
+ * (initial seed) is computed automatically,
+ * <SPAN CLASS="MATH"><I>Z</I></SPAN> steps ahead of the starting point of the previously created stream
+ * of the same type, and its current state is set equal to this starting point.
+ *
+ * <P>
+ * For each stream, one can advance by one step and generate one value,
+ * or go ahead to the beginning of the next substream within this stream,
+ * or go back to the beginning of the current substream, or to the beginning
+ * of the stream, or jump ahead or back by an arbitrary number of steps.
+ * Denote by <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> the current state of a stream <SPAN CLASS="MATH"><I>g</I></SPAN>,
+ * <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN> its initial state, <SPAN CLASS="MATH"><I>B</I><SUB>g</SUB></SPAN> the state at the beginning of the
+ * current substream, and <SPAN CLASS="MATH"><I>N</I><SUB>g</SUB></SPAN> the state at the beginning of the next substream.
+ *
+ * The form of the state of a stream depends on its type.
+ * For example, the state of a stream of class {@link MRG32k3a} is a vector
+ * of six 32-bit integers represented internally as floating-point numbers
+ * (in <TT>double</TT>).
+ *
+ * <P>
+ * The methods for manipulating the streams and generating random
+ * numbers are implemented differently for each type of RNG.
+ * The methods whose formal parameter types do not depend
+ * on the RNG type are specified in the interface <TT>RandomStream</TT>.
+ * The others (e.g., for setting the seeds) are given only in the
+ * classes that implement the specific RNG types.
+ *
+ * <P>
+ *
+ * <P>
+ * Methods for generating random variates from non-uniform distributions
+ * are provided in the {@link umontreal.iro.lecuyer.randvar randvar} package.
+ *
+ */
+public interface RandomStream {
+
+
+ /**
+ * Reinitializes the stream to its initial state <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN>:
+ * <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> and <SPAN CLASS="MATH"><I>B</I><SUB>g</SUB></SPAN> are set to <SPAN CLASS="MATH"><I>I</I><SUB>g</SUB></SPAN>.
+ *
+ */
+ public void resetStartStream();
+
+
+ /**
+ * Reinitializes the stream to the beginning of its current
+ * substream: <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> is set to <SPAN CLASS="MATH"><I>B</I><SUB>g</SUB></SPAN>.
+ *
+ */
+ public void resetStartSubstream();
+
+
+ /**
+ * Reinitializes the stream to the beginning of its next
+ * substream: <SPAN CLASS="MATH"><I>N</I><SUB>g</SUB></SPAN> is computed, and
+ * <SPAN CLASS="MATH"><I>C</I><SUB>g</SUB></SPAN> and <SPAN CLASS="MATH"><I>B</I><SUB>g</SUB></SPAN> are set to <SPAN CLASS="MATH"><I>N</I><SUB>g</SUB></SPAN>.
+ *
+ */
+ public void resetNextSubstream();
+
+
+ /**
+ * Returns a string containing the current state of this stream.
+ *
+ * @return the state of the generator formated as a string
+ *
+ */
+ public String toString();
+
+
+ /**
+ * Returns a (pseudo)random number from the uniform distribution
+ * over the interval <SPAN CLASS="MATH">(0, 1)</SPAN>, using this stream, after advancing its
+ * state by one step. The generators programmed in SSJ never
+ * return the values 0 or 1.
+ *
+ * @return the next generated uniform
+ *
+ */
+ public double nextDouble();
+
+
+ /**
+ * Generates <TT>n</TT> (pseudo)random numbers from the
+ * uniform distribution and stores them into the array <TT>u</TT>
+ * starting at index <TT>start</TT>.
+ *
+ * @param u array that will contain the generated uniforms
+ *
+ * @param start starting index, in the array <TT>u</TT>, to write uniforms from
+ *
+ * @param n number of uniforms to generate
+ *
+ *
+ */
+ public void nextArrayOfDouble (double[] u, int start, int n);
+
+
+ /**
+ * Returns a (pseudo)random number from the discrete uniform
+ * distribution over the integers
+ * <SPAN CLASS="MATH">{<I>i</I>, <I>i</I> + 1,..., <I>j</I>}</SPAN>,
+ * using this stream. (Calls <TT>nextDouble</TT> once.)
+ *
+ * @param i smallest integer that can be generated
+ *
+ * @param j greatest integer that can be generated
+ *
+ * @return the generated integer
+ *
+ */
+ public int nextInt (int i, int j);
+
+
+ /**
+ * Generates <TT>n</TT> (pseudo)random numbers
+ * from the discrete uniform
+ * distribution over the integers
+ * <SPAN CLASS="MATH">{<I>i</I>, <I>i</I> + 1,..., <I>j</I>}</SPAN>,
+ * using this stream and stores the result in the array <TT>u</TT>
+ * starting at index <TT>start</TT>. (Calls <TT>nextInt</TT> <TT>n</TT> times.)
+ *
+ * @param i smallest integer that can be generated
+ *
+ * @param j greatest integer that can be generated
+ *
+ * @param u array that will contain the generated values
+ *
+ * @param start starting index, in the array <TT>u</TT>, to write integers from
+ *
+ * @param n number of values being generated
+ *
+ *
+ */
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n);
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStream.tex b/source/umontreal/iro/lecuyer/rng/RandomStream.tex
new file mode 100644
index 0000000..e3555dc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStream.tex
@@ -0,0 +1,261 @@
+\defmodule {RandomStream}
+
+This interface defines the basic structures to handle multiple streams
+of uniform (pseudo)\-random numbers and convenient
+tools to move around within and across these streams.
+The actual random number generators (RNGs) are provided in classes
+that implement this \texttt{RandomStream} interface.
+%A variety of such RNGs are available, in classes
+%\texttt{RandMrg, RandTaus}, etc.
+Each stream of random numbers is an object of the class that implements
+this interface, and can be viewed as a virtual random number generator.
+% The random number streams can also be instantiated as points from
+% HUPS (see module \texttt{Qrand}).
+
+For each type of base RNG (i.e., each implementation of the
+\texttt{RandomStream} interface), the full period of the generator
+is cut into adjacent {\em streams\/} (or segments) of length $Z$,
+and each of these streams is partitioned into $V$ {\em substreams\/}
+of length $W$, where $Z = VW$.
+The values of $V$ and $W$ depend on the specific RNG, but are usually
+larger than $2^{50}$.
+Thus, the distance $Z$ between the starting points of two successive
+streams provided by an RNG usually exceeds $2^{100}$.
+The initial seed of the RNG is the starting point of the first stream.
+It has a default value for each type of RNG,
+but this initial value can be changed by calling \texttt{setPackageSeed}
+for the corresponding class.
+Each time a new \texttt{RandomStream} is created, its starting point
+(initial seed) is computed automatically,
+$Z$ steps ahead of the starting point of the previously created stream
+of the same type, and its current state is set equal to this starting point.
+
+For each stream, one can advance by one step and generate one value,
+or go ahead to the beginning of the next substream within this stream,
+or go back to the beginning of the current substream, or to the beginning
+of the stream, or jump ahead or back by an arbitrary number of steps.
+Denote by $C_g$ the current state of a stream $g$,
+$I_g$ its initial state, $B_g$ the state at the beginning of the
+current substream, and $N_g$ the state at the beginning of the next substream.
+\latex{The following diagram shows an example of a stream whose
+ state is at the 6th value of the third substream, i.e., $2W+5$
+ steps ahead of its initial state $I_g$ and 5 steps ahead of its
+ state $B_g$.}
+The form of the state of a stream depends on its type.
+For example, the state of a stream of class \class{MRG32k3a} is a vector
+of six 32-bit integers represented internally as floating-point numbers
+(in \texttt{double}).
+
+\begin{latexonly}
+%% see The TeXbook, Exercice 10.4
+\def\tick#1{\vrule height 0pt depth #1pt}
+\def\enskip{\hskip.5em\relax}
+\def\ld{\hbox to 0.24 in{\vtop{\kern3.0pt\hbox{\dotfill}}}}
+\def\ts{\enskip\tick4}
+\def\suba{\hbox to 1.2in {\vtop
+ {\hbox to 1.2in{\hbox to .66in{\hrulefill}\hbox to .30in{\dotfill}\hrulefill}
+ \hbox to 1.2in{\tick9\ts\ts\ts\ts\ts\ts\ts\hfill\ts\enskip}}}}
+\def\subb{\hbox to 1.2in {\vtop
+ {\hbox to 1.2in{\hbox to .84in {\hrulefill}
+ \hbox to .24in {\dotfill}\hbox to .12in {\hrulefill}}
+ \hbox to 1.2in{\tick9\ts\ts\ts\ts\ts\ts\ts\ts\hfill\ts\enskip}}}}
+
+$$
+\vbox{\offinterlineskip
+\hbox to 5.5in{\hskip 2.4in \hbox to 1.2in{\hfil\hskip1.07em $C_g$\hfil}\hfill}
+\vskip .1pt
+\hbox to 5.5in{\hskip 2.4in \hbox to 1.2in{\hfil\hskip1.07em $\Downarrow$\hfil}
+ \hfill}
+\vtop {\offinterlineskip\hskip 0.0in \hbox to 5.5 true in
+ {\suba\suba\subb\suba\tick9
+ \hbox to .12in {\hrulefill}\hbox to .24in{\dotfill}}}\vskip .1pt
+\hskip -0.65in\hbox to 1.2 in{\hfil$I_g$\hfil}\hskip 1.25in
+ \hbox to 1.2 in{\hfil$B_g$\hfil}\hbox to 1.2 in{\hfil$N_g$\hfil}\hfill
+\vskip .1pt }
+$$
+\end{latexonly}
+
+The methods for manipulating the streams and generating random
+numbers are implemented differently for each type of RNG.
+The methods whose formal parameter types do not depend
+on the RNG type are specified in the interface \texttt{RandomStream}.
+The others (e.g., for setting the seeds) are given only in the
+classes that implement the specific RNG types.
+
+\latex{See \cite{sLAW00a,rLEC91a,rLEC02a}
+ %and Section~\ref{sec:timeshared}
+ for examples of
+ situations where the multiple streams offered here are useful.}
+
+\iffalse %%%%%%%%%%%%%%%
+\begin {itemize}
+\item[(a)]
+You want to compare two or more similar systems, via simulation
+with common random numbers, with $n$ simulation runs for each system.
+% (For more details about common random numbers and other variance
+% reduction techniques, see for example \cite{sBRA87a,sLAW91a}.)
+To guarantee that the same random numbers are used across the systems
+with good synchronization, assign different streams to different
+places where random numbers are needed in the model
+(e.g., to compare queuing systems, use one stream for the
+inter-arrival times, one stream for the service times at each queue,
+one stream for routing decisions, etc.).
+To make sure that each stream starts from
+the same state across the different systems, assign run $i$ to the
+$i$th substream, for each stream.
+The experiment then proceeds as follows.
+For the first system, simulate run 1 by starting all the streams from
+their initial seed, $I_g$.
+Before each new run, advance all the streams to the initial state
+of their next substream, $N_g$.
+After the $n$th run, reset all the streams to their initial state, $I_g$.
+Repeat for each system that you want to compare.
+%
+\item[(b)]
+You want to run simulations on several processors, in parallel,
+and you want each processor to have its own (virtual)
+random number generator.
+In this case, you can simply use one stream for each processor.
+You need a central authority, or {\em monitor\/}, to manage the
+allocation of the streams, but once a stream is allocated,
+the processors can go their own way, independently of each other.
+In this setup, the processors use in fact the same generator,
+but with different {\em seeds\/}.
+This makes the implementation easier than if a different
+generator is used on each processor.
+\end {itemize}
+\fi %%%%%%%%%%%%%%%%%%%%
+
+Methods for generating random variates from non-uniform distributions
+are provided in the \externalclass{umontreal.iro.lecuyer}{randvar} package.
+%The class \texttt{DiscreteDist} can be used to construct an arbitrary
+%discrete distribution over a finite set of values, and to generate from it.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomStream
+ * Description: basic structures to handle multiple streams of uniform
+ (pseudo)-random numbers and tools to move around within
+ and across these streams
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre L'Ecuyer
+ * @since 2000
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;
+
+public interface RandomStream \begin{hide} { \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void resetStartStream();
+\end{code}
+ \begin{tabb} Reinitializes the stream to its initial state $I_g$:
+ $C_g$ and $B_g$ are set to $I_g$.
+ \end{tabb}
+\begin{code}
+
+ public void resetStartSubstream();
+\end{code}
+ \begin{tabb} Reinitializes the stream to the beginning of its current
+ substream: $C_g$ is set to $B_g$.
+ \end{tabb}
+\begin{code}
+
+ public void resetNextSubstream();
+\end{code}
+ \begin{tabb} Reinitializes the stream to the beginning of its next
+ substream: $N_g$ is computed, and
+ $C_g$ and $B_g$ are set to $N_g$.
+ \end{tabb}
+\begin{code}
+
+ public String toString();
+\end{code}
+ \begin{tabb} Returns a string containing the current state of this stream.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the state of the generator formated as a string}
+\end{htmlonly}
+\begin{code}
+
+ public double nextDouble();
+\end{code}
+ \begin{tabb} Returns a (pseudo)random number from the uniform distribution
+ over the interval $(0,1)$, using this stream, after advancing its
+ state by one step. The generators programmed in SSJ never
+ return the values 0 or 1.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the next generated uniform}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfDouble (double[] u, int start, int n);
+\end{code}
+ \begin{tabb} Generates \texttt{n} (pseudo)random numbers from the
+ uniform distribution and stores them into the array \texttt{u}
+ starting at index \texttt{start}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{u}{array that will contain the generated uniforms}
+ \param{start}{starting index, in the array \texttt{u}, to write uniforms from}
+ \param{n}{number of uniforms to generate}
+\end{htmlonly}
+\begin{code}
+
+ public int nextInt (int i, int j);
+\end{code}
+ \begin{tabb} Returns a (pseudo)random number from the discrete uniform
+ distribution over the integers $\{i,i+1,\dots,j\}$,
+ using this stream. (Calls \texttt{nextDouble} once.)
+ \end{tabb}
+\begin{htmlonly}
+ \param{i}{smallest integer that can be generated}
+ \param{j}{greatest integer that can be generated}
+ \return{the generated integer}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n);
+\end{code}
+ \begin{tabb} Generates \texttt{n} (pseudo)random numbers
+ from the discrete uniform
+ distribution over the integers $\{i,i+1,\dots,j\}$,
+ using this stream and stores the result in the array \texttt{u}
+ starting at index \texttt{start}. (Calls \texttt{nextInt} \texttt{n} times.)
+ \end{tabb}
+\begin{htmlonly}
+ \param{i}{smallest integer that can be generated}
+ \param{j}{greatest integer that can be generated}
+ \param{u}{array that will contain the generated values}
+ \param{start}{starting index, in the array \texttt{u}, to write integers from}
+ \param{n}{number of values being generated}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamBase.java b/source/umontreal/iro/lecuyer/rng/RandomStreamBase.java
new file mode 100644
index 0000000..b6f5d9b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamBase.java
@@ -0,0 +1,266 @@
+
+
+/*
+ * Class: RandomStreamBase
+ * Description: Base class of all random number generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.io.Serializable;
+
+/**
+ * This class provides a convenient foundation on which RNGs can
+ * be built. It implements all the methods which do not depend directly on
+ * the generator itself, but only on its output, which is to be defined
+ * by implementing the <TT>abstract</TT> method <TT>nextValue</TT>. In the present
+ * class, all methods returning random numbers directly or indirectly
+ * (<TT>nextDouble</TT>, <TT>nextArrayOfDouble</TT>, <TT>nextInt</TT> and
+ * <TT>nextArrayOfInt</TT>) call <TT>nextValue</TT>. Thus, to define a subclass
+ * that implements a RNG, it suffices to implement <TT>nextValue</TT>, in addition
+ * to the <TT>reset...</TT> and <TT>toString</TT> methods.
+ * Of course, the other methods may also be overridden for improved efficiency.
+ *
+ * <P>
+ * If the <TT>nextValue</TT> already generates numbers with a precision of
+ * 53-bits or higher, then {@link #nextDouble nextDouble} can be overridden to improve
+ * the performance. The mechanism for increasing the precision assumes that
+ * <TT>nextValue</TT> returns at least 29 bits of precision, in which case
+ * the higher precision numbers will have roughly 52 bits of precision.
+ * This mechanism was designed primarily for RNGs that return numbers with
+ * around 30 to 32 bits of precision.
+ *
+ * <P>
+ * {@link RandomStreamBase} and its subclasses are implementing the {@link Serializable} interface.
+ * Each class has a serial number wich represent the class version.
+ * For instance <TT>70510</TT> means that the last change was the <TT>10th May 2007</TT>.
+ *
+ */
+public abstract class RandomStreamBase implements CloneableRandomStream,
+ Serializable {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //constants
+ protected static double invtwo24 = 5.9604644775390625e-8; //2^(-24)
+ private static double EPSILON = 5.5511151231257827e-17; //2^(-54)
+
+ protected String name = null;
+
+ // prec53 keeps track if the precision has been increased or not.
+ protected boolean prec53 = false;
+ protected boolean anti = false; // Deprecated.
+
+
+
+ public abstract void resetStartStream();
+ public abstract void resetStartSubstream();
+ public abstract void resetNextSubstream();
+ public abstract String toString();
+
+
+ /**
+ * After calling this method with <TT>incp = true</TT>, each call to
+ * the RNG (direct or indirect) for this stream
+ * will return a uniform random number with more bits of precision than what
+ * is returned by <TT>nextValue</TT>,
+ * and will advance the state of the stream by 2 steps instead of 1
+ * (i.e., <TT>nextValue</TT> will be called twice for each random number).
+ *
+ * <P>
+ * More precisely, if <TT>s</TT> is a stream of a subclass of
+ * <TT>RandomStreamBase</TT>, when the precision has been increased,
+ * the instruction ``<TT>u = s.nextDouble()</TT>'', is equivalent to
+ * ``<TT>u = (s.nextValue() + s.nextValue()*fact) % 1.0</TT>''
+ * where the constant <TT>fact</TT> is equal to <SPAN CLASS="MATH">2<SUP>-24</SUP></SPAN>.
+ * This also applies when calling <TT>nextDouble</TT> indirectly
+ * (e.g., via <TT>nextInt</TT>, etc.).
+ * By default, or if this method is called again with <TT>incp = false</TT>,
+ * each call to <TT>nextDouble</TT> for this stream advances the state by 1 step
+ * and returns the same number as <TT>nextValue</TT>.
+ *
+ * @param incp if the generator will be set to high precision mode
+ *
+ *
+ */
+ public void increasedPrecision (boolean incp) {
+ prec53 = incp;
+ }
+
+
+ /**
+ * This method should return the next random number (between 0 and 1)
+ * from the current stream.
+ * If the stream is set to the high precision mode
+ * (<TT>increasedPrecision(true)</TT> was called), then each call to
+ * <TT>nextDouble</TT> will call <TT>nextValue</TT> twice, otherwise it will
+ * call it only once.
+ *
+ * @return a number in the interval (0,1)
+ *
+ */
+ protected abstract double nextValue();
+
+
+ /**
+ * Returns a uniform random number between 0 and 1 from the stream.
+ * Its behavior depends on the last call to {@link #increasedPrecision increasedPrecision}.
+ * The generators programmed in SSJ never return the values 0 or 1.
+ *
+ * @return a number in the interval (0,1)
+ *
+ */
+ public double nextDouble() {
+ double u = nextValue();
+ if (prec53)
+ u = (u + nextValue() * invtwo24) % 1.0 + EPSILON;
+ if(anti)
+ return 1.0 - u;
+ else
+ return u;
+ }
+
+
+ /**
+ * Calls <TT>nextDouble</TT> <TT>n</TT> times to fill the array <TT>u</TT>.
+ *
+ * @param u the array in which the numbers will be stored
+ *
+ * @param start the first index of <TT>u</TT> to be used
+ *
+ * @param n the number of random numbers to put in <TT>u</TT>
+ *
+ *
+ */
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ if(u.length == 0)
+ throw new NullPointerException("The array must be initialized.");
+ if (u.length < n + start)
+ throw new IndexOutOfBoundsException("The array is too small.");
+ if(start < 0)
+ throw new IndexOutOfBoundsException("Must start at a " +
+ "non-negative index.");
+ if(n < 0)
+ throw new IllegalArgumentException("Must have a non-negative " +
+ "number of elements.");
+
+ for(int ii = start; ii < start + n; ii++)
+ u[ii] = nextDouble();
+ }
+
+
+ /**
+ * Calls <TT>nextDouble</TT> once to create one integer between
+ * <TT>i</TT> and <TT>j</TT>. This method always uses the highest order bits
+ * of the random number. It should be overridden if a faster implementation
+ * exists for the specific generator.
+ *
+ * @param i the smallest possible returned integer
+ *
+ * @param j the largest possible returned integer
+ *
+ * @return a random integer between i and j
+ *
+ */
+ public int nextInt (int i, int j) {
+ if(i > j)
+ throw new IllegalArgumentException(i + " is larger than " +
+ j + ".");
+ // This works even for an interval [0, 2^31 - 1]. It would not with
+ // return i + (int)(nextDouble() * (j - i + 1));
+ return i + (int)(nextDouble() * (j - i + 1.0));
+ }
+
+
+ /**
+ * Calls <TT>nextInt</TT> <TT>n</TT> times to fill the array <TT>u</TT>.
+ * This method should be overridden if a faster implementation exists for
+ * the specific generator.
+ *
+ * @param i the smallest possible integer to put in <TT>u</TT>
+ *
+ * @param j the largest possible integer to put in <TT>u</TT>
+ *
+ * @param u the array in which the numbers will be stored
+ *
+ * @param start the first index of <TT>u</TT> to be used
+ *
+ * @param n the number of random numbers to put in <TT>u</TT>
+ *
+ *
+ */
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ if(u == null)
+ throw new NullPointerException("The array must be " +
+ "initialized.");
+ if (u.length < n + start)
+ throw new IndexOutOfBoundsException("The array is too small.");
+ if(start < 0)
+ throw new IndexOutOfBoundsException("Must start at a " +
+ "non-negative index.");
+ if(n < 0)
+ throw new IllegalArgumentException("Must have a non-negative " +
+ "number of elements.");
+
+ for(int ii = start; ii < start + n; ii++)
+ u[ii] = nextInt(i,j);
+ }
+
+
+ /**
+ * Use the <TT>toString</TT> method.
+ *
+ *
+ */
+ @Deprecated
+ public String formatState() {
+ return toString();
+ }
+
+
+ @Deprecated
+ public String formatStateFull() {
+ throw new UnsupportedOperationException (
+ " call the toStringFull() method instead.");
+ }
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public RandomStreamBase clone() {
+ RandomStreamBase retour = null;
+ try {
+ retour = (RandomStreamBase)super.clone();
+ }
+ catch(CloneNotSupportedException cnse) {
+ cnse.printStackTrace(System.err);
+ }
+ return retour;
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamBase.tex b/source/umontreal/iro/lecuyer/rng/RandomStreamBase.tex
new file mode 100644
index 0000000..b3848b9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamBase.tex
@@ -0,0 +1,267 @@
+\defmodule {RandomStreamBase}
+
+This class provides a convenient foundation on which RNGs can
+be built. It implements all the methods which do not depend directly on
+the generator itself, but only on its output, which is to be defined
+by implementing the \texttt{abstract} method \texttt{nextValue}. In the present
+class, all methods returning random numbers directly or indirectly
+(\texttt{nextDouble}, \texttt{nextArrayOfDouble}, \texttt{nextInt} and
+ \texttt{nextArrayOfInt}) call \texttt{nextValue}. Thus, to define a subclass
+that implements a RNG, it suffices to implement \texttt{nextValue}, in addition
+ to the \texttt{reset...} and \texttt{toString} methods.
+Of course, the other methods may also be overridden for improved efficiency.
+
+If the \texttt{nextValue} already generates numbers with a precision of
+ 53-bits or higher, then \method{nextDouble}{} can be overridden to improve
+the performance. The mechanism for increasing the precision assumes that
+ \texttt{nextValue} returns at least 29 bits of precision, in which case
+the higher precision numbers will have roughly 52 bits of precision.
+This mechanism was designed primarily for RNGs that return numbers with
+around 30 to 32 bits of precision.
+% If the precision of \method{nextValue}{} is less than 24 bits, then
+% \texttt{nextDouble}
+% must be overridden, otherwise there will be a loss of precision in the
+% middle-order bits.
+
+\class{RandomStreamBase} and its subclasses are implementing the \class{Serializable} interface.
+Each class has a serial number wich represent the class version.
+For instance \texttt{70510} means that the last change was the \texttt{10th May 2007}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomStreamBase
+ * Description: Base class of all random number generators
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import java.io.Serializable; \end{hide}
+
+public abstract class RandomStreamBase implements CloneableRandomStream,
+ Serializable \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ //constants
+ protected static double invtwo24 = 5.9604644775390625e-8; //2^(-24)
+ private static double EPSILON = 5.5511151231257827e-17; //2^(-54)
+
+ protected String name = null;
+
+ // prec53 keeps track if the precision has been increased or not.
+ protected boolean prec53 = false;
+ protected boolean anti = false; // Deprecated.
+
+\end{hide}
+
+ public abstract void resetStartStream();
+ public abstract void resetStartSubstream();
+ public abstract void resetNextSubstream();
+ public abstract String toString();
+\end{code}
+\begin{code}
+
+ public void increasedPrecision (boolean incp) \begin{hide} {
+ prec53 = incp;
+ }\end{hide}
+\end{code}
+ \begin{tabb} After calling this method with \texttt{incp = true}, each call to
+ the RNG (direct or indirect) for this stream
+ will return a uniform random number with more bits of precision than what
+ is returned by \texttt{nextValue},
+ and will advance the state of the stream by 2 steps instead of 1
+ (i.e., \texttt{nextValue} will be called twice for each random number).
+
+ More precisely, if \texttt{s} is a stream of a subclass of
+ \texttt{RandomStreamBase}, when the precision has been increased,
+ the instruction ``\texttt{u = s.nextDouble()}'', is equivalent to
+ ``\texttt{u = (s.nextValue() + s.nextValue()*fact) \%\ 1.0}''
+ where the constant \texttt{fact} is equal to $2^{-24}$.
+ This also applies when calling \texttt{nextDouble} indirectly
+ (e.g., via \texttt{nextInt}, etc.).
+ By default, or if this method is called again with \texttt{incp = false},
+ each call to \texttt{nextDouble} for this stream advances the state by 1 step
+ and returns the same number as \texttt{nextValue}.
+\end{tabb}
+\begin{htmlonly}
+ \param{incp}{if the generator will be set to high precision mode}
+\end{htmlonly}
+\begin{code}
+
+ protected abstract double nextValue();
+\end{code}
+\begin{tabb}
+ This method should return the next random number (between 0 and 1)
+ from the current stream.
+ If the stream is set to the high precision mode
+ (\texttt{increasedPrecision(true)} was called), then each call to
+ \texttt{nextDouble} will call \texttt{nextValue} twice, otherwise it will
+ call it only once.
+\end{tabb}
+\begin{htmlonly}
+ \return{a number in the interval (0,1)}
+\end{htmlonly}
+\begin{code}
+
+ public double nextDouble() \begin{hide} {
+ double u = nextValue();
+ if (prec53)
+ u = (u + nextValue() * invtwo24) % 1.0 + EPSILON;
+ if(anti)
+ return 1.0 - u;
+ else
+ return u;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a uniform random number between 0 and 1 from the stream.
+ Its behavior depends on the last call to \method{increasedPrecision}{}.
+ The generators programmed in SSJ never return the values 0 or 1.
+\end{tabb}
+\begin{htmlonly}
+ \return{a number in the interval (0,1)}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfDouble (double[] u, int start, int n) \begin{hide} {
+ if(u.length == 0)
+ throw new NullPointerException("The array must be initialized.");
+ if (u.length < n + start)
+ throw new IndexOutOfBoundsException("The array is too small.");
+ if(start < 0)
+ throw new IndexOutOfBoundsException("Must start at a " +
+ "non-negative index.");
+ if(n < 0)
+ throw new IllegalArgumentException("Must have a non-negative " +
+ "number of elements.");
+
+ for(int ii = start; ii < start + n; ii++)
+ u[ii] = nextDouble();
+ }\end{hide}
+\end{code}
+\begin{tabb} Calls \texttt{nextDouble} \texttt{n} times to fill the array \texttt{u}.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{the array in which the numbers will be stored}
+ \param{start}{the first index of \texttt{u} to be used}
+ \param{n}{the number of random numbers to put in \texttt{u}}
+\end{htmlonly}
+\begin{code}
+
+ public int nextInt (int i, int j) \begin{hide} {
+ if(i > j)
+ throw new IllegalArgumentException(i + " is larger than " +
+ j + ".");
+ // This works even for an interval [0, 2^31 - 1]. It would not with
+ // return i + (int)(nextDouble() * (j - i + 1));
+ return i + (int)(nextDouble() * (j - i + 1.0));
+ } \end{hide}
+\end{code}
+\begin{tabb} Calls \texttt{nextDouble} once to create one integer between
+ \texttt{i} and \texttt{j}. This method always uses the highest order bits
+ of the random number. It should be overridden if a faster implementation
+ exists for the specific generator.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the smallest possible returned integer}
+ \param{j}{the largest possible returned integer}
+ \return{a random integer between i and j}
+\end{htmlonly}
+\begin{code}
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) \begin{hide} {
+ if(u == null)
+ throw new NullPointerException("The array must be " +
+ "initialized.");
+ if (u.length < n + start)
+ throw new IndexOutOfBoundsException("The array is too small.");
+ if(start < 0)
+ throw new IndexOutOfBoundsException("Must start at a " +
+ "non-negative index.");
+ if(n < 0)
+ throw new IllegalArgumentException("Must have a non-negative " +
+ "number of elements.");
+
+ for(int ii = start; ii < start + n; ii++)
+ u[ii] = nextInt(i,j);
+ } \end{hide}
+\end{code}
+\begin{tabb} Calls \texttt{nextInt} \texttt{n} times to fill the array \texttt{u}.
+ This method should be overridden if a faster implementation exists for
+ the specific generator.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the smallest possible integer to put in \texttt{u}}
+ \param{j}{the largest possible integer to put in \texttt{u}}
+ \param{u}{the array in which the numbers will be stored}
+ \param{start}{the first index of \texttt{u} to be used}
+ \param{n}{the number of random numbers to put in \texttt{u}}
+\end{htmlonly}
+
+\begin{code}
+
+ @Deprecated
+ public String formatState() \begin{hide} {
+ return toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Use the \texttt{toString} method.
+\end{tabb}
+\begin{hide}
+\begin{code}
+
+ @Deprecated
+ public String formatStateFull() {
+ throw new UnsupportedOperationException (
+ " call the toStringFull() method instead.");
+ }\end{code}
+\begin{tabb} Use the \texttt{toStringFull} method.
+\end{tabb}
+\end{hide}
+\begin{code}
+
+ public RandomStreamBase clone() \begin{hide} {
+ RandomStreamBase retour = null;
+ try {
+ retour = (RandomStreamBase)super.clone();
+ }
+ catch(CloneNotSupportedException cnse) {
+ cnse.printStackTrace(System.err);
+ }
+ return retour;
+ }\end{hide}
+ \end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamFactory.java b/source/umontreal/iro/lecuyer/rng/RandomStreamFactory.java
new file mode 100644
index 0000000..e73dbfa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamFactory.java
@@ -0,0 +1,68 @@
+
+
+/*
+ * Class: RandomStreamFactory
+ * Description: random stream factory that can construct instances of
+ a given type of random stream
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.rng.MRG32k3a;
+
+
+/**
+ * Represents a random stream factory capable
+ * of constructing instances of a given type of random stream
+ * by invoking the {@link #newInstance(()) newInstance} method
+ * each time a new random stream is needed, instead of invoking
+ * directly the specific constructor of the desired type.
+ * Hence, if several random streams of a given type (class) must be
+ * constructed at different places in a large simulation program,
+ * and if we decide to change the type of stream in the future,
+ * there is no need to change the code at those different places.
+ * With the random stream factory, the class-specific
+ * code for constructing these streams appears at a single place,
+ * where the factory is constructed.
+ *
+ * <P>
+ * The class {@link BasicRandomStreamFactory} provides an
+ * implementation of this interface.
+ *
+ */
+public interface RandomStreamFactory {
+
+
+ /**
+ * Constructs and returns a new random stream.
+ * If the instantiation of the random stream fails,
+ * this method throws a {@link RandomStreamInstantiationException}.
+ *
+ * @return the newly-constructed random stream.
+ * @exception RandomStreamInstantiationException if the new
+ * random stream cannot be instantiated.
+ *
+ */
+ public RandomStream newInstance();
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamFactory.tex b/source/umontreal/iro/lecuyer/rng/RandomStreamFactory.tex
new file mode 100644
index 0000000..8ec82f9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamFactory.tex
@@ -0,0 +1,70 @@
+\defmodule{RandomStreamFactory}
+
+Represents a random stream factory capable
+of constructing instances of a given type of random stream
+by invoking the \method{newInstance}{()} method
+each time a new random stream is needed, instead of invoking
+directly the specific constructor of the desired type.
+Hence, if several random streams of a given type (class) must be
+constructed at different places in a large simulation program,
+and if we decide to change the type of stream in the future,
+there is no need to change the code at those different places.
+With the random stream factory, the class-specific
+code for constructing these streams appears at a single place,
+where the factory is constructed.
+
+The class \class{BasicRandomStreamFactory} provides an
+implementation of this interface.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomStreamFactory
+ * Description: random stream factory that can construct instances of
+ a given type of random stream
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+import umontreal.iro.lecuyer.rng.MRG32k3a;
+\end{hide}
+
+public interface RandomStreamFactory\begin{hide} {
+\end{hide}
+
+ public RandomStream newInstance();\begin{hide}
+
+}\end{hide}
+\end{code}
+\begin{tabb} Constructs and returns a new random stream.
+ If the instantiation of the random stream fails,
+ this method throws a \class{RandomStreamInstantiationException}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the newly-constructed random stream.}
+ \exception{RandomStreamInstantiationException}{if the new
+ random stream cannot be instantiated.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamInstantiationException.java b/source/umontreal/iro/lecuyer/rng/RandomStreamInstantiationException.java
new file mode 100644
index 0000000..b0e29dd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamInstantiationException.java
@@ -0,0 +1,147 @@
+
+
+/*
+ * Class: RandomStreamInstantiationException
+ * Description: thrown when a random stream factory cannot instantiate a stream
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+/**
+ * This exception is thrown when a random stream factory cannot instantiate a stream
+ * on a call to its {@link umontreal.iro.lecuyer.rng.RandomStreamFactory#newInstance(()) newInstance} method.
+ *
+ */
+public class RandomStreamInstantiationException extends RuntimeException {
+ private RandomStreamFactory factory;
+
+
+ /**
+ * Constructs a new random stream instantiation exception with
+ * no message, no cause, and thrown by the given <TT>factory</TT>.
+ *
+ * @param factory the random stream factory which thrown the exception.
+ *
+ *
+ */
+ public RandomStreamInstantiationException (RandomStreamFactory factory) {
+ super();
+ this.factory = factory;
+ }
+
+
+ /**
+ * Constructs a new random stream instantiation exception with
+ * the given <TT>message</TT>, no cause, and concerning <TT>factory</TT>.
+ *
+ * @param factory the random stream factory concerned by the exception.
+ *
+ * @param message the error message describing the exception.
+ *
+ *
+ */
+ public RandomStreamInstantiationException (RandomStreamFactory factory,
+ String message) {
+ super (message);
+ this.factory = factory;
+ }
+
+
+ /**
+ * Constructs a new random stream instantiation exception with
+ * no message, the given <TT>cause</TT>, and concerning <TT>factory</TT>.
+ *
+ * @param factory the random stream factory concerned by the exception.
+ *
+ * @param cause the cause of the exception.
+ *
+ *
+ */
+ public RandomStreamInstantiationException (RandomStreamFactory factory,
+ Throwable cause) {
+ super (cause);
+ this.factory = factory;
+ }
+
+
+ /**
+ * Constructs a new random stream instantiation exception with
+ * the given <TT>message</TT>, the supplied <TT>cause</TT>,
+ * and concerning <TT>factory</TT>.
+ *
+ * @param factory the random stream factory concerned by the exception.
+ *
+ * @param message the error message describing the exception.
+ *
+ * @param cause the cause of the exception.
+ *
+ *
+ */
+ public RandomStreamInstantiationException (RandomStreamFactory factory,
+ String message, Throwable cause) {
+ super (message, cause);
+ this.factory = factory;
+ }
+
+
+ /**
+ * Returns the random stream factory concerned by this exception.
+ *
+ * @return the random stream factory concerned by this exception.
+ *
+ */
+ public RandomStreamFactory getRandomStreamFactory() {
+ return factory;
+ }
+
+
+ /**
+ * Returns a short description of the exception.
+ * If {@link #getRandomStreamFactory(()) getRandomStreamFactory} returns <TT>null</TT>,
+ * this calls <TT>super.toString</TT>. Otherwise, the
+ * result is the concatenation of:
+ *
+ * a) the name of the actual class of the exception;
+ * <BR>
+ * b) the string <TT>": For random stream factory "</TT>;
+ * <BR>
+ * c) the result of {@link #getRandomStreamFactory(()) getRandomStreamFactory}<TT>.toString()</TT>;
+ * <BR>
+ * d) if {@link java.lang.Throwable#getMessage(()) getMessage} is non-<TT>null</TT>,
+ * <TT>", "</TT> followed by the result of
+ * {@link java.lang.Throwable#getMessage(()) getMessage}.
+ *
+ * @return a string representation of the exception.
+ */
+ public String toString() {
+ if (factory == null)
+ return super.toString();
+
+ StringBuffer sb = new StringBuffer (getClass().getName());
+ sb.append (": For random stream factory ");
+ sb.append (factory.toString());
+ String msg = getMessage();
+ if (msg != null)
+ sb.append (", ").append (msg);
+ return sb.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamInstantiationException.tex b/source/umontreal/iro/lecuyer/rng/RandomStreamInstantiationException.tex
new file mode 100644
index 0000000..bd8323c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamInstantiationException.tex
@@ -0,0 +1,140 @@
+\defmodule{RandomStreamInstantiationException}
+
+This exception is thrown when a random stream factory cannot instantiate a stream
+on a call to its \externalmethod{umontreal.iro.lecuyer.rng}{RandomStreamFactory}{newInstance}{()} method.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomStreamInstantiationException
+ * Description: thrown when a random stream factory cannot instantiate a stream
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;
+
+public class RandomStreamInstantiationException extends RuntimeException\begin{hide} {
+ private RandomStreamFactory factory;
+\end{hide}
+
+ public RandomStreamInstantiationException (RandomStreamFactory factory)\begin{hide} {
+ super();
+ this.factory = factory;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new random stream instantiation exception with
+ no message, no cause, and thrown by the given \texttt{factory}.
+\end{tabb}
+\begin{htmlonly}
+ \param{factory}{the random stream factory which thrown the exception.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStreamInstantiationException (RandomStreamFactory factory,
+ String message)\begin{hide} {
+ super (message);
+ this.factory = factory;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new random stream instantiation exception with
+ the given \texttt{message}, no cause, and concerning \texttt{factory}.
+\end{tabb}
+\begin{htmlonly}
+ \param{factory}{the random stream factory concerned by the exception.}
+ \param{message}{the error message describing the exception.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStreamInstantiationException (RandomStreamFactory factory,
+ Throwable cause)\begin{hide} {
+ super (cause);
+ this.factory = factory;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new random stream instantiation exception with
+ no message, the given \texttt{cause}, and concerning \texttt{factory}.
+\end{tabb}
+\begin{htmlonly}
+ \param{factory}{the random stream factory concerned by the exception.}
+ \param{cause}{the cause of the exception.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStreamInstantiationException (RandomStreamFactory factory,
+ String message, Throwable cause)\begin{hide} {
+ super (message, cause);
+ this.factory = factory;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new random stream instantiation exception with
+ the given \texttt{message}, the supplied \texttt{cause},
+ and concerning \texttt{factory}.
+\end{tabb}
+\begin{htmlonly}
+ \param{factory}{the random stream factory concerned by the exception.}
+ \param{message}{the error message describing the exception.}
+ \param{cause}{the cause of the exception.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStreamFactory getRandomStreamFactory()\begin{hide} {
+ return factory;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the random stream factory concerned by this exception.
+\end{tabb}
+\begin{htmlonly}
+ \return{the random stream factory concerned by this exception.}
+\end{htmlonly}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ if (factory == null)
+ return super.toString();
+
+ StringBuffer sb = new StringBuffer (getClass().getName());
+ sb.append (": For random stream factory ");
+ sb.append (factory.toString());
+ String msg = getMessage();
+ if (msg != null)
+ sb.append (", ").append (msg);
+ return sb.toString();
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Returns a short description of the exception.
+ If \method{getRandomStreamFactory}{()} returns \texttt{null},
+ this calls \texttt{super.toString}. Otherwise, the
+ result is the concatenation of:
+\end{tabb}
+\begin{tabbb}
+ a) the name of the actual class of the exception;\\
+ b) the string \texttt{": For random stream factory "};\\
+ c) the result of \method{getRandomStreamFactory}{()}\texttt{.toString()};\\
+ d) if \externalmethod{java.lang}{Throwable}{getMessage}{()} is non-\texttt{null},
+ \texttt{", "} followed by the result of
+ \externalmethod{java.lang}{Throwable}{getMessage}{()}.
+\end{tabbb}
+\begin{htmlonly}
+ \return{a string representation of the exception.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamManager.java b/source/umontreal/iro/lecuyer/rng/RandomStreamManager.java
new file mode 100644
index 0000000..178fb83
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamManager.java
@@ -0,0 +1,157 @@
+
+
+/*
+ * Class: RandomStreamManager
+ * Description: Manages a list of random streams
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * Manages a list of random streams for more convenient synchronization.
+ * All streams in the list can be reset simultaneously by a single
+ * call to the appropriate method of this stream manager,
+ * instead of calling explicitly the reset method for each
+ * individual stream.
+ *
+ * <P>
+ * After a random stream manager is constructed, any
+ * existing {@link RandomStream} object can be registered
+ * to this stream manager (i.e., added to the list)
+ * and eventually unregistered (removed from the list).
+ *
+ */
+public class RandomStreamManager {
+ private List streams = new ArrayList();
+
+
+ /**
+ * Adds the given <TT>stream</TT> to the internal list of
+ * this random stream manager and returns the added stream.
+ *
+ * @param stream the stream being added.
+ *
+ * @return the added stream.
+ * @exception NullPointerException if <TT>stream</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public RandomStream add (RandomStream stream) {
+ if (stream == null)
+ throw new NullPointerException();
+ if (streams.contains (stream))
+ return stream;
+ streams.add (stream);
+ return stream;
+ }
+
+
+ /**
+ * Removes the given stream from the internal list of this random
+ * stream manager. Returns <TT>true</TT> if the stream was
+ * properly removed, <TT>false</TT> otherwise.
+ *
+ * @param stream the stream being removed.
+ *
+ * @return the success indicator of the operation.
+ *
+ */
+ public boolean remove (RandomStream stream) {
+ return streams.remove (stream);
+ }
+
+
+ /**
+ * Removes all the streams from the internal list
+ * of this random stream manager.
+ *
+ */
+ public void clear() {
+ streams.clear();
+ }
+
+
+ /**
+ * Returns an unmodifiable list containing all the
+ * random streams in this random
+ * stream manager. The returned list, constructed by
+ * {@link java.util.Collections#unmodifiableList((List)) unmodifiableList}, can be assumed
+ * to contain non-<TT>null</TT> {@link RandomStream} instances.
+ *
+ * @return the list of managed random streams.
+ *
+ */
+ public List getStreams() {
+ return Collections.unmodifiableList (streams);
+ }
+
+
+ /**
+ * Forwards to the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream#resetStartStream(()) resetStartStream} methods
+ * of all streams in the list.
+ *
+ */
+ public void resetStartStream() {
+ for (int s = 0; s < streams.size(); s++)
+ ((RandomStream)streams.get (s)).resetStartStream();
+ }
+
+
+ /**
+ * Forwards to the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream#resetStartSubstream(()) resetStartSubstream} methods
+ * of all streams in the list.
+ *
+ */
+ public void resetStartSubstream() {
+ for (int s = 0; s < streams.size(); s++)
+ ((RandomStream)streams.get (s)).resetStartSubstream();
+ }
+
+
+ /**
+ * Forwards to the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream#resetNextSubstream(()) resetNextSubstream} methods
+ * of all streams in the list.
+ *
+ */
+ public void resetNextSubstream() {
+ for (int s = 0; s < streams.size(); s++)
+ ((RandomStream)streams.get (s)).resetNextSubstream();
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer (getClass().getName());
+ sb.append ('[');
+ sb.append ("number of stored streams: ").append (streams.size());
+ sb.append (']');
+ return sb.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamManager.tex b/source/umontreal/iro/lecuyer/rng/RandomStreamManager.tex
new file mode 100644
index 0000000..98a8147
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamManager.tex
@@ -0,0 +1,153 @@
+\defmodule{RandomStreamManager}
+
+Manages a list of random streams for more convenient synchronization.
+All streams in the list can be reset simultaneously by a single
+call to the appropriate method of this stream manager,
+instead of calling explicitly the reset method for each
+individual stream.
+
+After a random stream manager is constructed, any
+existing \class{RandomStream} object can be registered
+to this stream manager (i.e., added to the list)
+and eventually unregistered (removed from the list).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomStreamManager
+ * Description: Manages a list of random streams
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+public class RandomStreamManager\begin{hide} {
+ private List streams = new ArrayList();
+\end{hide}
+
+ public RandomStream add (RandomStream stream)\begin{hide} {
+ if (stream == null)
+ throw new NullPointerException();
+ if (streams.contains (stream))
+ return stream;
+ streams.add (stream);
+ return stream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds the given \texttt{stream} to the internal list of
+ this random stream manager and returns the added stream.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the stream being added.}
+ \return{the added stream.}
+ \exception{NullPointerException}{if \texttt{stream} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public boolean remove (RandomStream stream)\begin{hide} {
+ return streams.remove (stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes the given stream from the internal list of this random
+ stream manager. Returns \texttt{true} if the stream was
+ properly removed, \texttt{false} otherwise.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the stream being removed.}
+ \return{the success indicator of the operation.}
+\end{htmlonly}
+\begin{code}
+
+ public void clear()\begin{hide} {
+ streams.clear();
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes all the streams from the internal list
+ of this random stream manager.
+\end{tabb}
+\begin{code}
+
+ public List getStreams()\begin{hide} {
+ return Collections.unmodifiableList (streams);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns an unmodifiable list containing all the
+ random streams in this random
+ stream manager. The returned list, constructed by
+ \externalmethod{java.util}{Collections}{unmodifiableList}{(List)}, can be assumed
+ to contain non-\texttt{null} \class{RandomStream} instances.
+\end{tabb}
+\begin{htmlonly}
+ \return{the list of managed random streams.}
+\end{htmlonly}
+\begin{code}
+
+ public void resetStartStream()\begin{hide} {
+ for (int s = 0; s < streams.size(); s++)
+ ((RandomStream)streams.get (s)).resetStartStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Forwards to the
+ \externalmethod{umontreal.iro.lecuyer.rng}{RandomStream}{resetStartStream}{()} methods
+ of all streams in the list.
+\end{tabb}
+\begin{code}
+
+ public void resetStartSubstream()\begin{hide} {
+ for (int s = 0; s < streams.size(); s++)
+ ((RandomStream)streams.get (s)).resetStartSubstream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Forwards to the
+ \externalmethod{umontreal.iro.lecuyer.rng}{RandomStream}{resetStartSubstream}{()} methods
+ of all streams in the list.
+\end{tabb}
+\begin{code}
+
+ public void resetNextSubstream()\begin{hide} {
+ for (int s = 0; s < streams.size(); s++)
+ ((RandomStream)streams.get (s)).resetNextSubstream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Forwards to the
+ \externalmethod{umontreal.iro.lecuyer.rng}{RandomStream}{resetNextSubstream}{()} methods
+ of all streams in the list.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer (getClass().getName());
+ sb.append ('[');
+ sb.append ("number of stored streams: ").append (streams.size());
+ sb.append (']');
+ return sb.toString();
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamWithCache.java b/source/umontreal/iro/lecuyer/rng/RandomStreamWithCache.java
new file mode 100644
index 0000000..2523364
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamWithCache.java
@@ -0,0 +1,348 @@
+
+
+/*
+ * Class: RandomStreamWithCache
+ * Description: random stream whose uniforms are cached for more efficiency
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import cern.colt.list.DoubleArrayList;
+
+
+/**
+ * This class represents a random stream whose uniforms
+ * are cached for more efficiency when using
+ * common random numbers. An object
+ * from this class is constructed with a reference to a
+ * {@link RandomStream} instance used to
+ * get the random numbers. These numbers
+ * are stored in an internal array to be retrieved later.
+ * The dimension of the array increases as the values
+ * are generated.
+ * If the {@link #nextDouble(()) nextDouble} method is called after
+ * the object is reset, it gives back the cached
+ * values instead of computing new ones.
+ * If the cache is exhausted before the stream is reset,
+ * new values are computed, and added to the cache.
+ *
+ * <P>
+ * Such caching allows for a better performance with
+ * common random numbers, when
+ * generating uniforms is time-consuming.
+ * It can also help with restoring the simulation to a
+ * certain state without setting stream-specific
+ * seeds.
+ * However, using such caching may lead to memory problems if
+ * a large quantity of random numbers are needed.
+ *
+ */
+public class RandomStreamWithCache implements RandomStream {
+ private RandomStream stream;
+ private DoubleArrayList values;
+ private int index = 0;
+ private boolean caching = true;
+
+
+
+ /**
+ * Constructs a new cached random stream with
+ * internal stream <TT>stream</TT>.
+ *
+ * @param stream the random stream whose uniforms are cached.
+ *
+ * @exception NullPointerException if <TT>stream</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public RandomStreamWithCache (RandomStream stream) {
+ if (stream == null)
+ throw new NullPointerException
+ ("The given random stream cannot be null");
+ this.stream = stream;
+ values = new DoubleArrayList();
+ }
+
+
+ /**
+ * Constructs a new cached random stream
+ * with internal stream <TT>stream</TT>.
+ * The <TT>initialCapacity</TT>
+ * parameter is used to set the initial capacity of the internal array
+ * which can grow as needed; it does not
+ * limit the total size of the cache.
+ *
+ * @param stream the random stream whose values are cached.
+ *
+ * @param initialCapacity the initial capacity of the cache.
+ *
+ * @exception NullPointerException if <TT>stream</TT> is <TT>null</TT>.
+ *
+ */
+ public RandomStreamWithCache (RandomStream stream, int initialCapacity) {
+ if (stream == null)
+ throw new NullPointerException
+ ("The given random stream cannot be null");
+ this.stream = stream;
+ values = new DoubleArrayList (initialCapacity);
+ }
+
+
+ /**
+ * Determines if the random stream is caching values,
+ * default being <TT>true</TT>.
+ * When caching is turned OFF, the {@link #nextDouble(()) nextDouble}
+ * method simply calls the corresponding method on the internal
+ * random stream, without storing the generated uniforms.
+ *
+ * @return the caching indicator.
+ *
+ */
+ public boolean isCaching() {
+ return caching;
+ }
+
+
+ /**
+ * Sets the caching indicator to <TT>caching</TT>.
+ * If caching is turned OFF, this method calls {@link #clearCache(()) clearCache}
+ * to clear the cached values.
+ *
+ * @param caching the new value of the caching indicator.
+ *
+ *
+ */
+ public void setCaching (boolean caching) {
+ if (this.caching && !caching)
+ clearCache();
+ this.caching = caching;
+ }
+
+
+ /**
+ * Returns a reference to the random stream
+ * whose values are cached.
+ *
+ * @return a reference to the random stream whose values are cached.
+ *
+ */
+ public RandomStream getCachedStream() {
+ return stream;
+ }
+
+
+ /**
+ * Sets the random stream whose values are cached to
+ * <TT>stream</TT>. If the stream is changed, the {@link #clearCache(()) clearCache}
+ * method is called to clear the cache.
+ *
+ * @param stream the new random stream whose values will be cached.
+ *
+ * @exception NullPointerException if <TT>stream</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public void setCachedStream (RandomStream stream) {
+ if (stream == null)
+ throw new NullPointerException
+ ("The given random stream cannot be null");
+ if (stream == this.stream)
+ return;
+ this.stream = stream;
+ clearCache();
+ }
+
+
+ /**
+ * Clears the cached values for this random stream.
+ * Any subsequent call will then obtain new values
+ * from the internal stream.
+ *
+ */
+ public void clearCache() {
+ //values.clear();
+ // Keep the array previously returned by getCachedValues
+ // intact to allow caching values for several
+ // replications.
+ values = new DoubleArrayList();
+ index = 0;
+ }
+
+
+ /**
+ * Resets this random stream to recover values from the cache.
+ * Subsequent calls
+ * to {@link #nextDouble(()) nextDouble} will return the cached uniforms
+ * until all the values are returned. When the array
+ * of cached values is exhausted, the internal random stream
+ * is used to generate new values which are added
+ * to the internal array as well.
+ * This method is equivalent to calling {@link #setCacheIndex((int)) setCacheIndex}.
+ *
+ */
+ public void initCache() {
+ index = 0;
+ }
+
+
+ /**
+ * Returns the total number of values cached by this random stream.
+ *
+ * @return the total number of cached values.
+ *
+ */
+ public int getNumCachedValues() {
+ return values.size();
+ }
+
+
+ /**
+ * Return the index of the next cached value that will be
+ * returned by the stream.
+ * If the cache is exhausted, the
+ * returned value corresponds to the value returned by
+ * {@link #getNumCachedValues(()) getNumCachedValues}, and a subsequent call to
+ * {@link #nextDouble(()) nextDouble} will generate a new variate rather than
+ * reading a previous one from the cache.
+ * If caching is disabled, this always returns 0.
+ *
+ * @return the index of the next cached value.
+ *
+ */
+ public int getCacheIndex() {
+ return index;
+ }
+
+
+ /**
+ * Sets the index, in the cache, of the next value returned
+ * by {@link #nextDouble(()) nextDouble}.
+ * If <TT>newIndex</TT> is 0, this is equivalent to
+ * calling {@link #initCache(()) initCache}.
+ * If <TT>newIndex</TT> is {@link #getNumCachedValues(()) getNumCachedValues},
+ * subsequent calls to {@link #nextDouble(()) nextDouble} will add
+ * new values to the cache.
+ *
+ * @param newIndex the new index.
+ *
+ * @exception IllegalArgumentException if <TT>newIndex</TT>
+ * is negative or greater than or equal to the cache size.
+ *
+ *
+ */
+ public void setCacheIndex (int newIndex) {
+ if (newIndex < 0 || newIndex > values.size())
+ throw new IllegalArgumentException
+ ("newIndex must not be negative or greater than the cache size");
+ index = newIndex;
+ }
+
+
+ /**
+ * Returns an array list containing the values
+ * cached by this random stream.
+ *
+ * @return the array of cached values.
+ *
+ */
+ public DoubleArrayList getCachedValues() {
+ return values;
+ }
+
+
+ /**
+ * Sets the array list containing the cached
+ * values to <TT>values</TT>.
+ * This resets the cache index to
+ * the size of the given array.
+ *
+ * @param values the array list of cached values.
+ *
+ * @exception NullPointerException if <TT>values</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public void setCachedValues (DoubleArrayList values) {
+ if (values == null)
+ throw new NullPointerException();
+ this.values = values;
+ index = values.size();
+ }
+
+
+ public void resetStartStream () {
+ stream.resetStartStream();
+ }
+
+ public void resetStartSubstream () {
+ stream.resetStartSubstream();
+ }
+
+ public void resetNextSubstream () {
+ stream.resetNextSubstream();
+ }
+
+ public double nextDouble () {
+ if (!caching)
+ return stream.nextDouble();
+ else if (index >= values.size()) {
+ double v = stream.nextDouble();
+ values.add (v);
+ ++index;
+ return v;
+ }
+ else
+ return values.getQuick (index++);
+ }
+
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ if (!caching) {
+ stream.nextArrayOfDouble (u, start, n);
+ return;
+ }
+ int remainingValues = values.size() - index;
+ if (remainingValues < 0)
+ remainingValues = 0;
+ int ncpy = Math.min (n, remainingValues);
+ if (ncpy > 0) {
+ System.arraycopy (values.elements(), index, u, start, ncpy);
+ index += ncpy;
+ }
+ int ngen = n - ncpy;
+ if (ngen > 0) {
+ stream.nextArrayOfDouble (u, start + ncpy, ngen);
+ for (int i = ncpy; i < n; i++) {
+ values.add (u[start + i]);
+ ++index;
+ }
+ }
+ }
+
+ public int nextInt (int i, int j) {
+ return i + (int) (nextDouble () * (j - i + 1));
+ }
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ for (int x = start; x < start + n; x++)
+ u[x] = nextInt (i, j);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/RandomStreamWithCache.tex b/source/umontreal/iro/lecuyer/rng/RandomStreamWithCache.tex
new file mode 100644
index 0000000..09a1074
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/RandomStreamWithCache.tex
@@ -0,0 +1,344 @@
+\defmodule{RandomStreamWithCache}
+
+This class represents a random stream whose uniforms
+are cached for more efficiency when using
+common random numbers. An object
+from this class is constructed with a reference to a
+\class{RandomStream} instance used to
+get the random numbers. These numbers
+are stored in an internal array to be retrieved later.
+The dimension of the array increases as the values
+are generated.
+If the \method{nextDouble}{()} method is called after
+the object is reset, it gives back the cached
+values instead of computing new ones.
+If the cache is exhausted before the stream is reset,
+new values are computed, and added to the cache.
+
+Such caching allows for a better performance with
+common random numbers, when
+generating uniforms is time-consuming.
+It can also help with restoring the simulation to a
+certain state without setting stream-specific
+seeds.
+However, using such caching may lead to memory problems if
+a large quantity of random numbers are needed.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RandomStreamWithCache
+ * Description: random stream whose uniforms are cached for more efficiency
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import cern.colt.list.DoubleArrayList;
+\end{hide}
+
+public class RandomStreamWithCache implements RandomStream\begin{hide} {
+ private RandomStream stream;
+ private DoubleArrayList values;
+ private int index = 0;
+ private boolean caching = true;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public RandomStreamWithCache (RandomStream stream)\begin{hide} {
+ if (stream == null)
+ throw new NullPointerException
+ ("The given random stream cannot be null");
+ this.stream = stream;
+ values = new DoubleArrayList();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new cached random stream with
+ internal stream \texttt{stream}.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the random stream whose uniforms are cached.}
+ \exception{NullPointerException}{if \texttt{stream} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStreamWithCache (RandomStream stream, int initialCapacity)\begin{hide} {
+ if (stream == null)
+ throw new NullPointerException
+ ("The given random stream cannot be null");
+ this.stream = stream;
+ values = new DoubleArrayList (initialCapacity);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new cached random stream
+ with internal stream \texttt{stream}.
+ The \texttt{initialCapacity}
+ parameter is used to set the initial capacity of the internal array
+ which can grow as needed; it does not
+ limit the total size of the cache.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the random stream whose values are cached.}
+ \param{initialCapacity}{the initial capacity of the cache.}
+ \exception{NullPointerException}{if \texttt{stream} is \texttt{null}.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public boolean isCaching()\begin{hide} {
+ return caching;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if the random stream is caching values,
+ default being \texttt{true}.
+ When caching is turned OFF, the \method{nextDouble}{()}
+ method simply calls the corresponding method on the internal
+ random stream, without storing the generated uniforms.
+\end{tabb}
+\begin{htmlonly}
+ \return{the caching indicator.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCaching (boolean caching)\begin{hide} {
+ if (this.caching && !caching)
+ clearCache();
+ this.caching = caching;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the caching indicator to \texttt{caching}.
+ If caching is turned OFF, this method calls \method{clearCache}{()}
+ to clear the cached values.
+\end{tabb}
+\begin{htmlonly}
+ \param{caching}{the new value of the caching indicator.}
+\end{htmlonly}
+\begin{code}
+
+ public RandomStream getCachedStream()\begin{hide} {
+ return stream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to the random stream
+ whose values are cached.
+\end{tabb}
+\begin{htmlonly}
+ \return{a reference to the random stream whose values are cached.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCachedStream (RandomStream stream)\begin{hide} {
+ if (stream == null)
+ throw new NullPointerException
+ ("The given random stream cannot be null");
+ if (stream == this.stream)
+ return;
+ this.stream = stream;
+ clearCache();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the random stream whose values are cached to
+ \texttt{stream}. If the stream is changed, the \method{clearCache}{()}
+ method is called to clear the cache.
+\end{tabb}
+\begin{htmlonly}
+ \param{stream}{the new random stream whose values will be cached.}
+ \exception{NullPointerException}{if \texttt{stream} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public void clearCache()\begin{hide} {
+ //values.clear();
+ // Keep the array previously returned by getCachedValues
+ // intact to allow caching values for several
+ // replications.
+ values = new DoubleArrayList();
+ index = 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Clears the cached values for this random stream.
+ Any subsequent call will then obtain new values
+ from the internal stream.
+\end{tabb}
+\begin{code}
+
+ public void initCache()\begin{hide} {
+ index = 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Resets this random stream to recover values from the cache.
+ Subsequent calls
+ to \method{nextDouble}{()} will return the cached uniforms
+ until all the values are returned. When the array
+ of cached values is exhausted, the internal random stream
+ is used to generate new values which are added
+ to the internal array as well.
+ This method is equivalent to calling \method{setCacheIndex}{(int)}.
+\end{tabb}
+\begin{code}
+
+ public int getNumCachedValues()\begin{hide} {
+ return values.size();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the total number of values cached by this random stream.
+\end{tabb}
+\begin{htmlonly}
+ \return{the total number of cached values.}
+\end{htmlonly}
+\begin{code}
+
+ public int getCacheIndex()\begin{hide} {
+ return index;
+ }\end{hide}
+\end{code}
+\begin{tabb} Return the index of the next cached value that will be
+ returned by the stream.
+ If the cache is exhausted, the
+ returned value corresponds to the value returned by
+ \method{getNumCachedValues}{()}, and a subsequent call to
+ \method{nextDouble}{()} will generate a new variate rather than
+ reading a previous one from the cache.
+ If caching is disabled, this always returns 0.
+\end{tabb}
+\begin{htmlonly}
+ \return{the index of the next cached value.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCacheIndex (int newIndex)\begin{hide} {
+ if (newIndex < 0 || newIndex > values.size())
+ throw new IllegalArgumentException
+ ("newIndex must not be negative or greater than the cache size");
+ index = newIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the index, in the cache, of the next value returned
+ by \method{nextDouble}{()}.
+ If \texttt{newIndex} is 0, this is equivalent to
+ calling \method{initCache}{()}.
+ If \texttt{newIndex} is \method{getNumCachedValues}{()},
+ subsequent calls to \method{nextDouble}{()} will add
+ new values to the cache.
+\end{tabb}
+\begin{htmlonly}
+ \param{newIndex}{the new index.}
+ \exception{IllegalArgumentException}{if \texttt{newIndex}
+ is negative or greater than or equal to the cache size.}
+\end{htmlonly}
+\begin{code}
+
+ public DoubleArrayList getCachedValues()\begin{hide} {
+ return values;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns an array list containing the values
+ cached by this random stream.
+\end{tabb}
+\begin{htmlonly}
+ \return{the array of cached values.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCachedValues (DoubleArrayList values)\begin{hide} {
+ if (values == null)
+ throw new NullPointerException();
+ this.values = values;
+ index = values.size();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the array list containing the cached
+ values to \texttt{values}.
+ This resets the cache index to
+ the size of the given array.
+\end{tabb}
+\begin{htmlonly}
+ \param{values}{the array list of cached values.}
+ \exception{NullPointerException}{if \texttt{values} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void resetStartStream () {
+ stream.resetStartStream();
+ }
+
+ public void resetStartSubstream () {
+ stream.resetStartSubstream();
+ }
+
+ public void resetNextSubstream () {
+ stream.resetNextSubstream();
+ }
+
+ public double nextDouble () {
+ if (!caching)
+ return stream.nextDouble();
+ else if (index >= values.size()) {
+ double v = stream.nextDouble();
+ values.add (v);
+ ++index;
+ return v;
+ }
+ else
+ return values.getQuick (index++);
+ }
+
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ if (!caching) {
+ stream.nextArrayOfDouble (u, start, n);
+ return;
+ }
+ int remainingValues = values.size() - index;
+ if (remainingValues < 0)
+ remainingValues = 0;
+ int ncpy = Math.min (n, remainingValues);
+ if (ncpy > 0) {
+ System.arraycopy (values.elements(), index, u, start, ncpy);
+ index += ncpy;
+ }
+ int ngen = n - ncpy;
+ if (ngen > 0) {
+ stream.nextArrayOfDouble (u, start + ncpy, ngen);
+ for (int i = ncpy; i < n; i++) {
+ values.add (u[start + i]);
+ ++index;
+ }
+ }
+ }
+
+ public int nextInt (int i, int j) {
+ return i + (int) (nextDouble () * (j - i + 1));
+ }
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ for (int x = start; x < start + n; x++)
+ u[x] = nextInt (i, j);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/Rijndael_Algorithm.java b/source/umontreal/iro/lecuyer/rng/Rijndael_Algorithm.java
new file mode 100644
index 0000000..092b5a0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/Rijndael_Algorithm.java
@@ -0,0 +1,860 @@
+// $Id: $
+//
+// $Log: $
+// Revision 1.1 1998/04/12 Paulo
+// + optimized methods for the default 128-bit block size.
+//
+// Revision 1.0 1998/03/11 Raif
+// + original version.
+//
+// $Endlog$
+/*
+ * Copyright (c) 1997, 1998 Systemics Ltd on behalf of
+ * the Cryptix Development Team. All rights reserved.
+ */
+package umontreal.iro.lecuyer.rng;
+
+import java.io.PrintWriter;
+import java.security.InvalidKeyException;
+
+//...........................................................................
+/**
+ * Rijndael --pronounced Reindaal-- is a variable block-size (128-, 192- and
+ * 256-bit), variable key-size (128-, 192- and 256-bit) symmetric cipher.<p>
+ *
+ * Rijndael was written by <a href="mailto:rijmen at esat.kuleuven.ac.be">Vincent
+ * Rijmen</a> and <a href="mailto:Joan.Daemen at village.uunet.be">Joan Daemen</a>.<p>
+ *
+ * Portions of this code are <b>Copyright</b> © 1997, 1998
+ * <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the
+ * <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>.
+ * <br>All rights reserved.<p>
+ *
+ * <b>$Revision: $</b>
+ * @author Raif S. Naffah
+ * @author Paulo S. L. M. Barreto
+ */
+final class Rijndael_Algorithm // implicit no-argument constructor
+{
+// Debugging methods and variables
+//...........................................................................
+
+ static final String NAME = "Rijndael_Algorithm";
+ static final boolean IN = true, OUT = false;
+
+ static final boolean DEBUG = Rijndael_Properties.GLOBAL_DEBUG;
+ static final int debuglevel = DEBUG ? Rijndael_Properties.getLevel(NAME) : 0;
+ static final PrintWriter err = DEBUG ? Rijndael_Properties.getOutput() : null;
+
+ static final boolean TRACE = Rijndael_Properties.isTraceable(NAME);
+
+ static void debug (String s) { err.println(">>> "+NAME+": "+s); }
+ static void trace (boolean in, String s) {
+ if (TRACE) err.println((in?"==> ":"<== ")+NAME+"."+s);
+ }
+ static void trace (String s) { if (TRACE) err.println("<=> "+NAME+"."+s); }
+
+
+// Constants and variables
+//...........................................................................
+
+ static final int BLOCK_SIZE = 16; // default block size in bytes
+
+ static final int[] alog = new int[256];
+ static final int[] log = new int[256];
+
+ static final byte[] S = new byte[256];
+ static final byte[] Si = new byte[256];
+ static final int[] T1 = new int[256];
+ static final int[] T2 = new int[256];
+ static final int[] T3 = new int[256];
+ static final int[] T4 = new int[256];
+ static final int[] T5 = new int[256];
+ static final int[] T6 = new int[256];
+ static final int[] T7 = new int[256];
+ static final int[] T8 = new int[256];
+ static final int[] U1 = new int[256];
+ static final int[] U2 = new int[256];
+ static final int[] U3 = new int[256];
+ static final int[] U4 = new int[256];
+ static final byte[] rcon = new byte[30];
+
+ static final int[][][] shifts = new int[][][] {
+ { {0, 0}, {1, 3}, {2, 2}, {3, 1} },
+ { {0, 0}, {1, 5}, {2, 4}, {3, 3} },
+ { {0, 0}, {1, 7}, {3, 5}, {4, 4} }
+ };
+
+ private static final char[] HEX_DIGITS = {
+ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
+ };
+
+
+// Static code - to intialise S-boxes and T-boxes
+//...........................................................................
+
+ static {
+ long time = System.currentTimeMillis();
+
+if (DEBUG && debuglevel > 6) {
+System.out.println("Algorithm Name: "+Rijndael_Properties.FULL_NAME);
+System.out.println("Electronic Codebook (ECB) Mode");
+System.out.println();
+}
+ int ROOT = 0x11B;
+ int i, j = 0;
+
+ //
+ // produce log and alog tables, needed for multiplying in the
+ // field GF(2^m) (generator = 3)
+ //
+ alog[0] = 1;
+ for (i = 1; i < 256; i++) {
+ j = (alog[i-1] << 1) ^ alog[i-1];
+ if ((j & 0x100) != 0) j ^= ROOT;
+ alog[i] = j;
+ }
+ for (i = 1; i < 255; i++) log[alog[i]] = i;
+ byte[][] A = new byte[][] {
+ {1, 1, 1, 1, 1, 0, 0, 0},
+ {0, 1, 1, 1, 1, 1, 0, 0},
+ {0, 0, 1, 1, 1, 1, 1, 0},
+ {0, 0, 0, 1, 1, 1, 1, 1},
+ {1, 0, 0, 0, 1, 1, 1, 1},
+ {1, 1, 0, 0, 0, 1, 1, 1},
+ {1, 1, 1, 0, 0, 0, 1, 1},
+ {1, 1, 1, 1, 0, 0, 0, 1}
+ };
+ byte[] B = new byte[] { 0, 1, 1, 0, 0, 0, 1, 1};
+
+ //
+ // substitution box based on F^{-1}(x)
+ //
+ int t;
+ byte[][] box = new byte[256][8];
+ box[1][7] = 1;
+ for (i = 2; i < 256; i++) {
+ j = alog[255 - log[i]];
+ for (t = 0; t < 8; t++)
+ box[i][t] = (byte)((j >>> (7 - t)) & 0x01);
+ }
+ //
+ // affine transform: box[i] <- B + A*box[i]
+ //
+ byte[][] cox = new byte[256][8];
+ for (i = 0; i < 256; i++)
+ for (t = 0; t < 8; t++) {
+ cox[i][t] = B[t];
+ for (j = 0; j < 8; j++)
+ cox[i][t] ^= A[t][j] * box[i][j];
+ }
+ //
+ // S-boxes and inverse S-boxes
+ //
+ for (i = 0; i < 256; i++) {
+ S[i] = (byte)(cox[i][0] << 7);
+ for (t = 1; t < 8; t++)
+ S[i] ^= cox[i][t] << (7-t);
+ Si[S[i] & 0xFF] = (byte) i;
+ }
+ //
+ // T-boxes
+ //
+ byte[][] G = new byte[][] {
+ {2, 1, 1, 3},
+ {3, 2, 1, 1},
+ {1, 3, 2, 1},
+ {1, 1, 3, 2}
+ };
+ byte[][] AA = new byte[4][8];
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) AA[i][j] = G[i][j];
+ AA[i][i+4] = 1;
+ }
+ byte pivot, tmp;
+ byte[][] iG = new byte[4][4];
+ for (i = 0; i < 4; i++) {
+ pivot = AA[i][i];
+ if (pivot == 0) {
+ t = i + 1;
+ while ((AA[t][i] == 0) && (t < 4))
+ t++;
+ if (t == 4)
+ throw new RuntimeException("G matrix is not invertible");
+ else {
+ for (j = 0; j < 8; j++) {
+ tmp = AA[i][j];
+ AA[i][j] = AA[t][j];
+ AA[t][j] = (byte) tmp;
+ }
+ pivot = AA[i][i];
+ }
+ }
+ for (j = 0; j < 8; j++)
+ if (AA[i][j] != 0)
+ AA[i][j] = (byte)
+ alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255];
+ for (t = 0; t < 4; t++)
+ if (i != t) {
+ for (j = i+1; j < 8; j++)
+ AA[t][j] ^= mul(AA[i][j], AA[t][i]);
+ AA[t][i] = 0;
+ }
+ }
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 4; j++) iG[i][j] = AA[i][j + 4];
+
+ int s;
+ for (t = 0; t < 256; t++) {
+ s = S[t];
+ T1[t] = mul4(s, G[0]);
+ T2[t] = mul4(s, G[1]);
+ T3[t] = mul4(s, G[2]);
+ T4[t] = mul4(s, G[3]);
+
+ s = Si[t];
+ T5[t] = mul4(s, iG[0]);
+ T6[t] = mul4(s, iG[1]);
+ T7[t] = mul4(s, iG[2]);
+ T8[t] = mul4(s, iG[3]);
+
+ U1[t] = mul4(t, iG[0]);
+ U2[t] = mul4(t, iG[1]);
+ U3[t] = mul4(t, iG[2]);
+ U4[t] = mul4(t, iG[3]);
+ }
+ //
+ // round constants
+ //
+ rcon[0] = 1;
+ int r = 1;
+ for (t = 1; t < 30; ) rcon[t++] = (byte)(r = mul(2, r));
+
+ time = System.currentTimeMillis() - time;
+
+if (DEBUG && debuglevel > 8) {
+System.out.println("==========");
+System.out.println();
+System.out.println("Static Data");
+System.out.println();
+System.out.println("S[]:"); for(i=0;i<16;i++) { for(j=0;j<16;j++) System.out.print("0x"+byteToString(S[i*16+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("Si[]:"); for(i=0;i<16;i++) { for(j=0;j<16;j++) System.out.print("0x"+byteToString(Si[i*16+j])+", "); System.out.println();}
+
+System.out.println();
+System.out.println("iG[]:"); for(i=0;i<4;i++){for(j=0;j<4;j++) System.out.print("0x"+byteToString(iG[i][j])+", "); System.out.println();}
+
+System.out.println();
+System.out.println("T1[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T1[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("T2[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T2[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("T3[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T3[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("T4[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T4[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("T5[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T5[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("T6[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T6[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("T7[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T7[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("T8[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(T8[i*4+j])+", "); System.out.println();}
+
+System.out.println();
+System.out.println("U1[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U1[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("U2[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U2[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("U3[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U3[i*4+j])+", "); System.out.println();}
+System.out.println();
+System.out.println("U4[]:"); for(i=0;i<64;i++){for(j=0;j<4;j++) System.out.print("0x"+intToString(U4[i*4+j])+", "); System.out.println();}
+
+System.out.println();
+System.out.println("rcon[]:"); for(i=0;i<5;i++){for(j=0;j<6;j++) System.out.print("0x"+byteToString(rcon[i*6+j])+", "); System.out.println();}
+
+System.out.println();
+System.out.println("Total initialization time: "+time+" ms.");
+System.out.println();
+}
+ }
+
+ // multiply two elements of GF(2^m)
+ static final int mul (int a, int b) {
+ return (a != 0 && b != 0) ?
+ alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] :
+ 0;
+ }
+
+ // convenience method used in generating Transposition boxes
+ static final int mul4 (int a, byte[] b) {
+ if (a == 0) return 0;
+ a = log[a & 0xFF];
+ int a0 = (b[0] != 0) ? alog[(a + log[b[0] & 0xFF]) % 255] & 0xFF : 0;
+ int a1 = (b[1] != 0) ? alog[(a + log[b[1] & 0xFF]) % 255] & 0xFF : 0;
+ int a2 = (b[2] != 0) ? alog[(a + log[b[2] & 0xFF]) % 255] & 0xFF : 0;
+ int a3 = (b[3] != 0) ? alog[(a + log[b[3] & 0xFF]) % 255] & 0xFF : 0;
+ return a0 << 24 | a1 << 16 | a2 << 8 | a3;
+ }
+
+
+// Basic API methods
+//...........................................................................
+
+ /**
+ * Convenience method to expand a user-supplied key material into a
+ * session key, assuming Rijndael's default block size (128-bit).
+ *
+ * @param key The 128/192/256-bit user-key to use.
+ * @exception InvalidKeyException If the key is invalid.
+ */
+ public static Object makeKey (byte[] k) throws InvalidKeyException {
+ return makeKey(k, BLOCK_SIZE);
+ }
+
+ /**
+ * Convenience method to encrypt exactly one block of plaintext, assuming
+ * Rijndael's default block size (128-bit).
+ *
+ * @param in The plaintext.
+ * @param inOffset Index of in from which to start considering data.
+ * @param sessionKey The session key to use for encryption.
+ * @return The ciphertext generated from a plaintext using the session key.
+ */
+ public static byte[]
+ blockEncrypt (byte[] in, int inOffset, Object sessionKey) {
+if (DEBUG) trace(IN, "blockEncrypt("+in+", "+inOffset+", "+sessionKey+")");
+ int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract encryption round keys
+ int ROUNDS = Ke.length - 1;
+ int[] Ker = Ke[0];
+
+ // plaintext to ints + key
+ int t0 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Ker[0];
+ int t1 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Ker[1];
+ int t2 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Ker[2];
+ int t3 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Ker[3];
+
+ int a0, a1, a2, a3;
+ for (int r = 1; r < ROUNDS; r++) { // apply round transforms
+ Ker = Ke[r];
+ a0 = (T1[(t0 >>> 24) & 0xFF] ^
+ T2[(t1 >>> 16) & 0xFF] ^
+ T3[(t2 >>> 8) & 0xFF] ^
+ T4[ t3 & 0xFF] ) ^ Ker[0];
+ a1 = (T1[(t1 >>> 24) & 0xFF] ^
+ T2[(t2 >>> 16) & 0xFF] ^
+ T3[(t3 >>> 8) & 0xFF] ^
+ T4[ t0 & 0xFF] ) ^ Ker[1];
+ a2 = (T1[(t2 >>> 24) & 0xFF] ^
+ T2[(t3 >>> 16) & 0xFF] ^
+ T3[(t0 >>> 8) & 0xFF] ^
+ T4[ t1 & 0xFF] ) ^ Ker[2];
+ a3 = (T1[(t3 >>> 24) & 0xFF] ^
+ T2[(t0 >>> 16) & 0xFF] ^
+ T3[(t1 >>> 8) & 0xFF] ^
+ T4[ t2 & 0xFF] ) ^ Ker[3];
+ t0 = a0;
+ t1 = a1;
+ t2 = a2;
+ t3 = a3;
+if (DEBUG && debuglevel > 6) System.out.println("CT"+r+"="+intToString(t0)+intToString(t1)+intToString(t2)+intToString(t3));
+ }
+
+ // last round is special
+ byte[] result = new byte[BLOCK_SIZE]; // the resulting ciphertext
+ Ker = Ke[ROUNDS];
+ int tt = Ker[0];
+ result[ 0] = (byte)(S[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[ 1] = (byte)(S[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[ 2] = (byte)(S[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[ 3] = (byte)(S[ t3 & 0xFF] ^ tt );
+ tt = Ker[1];
+ result[ 4] = (byte)(S[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[ 5] = (byte)(S[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[ 6] = (byte)(S[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[ 7] = (byte)(S[ t0 & 0xFF] ^ tt );
+ tt = Ker[2];
+ result[ 8] = (byte)(S[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[ 9] = (byte)(S[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[10] = (byte)(S[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[11] = (byte)(S[ t1 & 0xFF] ^ tt );
+ tt = Ker[3];
+ result[12] = (byte)(S[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[13] = (byte)(S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[14] = (byte)(S[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[15] = (byte)(S[ t2 & 0xFF] ^ tt );
+if (DEBUG && debuglevel > 6) {
+System.out.println("CT="+toString(result));
+System.out.println();
+}
+if (DEBUG) trace(OUT, "blockEncrypt()");
+ return result;
+ }
+
+ /**
+ * Convenience method to decrypt exactly one block of plaintext, assuming
+ * Rijndael's default block size (128-bit).
+ *
+ * @param in The ciphertext.
+ * @param inOffset Index of in from which to start considering data.
+ * @param sessionKey The session key to use for decryption.
+ * @return The plaintext generated from a ciphertext using the session key.
+ */
+ public static byte[]
+ blockDecrypt (byte[] in, int inOffset, Object sessionKey) {
+if (DEBUG) trace(IN, "blockDecrypt("+in+", "+inOffset+", "+sessionKey+")");
+ int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys
+ int ROUNDS = Kd.length - 1;
+ int[] Kdr = Kd[0];
+
+ // ciphertext to ints + key
+ int t0 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Kdr[0];
+ int t1 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Kdr[1];
+ int t2 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Kdr[2];
+ int t3 = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Kdr[3];
+
+ int a0, a1, a2, a3;
+ for (int r = 1; r < ROUNDS; r++) { // apply round transforms
+ Kdr = Kd[r];
+ a0 = (T5[(t0 >>> 24) & 0xFF] ^
+ T6[(t3 >>> 16) & 0xFF] ^
+ T7[(t2 >>> 8) & 0xFF] ^
+ T8[ t1 & 0xFF] ) ^ Kdr[0];
+ a1 = (T5[(t1 >>> 24) & 0xFF] ^
+ T6[(t0 >>> 16) & 0xFF] ^
+ T7[(t3 >>> 8) & 0xFF] ^
+ T8[ t2 & 0xFF] ) ^ Kdr[1];
+ a2 = (T5[(t2 >>> 24) & 0xFF] ^
+ T6[(t1 >>> 16) & 0xFF] ^
+ T7[(t0 >>> 8) & 0xFF] ^
+ T8[ t3 & 0xFF] ) ^ Kdr[2];
+ a3 = (T5[(t3 >>> 24) & 0xFF] ^
+ T6[(t2 >>> 16) & 0xFF] ^
+ T7[(t1 >>> 8) & 0xFF] ^
+ T8[ t0 & 0xFF] ) ^ Kdr[3];
+ t0 = a0;
+ t1 = a1;
+ t2 = a2;
+ t3 = a3;
+if (DEBUG && debuglevel > 6) System.out.println("PT"+r+"="+intToString(t0)+intToString(t1)+intToString(t2)+intToString(t3));
+ }
+
+ // last round is special
+ byte[] result = new byte[16]; // the resulting plaintext
+ Kdr = Kd[ROUNDS];
+ int tt = Kdr[0];
+ result[ 0] = (byte)(Si[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[ 1] = (byte)(Si[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[ 2] = (byte)(Si[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[ 3] = (byte)(Si[ t1 & 0xFF] ^ tt );
+ tt = Kdr[1];
+ result[ 4] = (byte)(Si[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[ 5] = (byte)(Si[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[ 6] = (byte)(Si[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[ 7] = (byte)(Si[ t2 & 0xFF] ^ tt );
+ tt = Kdr[2];
+ result[ 8] = (byte)(Si[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[ 9] = (byte)(Si[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[10] = (byte)(Si[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[11] = (byte)(Si[ t3 & 0xFF] ^ tt );
+ tt = Kdr[3];
+ result[12] = (byte)(Si[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[13] = (byte)(Si[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[14] = (byte)(Si[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[15] = (byte)(Si[ t0 & 0xFF] ^ tt );
+if (DEBUG && debuglevel > 6) {
+System.out.println("PT="+toString(result));
+System.out.println();
+}
+if (DEBUG) trace(OUT, "blockDecrypt()");
+ return result;
+ }
+
+ /** A basic symmetric encryption/decryption test. */
+ public static boolean self_test() { return self_test(BLOCK_SIZE); }
+
+
+// Rijndael own methods
+//...........................................................................
+
+ /** @return The default length in bytes of the Algorithm input block. */
+ public static int blockSize() { return BLOCK_SIZE; }
+
+ /**
+ * Expand a user-supplied key material into a session key.
+ *
+ * @param key The 128/192/256-bit user-key to use.
+ * @param blockSize The block size in bytes of this Rijndael.
+ * @exception InvalidKeyException If the key is invalid.
+ */
+ public static synchronized Object makeKey (byte[] k, int blockSize)
+ throws InvalidKeyException {
+if (DEBUG) trace(IN, "makeKey("+k+", "+blockSize+")");
+ if (k == null)
+ throw new InvalidKeyException("Empty key");
+ if (!(k.length == 16 || k.length == 24 || k.length == 32))
+ throw new InvalidKeyException("Incorrect key length");
+ int ROUNDS = getRounds(k.length, blockSize);
+ int BC = blockSize / 4;
+ int[][] Ke = new int[ROUNDS + 1][BC]; // encryption round keys
+ int[][] Kd = new int[ROUNDS + 1][BC]; // decryption round keys
+ int ROUND_KEY_COUNT = (ROUNDS + 1) * BC;
+ int KC = k.length / 4;
+ int[] tk = new int[KC];
+ int i, j;
+
+ // copy user material bytes into temporary ints
+ for (i = 0, j = 0; i < KC; )
+ tk[i++] = (k[j++] & 0xFF) << 24 |
+ (k[j++] & 0xFF) << 16 |
+ (k[j++] & 0xFF) << 8 |
+ (k[j++] & 0xFF);
+ // copy values into round key arrays
+ int t = 0;
+ for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
+ Ke[t / BC][t % BC] = tk[j];
+ Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
+ }
+ int tt, rconpointer = 0;
+ while (t < ROUND_KEY_COUNT) {
+ // extrapolate using phi (the round key evolution function)
+ tt = tk[KC - 1];
+ tk[0] ^= (S[(tt >>> 16) & 0xFF] & 0xFF) << 24 ^
+ (S[(tt >>> 8) & 0xFF] & 0xFF) << 16 ^
+ (S[ tt & 0xFF] & 0xFF) << 8 ^
+ (S[(tt >>> 24) & 0xFF] & 0xFF) ^
+ (rcon[rconpointer++] & 0xFF) << 24;
+ if (KC != 8)
+ for (i = 1, j = 0; i < KC; ) tk[i++] ^= tk[j++];
+ else {
+ for (i = 1, j = 0; i < KC / 2; ) tk[i++] ^= tk[j++];
+ tt = tk[KC / 2 - 1];
+ tk[KC / 2] ^= (S[ tt & 0xFF] & 0xFF) ^
+ (S[(tt >>> 8) & 0xFF] & 0xFF) << 8 ^
+ (S[(tt >>> 16) & 0xFF] & 0xFF) << 16 ^
+ (S[(tt >>> 24) & 0xFF] & 0xFF) << 24;
+ for (j = KC / 2, i = j + 1; i < KC; ) tk[i++] ^= tk[j++];
+ }
+ // copy values into round key arrays
+ for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
+ Ke[t / BC][t % BC] = tk[j];
+ Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
+ }
+ }
+ for (int r = 1; r < ROUNDS; r++) // inverse MixColumn where needed
+ for (j = 0; j < BC; j++) {
+ tt = Kd[r][j];
+ Kd[r][j] = U1[(tt >>> 24) & 0xFF] ^
+ U2[(tt >>> 16) & 0xFF] ^
+ U3[(tt >>> 8) & 0xFF] ^
+ U4[ tt & 0xFF];
+ }
+ // assemble the encryption (Ke) and decryption (Kd) round keys into
+ // one sessionKey object
+ Object[] sessionKey = new Object[] {Ke, Kd};
+if (DEBUG) trace(OUT, "makeKey()");
+ return sessionKey;
+ }
+
+ /**
+ * Encrypt exactly one block of plaintext.
+ *
+ * @param in The plaintext.
+ * @param inOffset Index of in from which to start considering data.
+ * @param sessionKey The session key to use for encryption.
+ * @param blockSize The block size in bytes of this Rijndael.
+ * @return The ciphertext generated from a plaintext using the session key.
+ */
+ public static byte[]
+ blockEncrypt (byte[] in, int inOffset, Object sessionKey, int blockSize) {
+ if (blockSize == BLOCK_SIZE)
+ return blockEncrypt(in, inOffset, sessionKey);
+if (DEBUG) trace(IN, "blockEncrypt("+in+", "+inOffset+", "+sessionKey+", "+blockSize+")");
+ Object[] sKey = (Object[]) sessionKey; // extract encryption round keys
+ int[][] Ke = (int[][]) sKey[0];
+
+ int BC = blockSize / 4;
+ int ROUNDS = Ke.length - 1;
+ int SC = BC == 4 ? 0 : (BC == 6 ? 1 : 2);
+ int s1 = shifts[SC][1][0];
+ int s2 = shifts[SC][2][0];
+ int s3 = shifts[SC][3][0];
+ int[] a = new int[BC];
+ int[] t = new int[BC]; // temporary work array
+ int i;
+ byte[] result = new byte[blockSize]; // the resulting ciphertext
+ int j = 0, tt;
+
+ for (i = 0; i < BC; i++) // plaintext to ints + key
+ t[i] = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Ke[0][i];
+ for (int r = 1; r < ROUNDS; r++) { // apply round transforms
+ for (i = 0; i < BC; i++)
+ a[i] = (T1[(t[ i ] >>> 24) & 0xFF] ^
+ T2[(t[(i + s1) % BC] >>> 16) & 0xFF] ^
+ T3[(t[(i + s2) % BC] >>> 8) & 0xFF] ^
+ T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i];
+ System.arraycopy(a, 0, t, 0, BC);
+if (DEBUG && debuglevel > 6) System.out.println("CT"+r+"="+toString(t));
+ }
+ for (i = 0; i < BC; i++) { // last round is special
+ tt = Ke[ROUNDS][i];
+ result[j++] = (byte)(S[(t[ i ] >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[j++] = (byte)(S[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[j++] = (byte)(S[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[j++] = (byte)(S[ t[(i + s3) % BC] & 0xFF] ^ tt);
+ }
+if (DEBUG && debuglevel > 6) {
+System.out.println("CT="+toString(result));
+System.out.println();
+}
+if (DEBUG) trace(OUT, "blockEncrypt()");
+ return result;
+ }
+
+ /**
+ * Decrypt exactly one block of ciphertext.
+ *
+ * @param in The ciphertext.
+ * @param inOffset Index of in from which to start considering data.
+ * @param sessionKey The session key to use for decryption.
+ * @param blockSize The block size in bytes of this Rijndael.
+ * @return The plaintext generated from a ciphertext using the session key.
+ */
+ public static byte[]
+ blockDecrypt (byte[] in, int inOffset, Object sessionKey, int blockSize) {
+ if (blockSize == BLOCK_SIZE)
+ return blockDecrypt(in, inOffset, sessionKey);
+if (DEBUG) trace(IN, "blockDecrypt("+in+", "+inOffset+", "+sessionKey+", "+blockSize+")");
+ Object[] sKey = (Object[]) sessionKey; // extract decryption round keys
+ int[][] Kd = (int[][]) sKey[1];
+
+ int BC = blockSize / 4;
+ int ROUNDS = Kd.length - 1;
+ int SC = BC == 4 ? 0 : (BC == 6 ? 1 : 2);
+ int s1 = shifts[SC][1][1];
+ int s2 = shifts[SC][2][1];
+ int s3 = shifts[SC][3][1];
+ int[] a = new int[BC];
+ int[] t = new int[BC]; // temporary work array
+ int i;
+ byte[] result = new byte[blockSize]; // the resulting plaintext
+ int j = 0, tt;
+
+ for (i = 0; i < BC; i++) // ciphertext to ints + key
+ t[i] = ((in[inOffset++] & 0xFF) << 24 |
+ (in[inOffset++] & 0xFF) << 16 |
+ (in[inOffset++] & 0xFF) << 8 |
+ (in[inOffset++] & 0xFF) ) ^ Kd[0][i];
+ for (int r = 1; r < ROUNDS; r++) { // apply round transforms
+ for (i = 0; i < BC; i++)
+ a[i] = (T5[(t[ i ] >>> 24) & 0xFF] ^
+ T6[(t[(i + s1) % BC] >>> 16) & 0xFF] ^
+ T7[(t[(i + s2) % BC] >>> 8) & 0xFF] ^
+ T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i];
+ System.arraycopy(a, 0, t, 0, BC);
+if (DEBUG && debuglevel > 6) System.out.println("PT"+r+"="+toString(t));
+ }
+ for (i = 0; i < BC; i++) { // last round is special
+ tt = Kd[ROUNDS][i];
+ result[j++] = (byte)(Si[(t[ i ] >>> 24) & 0xFF] ^ (tt >>> 24));
+ result[j++] = (byte)(Si[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ (tt >>> 16));
+ result[j++] = (byte)(Si[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ (tt >>> 8));
+ result[j++] = (byte)(Si[ t[(i + s3) % BC] & 0xFF] ^ tt);
+ }
+if (DEBUG && debuglevel > 6) {
+System.out.println("PT="+toString(result));
+System.out.println();
+}
+if (DEBUG) trace(OUT, "blockDecrypt()");
+ return result;
+ }
+
+ /** A basic symmetric encryption/decryption test for a given key size. */
+ private static boolean self_test (int keysize) {
+if (DEBUG) trace(IN, "self_test("+keysize+")");
+ boolean ok = false;
+ try {
+ byte[] kb = new byte[keysize];
+ byte[] pt = new byte[BLOCK_SIZE];
+ int i;
+
+ for (i = 0; i < keysize; i++)
+ kb[i] = (byte) i;
+ for (i = 0; i < BLOCK_SIZE; i++)
+ pt[i] = (byte) i;
+
+if (DEBUG && debuglevel > 6) {
+System.out.println("==========");
+System.out.println();
+System.out.println("KEYSIZE="+(8*keysize));
+System.out.println("KEY="+toString(kb));
+System.out.println();
+}
+ Object key = makeKey(kb, BLOCK_SIZE);
+
+if (DEBUG && debuglevel > 6) {
+System.out.println("Intermediate Ciphertext Values (Encryption)");
+System.out.println();
+System.out.println("PT="+toString(pt));
+}
+ byte[] ct = blockEncrypt(pt, 0, key, BLOCK_SIZE);
+
+if (DEBUG && debuglevel > 6) {
+System.out.println("Intermediate Plaintext Values (Decryption)");
+System.out.println();
+System.out.println("CT="+toString(ct));
+}
+ byte[] cpt = blockDecrypt(ct, 0, key, BLOCK_SIZE);
+
+ ok = areEqual(pt, cpt);
+ if (!ok)
+ throw new RuntimeException("Symmetric operation failed");
+ }
+ catch (Exception x) {
+if (DEBUG && debuglevel > 0) {
+ debug("Exception encountered during self-test: " + x.getMessage());
+ x.printStackTrace();
+}
+ }
+if (DEBUG && debuglevel > 0) debug("Self-test OK? " + ok);
+if (DEBUG) trace(OUT, "self_test()");
+ return ok;
+ }
+
+ /**
+ * Return The number of rounds for a given Rijndael's key and block sizes.
+ *
+ * @param keySize The size of the user key material in bytes.
+ * @param blockSize The desired block size in bytes.
+ * @return The number of rounds for a given Rijndael's key and
+ * block sizes.
+ */
+ public static int getRounds (int keySize, int blockSize) {
+ switch (keySize) {
+ case 16:
+ return blockSize == 16 ? 10 : (blockSize == 24 ? 12 : 14);
+ case 24:
+ return blockSize != 32 ? 12 : 14;
+ default: // 32 bytes = 256 bits
+ return 14;
+ }
+ }
+
+
+// utility static methods (from cryptix.util.core ArrayUtil and Hex classes)
+//...........................................................................
+
+ /**
+ * Compares two byte arrays for equality.
+ *
+ * @return true if the arrays have identical contents
+ */
+ private static boolean areEqual (byte[] a, byte[] b) {
+ int aLength = a.length;
+ if (aLength != b.length)
+ return false;
+ for (int i = 0; i < aLength; i++)
+ if (a[i] != b[i])
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns a string of 2 hexadecimal digits (most significant
+ * digit first) corresponding to the lowest 8 bits of <i>n</i>.
+ */
+ private static String byteToString (int n) {
+ char[] buf = {
+ HEX_DIGITS[(n >>> 4) & 0x0F],
+ HEX_DIGITS[ n & 0x0F]
+ };
+ return new String(buf);
+ }
+
+ /**
+ * Returns a string of 8 hexadecimal digits (most significant
+ * digit first) corresponding to the integer <i>n</i>, which is
+ * treated as unsigned.
+ */
+ private static String intToString (int n) {
+ char[] buf = new char[8];
+ for (int i = 7; i >= 0; i--) {
+ buf[i] = HEX_DIGITS[n & 0x0F];
+ n >>>= 4;
+ }
+ return new String(buf);
+ }
+
+ /**
+ * Returns a string of hexadecimal digits from a byte array. Each
+ * byte is converted to 2 hex symbols.
+ */
+ private static String toString (byte[] ba) {
+ int length = ba.length;
+ char[] buf = new char[length * 2];
+ for (int i = 0, j = 0, k; i < length; ) {
+ k = ba[i++];
+ buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
+ buf[j++] = HEX_DIGITS[ k & 0x0F];
+ }
+ return new String(buf);
+ }
+
+ /**
+ * Returns a string of hexadecimal digits from an integer array. Each
+ * int is converted to 4 hex symbols.
+ */
+ private static String toString (int[] ia) {
+ int length = ia.length;
+ char[] buf = new char[length * 8];
+ for (int i = 0, j = 0, k; i < length; i++) {
+ k = ia[i];
+ buf[j++] = HEX_DIGITS[(k >>> 28) & 0x0F];
+ buf[j++] = HEX_DIGITS[(k >>> 24) & 0x0F];
+ buf[j++] = HEX_DIGITS[(k >>> 20) & 0x0F];
+ buf[j++] = HEX_DIGITS[(k >>> 16) & 0x0F];
+ buf[j++] = HEX_DIGITS[(k >>> 12) & 0x0F];
+ buf[j++] = HEX_DIGITS[(k >>> 8) & 0x0F];
+ buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
+ buf[j++] = HEX_DIGITS[ k & 0x0F];
+ }
+ return new String(buf);
+ }
+
+
+// main(): use to generate the Intermediate Values KAT
+//...........................................................................
+
+ public static void main (String[] args) {
+ self_test(16);
+ self_test(24);
+ self_test(32);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/Rijndael_Properties.java b/source/umontreal/iro/lecuyer/rng/Rijndael_Properties.java
new file mode 100644
index 0000000..213dba4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/Rijndael_Properties.java
@@ -0,0 +1,201 @@
+// $Id: $
+//
+// $Log: $
+// Revision 1.0 1998/04/07 raif
+// + original version.
+//
+// $Endlog$
+/*
+ * Copyright (c) 1997, 1998 Systemics Ltd on behalf of
+ * the Cryptix Development Team. All rights reserved.
+ */
+package umontreal.iro.lecuyer.rng;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * This class acts as a central repository for an algorithm specific
+ * properties. It reads an (algorithm).properties file containing algorithm-
+ * specific properties. When using the AES-Kit, this (algorithm).properties
+ * file is located in the (algorithm).jar file produced by the "jarit" batch/
+ * script command.<p>
+ *
+ * <b>Copyright</b> © 1997, 1998
+ * <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the
+ * <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>.
+ * <br>All rights reserved.<p>
+ *
+ * <b>$Revision: $</b>
+ * @author David Hopwood
+ * @author Jill Baker
+ * @author Raif S. Naffah
+ */
+
+class Rijndael_Properties // implicit no-argument constructor
+{
+// Constants and variables with relevant static code
+//...........................................................................
+
+ static final boolean GLOBAL_DEBUG = false;
+
+ static final String ALGORITHM = "Rijndael";
+ static final double VERSION = 0.1;
+ static final String FULL_NAME = ALGORITHM + " ver. " + VERSION;
+ static final String NAME = "Rijndael_Properties";
+
+ static final Properties properties = new Properties();
+
+ /** Default properties in case .properties file was not found. */
+ private static final String[][] DEFAULT_PROPERTIES = {
+ {"Trace.Rijndael_Algorithm", "true"},
+ {"Debug.Level.*", "1"},
+ {"Debug.Level.Rijndael_Algorithm", "9"},
+ };
+
+ static {
+if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Looking for " + ALGORITHM + " properties");
+ String it = ALGORITHM + ".properties";
+ InputStream is = Rijndael_Properties.class.getResourceAsStream(it);
+ boolean ok = is != null;
+ if (ok)
+ try {
+ properties.load(is);
+ is.close();
+if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Properties file loaded OK...");
+ } catch (Exception x) {
+ ok = false;
+ }
+ if (!ok) {
+if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": WARNING: Unable to load \"" + it + "\" from CLASSPATH.");
+if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Will use default values instead...");
+ int n = DEFAULT_PROPERTIES.length;
+ for (int i = 0; i < n; i++)
+ properties.put(
+ DEFAULT_PROPERTIES[i][0], DEFAULT_PROPERTIES[i][1]);
+if (GLOBAL_DEBUG) System.err.println(">>> " + NAME + ": Default properties now set...");
+ }
+ }
+
+
+// Properties methods (excluding load and save, which are deliberately not
+// supported).
+//...........................................................................
+
+ /** Get the value of a property for this algorithm. */
+ public static String getProperty (String key) {
+ return properties.getProperty(key);
+ }
+
+ /**
+ * Get the value of a property for this algorithm, or return
+ * <i>value</i> if the property was not set.
+ */
+ public static String getProperty (String key, String value) {
+ return properties.getProperty(key, value);
+ }
+
+ /** List algorithm properties to the PrintStream <i>out</i>. */
+ public static void list (PrintStream out) {
+ list(new PrintWriter(out, true));
+ }
+
+ /** List algorithm properties to the PrintWriter <i>out</i>. */
+ public static void list (PrintWriter out) {
+ out.println("#");
+ out.println("# ----- Begin "+ALGORITHM+" properties -----");
+ out.println("#");
+ String key, value;
+ Enumeration myEnum = properties.propertyNames();
+ while (myEnum.hasMoreElements()) {
+ key = (String) myEnum.nextElement();
+ value = getProperty(key);
+ out.println(key + " = " + value);
+ }
+ out.println("#");
+ out.println("# ----- End "+ALGORITHM+" properties -----");
+ }
+
+// public synchronized void load(InputStream in) throws IOException {}
+
+ public static Enumeration propertyNames() {
+ return properties.propertyNames();
+ }
+
+// public void save (OutputStream os, String comment) {}
+
+
+// Developer support: Tracing and debugging enquiry methods (package-private)
+//...........................................................................
+
+ /**
+ * Return true if tracing is requested for a given class.<p>
+ *
+ * User indicates this by setting the tracing <code>boolean</code>
+ * property for <i>label</i> in the <code>(algorithm).properties</code>
+ * file. The property's key is "<code>Trace.<i>label</i></code>".<p>
+ *
+ * @param label The name of a class.
+ * @return True iff a boolean true value is set for a property with
+ * the key <code>Trace.<i>label</i></code>.
+ */
+ static boolean isTraceable (String label) {
+ String s = getProperty("Trace." + label);
+ if (s == null)
+ return false;
+ return new Boolean(s).booleanValue();
+ }
+
+ /**
+ * Return the debug level for a given class.<p>
+ *
+ * User indicates this by setting the numeric property with key
+ * "<code>Debug.Level.<i>label</i></code>".<p>
+ *
+ * If this property is not set, "<code>Debug.Level.*</code>" is looked up
+ * next. If neither property is set, or if the first property found is
+ * not a valid decimal integer, then this method returns 0.
+ *
+ * @param label The name of a class.
+ * @return The required debugging level for the designated class.
+ */
+ static int getLevel(String label) {
+ String s = getProperty("Debug.Level." + label);
+ if (s == null) {
+ s = getProperty("Debug.Level.*");
+ if (s == null)
+ return 0;
+ }
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Return the PrintWriter to which tracing and debugging output is to
+ * be sent.<p>
+ *
+ * User indicates this by setting the property with key <code>Output</code>
+ * to the literal <code>out</code> or <code>err</code>.<p>
+ *
+ * By default or if the set value is not allowed, <code>System.err</code>
+ * will be used.
+ */
+ static PrintWriter getOutput() {
+ PrintWriter pw;
+ String name = getProperty("Output");
+ if (name != null && name.equals("out"))
+ pw = new PrintWriter(System.out, true);
+ else
+ pw = new PrintWriter(System.err, true);
+ return pw;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/TruncatedRandomStream.java b/source/umontreal/iro/lecuyer/rng/TruncatedRandomStream.java
new file mode 100644
index 0000000..6d414a2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/TruncatedRandomStream.java
@@ -0,0 +1,96 @@
+
+
+/*
+ * Class: TruncatedRandomStream
+ * Description: container random stream generating numbers in an interval
+ (a,b) instead of in (0,1)
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+/**
+ * Represents a container random stream generating numbers in an interval
+ * <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>)</SPAN> instead of in <SPAN CLASS="MATH">(0, 1)</SPAN>, where
+ * <SPAN CLASS="MATH">0 <= <I>a</I> < <I>b</I> <= 1</SPAN>,
+ * by using the contained stream.
+ * If <TT>nextDouble</TT> returns <SPAN CLASS="MATH"><I>u</I></SPAN> for the contained stream,
+ * it will return
+ * <SPAN CLASS="MATH"><I>v</I> = <I>a</I> + (<I>b</I> - <I>a</I>)<I>u</I></SPAN>, which is uniform over <SPAN CLASS="MATH">(<I>a</I>, <I>b</I>)</SPAN>,
+ * for the truncated stream.
+ * The method <TT>nextInt</TT> returns the integer that corresponds to <SPAN CLASS="MATH"><I>v</I></SPAN>
+ * (by inversion); this integer is no longer uniformly distributed in general.
+ *
+ */
+public class TruncatedRandomStream implements RandomStream {
+ private RandomStream stream;
+ private double a;
+ private double bminusa;
+
+
+
+ public TruncatedRandomStream (RandomStream stream, double a, double b) {
+ if (stream == null)
+ throw new NullPointerException ("The given stream must not be null");
+ if (a >= b)
+ throw new IllegalArgumentException ("a must be smaller than b");
+ if (a < 0 || b < 0 || a > 1 || b > 1)
+ throw new IllegalArgumentException ("a and b must be in [0, 1]");
+ this.stream = stream;
+ this.a = a;
+ bminusa = b - a;
+ }
+
+ public void resetStartStream () {
+ stream.resetStartStream ();
+ }
+
+ public void resetStartSubstream () {
+ stream.resetStartSubstream ();
+ }
+
+ public void resetNextSubstream () {
+ stream.resetNextSubstream ();
+ }
+
+ public double nextDouble () {
+ double v = stream.nextDouble ();
+ return a + v * bminusa;
+ }
+
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ stream.nextArrayOfDouble (u, start, n);
+ for (int i = start; i < start + n; i++)
+ u[i] = a + u[i] * bminusa;
+ }
+
+ public int nextInt (int i, int j) {
+ return i + (int) (nextDouble () * (j - i + 1));
+ }
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ for (int x = start; x < start + n; x++)
+ u[x] = nextInt (i, j);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/TruncatedRandomStream.tex b/source/umontreal/iro/lecuyer/rng/TruncatedRandomStream.tex
new file mode 100644
index 0000000..353b7dc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/TruncatedRandomStream.tex
@@ -0,0 +1,101 @@
+\defmodule{TruncatedRandomStream}
+
+Represents a container random stream generating numbers in an interval
+$(a,b)$ instead of in $(0,1)$, where $0\le a < b \le 1$,
+by using the contained stream.
+If \texttt{nextDouble} returns $u$ for the contained stream,
+it will return $v = a + (b-a)u$, which is uniform over $(a,b)$,
+for the truncated stream.
+The method \texttt{nextInt} returns the integer that corresponds to $v$
+(by inversion); this integer is no longer uniformly distributed in general.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: TruncatedRandomStream
+ * Description: container random stream generating numbers in an interval
+ (a,b) instead of in (0,1)
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng;\begin{hide}
+
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+public class TruncatedRandomStream implements RandomStream\begin{hide} {
+ private RandomStream stream;
+ private double a;
+ private double bminusa;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public TruncatedRandomStream (RandomStream stream, double a, double b)\begin{hide} {
+ if (stream == null)
+ throw new NullPointerException ("The given stream must not be null");
+ if (a >= b)
+ throw new IllegalArgumentException ("a must be smaller than b");
+ if (a < 0 || b < 0 || a > 1 || b > 1)
+ throw new IllegalArgumentException ("a and b must be in [0, 1]");
+ this.stream = stream;
+ this.a = a;
+ bminusa = b - a;
+ }
+
+ public void resetStartStream () {
+ stream.resetStartStream ();
+ }
+
+ public void resetStartSubstream () {
+ stream.resetStartSubstream ();
+ }
+
+ public void resetNextSubstream () {
+ stream.resetNextSubstream ();
+ }
+
+ public double nextDouble () {
+ double v = stream.nextDouble ();
+ return a + v * bminusa;
+ }
+
+ public void nextArrayOfDouble (double[] u, int start, int n) {
+ stream.nextArrayOfDouble (u, start, n);
+ for (int i = start; i < start + n; i++)
+ u[i] = a + u[i] * bminusa;
+ }
+
+ public int nextInt (int i, int j) {
+ return i + (int) (nextDouble () * (j - i + 1));
+ }
+
+ public void nextArrayOfInt (int i, int j, int[] u, int start, int n) {
+ for (int x = start; x < start + n; x++)
+ u[x] = nextInt (i, j);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/WELL1024.java b/source/umontreal/iro/lecuyer/rng/WELL1024.java
new file mode 100644
index 0000000..fd60c2d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/WELL1024.java
@@ -0,0 +1,325 @@
+
+
+/*
+ * Class: WELL1024
+ * Description: a Well Equidistributed Long period Linear Random Number
+ Generator with a state size of 1024 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.io.Serializable;
+
+
+/**
+ * Implements the {@link RandomStream} interface via inheritance from
+ * {@link RandomStreamBase}. The backbone generator is a Well Equidistributed
+ * Long period Linear Random Number Generator (WELL), proposed by F.
+ * Panneton, and which has a state size
+ * of 1024 bits and a period length of approximatively
+ * <SPAN CLASS="MATH">2<SUP>1024</SUP></SPAN>. The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>300</SUP></SPAN>,
+ * <SPAN CLASS="MATH">2<SUP>400</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>700</SUP></SPAN> respectively (see {@link RandomStream} for their
+ * definition). The seed of the RNG, and the state of a stream at any given
+ * step, is a 16-dimensional vector of 32-bit integers.
+ * The output of <TT>nextValue</TT> has 32 bits of precision.
+ *
+ */
+public class WELL1024 extends RandomStreamBase {
+
+ private static final long serialVersionUID = 120307L;
+ //La date de modification a l'envers, lire 12/03/07
+
+ private static final double NORM = 2.32830643653869628906e-10;
+
+ private static final int MASK = 0x1F;
+
+ private static final int W = 32;
+ private static final int R = 32;
+ private static final int P = 0;
+ private static final int M1 = 3;
+ private static final int M2 = 24;
+ private static final int M3 = 10;
+
+ private static final int A1 = 0xDB79FB31;
+ private static final int B1 = 0x0FDAA2C1;
+ private static final int B2 = 0x27C5A58D;
+ private static final int C1 = 0x71E993FE;
+
+ private int state_i = 0;
+ private int[] state;
+
+ //stream and substream variables
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream =
+ new int[] {0xDE410B75, 0x904FA5C7, 0x8BD4701E, 0x011EA361,
+ 0x6EB189E0, 0x7A2B0CE1, 0xE02631CA, 0x72EBA132,
+ 0x5189DA0F, 0x3EB72A2C, 0x51ABE513, 0x6D9EA57C,
+ 0x4D690BF1, 0x84217FCA, 0x7290DE1A, 0x429F5A48,
+ 0x6EC42EF3, 0x960AB315, 0x72C3A743, 0x48E13BF1,
+ 0x8917EAC8, 0x284AE026, 0x357BF240, 0x913B51AC,
+ 0x136AF195, 0x361ABC18, 0x731AB725, 0x63D3A7C9,
+ 0xE5F32A18, 0x91A8E164, 0x04EA61B5, 0xC72A6091};
+
+
+ // P(z) = {0x00000001, 0x00000000, 0x00000000, 0x00000000,
+ // 0x028a0008, 0x02288020, 0x2baaa20a, 0x0209aa00,
+ // 0x3f871248, 0x80172a7b, 0xee101d14, 0xef2221f3,
+ // 0xb5bf7be1, 0xab57e80c, 0xfa24ee53, 0x37dab9aa,
+ // 0xd353180b, 0xf1c5d9ed, 0xd6465866, 0x7a048625,
+ // 0x892b7ef6, 0x2ca9170f, 0xa8a3f324, 0x36be065f,
+ // 0x57aee2ab, 0xb20f4dd9, 0xa0eaa2ee, 0xa678c37a,
+ // 0x5792d2ae, 0xac449456, 0x51549f89, 0x0, 0x1}
+
+ // Ce tableau represente les 1024 coefficients du polynome suivant
+ // (z^(2^400) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pw = new int[]
+ { 0xe44294e, 0xef237eff, 0x5e8b6bfb, 0xa724e67a,
+ 0x59994cfd, 0x6f7c3de1, 0x6735d50d, 0x4bfe199a,
+ 0x39c28e61, 0xfd075266, 0x96cc6d1f, 0x5dc1a685,
+ 0xd67fa444, 0xccc01b86, 0x8ff861c, 0xce113725,
+ 0x66707603, 0x38abb0fd, 0x7681f64, 0x104535c5,
+ 0xce4ae5f4, 0x50e37105, 0xd0c5f77f, 0x74c1ebf6,
+ 0x2ccf1505, 0xd1f21b86, 0x9a6c402e, 0xea34a31c,
+ 0x65e13d13, 0xde8f2f05, 0x89db804f, 0x8dc387f2};
+
+ // Ce tableau represente les 1024 coefficients du polynome suivant
+ // (z^(2^700) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pz = new int[]
+ {0x7cab7da4, 0xef28b275, 0x18ffa66a, 0x2aa41e52,
+ 0x15b6bd86, 0x560d0d76, 0xcdeda011, 0x96231727,
+ 0xeec6a7f2, 0x99fd2be6, 0x92afa886, 0xcca777f0,
+ 0x972eff38, 0xa29f8e49, 0x22b4b9b6, 0x1089c898,
+ 0x6d569b25, 0x879044c2, 0x5e41b523, 0x33f19dd6,
+ 0x7c005fc5, 0x7f9a1907, 0x39bf9eed, 0x4bd86a74,
+ 0xe1e47e3, 0x96ead7ac, 0xc834f9ee, 0xd9ff4a4f,
+ 0x717f044c, 0xfd0e15e6, 0x6c18ef3, 0xbfdd2942};
+
+
+
+ private void advanceSeed(int[] seed, int [] p) {
+ int b;
+ int [] x = new int[R];
+
+ for (int i = 0; i < R; i++) {
+ state[i] = seed[i];
+ }
+ state_i = 0;
+
+ for (int j = 0; j < R; ++j) {
+ b = p[j];
+ for (int k = 0; k < W; ++k) {
+ if ((b & 1) == 1) {
+ for (int i = 0; i < R; i++) {
+ x[i] ^= state[(state_i + i) & MASK];
+ }
+ }
+ b >>= 1;
+ nextValue();
+ }
+ }
+
+ for (int i = 0; i < R; i++) {
+ seed[i] = x[i];
+ }
+ }
+
+ private static void verifySeed(int[] seed) {
+ if (seed.length < R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ " values");
+ for(int i = 0; i < R; i++)
+ if (seed[i] != 0)
+ return;
+ throw new IllegalArgumentException
+ ("At least one of the element of the seed must not be 0.");
+ }
+
+ private WELL1024(int i) {
+ //unit vector (to build the state transition matrices)
+ state = new int[R];
+ for(int j = 0; j < R; j++)
+ state[j] = 0;
+ state[i / W] = 1 << (i % W);
+ state_i = 0;
+ }
+
+
+ /**
+ * Constructs a new stream.
+ *
+ */
+ public WELL1024() {
+ state = new int[R];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+ advanceSeed(curr_stream, pz);
+ resetStartStream();
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used in the <TT>toString</TT> method).
+ *
+ * @param name name of the stream
+ *
+ */
+ public WELL1024 (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed of this class to the 32
+ * integers of array <TT>seed[0..31]</TT>.
+ * This will be the initial seed of the class and of the next created stream.
+ * At least one of the integers must be non-zero.
+ *
+ * @param seed array of 32 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (int seed[]) {
+ verifySeed (seed);
+ for(int i = 0 ; i < R; i++)
+ curr_stream[i] = seed[i];
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * seed <TT>seed[0..31]</TT>. The seed must satisfy the same
+ * conditions as in <TT>setPackageSeed</TT>.
+ * This method only affects the specified stream; the others are not
+ * modified. Hence after calling this method, the beginning of the streams
+ * will no longer be spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very exceptional cases;
+ * proper use of the <TT>reset...</TT> methods and of the stream constructor is
+ * preferable.
+ *
+ * @param seed array of 32 elements representing the seed
+ *
+ *
+ */
+ public void setSeed (int seed[]) {
+ verifySeed (seed);
+ for(int i = 0 ; i < R; i ++)
+ stream[i] = seed[i];
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the stream, represented as an
+ * array of 32 integers.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public int[] getState() {
+ int[] result = new int[R];
+ for(int i = 0 ; i < R; i ++)
+ result[i] = state[(state_i + i) & MASK];
+ return result;
+ }
+
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, pw);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ if(name == null)
+ sb.append("The state of this WELL1024 is : {");
+ else
+ sb.append("The state of " + name + " is : {");
+ for(int i = 0; i < R - 1; i++)
+ sb.append(state[(state_i + i) & MASK] + ", ");
+ sb.append(state[(state_i + R - 1) & MASK] + "}");
+
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ int z0, z1, z2;
+
+ z0 = state[(state_i + 31) & MASK];
+ z1 = state[state_i] ^ (state[(state_i + M1) & MASK] ^
+ (state[(state_i + M1) & MASK] >>> 8));
+ z2 = (state[(state_i + M2) & MASK] ^
+ (state[(state_i + M2) & MASK] << 19)) ^
+ (state[(state_i + M3) & MASK] ^
+ (state[(state_i + M3) & MASK] << 14));
+ state[state_i] = z1 ^ z2;
+ state[(state_i + 31) & MASK] = (z0 ^ (z0 << 11)) ^
+ (z1 ^ (z1 << 7)) ^ (z2 ^ (z2 << 13));
+ state_i = (state_i + 31) & MASK;
+
+ long result = state[state_i];
+
+ return ((double) (result > 0 ? result : result + 0x100000000L) * NORM);
+ }
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public WELL1024 clone() {
+ WELL1024 retour = null;
+ retour = (WELL1024)super.clone();
+ retour.state = new int[R];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+ for (int i = 0; i<R; i++) {
+ retour.state[i] = state[i];
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/WELL1024.tex b/source/umontreal/iro/lecuyer/rng/WELL1024.tex
new file mode 100644
index 0000000..2fc37d8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/WELL1024.tex
@@ -0,0 +1,338 @@
+\defmodule {WELL1024}
+
+Implements the \class{RandomStream} interface via inheritance from
+\class{RandomStreamBase}. The backbone generator is a Well Equidistributed
+Long period Linear Random Number Generator (WELL), proposed by F.
+ Panneton\html{,}\latex{ in \cite{rPAN06b,rPAN04t},} and which has a state size
+of 1024 bits and a period length of \html{approximatively}
+\latex{$\rho\approx$} $2^{1024}$. The values of $V$, $W$ and $Z$ are $2^{300}$,
+$2^{400}$ and $2^{700}$ respectively (see \class{RandomStream} for their
+definition). The seed of the RNG, and the state of a stream at any given
+step, is a 16-dimensional vector of 32-bit integers.
+The output of \texttt{nextValue} has 32 bits of precision.
+
+% This implementation requires the use of about
+% 250K of memory to run. This memory is shared between all instances of the
+% class, and is only loaded when the first instance is created.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: WELL1024
+ * Description: a Well Equidistributed Long period Linear Random Number
+ Generator with a state size of 1024 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import java.io.Serializable;
+\end{hide}
+
+public class WELL1024 extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 120307L;
+ //La date de modification a l'envers, lire 12/03/07
+
+ private static final double NORM = 2.32830643653869628906e-10;
+
+ private static final int MASK = 0x1F;
+
+ private static final int W = 32;
+ private static final int R = 32;
+ private static final int P = 0;
+ private static final int M1 = 3;
+ private static final int M2 = 24;
+ private static final int M3 = 10;
+
+ private static final int A1 = 0xDB79FB31;
+ private static final int B1 = 0x0FDAA2C1;
+ private static final int B2 = 0x27C5A58D;
+ private static final int C1 = 0x71E993FE;
+
+ private int state_i = 0;
+ private int[] state;
+
+ //stream and substream variables
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream =
+ new int[] {0xDE410B75, 0x904FA5C7, 0x8BD4701E, 0x011EA361,
+ 0x6EB189E0, 0x7A2B0CE1, 0xE02631CA, 0x72EBA132,
+ 0x5189DA0F, 0x3EB72A2C, 0x51ABE513, 0x6D9EA57C,
+ 0x4D690BF1, 0x84217FCA, 0x7290DE1A, 0x429F5A48,
+ 0x6EC42EF3, 0x960AB315, 0x72C3A743, 0x48E13BF1,
+ 0x8917EAC8, 0x284AE026, 0x357BF240, 0x913B51AC,
+ 0x136AF195, 0x361ABC18, 0x731AB725, 0x63D3A7C9,
+ 0xE5F32A18, 0x91A8E164, 0x04EA61B5, 0xC72A6091};
+
+
+ // P(z) = {0x00000001, 0x00000000, 0x00000000, 0x00000000,
+ // 0x028a0008, 0x02288020, 0x2baaa20a, 0x0209aa00,
+ // 0x3f871248, 0x80172a7b, 0xee101d14, 0xef2221f3,
+ // 0xb5bf7be1, 0xab57e80c, 0xfa24ee53, 0x37dab9aa,
+ // 0xd353180b, 0xf1c5d9ed, 0xd6465866, 0x7a048625,
+ // 0x892b7ef6, 0x2ca9170f, 0xa8a3f324, 0x36be065f,
+ // 0x57aee2ab, 0xb20f4dd9, 0xa0eaa2ee, 0xa678c37a,
+ // 0x5792d2ae, 0xac449456, 0x51549f89, 0x0, 0x1}
+
+ // Ce tableau represente les 1024 coefficients du polynome suivant
+ // (z^(2^400) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pw = new int[]
+ { 0xe44294e, 0xef237eff, 0x5e8b6bfb, 0xa724e67a,
+ 0x59994cfd, 0x6f7c3de1, 0x6735d50d, 0x4bfe199a,
+ 0x39c28e61, 0xfd075266, 0x96cc6d1f, 0x5dc1a685,
+ 0xd67fa444, 0xccc01b86, 0x8ff861c, 0xce113725,
+ 0x66707603, 0x38abb0fd, 0x7681f64, 0x104535c5,
+ 0xce4ae5f4, 0x50e37105, 0xd0c5f77f, 0x74c1ebf6,
+ 0x2ccf1505, 0xd1f21b86, 0x9a6c402e, 0xea34a31c,
+ 0x65e13d13, 0xde8f2f05, 0x89db804f, 0x8dc387f2};
+
+ // Ce tableau represente les 1024 coefficients du polynome suivant
+ // (z^(2^700) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pz = new int[]
+ {0x7cab7da4, 0xef28b275, 0x18ffa66a, 0x2aa41e52,
+ 0x15b6bd86, 0x560d0d76, 0xcdeda011, 0x96231727,
+ 0xeec6a7f2, 0x99fd2be6, 0x92afa886, 0xcca777f0,
+ 0x972eff38, 0xa29f8e49, 0x22b4b9b6, 0x1089c898,
+ 0x6d569b25, 0x879044c2, 0x5e41b523, 0x33f19dd6,
+ 0x7c005fc5, 0x7f9a1907, 0x39bf9eed, 0x4bd86a74,
+ 0xe1e47e3, 0x96ead7ac, 0xc834f9ee, 0xd9ff4a4f,
+ 0x717f044c, 0xfd0e15e6, 0x6c18ef3, 0xbfdd2942};
+
+
+
+ private void advanceSeed(int[] seed, int [] p) {
+ int b;
+ int [] x = new int[R];
+
+ for (int i = 0; i < R; i++) {
+ state[i] = seed[i];
+ }
+ state_i = 0;
+
+ for (int j = 0; j < R; ++j) {
+ b = p[j];
+ for (int k = 0; k < W; ++k) {
+ if ((b & 1) == 1) {
+ for (int i = 0; i < R; i++) {
+ x[i] ^= state[(state_i + i) & MASK];
+ }
+ }
+ b >>= 1;
+ nextValue();
+ }
+ }
+
+ for (int i = 0; i < R; i++) {
+ seed[i] = x[i];
+ }
+ }
+
+ private static void verifySeed(int[] seed) {
+ if (seed.length < R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ " values");
+ for(int i = 0; i < R; i++)
+ if (seed[i] != 0)
+ return;
+ throw new IllegalArgumentException
+ ("At least one of the element of the seed must not be 0.");
+ }
+
+ private WELL1024(int i) {
+ //unit vector (to build the state transition matrices)
+ state = new int[R];
+ for(int j = 0; j < R; j++)
+ state[j] = 0;
+ state[i / W] = 1 << (i % W);
+ state_i = 0;
+ }
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public WELL1024() \begin{hide} {
+ state = new int[R];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+ advanceSeed(curr_stream, pz);
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream.
+\end{tabb}
+\begin{code}
+
+ public WELL1024 (String name) \begin{hide} {
+ this();
+ this.name = name;
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used in the \texttt{toString} method).
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (int seed[]) \begin{hide} {
+ verifySeed (seed);
+ for(int i = 0 ; i < R; i++)
+ curr_stream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed of this class to the 32
+ integers of array \texttt{seed[0..31]}.
+ This will be the initial seed of the class and of the next created stream.
+ At least one of the integers must be non-zero.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 32 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (int seed[]) \begin{hide} {
+ verifySeed (seed);
+ for(int i = 0 ; i < R; i ++)
+ stream[i] = seed[i];
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ seed \texttt{seed[0..31]}. The seed must satisfy the same
+ conditions as in \texttt{setPackageSeed}.
+ This method only affects the specified stream; the others are not
+ modified. Hence after calling this method, the beginning of the streams
+ will no longer be spaced $Z$ values apart.
+ For this reason, this method should only be used in very exceptional cases;
+ proper use of the \texttt{reset...} methods and of the stream constructor is
+ preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 32 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getState() \begin{hide} {
+ int[] result = new int[R];
+ for(int i = 0 ; i < R; i ++)
+ result[i] = state[(state_i + i) & MASK];
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the stream, represented as an
+ array of 32 integers.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}\begin{hide}
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, pw);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ if(name == null)
+ sb.append("The state of this WELL1024 is : {");
+ else
+ sb.append("The state of " + name + " is : {");
+ for(int i = 0; i < R - 1; i++)
+ sb.append(state[(state_i + i) & MASK] + ", ");
+ sb.append(state[(state_i + R - 1) & MASK] + "}");
+
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ int z0, z1, z2;
+
+ z0 = state[(state_i + 31) & MASK];
+ z1 = state[state_i] ^ (state[(state_i + M1) & MASK] ^
+ (state[(state_i + M1) & MASK] >>> 8));
+ z2 = (state[(state_i + M2) & MASK] ^
+ (state[(state_i + M2) & MASK] << 19)) ^
+ (state[(state_i + M3) & MASK] ^
+ (state[(state_i + M3) & MASK] << 14));
+ state[state_i] = z1 ^ z2;
+ state[(state_i + 31) & MASK] = (z0 ^ (z0 << 11)) ^
+ (z1 ^ (z1 << 7)) ^ (z2 ^ (z2 << 13));
+ state_i = (state_i + 31) & MASK;
+
+ long result = state[state_i];
+
+ return ((double) (result > 0 ? result : result + 0x100000000L) * NORM);
+ }\end{hide}
+
+ public WELL1024 clone() \begin{hide} {
+ WELL1024 retour = null;
+ retour = (WELL1024)super.clone();
+ retour.state = new int[R];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+ for (int i = 0; i<R; i++) {
+ retour.state[i] = state[i];
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/WELL512.java b/source/umontreal/iro/lecuyer/rng/WELL512.java
new file mode 100644
index 0000000..7857a7b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/WELL512.java
@@ -0,0 +1,304 @@
+
+
+/*
+ * Class: WELL512
+ * Description: a Well Equidistributed Long period Linear Random Number
+ Generator with a state size of 512 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.io.Serializable;
+
+
+/**
+ * This class implements the {@link RandomStream} interface via inheritance from
+ * {@link RandomStreamBase}. The backbone generator is a Well Equidistributed
+ * Long period Linear Random Number Generator (WELL), proposed by F.
+ * Panneton, and which has a state size
+ * of 512 bits and a period length of approximatively
+ * <SPAN CLASS="MATH">2<SUP>512</SUP></SPAN>. The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>150</SUP></SPAN>,
+ * <SPAN CLASS="MATH">2<SUP>200</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>350</SUP></SPAN> respectively (see {@link RandomStream} for their
+ * definition). The seed of the RNG, and the state of a stream at any given
+ * step, is a 16-dimensional vector of 32-bit integers.
+ *
+ */
+public class WELL512 extends RandomStreamBase {
+
+ private static final long serialVersionUID = 120307L;
+ //La date de modification a l'envers, lire 07/03/2012
+
+ private static final double NORM = (1.0 / 0x100000001L);
+
+ private static final int W = 32;
+ private static final int R = 16;
+ private static final int P = 0;
+ private static final int M1 = 13;
+ private static final int M2 = 9;
+ private static final int M3 = 5;
+ private static final int MASK = 0xF; // = 15
+
+ //state variables
+ private int state_i;
+ private int[] state;
+
+ //stream and substream variables :
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream = new int[]
+ {0xA341BF9A, 0xAFE4901B, 0x6B10DE18, 0x05FE1420,
+ 0xE48B1A9C, 0x590AE15E, 0xC5EB82A7, 0x37EAB2F9,
+ 0x90E1C6EA, 0x3AE63902, 0x735DC91C, 0x902E3A8C,
+ 0x6CB28A5D, 0x8474E7D1, 0x843E01A3, 0x5A7370EF};
+
+ // P(z) = {0xa7600001, 0xe0f4f3e2, 0xcb30e185, 0x7d6b79a9,
+ // 0xf3d46237, 0x13a524cb, 0x38e3c2d2, 0xa1381bcb,
+ // 0xf7ab5f06, 0x04a72cda, 0x4e302521, 0xaca072f1,
+ // 0x4dd96181, 0x24aa25c9, 0x3c417e7, 0x0, 0x1}
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^200) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pw = new int[]
+ {0x280009a9, 0x31e221d0, 0xa00c0296, 0x763d492b,
+ 0x63875b75, 0xef2acc3a, 0x1400839f, 0x5e0c8526,
+ 0x514e11b, 0x56b398e4, 0x9436c8b9, 0xa6d8130b,
+ 0xc0a48a78, 0x26ad57d0, 0xa3a0c62a, 0x3ff16c9b};
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^350) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pz = new int[]
+ {0xcd68f2fe, 0x183e969a, 0x760449ae, 0xaa0ce54e,
+ 0xfb5363af, 0x79deea9b, 0xef66c516, 0x103543cb,
+ 0x244d1a97, 0x7570bc91, 0x31203fc7, 0x455ea2ca,
+ 0xd77d327d, 0xd8c6a83c, 0xc51b05e7, 0x300c1501};
+
+
+ private void advanceSeed(int[] seed, int [] p) {
+ int b;
+ int [] x = new int[R];
+
+ for (int i = 0; i < R; i++) {
+ state[i] = seed[i];
+ }
+ state_i = 0;
+
+ for (int j = 0; j < R; ++j) {
+ b = p[j];
+ for (int k = 0; k < W; ++k) {
+ if ((b & 1) == 1) {
+ for (int i = 0; i < R; i++) {
+ x[i] ^= state[(state_i + i) & MASK];
+ }
+ }
+ b >>= 1;
+
+ nextValue();
+ }
+ }
+
+ for (int i = 0; i < R; i++) {
+ seed[i] = x[i];
+ }
+ }
+
+
+ private static void verifySeed(int[] seed) {
+ if (seed.length < R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ "values");
+ for (int i = 0; i < R; i++)
+ if (seed[i] != 0)
+ return;
+ throw new IllegalArgumentException
+ ("At least one of the element of the seed must not be 0.");
+ }
+
+ private WELL512(int i) {
+ //unit vector (to build the state transition matrice)
+ state = new int[R];
+ for(int j = 0; j < R; j++)
+ state[j] = 0;
+ state[i / W] = 1 << (i % W);
+ state_i = 0;
+ }
+
+
+ /**
+ * Constructs a new stream.
+ *
+ */
+ public WELL512() {
+ state = new int[R];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+ advanceSeed(curr_stream, pz);
+ resetStartStream();
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used in the <TT>toString</TT> method).
+ *
+ * @param name name of the stream
+ *
+ */
+ public WELL512 (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed of the class <TT>WELL512</TT> to the 16
+ * integers of the vector <TT>seed[0..15]</TT>.
+ * This will be the initial seed of the class of the next created stream.
+ * At least one of the integers must be non-zero.
+ *
+ * @param seed array of 16 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (int seed[]) {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ curr_stream[i] = seed[i];
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * seed <TT>seed[0..15]</TT>. The seed must satisfy the same
+ * conditions as in <TT>setPackageSeed</TT>.
+ * This method only affects the specified stream; the others are not
+ * modified. Hence after calling this method, the beginning of the streams
+ * will no longer be spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very exceptional cases;
+ * proper use of the <TT>reset...</TT> methods and of the stream constructor is
+ * preferable.
+ *
+ * @param seed array of 16 elements representing the seed
+ *
+ *
+ */
+ public void setSeed (int seed[]) {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the stream, represented as an
+ * array of 16 integers.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public int[] getState() {
+ int[] result = new int[R];
+ for(int i = 0; i < R; i++)
+ result[i] = state[(state_i + i) & MASK];
+ return result;
+ }
+
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public WELL512 clone() {
+ WELL512 retour = null;
+
+ retour = (WELL512)super.clone();
+ retour.state = new int[R];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+
+ for (int i = 0; i<R; i++) {
+ retour.state[i] = state[i];
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, pw);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ if(name == null)
+ sb.append("The state of this WELL512 is : {");
+ else
+ sb.append("The state of " + name + " is : {");
+ for(int i = 0; i < R - 1; i++)
+ sb.append(state[(state_i + i) & MASK] + ", ");
+ sb.append(state[(state_i + R - 1) & MASK] + "}");
+
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ int z0, z1, z2;
+ z0 = state[(state_i + 15) & MASK];
+ z1 = (state[state_i] ^ (state[state_i] << 16)) ^
+ (state[(state_i+M1) & MASK] ^ (state[(state_i+M1) & MASK] << 15));
+ z2 = (state[(state_i+M2) & MASK] ^
+ (state[(state_i+M2) & MASK] >>> 11));
+ state[state_i] = z1 ^ z2;
+ state[(state_i + 15) & MASK] = (z0 ^ (z0 << 2)) ^ (z1 ^ (z1 << 18)) ^
+ (z2 << 28) ^ (state[state_i] ^
+ ((state[state_i] << 5) & 0xDA442D24));
+ state_i = (state_i + 15) & MASK;
+
+ long result = state[state_i];
+
+ return (double)(result > 0 ? result : (result + 0x100000000L)) * NORM;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/WELL512.tex b/source/umontreal/iro/lecuyer/rng/WELL512.tex
new file mode 100644
index 0000000..f46042b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/WELL512.tex
@@ -0,0 +1,318 @@
+\defmodule {WELL512}
+
+This class implements the \class{RandomStream} interface via inheritance from
+\class{RandomStreamBase}. The backbone generator is a Well Equidistributed
+Long period Linear Random Number Generator (WELL), proposed by F.
+ Panneton\html{,}\latex{ in \cite{rPAN06b,rPAN04t},} and which has a state size
+of 512 bits and a period length of \html{approximatively}
+\latex{$\rho\approx$} $2^{512}$. The values of $V$, $W$ and $Z$ are $2^{150}$,
+$2^{200}$ and $2^{350}$ respectively (see \class{RandomStream} for their
+definition). The seed of the RNG, and the state of a stream at any given
+step, is a 16-dimensional vector of 32-bit integers.
+
+% This implementation requires about
+% 70K of memory to run. This memory is shared between all instances of the
+% class, and is only loaded when the first instance is created.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: WELL512
+ * Description: a Well Equidistributed Long period Linear Random Number
+ Generator with a state size of 512 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import java.io.Serializable;
+\end{hide}
+
+public class WELL512 extends RandomStreamBase \begin{hide} {
+
+ private static final long serialVersionUID = 120307L;
+ //La date de modification a l'envers, lire 07/03/2012
+
+ private static final double NORM = (1.0 / 0x100000001L);
+
+ private static final int W = 32;
+ private static final int R = 16;
+ private static final int P = 0;
+ private static final int M1 = 13;
+ private static final int M2 = 9;
+ private static final int M3 = 5;
+ private static final int MASK = 0xF; // = 15
+
+ //state variables
+ private int state_i;
+ private int[] state;
+
+ //stream and substream variables :
+ private int[] stream;
+ private int[] substream;
+ private static int[] curr_stream = new int[]
+ {0xA341BF9A, 0xAFE4901B, 0x6B10DE18, 0x05FE1420,
+ 0xE48B1A9C, 0x590AE15E, 0xC5EB82A7, 0x37EAB2F9,
+ 0x90E1C6EA, 0x3AE63902, 0x735DC91C, 0x902E3A8C,
+ 0x6CB28A5D, 0x8474E7D1, 0x843E01A3, 0x5A7370EF};
+
+ // P(z) = {0xa7600001, 0xe0f4f3e2, 0xcb30e185, 0x7d6b79a9,
+ // 0xf3d46237, 0x13a524cb, 0x38e3c2d2, 0xa1381bcb,
+ // 0xf7ab5f06, 0x04a72cda, 0x4e302521, 0xaca072f1,
+ // 0x4dd96181, 0x24aa25c9, 0x3c417e7, 0x0, 0x1}
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^200) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pw = new int[]
+ {0x280009a9, 0x31e221d0, 0xa00c0296, 0x763d492b,
+ 0x63875b75, 0xef2acc3a, 0x1400839f, 0x5e0c8526,
+ 0x514e11b, 0x56b398e4, 0x9436c8b9, 0xa6d8130b,
+ 0xc0a48a78, 0x26ad57d0, 0xa3a0c62a, 0x3ff16c9b};
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^350) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ private static final int [] pz = new int[]
+ {0xcd68f2fe, 0x183e969a, 0x760449ae, 0xaa0ce54e,
+ 0xfb5363af, 0x79deea9b, 0xef66c516, 0x103543cb,
+ 0x244d1a97, 0x7570bc91, 0x31203fc7, 0x455ea2ca,
+ 0xd77d327d, 0xd8c6a83c, 0xc51b05e7, 0x300c1501};
+
+
+ private void advanceSeed(int[] seed, int [] p) {
+ int b;
+ int [] x = new int[R];
+
+ for (int i = 0; i < R; i++) {
+ state[i] = seed[i];
+ }
+ state_i = 0;
+
+ for (int j = 0; j < R; ++j) {
+ b = p[j];
+ for (int k = 0; k < W; ++k) {
+ if ((b & 1) == 1) {
+ for (int i = 0; i < R; i++) {
+ x[i] ^= state[(state_i + i) & MASK];
+ }
+ }
+ b >>= 1;
+
+ nextValue();
+ }
+ }
+
+ for (int i = 0; i < R; i++) {
+ seed[i] = x[i];
+ }
+ }
+
+
+ private static void verifySeed(int[] seed) {
+ if (seed.length < R)
+ throw new IllegalArgumentException("Seed must contain " + R +
+ "values");
+ for (int i = 0; i < R; i++)
+ if (seed[i] != 0)
+ return;
+ throw new IllegalArgumentException
+ ("At least one of the element of the seed must not be 0.");
+ }
+
+ private WELL512(int i) {
+ //unit vector (to build the state transition matrice)
+ state = new int[R];
+ for(int j = 0; j < R; j++)
+ state[j] = 0;
+ state[i / W] = 1 << (i % W);
+ state_i = 0;
+ }
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public WELL512() \begin{hide} {
+ state = new int[R];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+ advanceSeed(curr_stream, pz);
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream.
+\end{tabb}
+\begin{code}
+
+ public WELL512 (String name) \begin{hide} {
+ this();
+ this.name = name;
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used in the \texttt{toString} method).
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (int seed[]) \begin{hide} {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ curr_stream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed of the class \texttt{WELL512} to the 16
+ integers of the vector \texttt{seed[0..15]}.
+ This will be the initial seed of the class of the next created stream.
+ At least one of the integers must be non-zero.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 16 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (int seed[]) \begin{hide} {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ seed \texttt{seed[0..15]}. The seed must satisfy the same
+ conditions as in \texttt{setPackageSeed}.
+ This method only affects the specified stream; the others are not
+ modified. Hence after calling this method, the beginning of the streams
+ will no longer be spaced $Z$ values apart.
+ For this reason, this method should only be used in very exceptional cases;
+ proper use of the \texttt{reset...} methods and of the stream constructor is
+ preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 16 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getState() \begin{hide} {
+ int[] result = new int[R];
+ for(int i = 0; i < R; i++)
+ result[i] = state[(state_i + i) & MASK];
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the stream, represented as an
+ array of 16 integers.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}
+
+ public WELL512 clone() \begin{hide} {
+ WELL512 retour = null;
+
+ retour = (WELL512)super.clone();
+ retour.state = new int[R];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+
+ for (int i = 0; i<R; i++) {
+ retour.state[i] = state[i];
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+ \begin{hide}
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i = 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, pw);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ if(name == null)
+ sb.append("The state of this WELL512 is : {");
+ else
+ sb.append("The state of " + name + " is : {");
+ for(int i = 0; i < R - 1; i++)
+ sb.append(state[(state_i + i) & MASK] + ", ");
+ sb.append(state[(state_i + R - 1) & MASK] + "}");
+
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ int z0, z1, z2;
+ z0 = state[(state_i + 15) & MASK];
+ z1 = (state[state_i] ^ (state[state_i] << 16)) ^
+ (state[(state_i+M1) & MASK] ^ (state[(state_i+M1) & MASK] << 15));
+ z2 = (state[(state_i+M2) & MASK] ^
+ (state[(state_i+M2) & MASK] >>> 11));
+ state[state_i] = z1 ^ z2;
+ state[(state_i + 15) & MASK] = (z0 ^ (z0 << 2)) ^ (z1 ^ (z1 << 18)) ^
+ (z2 << 28) ^ (state[state_i] ^
+ ((state[state_i] << 5) & 0xDA442D24));
+ state_i = (state_i + 15) & MASK;
+
+ long result = state[state_i];
+
+ return (double)(result > 0 ? result : (result + 0x100000000L)) * NORM;
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/WELL607.java b/source/umontreal/iro/lecuyer/rng/WELL607.java
new file mode 100644
index 0000000..70920e4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/WELL607.java
@@ -0,0 +1,229 @@
+
+
+/*
+ * Class: WELL607
+ * Description: a Well Equidistributed Long period Linear Random Number
+ Generator with a state size of 607 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.rng;
+
+import java.io.Serializable;
+
+/**
+ * This class implements the {@link RandomStream} interface via inheritance
+ * from {@link RandomStreamBase}. The backbone generator is a Well
+ * Equidistributed Long period Linear Random Number Generator (WELL),
+ * proposed by F. Panneton.
+ * The implemented generator is the <TT>WELL607</TT>, which has a state
+ * size of 607 bits
+ * and a period length of approximatively
+ * <SPAN CLASS="MATH">2<SUP>607</SUP></SPAN>. The values of <SPAN CLASS="MATH"><I>V</I></SPAN>, <SPAN CLASS="MATH"><I>W</I></SPAN> and <SPAN CLASS="MATH"><I>Z</I></SPAN> are <SPAN CLASS="MATH">2<SUP>150</SUP></SPAN>,
+ * <SPAN CLASS="MATH">2<SUP>250</SUP></SPAN> and <SPAN CLASS="MATH">2<SUP>400</SUP></SPAN> respectively (see {@link RandomStream} for their
+ * definition). The seed of the RNG, and the state of a stream at any given
+ * step, is a 19-dimensional vector of 32-bit integers.
+ * The output of <TT>nextValue</TT> has 32 bits of precision.
+ *
+ */
+public class WELL607 extends WELL607base {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ private static int[] curr_stream = {0xD6AFB71C, 0x82ADB18E, 0x326E714E,
+ 0xB1EE42B6, 0xF1A834ED, 0x04AE5721,
+ 0xC5EA2843, 0xFA04116B, 0x6ACE14EF,
+ 0xCD5781A0, 0x6B1F731C, 0x7E3B8E3D,
+ 0x8B34DE2A, 0x74EC15F5, 0x84EBC216,
+ 0x83EA2C61, 0xE4A83B1E, 0xA5D82CB9,
+ 0x9E1A6C89};
+
+ // P(z) = {0x987b2631, 0x2e33283d, 0x6a398474, 0xe9d24da1,
+ // 0x31235359, 0x6a2baf48, 0x7f97efd4, 0x468280f4,
+ // 0x7d9d9424, 0xa3238f8e, 0xe3edb4ef, 0x0e0a25f7,
+ // 0x92c4dff5, 0x55d0b8da, 0x7b982dec, 0xa06c078f,
+ // 0x38b65c31, 0xc8c3788d, 0x8000b200}
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^250) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ static final int [] pw = new int[]
+ {0x83167621, 0x6b5515c8, 0x61a62bd2, 0xbceaa78f,
+ 0xac04b304, 0x28a75ea4, 0xa9104058, 0x595ea53b,
+ 0x35687e95, 0x7f8eca9b, 0x30beffb8, 0xc61e6111,
+ 0x284ee30e, 0x4e9cd901, 0x659633ba, 0x344cc69e,
+ 0xd6052ac1, 0x5d508b69, 0x62cf130};
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^400) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ static final int [] pz = new int[]
+ {0x70b2bdee, 0x595828f1, 0x85a17885, 0x5100c7b2,
+ 0xd3333da2, 0xb42857de, 0xf8a7a4a7, 0xabad2a33,
+ 0xa2580cf, 0xf94c465e, 0x7df951d5, 0x35467053,
+ 0xb3c9a4e, 0x6a33977, 0x443910e, 0xc25aec3d,
+ 0xeb72e8c5, 0x8873b01, 0x7da57636};
+
+
+ /**
+ * Constructs a new stream.
+ *
+ */
+ public WELL607() {
+ state = new int[BUFFER_SIZE];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+ advanceSeed(curr_stream, pz);
+ resetStartStream();
+ }
+
+
+ /**
+ * Constructs a new stream with the identifier <TT>name</TT>
+ * (used in the <TT>toString</TT> method).
+ *
+ * @param name name of the stream
+ *
+ */
+ public WELL607 (String name) {
+ this();
+ this.name = name;
+ }
+
+ /**
+ * Sets the initial seed of the class <TT>WELL607</TT> to the 19
+ * integers of the vector <TT>seed[0..18]</TT>.
+ * This will be the initial seed of the next created stream.
+ * At least one of the integers must not be zero and if this integer is
+ * the last one, it must not be equal to <TT>0x80000000</TT>.
+ *
+ * @param seed array of 19 elements representing the seed
+ *
+ *
+ */
+ public static void setPackageSeed (int seed[]) {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ curr_stream[i] = seed[i];
+ }
+
+
+ /**
+ * This method is discouraged for normal use.
+ * Initializes the stream at the beginning of a stream with the initial
+ * seed <TT>seed[0..18]</TT>. The seed must satisfy the same
+ * conditions as in <TT>setPackageSeed</TT>.
+ * This method only affects the specified stream; the others are not
+ * modified. Hence after calling this method, the beginning of the streams
+ * will no longer be spaced <SPAN CLASS="MATH"><I>Z</I></SPAN> values apart.
+ * For this reason, this method should only be used in very exceptional cases;
+ * proper use of the <TT>reset...</TT> methods and of the stream constructor is
+ * preferable.
+ *
+ * @param seed array of 19 elements representing the seed
+ *
+ *
+ */
+ public void setSeed (int seed[]) {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ }
+
+
+ /**
+ * Returns the current state of the stream, represented as an
+ * array of 19 integers.
+ *
+ * @return the current state of the stream
+ *
+ */
+ public int[] getState() {
+ return super.getState();
+ }
+
+
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i= 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, pw);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if(name == null)
+ sb.append("The state of this WELL607 is : {");
+ else
+ sb.append("The state of " + name + " is : {");
+ sb.append(super.stringState());
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ long result = nextInt();
+ if(result <= 0)
+ result += 0x100000000L;
+ return result * NORM;
+ }
+
+ /**
+ * Clones the current generator and return its copy.
+ *
+ * @return A deep copy of the current generator
+ *
+ */
+ public WELL607 clone() {
+ WELL607 retour = null;
+ retour = (WELL607)super.clone();
+ retour.state = new int[BUFFER_SIZE];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+
+ for (int i = 0; i<R; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ for (int i = 0; i<BUFFER_SIZE; i++) {
+ retour.state[i] = state[i];
+ }
+
+ return retour;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/rng/WELL607.tex b/source/umontreal/iro/lecuyer/rng/WELL607.tex
new file mode 100644
index 0000000..45d6263
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/WELL607.tex
@@ -0,0 +1,239 @@
+\defmodule {WELL607}
+
+This class implements the \class{RandomStream} interface via inheritance
+from \class{RandomStreamBase}. The backbone generator is a Well
+Equidistributed Long period Linear Random Number Generator (WELL),
+proposed by F. Panneton\html{.}\latex{ in \cite{rPAN06b,rPAN04t}.}
+The implemented generator is the \texttt{WELL607}, which has a state
+size of 607 bits
+and a period length of \html{approximatively}
+\latex{$\rho\approx$} $2^{607}$. The values of $V$, $W$ and $Z$ are $2^{150}$,
+$2^{250}$ and $2^{400}$ respectively (see \class{RandomStream} for their
+definition). The seed of the RNG, and the state of a stream at any given
+step, is a 19-dimensional vector of 32-bit integers.
+The output of \texttt{nextValue} has 32 bits of precision.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: WELL607
+ * Description: a Well Equidistributed Long period Linear Random Number
+ Generator with a state size of 607 bits
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.rng; \begin{hide}
+
+import java.io.Serializable;\end{hide}
+
+public class WELL607 extends WELL607base \begin{hide} {
+
+ private static final long serialVersionUID = 70510L;
+ //La date de modification a l'envers, lire 10/05/2007
+
+ private static int[] curr_stream = {0xD6AFB71C, 0x82ADB18E, 0x326E714E,
+ 0xB1EE42B6, 0xF1A834ED, 0x04AE5721,
+ 0xC5EA2843, 0xFA04116B, 0x6ACE14EF,
+ 0xCD5781A0, 0x6B1F731C, 0x7E3B8E3D,
+ 0x8B34DE2A, 0x74EC15F5, 0x84EBC216,
+ 0x83EA2C61, 0xE4A83B1E, 0xA5D82CB9,
+ 0x9E1A6C89};
+
+ // P(z) = {0x987b2631, 0x2e33283d, 0x6a398474, 0xe9d24da1,
+ // 0x31235359, 0x6a2baf48, 0x7f97efd4, 0x468280f4,
+ // 0x7d9d9424, 0xa3238f8e, 0xe3edb4ef, 0x0e0a25f7,
+ // 0x92c4dff5, 0x55d0b8da, 0x7b982dec, 0xa06c078f,
+ // 0x38b65c31, 0xc8c3788d, 0x8000b200}
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^250) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ static final int [] pw = new int[]
+ {0x83167621, 0x6b5515c8, 0x61a62bd2, 0xbceaa78f,
+ 0xac04b304, 0x28a75ea4, 0xa9104058, 0x595ea53b,
+ 0x35687e95, 0x7f8eca9b, 0x30beffb8, 0xc61e6111,
+ 0x284ee30e, 0x4e9cd901, 0x659633ba, 0x344cc69e,
+ 0xd6052ac1, 0x5d508b69, 0x62cf130};
+
+ // Ce tableau represente les 512 coefficients du polynome suivant
+ // (z^(2^400) mod P(z)) mod 2
+ // P(z) est le polynome caracteristique du generateur.
+ static final int [] pz = new int[]
+ {0x70b2bdee, 0x595828f1, 0x85a17885, 0x5100c7b2,
+ 0xd3333da2, 0xb42857de, 0xf8a7a4a7, 0xabad2a33,
+ 0xa2580cf, 0xf94c465e, 0x7df951d5, 0x35467053,
+ 0xb3c9a4e, 0x6a33977, 0x443910e, 0xc25aec3d,
+ 0xeb72e8c5, 0x8873b01, 0x7da57636};
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public WELL607() \begin{hide} {
+ state = new int[BUFFER_SIZE];
+ stream = new int[R];
+ substream = new int[R];
+
+ for(int i = 0; i < R; i++)
+ stream[i] = curr_stream[i];
+
+ advanceSeed(curr_stream, pz);
+ resetStartStream();
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream.
+\end{tabb}
+\begin{code}
+
+ public WELL607 (String name) \begin{hide} {
+ this();
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new stream with the identifier \texttt{name}
+ (used in the \texttt{toString} method).
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the stream}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public static void setPackageSeed (int seed[]) \begin{hide} {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ curr_stream[i] = seed[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the initial seed of the class \texttt{WELL607} to the 19
+ integers of the vector \texttt{seed[0..18]}.
+ This will be the initial seed of the next created stream.
+ At least one of the integers must not be zero and if this integer is
+ the last one, it must not be equal to \texttt{0x80000000}.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 19 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public void setSeed (int seed[]) \begin{hide} {
+ verifySeed(seed);
+ for(int i = 0; i < R; i++)
+ stream[i] = seed[i];
+ resetStartStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} This method is discouraged for normal use.
+ Initializes the stream at the beginning of a stream with the initial
+ seed \texttt{seed[0..18]}. The seed must satisfy the same
+ conditions as in \texttt{setPackageSeed}.
+ This method only affects the specified stream; the others are not
+ modified. Hence after calling this method, the beginning of the streams
+ will no longer be spaced $Z$ values apart.
+ For this reason, this method should only be used in very exceptional cases;
+ proper use of the \texttt{reset...} methods and of the stream constructor is
+ preferable.
+\end{tabb}
+\begin{htmlonly}
+ \param{seed}{array of 19 elements representing the seed}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getState() \begin{hide} {
+ return super.getState();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the current state of the stream, represented as an
+ array of 19 integers.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current state of the stream}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void resetStartStream() {
+ for(int i = 0; i < R; i++)
+ substream[i] = stream[i];
+ resetStartSubstream();
+ }
+
+ public void resetStartSubstream() {
+ state_i= 0;
+ for(int i = 0; i < R; i++)
+ state[i] = substream[i];
+ }
+
+ public void resetNextSubstream() {
+ advanceSeed(substream, pw);
+ resetStartSubstream();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if(name == null)
+ sb.append("The state of this WELL607 is : {");
+ else
+ sb.append("The state of " + name + " is : {");
+ sb.append(super.stringState());
+ return sb.toString();
+ }
+
+ protected double nextValue() {
+ long result = nextInt();
+ if(result <= 0)
+ result += 0x100000000L;
+ return result * NORM;
+ }\end{hide}
+
+ public WELL607 clone() \begin{hide} {
+ WELL607 retour = null;
+ retour = (WELL607)super.clone();
+ retour.state = new int[BUFFER_SIZE];
+ retour.substream = new int[R];
+ retour.stream = new int[R];
+
+ for (int i = 0; i<R; i++) {
+ retour.substream[i] = substream[i];
+ retour.stream[i] = stream[i];
+ }
+ for (int i = 0; i<BUFFER_SIZE; i++) {
+ retour.state[i] = state[i];
+ }
+
+ return retour;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Clones the current generator and return its copy.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{A deep copy of the current generator}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/rng/WELL607base.java b/source/umontreal/iro/lecuyer/rng/WELL607base.java
new file mode 100644
index 0000000..816f53b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/WELL607base.java
@@ -0,0 +1,167 @@
+package umontreal.iro.lecuyer.rng;
+
+import umontreal.iro.lecuyer.util.BitVector;
+import umontreal.iro.lecuyer.util.BitMatrix;
+import java.io.Serializable;
+import java.io.*;
+
+
+/*
+ * Class: WELL607base
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+
+abstract class WELL607base extends RandomStreamBase
+{
+
+ private static final long serialVersionUID = 120307L;
+ // La date de modification a l'envers, lire 07/03/2012
+
+ // constants
+ static final double NORM = (1.0 / 0x100000001L);
+ // static final double NORM = 2.32830643653869628906e-10;
+
+ static final int R = 19; // useful length of the state
+ static final int BUFFER_SIZE = 32; // length of the state
+ static final int NUM_BITS = 608;
+ static final int MASK_STATE = 0x0000001F; // = 31
+ static final int W = 32;
+ static final int P = 1;
+ static final int MASKU = (0xffffffff >>> (W - P));
+ static final int MASKL = (~MASKU);
+ static final int M1 = 16;
+ static final int M2 = 15;
+ static final int M3 = 14;
+ static final int R1 = 18; // R - 1
+ static final int R2 = 17; // R - 2
+
+ // state variables
+ int state_i;
+ int[] state;
+
+ // stream and substream
+ int[] stream;
+ int[] substream;
+
+ // length of the jumps
+ static final int w = 250;
+ static final int v = 150;
+
+ // advance the state by a transition matrice
+ protected void advanceSeed(int[] seed, int [] p)
+ {
+ int b;
+ int[] x = new int[R];
+
+ for (int i = 0; i < R; i++) {
+ state[i] = seed[i];
+ }
+ state_i = 0;
+
+ for (int j = 0; j < R; ++j) {
+ b = p[j];
+ for (int k = 0; k < W; ++k) {
+ if ((b & 1) == 1) {
+ for (int i = 0; i < R; i++) {
+ x[i] ^= state[(state_i + i) & MASK_STATE];
+ }
+ }
+ b >>= 1;
+ nextInt ();
+ }
+ }
+
+ for (int i = 0; i < R; i++) {
+ seed[i] = x[i];
+ }
+ }
+
+
+ static void verifySeed (int seed[])
+ {
+ if (seed.length < R)
+ throw new IllegalArgumentException ("Seed must contain " + R +
+ " values");
+
+ boolean goodSeed = false;
+ for (int i = 0; !goodSeed && i < R; i++)
+ if (seed[i] != 0)
+ goodSeed = true;
+ if (!goodSeed)
+ if (seed[R - 1] == 0x80000000)
+ throw new IllegalArgumentException
+ ("At least one of the element of the seed must not be 0. " +
+ "If this element is the last one, it mustn't be equal " +
+ "to 0x80000000 (" + 0x80000000 + ").");
+ }
+
+
+ int[] getState ()
+ {
+ int[] result = new int[R];
+ for (int i = 0; i < R; i++)
+ result[i] = state[(state_i + i) & MASK_STATE];
+ return result;
+ }
+
+
+ // just like formatState, but not public
+ String stringState ()
+ {
+ StringBuffer sb = new StringBuffer ();
+ for (int i = 0; i < R - 1; i++)
+ sb.append (state[(state_i + i) & MASK_STATE] + ", ");
+ sb.append (state[(state_i + R - 1) & MASK_STATE] + "}");
+ return sb.toString ();
+ }
+
+
+ int nextInt ()
+ {
+ int z0, z1, z2;
+
+ z0 = (state[(state_i + R1) & MASK_STATE] & MASKL) |
+ (state[(state_i + R2) & MASK_STATE] & MASKU);
+ z1 = (state[state_i] ^ (state[state_i] >>> 19)) ^
+ (state[(state_i + M1) & MASK_STATE] ^
+ (state[(state_i + M1) & MASK_STATE] >>> 11));
+ z2 = (state[(state_i + M2) & MASK_STATE] ^
+ (state[(state_i + M2) & MASK_STATE] << (14))) ^
+ state[(state_i + M3) & MASK_STATE];
+ state[state_i] = z1 ^ z2;
+ state[(state_i - 1) & MASK_STATE] = (z0 ^ (z0 >>> 18)) ^
+ z1 ^ (state[state_i] ^ (state[state_i] << 5));
+
+ state_i--;
+ state_i &= MASK_STATE;
+ return state[state_i];
+ }
+
+
+ public WELL607base clone ()
+ {
+ WELL607base retour = null;
+ retour = (WELL607base) super.clone ();
+ return retour;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/rng/guiderng.bbl b/source/umontreal/iro/lecuyer/rng/guiderng.bbl
new file mode 100644
index 0000000..c30d5e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/guiderng.bbl
@@ -0,0 +1,105 @@
+\begin{thebibliography}{10}
+
+\bibitem{rKNU98a}
+D.~E. Knuth.
+\newblock {\em The Art of Computer Programming, Volume 2: Seminumerical
+ Algorithms}.
+\newblock Addison-Wesley, Reading, MA, third edition, 1998.
+
+\bibitem{sLAW00a}
+A.~M. Law and W.~D. Kelton.
+\newblock {\em Simulation Modeling and Analysis}.
+\newblock McGraw-Hill, New York, NY, third edition, 2000.
+
+\bibitem{rLEC96a}
+P.~L'Ecuyer.
+\newblock Maximally equidistributed combined {T}ausworthe generators.
+\newblock {\em Mathematics of Computation}, 65(213):203--213, 1996.
+
+\bibitem{rLEC99b}
+P.~L'Ecuyer.
+\newblock Good parameters and implementations for combined multiple recursive
+ random number generators.
+\newblock {\em Operations Research}, 47(1):159--164, 1999.
+
+\bibitem{rLEC99a}
+P.~L'Ecuyer.
+\newblock Tables of maximally equidistributed combined {LFSR} generators.
+\newblock {\em Mathematics of Computation}, 68(225):261--269, 1999.
+
+\bibitem{rLEC01d}
+P.~L'Ecuyer.
+\newblock Software for uniform random number generation: Distinguishing the
+ good and the bad.
+\newblock In {\em Proceedings of the 2001 Winter Simulation Conference}, pages
+ 95--105, Piscataway, NJ, 2001. {IEEE} Press.
+
+\bibitem{rLEC04b}
+P.~L'Ecuyer.
+\newblock Random number generation.
+\newblock In J.~E. Gentle, W.~Haerdle, and Y.~Mori, editors, {\em Handbook of
+ Computational Statistics}, pages 35--70. Springer-Verlag, Berlin, 2004.
+\newblock Chapter II.2.
+
+\bibitem{rLEC97d}
+P.~L'Ecuyer and T.~H. Andres.
+\newblock A random number generator based on the combination of four {LCG}s.
+\newblock {\em Mathematics and Computers in Simulation}, 44:99--107, 1997.
+
+\bibitem{rLEC91a}
+P.~L'Ecuyer and S.~C{\^o}t{\'e}.
+\newblock Implementing a random number package with splitting facilities.
+\newblock {\em ACM Transactions on Mathematical Software}, 17(1):98--111, 1991.
+
+\bibitem{rLEC03c}
+P.~L'Ecuyer and J.~Granger-Pich\'e.
+\newblock Combined generators with components from different families.
+\newblock {\em Mathematics and Computers in Simulation}, 62:395--404, 2003.
+
+\bibitem{rLEC02a}
+P.~L'Ecuyer, R.~Simard, E.~J. Chen, and W.~D. Kelton.
+\newblock An object-oriented random-number package with many long streams and
+ substreams.
+\newblock {\em Operations Research}, 50(6):1073--1075, 2002.
+
+\bibitem{rLEC00b}
+P.~L'Ecuyer and R.~Touzin.
+\newblock Fast combined multiple recursive generators with multipliers of the
+ form $a = \pm 2^q \pm 2^r$.
+\newblock In {\em Proceedings of the 2000 Winter Simulation Conference}, pages
+ 683--689, Piscataway, NJ, 2000. {IEEE} Press.
+
+\bibitem{rMAT98a}
+M.~Matsumoto and T.~Nishimura.
+\newblock Mersenne twister: A 623-dimensionally equidistributed uniform
+ pseudo-random number generator.
+\newblock {\em ACM Transactions on Modeling and Computer Simulation},
+ 8(1):3--30, 1998.
+
+\bibitem{rPAN04t}
+F.~Panneton.
+\newblock {\em Construction d'ensembles de points bas\'ee sur des r\'ecurrences
+ lin\'eaires dans un corps fini de caract\'eristique 2 pour la simulation
+ {M}onte {C}arlo et l'int\'egration quasi-{M}onte {C}arlo}.
+\newblock PhD thesis, D\'epartement d'informatique et de recherche
+ op\'erationnelle, Universit\'e de Montr\'eal, Canada, August 2004.
+
+\bibitem{rPAN04a}
+F.~Panneton and P.~L'Ecuyer.
+\newblock Random number generators based on linear recurrences in {$F_{2^w}$}.
+\newblock In H.~Niederreiter, editor, {\em {M}onte {C}arlo and Quasi-{M}onte
+ {C}arlo Methods 2002}, pages 367--378, Berlin, 2004. Springer-Verlag.
+
+\bibitem{rPAN06b}
+F.~Panneton, P.~L'Ecuyer, and M.~Matsumoto.
+\newblock Improved long-period generators based on linear recurrences modulo 2.
+\newblock {\em {ACM} Transactions on Mathematical Software}, 32(1):1--16, 2006.
+
+\bibitem{rTEZ91b}
+S.~Tezuka and P.~L'Ecuyer.
+\newblock Efficient and portable combined {T}ausworthe random number
+ generators.
+\newblock {\em ACM Transactions on Modeling and Computer Simulation},
+ 1(2):99--112, 1991.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/rng/guiderng.tex b/source/umontreal/iro/lecuyer/rng/guiderng.tex
new file mode 100644
index 0000000..a74f849
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/guiderng.tex
@@ -0,0 +1,60 @@
+\documentclass [twoside,12pt]{article}
+\usepackage{amssymb}
+\usepackage{ssj}
+
+\mytwoheads
+% \dateheadtrue
+% \fulltrue %% Writes all the hidden code.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+\begin{titlepage}
+
+\title{rng}{Random Number Generators}
+
+\vfill
+\end{titlepage}
+
+\pagenumbering{roman}
+\tableofcontents
+% \include{avantpro}
+%
+\pagenumbering{arabic}
+
+\include{overview}
+
+\include{RandomStream}
+\include{CloneableRandomStream}
+\include{RandomStreamBase}
+\include{RandomPermutation}
+\include{RandomStreamManager}
+\include{RandomStreamFactory}
+\include{BasicRandomStreamFactory}
+\include{RandomStreamInstantiationException}
+
+\include{RandomStreamWithCache}
+\include{AntitheticStream}
+\include{BakerTransformedStream}
+\include{TruncatedRandomStream}
+
+\include{RandMrg}
+\include{MRG32k3a}
+\include{MRG32k3aL}
+\include{MRG31k3p}
+\include{LFSR113}
+\include{LFSR258}
+\include{WELL512}
+\include{WELL607}
+\include{WELL1024}
+\include{GenF2w32}
+\include{MT19937}
+\include{F2NL607}
+\include{RandRijndael}
+
+%\setcounter{section}{1}
+%\renewcommand{\thesection}{\Alph{section}.}
+
+\bibliography{simul,random,ift,stat,prob}
+\bibliographystyle{plain}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/rng/overview.tex b/source/umontreal/iro/lecuyer/rng/overview.tex
new file mode 100644
index 0000000..23aa515
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/rng/overview.tex
@@ -0,0 +1,98 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+
+This package offers the basic facilities for generating uniform random
+numbers. It provides an interface called
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+and some implementations of that interface.
+The interface specifies that each stream of random numbers
+is partitioned into multiple substreams and that methods are available to jump
+between the substreams, as discussed in \cite{rLEC91a,rLEC97d,rLEC02a}.
+For an example of how to use these streams properly, see
+\texttt{InventoryCRN} in the set of example programs.
+
+Each implementation uses a specific backbone
+uniform random number generator (RNG), whose period length is typically
+partitioned into very long non-overlapping segments to provide the
+streams and substreams.
+A stream can generate uniform variates (real numbers) over the interval (0,1),
+uniform integers over a given range of values $\{i,\dots,j\}$,
+and arrays of these.
+
+The generators provided here have various speeds and period lengths.
+\externalclass{umontreal.iro.lecuyer.rng}{MRG32k3a} is the one that
+ has been most extensively tested, but it is not among the fastest.
+The \externalclass{umontreal.iro.lecuyer.rng}{LFSR113}, \externalclass{umontreal.iro.lecuyer.rng}{GenF2w32},
+ \externalclass{umontreal.iro.lecuyer.rng}{MT19937}, and the \texttt{WELL} generators produce sequences
+of bits that obey a linear recurrence, so they eventually fail statistical
+tests that measure the linear complexity of these bits sequences.
+But this can affect only very special types of applications.
+
+For each generator, the following tables give
+the approximate period length (period),
+the CPU time (in seconds) to generate $10^9$ $U(0,1)$ random numbers
+(gen.\ time), and the CPU time to jump ahead $10^6$ times
+to the next substream (jump time).
+The following timings are on a 2100 MHz 32-bit AMD Athlon XP 2800+
+computer running Linux, with the JDK 1.4.2.
+
+\begin{center}
+\begin{tabular}{|l|lcr|}
+\hline
+ RNG & period & gen.\ time & jump time \\
+\hline
+ LFSR113\rule{0pt}{1em} & $2^{113}$ & \phantom{0}51 & 0.08 \\
+ WELL512 & $2^{512}$ & \phantom{0}55 & 372 \\
+ WELL1024 & $2^{1024}$ & \phantom{0}55 & 1450 \\
+ MT19937 & $2^{19937}$ & \phantom{0}56 & 60 \\
+ WELL607 & $2^{607}$ & \phantom{0}61 & 523 \\
+ GenF2w32 & $2^{800}$ & \phantom{0}62 & 937 \\
+ MRG31k3p & $2^{185}$ & \phantom{0}66 & 1.8 \\
+ MRG32k3a & $2^{191}$ & 109 & 2.3 \\
+% MRG32k3aL & $2^{191}$ & ? & ? \\
+ F2NL607 & $2^{637}$ & 125 & 523 \\
+ RandRijndael & $2^{130}$ & 260 & 0.9 \\
+\hline
+\end{tabular}
+\end{center}
+
+The following timings are on a 2400 MHz 64-bit AMD Athlon 64 Processor 4000+
+computer running Linux, with the JDK 1.5.0.
+
+%Les tests ont ete effectues sur Papaye
+
+\begin{center}
+\begin{tabular}{|l|lcr|}
+\hline
+ RNG & period & gen.\ time & jump time \\
+\hline
+ LFSR113\rule{0pt}{1em} & $2^{113}$ & \phantom{0}31 & 0.08 \\
+ WELL607 & $2^{607}$ & \phantom{0}33 & 329 \\
+ WELL512 & $2^{512}$ & \phantom{0}33 & 234 \\
+ WELL1024 & $2^{1024}$ & \phantom{0}34 & 917 \\
+ LFSR258 & $2^{258}$ & \phantom{0}35 & 0.18 \\
+ MT19937 & $2^{19937}$ & \phantom{0}36 & 46 \\
+ GenF2w32 & $2^{800}$ & \phantom{0}43 & 556 \\
+ MRG31k3p & $2^{185}$ & \phantom{0}51 & 0.89 \\
+ F2NL607 & $2^{637}$ & \phantom{0}65 & 329 \\
+ MRG32k3a & $2^{191}$ & \phantom{0}70 & 1.1 \\
+% MRG32k3aL & $2^{191}$ & ? & ? \\
+ RandRijndael & $2^{130}$ & 127 & 0.6 \\
+\hline
+\end{tabular}
+\end{center}
+
+
+Other tools included in this package permit one to manage
+and synchronize several streams simultaneously
+(\externalclass{umontreal.iro.lecuyer.rng}{RandomStreamManager}),
+to create random stream factories for a given type of stream
+(\externalclass{umontreal.iro.lecuyer.rng}{BasicRandomStreamFactory}),
+and to apply automatic transformations to the output of a given
+stream (\externalclass{umontreal.iro.lecuyer.rng}{AntitheticStream} and
+\externalclass{umontreal.iro.lecuyer.rng}{BakerTransformedStream}).
+
+For further details about uniform RNGs, we refer the reader to
+\cite{rKNU98a,rLEC01d,rLEC04b}.
+
+\hpierre{Should add an example somewhere.}
+
diff --git a/source/umontreal/iro/lecuyer/simevents/Accumulate.java b/source/umontreal/iro/lecuyer/simevents/Accumulate.java
new file mode 100644
index 0000000..9c7ff4f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Accumulate.java
@@ -0,0 +1,318 @@
+
+/*
+ * Class: Accumulate
+ * Description: collects statistics on a variable that evolves in
+ simulation time
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+// This class doesn't belong to package stat because objects of this class
+// always depend of Simulator
+
+import java.util.Observable;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.stat.StatProbe;
+
+
+/**
+ * A subclass of {@link umontreal.iro.lecuyer.stat.StatProbe StatProbe},
+ * for collecting statistics on a
+ * variable that evolves in simulation time, with a piecewise-constant trajectory.
+ * Each time the variable changes its value, the method {@link #update(double) update}
+ * must be called to inform the probe of the new value.
+ * The probe can be reinitialized by {@link #init init}.
+ *
+ */
+public class Accumulate extends StatProbe implements Cloneable {
+
+ private double initTime; // Initialization time.
+ private double lastTime; // Last update time.
+ private double lastValue; // Value since last update.
+ private Simulator sim;
+
+
+
+ /**
+ * Constructs a new <TT>Accumulate</TT> statistical probe using the
+ * default simulator and initializes it by invoking <TT>init()</TT>.
+ *
+ */
+ public Accumulate() {
+ super();
+ sim = Simulator.getDefaultSimulator();
+ init();
+ }
+
+
+ /**
+ * Constructs a new <TT>Accumulate</TT> statistical probe linked to
+ * the given simulator,
+ * and initializes it by invoking <TT>init()</TT>.
+ *
+ * @param inSim the simulator of the current variable
+ *
+ *
+ */
+ public Accumulate (Simulator inSim) {
+ super();
+ if (inSim == null)
+ throw new NullPointerException();
+ sim = inSim;
+ init();
+ }
+
+
+ /**
+ * Constructs and initializes a new <TT>Accumulate</TT>
+ * statistical probe with name <TT>name</TT> and initial time 0, using the default simulator.
+ *
+ */
+ public Accumulate (String name) {
+ super();
+ sim = Simulator.getDefaultSimulator();
+ this.name = name;
+ init();
+ }
+
+
+ /**
+ * Constructs-initializes a new <TT>Accumulate</TT>
+ * statistical probe with name <TT>name</TT> and initial time 0.
+ *
+ * @param name descriptive name for the probe
+ *
+ * @param inSim the simulator of the current variable
+ *
+ */
+ public Accumulate (Simulator inSim, String name) {
+ super();
+ if (inSim == null)
+ throw new NullPointerException();
+ sim = inSim;
+ this.name = name;
+ init();
+ }
+
+
+ /**
+ * Initializes the statistical collector and puts the current
+ * value of the corresponding variable to 0.
+ * <SPAN CLASS="textbf">Note:</SPAN> the initialization time, the last update time and
+ * the simulation time are not reset to 0 by this method. For this,
+ * <TT>Sim.init()</TT> must be used.
+ *
+ */
+ public void init() {
+ maxValue = Double.MIN_VALUE;
+ minValue = Double.MAX_VALUE;
+ lastValue = 0.0;
+ sumValue = 0.0;
+ // May start the accumulator at t > 0; for ex., a warm-up period or
+ // other reasons
+ initTime = lastTime = sim.time();
+ }
+
+
+ /**
+ * Same as {@link #init init} followed by {@link #update(double) update}<TT>(x)</TT>.
+ *
+ * @param x initial value of the probe
+ *
+ *
+ */
+ public void init (double x) {
+ init(); update (x);
+ }
+
+
+ /**
+ * Updates the accumulator using the last value passed
+ * to {@link #update(double) update}.
+ *
+ */
+ public void update() {
+ update (lastValue);
+ }
+
+
+ /**
+ * Gives a new observation <TT>x</TT> to the statistical collector.
+ * If broadcasting to observers is activated for this object,
+ * this method will also transmit the new information to the
+ * registered observers by invoking the methods
+ * {@link #notifyListeners(double) notifyListeners}.
+ *
+ * @param x new observation given to the probe
+ *
+ *
+ */
+ public void update (double x) {
+ if (collect) {
+ double time = sim.time();
+ if (x < minValue) minValue = x;
+ if (x > maxValue) maxValue = x;
+ sumValue += lastValue * (time - lastTime);
+ lastValue = x;
+ lastTime = time;
+ }
+ if (broadcast) {
+ //setChanged();
+ notifyListeners (x);
+ }
+ }
+
+
+ public double sum() {
+ update (lastValue);
+ return sumValue;
+ }
+
+ /**
+ * Returns the time-average since the last initialization
+ * to the last call to <TT>update</TT>.
+ *
+ */
+ public double average() {
+ update (lastValue);
+ double periode = lastTime - initTime;
+ if (periode > 0.0) return sumValue/periode;
+ else return 0.0;
+ }
+
+ public String shortReportHeader() {
+ PrintfFormat pf = new PrintfFormat();
+ pf.append (-9, "from time").append (" ");
+ pf.append (-9, "to time").append (" ");
+ pf.append (-8, " min").append (" ");
+ pf.append (-8, " max").append (" ");
+ pf.append (-10, " average");
+ return pf.toString();
+ }
+
+ public String shortReport() {
+ update();
+ PrintfFormat pf = new PrintfFormat();
+ pf.append (9, 2, 2, getInitTime()).append (" ");
+ pf.append (9, 2, 2, getLastTime()).append (" ");
+ pf.append (8, 3, 2, min()).append (" ");
+ pf.append (8, 3, 2, max()).append (" ");
+ pf.append (10, 3, 2, average());
+ return pf.toString();
+ }
+
+
+
+ public String report() {
+ update (lastValue);
+ PrintfFormat str = new PrintfFormat();
+ str.append ("REPORT on Accumulate stat. collector ==> " + name);
+ str.append (PrintfFormat.NEWLINE + " from time to time min max");
+ str.append (" average").append(PrintfFormat.NEWLINE);
+ str.append (12, 2, 2, initTime);
+ str.append (13, 2, 2, lastTime);
+ str.append (11, 3, 2, minValue);
+ str.append (12, 3, 2, (double)maxValue);
+ str.append (14, 3, 2, (double)average()).append (PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+
+ /**
+ * Returns the initialization time for this object.
+ * This is the simulation time when {@link #init init} was called for
+ * the last time.
+ *
+ * @return the initialization time for this object
+ *
+ */
+ public double getInitTime() {
+ return initTime;
+ }
+
+
+ /**
+ * Returns the last update time for this object.
+ * This is the simulation time of the last call to {@link #update update} or
+ * the initialization time if {@link #update update} was never called after
+ * {@link #init init}.
+ *
+ * @return the last update time of this object
+ *
+ */
+ public double getLastTime() {
+ return lastTime;
+ }
+
+
+ /**
+ * Returns the value passed to this probe by the last call
+ * to its {@link #update update} method (or the initial value if
+ * {@link #update update} was never called after {@link #init init}).
+ *
+ * @return the last update value for this object
+ *
+ */
+ public double getLastValue() {
+ return lastValue;
+ }
+
+
+ /**
+ * Returns the simulator associated with this statistical probe.
+ *
+ * @return the associated simulator.
+ *
+ */
+ public Simulator simulator() {
+ return sim;
+ }
+
+
+ /**
+ * Sets the simulator associated with this probe to <TT>sim</TT>.
+ * One should call {@link #init init} after this method to reset the statistical probe.
+ *
+ * @param sim the simulator of this probe
+ *
+ *
+ */
+ public void setSimulator(Simulator sim) {
+ if (sim == null)
+ throw new NullPointerException();
+ this.sim = sim;
+ }
+
+
+ /**
+ * Clone this object.
+ *
+ */
+ public Accumulate clone() {
+ try {
+ return (Accumulate)super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException ("Accumulate can't clone");
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/Accumulate.tex b/source/umontreal/iro/lecuyer/simevents/Accumulate.tex
new file mode 100644
index 0000000..451c8ad
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Accumulate.tex
@@ -0,0 +1,330 @@
+\defmodule {Accumulate}
+
+A subclass of \externalclass{umontreal.iro.lecuyer.stat}{StatProbe},
+for collecting statistics on a
+variable that evolves in simulation time, with a piecewise-constant trajectory.
+Each time the variable changes its value, the method \method{update}{double}
+must be called to inform the probe of the new value.
+The probe can be reinitialized by \method{init}{}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * Class: Accumulate
+ * Description: collects statistics on a variable that evolves in
+ simulation time
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;\begin{hide}
+// This class doesn't belong to package stat because objects of this class
+// always depend of Simulator
+
+import java.util.Observable;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.stat.StatProbe;
+\end{hide}
+
+public class Accumulate extends StatProbe implements Cloneable \begin{hide} {
+
+ private double initTime; // Initialization time.
+ private double lastTime; // Last update time.
+ private double lastValue; // Value since last update.
+ private Simulator sim;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Accumulate() \begin{hide} {
+ super();
+ sim = Simulator.getDefaultSimulator();
+ init();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new \texttt{Accumulate} statistical probe using the
+ default simulator and initializes it by invoking \texttt{init()}.
+ \end{tabb}
+\begin{code}
+
+ public Accumulate (Simulator inSim) \begin{hide} {
+ super();
+ if (inSim == null)
+ throw new NullPointerException();
+ sim = inSim;
+ init();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new \texttt{Accumulate} statistical probe linked to
+ the given simulator,
+ and initializes it by invoking \texttt{init()}.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{inSim}{the simulator of the current variable}
+\end{htmlonly}
+\begin{code}
+
+ public Accumulate (String name) \begin{hide} {
+ super();
+ sim = Simulator.getDefaultSimulator();
+ this.name = name;
+ init();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs and initializes a new \texttt{Accumulate}
+ statistical probe with name \texttt{name} and initial time 0, using the default simulator.
+ \end{tabb}
+\begin{code}
+
+ public Accumulate (Simulator inSim, String name) \begin{hide} {
+ super();
+ if (inSim == null)
+ throw new NullPointerException();
+ sim = inSim;
+ this.name = name;
+ init();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs-initializes a new \texttt{Accumulate}
+ statistical probe with name \texttt{name} and initial time 0.
+ \end{tabb}
+\begin{htmlonly}
+ \param{name}{descriptive name for the probe}
+ \param{inSim}{the simulator of the current variable}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void init() \begin{hide} {
+ maxValue = Double.MIN_VALUE;
+ minValue = Double.MAX_VALUE;
+ lastValue = 0.0;
+ sumValue = 0.0;
+ // May start the accumulator at t > 0; for ex., a warm-up period or
+ // other reasons
+ initTime = lastTime = sim.time();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Initializes the statistical collector and puts the current
+ value of the corresponding variable to 0.
+ \textbf{Note:} the initialization time, the last update time and
+ the simulation time are not reset to 0 by this method. For this,
+ \texttt{Sim.init()} must be used.
+ \end{tabb}
+\begin{code}
+
+ public void init (double x) \begin{hide} {
+ init(); update (x);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Same as \method{init}{} followed by \method{update}{double}\texttt{(x)}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x}{initial value of the probe}
+\end{htmlonly}
+\begin{code}
+
+ public void update()\begin{hide} {
+ update (lastValue);
+ }\end{hide}
+\end{code}
+\begin{tabb} Updates the accumulator using the last value passed
+ to \method{update}{double}.
+\end{tabb}
+\begin{code}
+
+ public void update (double x) \begin{hide} {
+ if (collect) {
+ double time = sim.time();
+ if (x < minValue) minValue = x;
+ if (x > maxValue) maxValue = x;
+ sumValue += lastValue * (time - lastTime);
+ lastValue = x;
+ lastTime = time;
+ }
+ if (broadcast) {
+ //setChanged();
+ notifyListeners (x);
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Gives a new observation \texttt{x} to the statistical collector.
+ If broadcasting to observers is activated for this object,
+ this method will also transmit the new information to the
+ registered observers by invoking the methods
+% \texttt{setChanged}
+% and \externalmethod{java.util}{Observable}{notifyListeners}{}~{\tt
+% (new Double (x))}
+ \method{notifyListeners}{double}.
+% inherited from
+% \externalclass{java.util}{Observable}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x}{new observation given to the probe}
+\end{htmlonly}
+\begin{hide}
+\begin{code}
+
+ public double sum() \begin{hide} {
+ update (lastValue);
+ return sumValue;
+ } \end{hide}
+
+ public double average() \begin{hide} {
+ update (lastValue);
+ double periode = lastTime - initTime;
+ if (periode > 0.0) return sumValue/periode;
+ else return 0.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the time-average since the last initialization
+ to the last call to \texttt{update}.
+ \end{tabb}
+\begin{code}\begin{hide}
+ public String shortReportHeader() {
+ PrintfFormat pf = new PrintfFormat();
+ pf.append (-9, "from time").append (" ");
+ pf.append (-9, "to time").append (" ");
+ pf.append (-8, " min").append (" ");
+ pf.append (-8, " max").append (" ");
+ pf.append (-10, " average");
+ return pf.toString();
+ }
+
+ public String shortReport() {
+ update();
+ PrintfFormat pf = new PrintfFormat();
+ pf.append (9, 2, 2, getInitTime()).append (" ");
+ pf.append (9, 2, 2, getLastTime()).append (" ");
+ pf.append (8, 3, 2, min()).append (" ");
+ pf.append (8, 3, 2, max()).append (" ");
+ pf.append (10, 3, 2, average());
+ return pf.toString();
+ }
+\end{hide}
+\end{code}
+\begin{code}
+
+ public String report() \begin{hide} {
+ update (lastValue);
+ PrintfFormat str = new PrintfFormat();
+ str.append ("REPORT on Accumulate stat. collector ==> " + name);
+ str.append (PrintfFormat.NEWLINE + " from time to time min max");
+ str.append (" average").append(PrintfFormat.NEWLINE);
+ str.append (12, 2, 2, initTime);
+ str.append (13, 2, 2, lastTime);
+ str.append (11, 3, 2, minValue);
+ str.append (12, 3, 2, (double)maxValue);
+ str.append (14, 3, 2, (double)average()).append (PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a string containing a report on this collector since its
+ last initialization.
+ \end{tabb}
+\end{hide}
+\begin{code}
+
+ public double getInitTime()\begin{hide} {
+ return initTime;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the initialization time for this object.
+ This is the simulation time when \method{init}{} was called for
+ the last time.
+\end{tabb}
+\begin{htmlonly}
+ \return{the initialization time for this object}
+\end{htmlonly}
+\begin{code}
+
+ public double getLastTime()\begin{hide} {
+ return lastTime;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the last update time for this object.
+ This is the simulation time of the last call to \method{update}{} or
+ the initialization time if \method{update}{} was never called after
+ \method{init}{}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the last update time of this object}
+\end{htmlonly}
+\begin{code}
+
+ public double getLastValue()\begin{hide} {
+ return lastValue;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value passed to this probe by the last call
+ to its \method{update}{} method (or the initial value if
+ \method{update}{} was never called after \method{init}{}).
+\end{tabb}
+\begin{htmlonly}
+ \return{the last update value for this object}
+\end{htmlonly}
+\begin{code}
+
+ public Simulator simulator() \begin{hide} {
+ return sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the simulator associated with this statistical probe.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the associated simulator.}
+\end{htmlonly}
+\begin{code}
+
+ public void setSimulator(Simulator sim) \begin{hide} {
+ if (sim == null)
+ throw new NullPointerException();
+ this.sim = sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Sets the simulator associated with this probe to \texttt{sim}.
+ One should call \method{init}{} after this method to reset the statistical probe.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{the simulator of this probe}
+\end{htmlonly}
+\begin{code}
+
+ public Accumulate clone()\begin{hide} {
+ try {
+ return (Accumulate)super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException ("Accumulate can't clone");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Clone this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/Continuous.java b/source/umontreal/iro/lecuyer/simevents/Continuous.java
new file mode 100644
index 0000000..bffa171
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Continuous.java
@@ -0,0 +1,268 @@
+
+
+/*
+ * Class: Continuous
+ * Description: provides the basic structures and tools for
+ continuous-time simulation
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+
+/**
+ * Represents a variable in a continuous-time simulation.
+ * This abstract class provides the basic structures and tools
+ * for continuous-time simulation, where certain variables evolve
+ * continuously in time, according to differential equations.
+ * Such continuous variables can be mixed with events and processes.
+ *
+ * <P>
+ * Each type of continuous-time variable should be defined as a
+ * subclass of <TT>Continuous</TT>.
+ * The instances of these subclasses are the actual continuous-time
+ * variables. Each subclass must implement the method
+ * {@link #derivative derivative} which returns its derivative with respect to time.
+ * The trajectory of this variable is determined by integrating this
+ * derivative.
+ * The subclass may also reimplement the method {@link #afterEachStep afterEachStep},
+ * which is executed immediately after each integration step.
+ *
+ * By default (in the class <TT>Continuous</TT>), this method does nothing.
+ * This method could, for example, verify if the variable has reached
+ * a given threshold, or update a graphical illustration of the variable
+ * trajectory.
+ *
+ * <P>
+ * When creating a class representing a continuous variable,
+ * the {@link #toString toString} method can be overridden to display
+ * information about the continuous variable. This information will
+ * be displayed when formating the event list as a string.
+ *
+ * <P>
+ * Each continuous variable has a linked simulator represented by an instance of the
+ * {@link Simulator} class.
+ * If no simulator is provided explicitly when constructing a variable,
+ * the default simulator returned by
+ * <TT>Simulator.getDefaultSimulator</TT> is used.
+ *
+ */
+public abstract class Continuous {
+
+ // Private variables:
+
+ boolean active; // This variable is currently being integrated.
+ double value; // Current value of the variable.
+ Event ev; // Event to be executed after each integ. step,
+
+ //String name;
+ double phi;
+ double pi;
+ double buffer;
+ double sum;
+
+ private Simulator sim;
+
+
+
+
+ /**
+ * Constructs a new continuous-time variable
+ * linked to the default simulator, <EM>without</EM> initializing it.
+ *
+ */
+ public Continuous() {
+ active = false;
+ this.sim = Simulator.getDefaultSimulator();
+ }
+
+
+ /**
+ * Constructs a new continuous-time variable linked to
+ * the given simulator, <EM>without</EM>
+ * initializing it.
+ *
+ * @param sim the simulator associated to this variable.
+ *
+ */
+ public Continuous (Simulator sim) {
+ if (sim == null)
+ throw new NullPointerException();
+ active = false;
+ this.sim = sim;
+ }
+
+
+ /**
+ * Initializes or reinitializes the continuous-time variable
+ * to <TT>val</TT>.
+ *
+ * @param val initial value of the variable
+ *
+ *
+ */
+ public void init (double val) {
+ value = val;
+ }
+
+
+ /**
+ * Returns the current value of this continuous-time variable.
+ *
+ * @return the current value of the variable
+ *
+ */
+ public double value() {
+ return value;
+ }
+
+
+ /**
+ * Returns the simulator linked to this continuous-time variable.
+ *
+ * @return the current simulator of the variable
+ *
+ */
+ public Simulator simulator() {
+ return sim;
+ }
+
+
+ /**
+ * Sets the simulator linked to this continuous-time variable.
+ * This method should not be called while this variable is active.
+ *
+ * @param sim the simulator of the current variable
+ *
+ *
+ */
+ public void setSimulator(Simulator sim) {
+ if (sim == null)
+ throw new NullPointerException();
+ this.sim = sim;
+ }
+
+
+ /**
+ * Starts the integration process that will change the state of
+ * this variable at each integration step.
+ *
+ */
+ public void startInteg() {
+ sim.continuousState().startInteg(this);
+ }
+
+
+ /**
+ * Same as {@link #startInteg startInteg}, after initializing the variable
+ * to <TT>val</TT>.
+ *
+ * @param val initial value to start integration from
+ *
+ *
+ */
+ public void startInteg (double val) {
+ init (val); startInteg();
+ }
+
+
+ /**
+ * Stops the integration process for this continuous variable.
+ * The variable keeps the value it took at the last integration step
+ * before calling <TT>stopInteg</TT>.
+ *
+ */
+ public void stopInteg() {
+ sim.continuousState().stopInteg(this);
+ }
+
+
+ /**
+ * This method should return the derivative of this variable
+ * with respect to time, at time <SPAN CLASS="MATH"><I>t</I></SPAN>.
+ * Every subclass of <TT>Continuous</TT> that is to be instantiated
+ * must implement it.
+ * If the derivative does not depend explicitly on time, <SPAN CLASS="MATH"><I>t</I></SPAN> becomes
+ * a dummy parameter. Internally, the method is used with <SPAN CLASS="MATH"><I>t</I></SPAN> not
+ * necessarily equal to the current simulation time.
+ *
+ * @param t time at which the derivative must be computed
+ *
+ *
+ */
+ public abstract double derivative (double t);
+
+
+ /**
+ * This method is executed after each integration step
+ * for this <TT>Continuous</TT> variable.
+ * Here, it does nothing, but every subclass of <TT>Continuous</TT> may
+ * reimplement it.
+ *
+ */
+ public void afterEachStep() {
+ }
+
+
+ /**
+ * Selects the Euler method as the integration method,
+ * with the integration step size <TT>h</TT>, in time units, for the default simulator.
+ * The non-static method {@link #selectEuler selectEuler} in {@link ContinuousState}
+ * can be used to set the integration method for any given simulator.
+ * This method appears here only to keep compatibility with older versions of SSJ; using
+ * a non-static {@link Simulator} instance rather than the default simulator is recommended.
+ *
+ * @param h integration step, in simulation time units
+ *
+ *
+ */
+ public static void selectEuler(double h) {
+ Simulator.getDefaultSimulator().continuousState().selectEuler(h);
+ }
+
+
+ /**
+ * Selects a Runge-Kutta method of order 4 as the integration
+ * method to be used, with step size <TT>h</TT>.
+ * The non-static method {@link #selectRungeKutta4 selectRungeKutta4} in {@link ContinuousState}
+ * can be used to set the integration method for any given simulator.
+ * This method appears here only to keep compatibility with older versions of SSJ; using
+ * a non-static {@link Simulator} instance rather than the default simulator is recommended.
+ *
+ */
+ public static void selectRungeKutta4(double h) {
+ Simulator.getDefaultSimulator().continuousState().selectRungeKutta4(h);
+ }
+
+
+ /**
+ * Selects a Runge-Kutta method of order 2 as the integration
+ * method to be used, with step size <TT>h</TT>.
+ * The non-static method {@link #selectRungeKutta2 selectRungeKutta2} in {@link ContinuousState}
+ * can be used to set the integration method for any given simulator.
+ * This method appears here only to keep compatibility with older versions of SSJ; using
+ * a non-static {@link Simulator} instance rather than the default simulator is recommended.
+ *
+ */
+ public static void selectRungeKutta2(double h) {
+ Simulator.getDefaultSimulator().continuousState().selectRungeKutta2(h);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/Continuous.tex b/source/umontreal/iro/lecuyer/simevents/Continuous.tex
new file mode 100644
index 0000000..b815e14
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Continuous.tex
@@ -0,0 +1,303 @@
+\defmodule {Continuous}
+
+Represents a variable in a continuous-time simulation.
+This abstract class provides the basic structures and tools
+for continuous-time simulation, where certain variables evolve
+continuously in time, according to differential equations.
+Such continuous variables can be mixed with events and processes.
+
+Each type of continuous-time variable should be defined as a
+subclass of \texttt{Continuous}.
+The instances of these subclasses are the actual continuous-time
+variables. Each subclass must implement the method
+\method{derivative}{} which returns its derivative with respect to time.
+The trajectory of this variable is determined by integrating this
+derivative.
+The subclass may also reimplement the method \method{afterEachStep}{},
+which is executed immediately after each integration step.
+\hpierre {It would probably be better if we could change this method
+ when calling \texttt{StartInteg}, as in SIMOD....}
+By default (in the class \texttt{Continuous}), this method does nothing.
+This method could, for example, verify if the variable has reached
+a given threshold, or update a graphical illustration of the variable
+trajectory.
+
+When creating a class representing a continuous variable,
+the \method{toString}{} method can be overridden to display
+information about the continuous variable. This information will
+be displayed when formating the event list as a string.
+
+Each continuous variable has a linked simulator represented by an instance of the
+\class{Simulator} class.
+If no simulator is provided explicitly when constructing a variable,
+the default simulator returned by
+\texttt{Simulator.getDefaultSimulator} is used.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Continuous
+ * Description: provides the basic structures and tools for
+ continuous-time simulation
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;
+
+public abstract class Continuous \begin{hide} {
+
+ // Private variables:
+
+ boolean active; // This variable is currently being integrated.
+ double value; // Current value of the variable.
+ Event ev; // Event to be executed after each integ. step,
+
+ //String name;
+ double phi;
+ double pi;
+ double buffer;
+ double sum;
+
+ private Simulator sim;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Continuous() \begin{hide} {
+ active = false;
+ this.sim = Simulator.getDefaultSimulator();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new continuous-time variable
+ linked to the default simulator, {\em without\/} initializing it.
+ \end{tabb}
+\begin{code}
+
+ public Continuous (Simulator sim) \begin{hide} {
+ if (sim == null)
+ throw new NullPointerException();
+ active = false;
+ this.sim = sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new continuous-time variable linked to
+ the given simulator, {\em without\/}
+ initializing it.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{the simulator associated to this variable.}
+\end{htmlonly}
+%%%%%%%%%%%%
+\begin{comment}
+\begin{vcode}
+
+ public Continuous (String name) \begin{hide} {
+ this();
+ this.name = name;
+ } \end{hide}
+\end{vcode}
+ \begin{tabb} Constructs a new continuous-time variable
+ (same as \texttt{Continuous()}) with name \texttt{name}
+ and linked to the default simulator.
+ This name can be used to identify the \texttt{Continuous}
+ variable in traces and reports.
+ \end{tabb}
+%\begin{htmlonly}
+% \param{name}{name associated to this variable}
+%\end{htmlonly}
+\begin{vcode}
+
+ public Continuous (Simulator sim, String name) \begin{hide} {
+ this(sim);
+ this.name = name;
+ } \end{hide}
+\end{vcode}
+ \begin{tabb} Constructs a new continuous-time variable
+ (same as \texttt{Continuous(sim)}) with name \texttt{name}.
+ This name can be used to identify the \texttt{Continuous}
+ variable in traces and reports.
+ \end{tabb}
+%\begin{htmlonly}
+% \param{name}{name associated to this variable}
+% \param{sim}{simulator associated to this variable}
+%\end{htmlonly}
+\end{comment}
+%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void init (double val) \begin{hide} {
+ value = val;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Initializes or reinitializes the continuous-time variable
+ to \texttt{val}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{val}{initial value of the variable}
+\end{htmlonly}
+\begin{code}
+
+ public double value() \begin{hide} {
+ return value;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the current value of this continuous-time variable.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the current value of the variable}
+\end{htmlonly}
+\begin{code}
+
+ public Simulator simulator() \begin{hide} {
+ return sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the simulator linked to this continuous-time variable.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the current simulator of the variable}
+\end{htmlonly}
+\begin{code}
+
+ public void setSimulator(Simulator sim) \begin{hide} {
+ if (sim == null)
+ throw new NullPointerException();
+ this.sim = sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Sets the simulator linked to this continuous-time variable.
+ This method should not be called while this variable is active.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{the simulator of the current variable}
+\end{htmlonly}
+\begin{code}
+
+ public void startInteg() \begin{hide} {
+ sim.continuousState().startInteg(this);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Starts the integration process that will change the state of
+ this variable at each integration step.
+ \end{tabb}
+\begin{code}
+
+ public void startInteg (double val) \begin{hide} {
+ init (val); startInteg();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Same as \method{startInteg}{}, after initializing the variable
+ to \texttt{val}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{val}{initial value to start integration from}
+\end{htmlonly}
+\begin{code}
+
+ public void stopInteg() \begin{hide} {
+ sim.continuousState().stopInteg(this);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Stops the integration process for this continuous variable.
+ The variable keeps the value it took at the last integration step
+ before calling \texttt{stopInteg}.
+ \end{tabb}
+\begin{code}
+
+ public abstract double derivative (double t);
+\end{code}
+ \begin{tabb} This method should return the derivative of this variable
+ with respect to time, at time $t$.
+ Every subclass of \texttt{Continuous} that is to be instantiated
+ must implement it.
+ If the derivative does not depend explicitly on time, $t$ becomes
+ a dummy parameter. Internally, the method is used with $t$ not
+ necessarily equal to the current simulation time.
+ \end{tabb}
+\begin{htmlonly}
+ \param{t}{time at which the derivative must be computed}
+\end{htmlonly}
+\begin{code}
+
+ public void afterEachStep() \begin{hide} {
+ } \end{hide}
+\end{code}
+ \begin{tabb} This method is executed after each integration step
+ for this \texttt{Continuous} variable.
+ Here, it does nothing, but every subclass of \texttt{Continuous} may
+ reimplement it.
+ \end{tabb}
+\begin{code}
+
+ public static void selectEuler(double h) \begin{hide} {
+ Simulator.getDefaultSimulator().continuousState().selectEuler(h);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Selects the Euler method as the integration method,
+ with the integration step size \texttt{h}, in time units, for the default simulator.
+ The non-static method \method{selectEuler}{} in \class{ContinuousState}
+ can be used to set the integration method for any given simulator.
+ This method appears here only to keep compatibility with older versions of SSJ; using
+ a non-static \class{Simulator} instance rather than the default simulator is recommended.
+ \end{tabb}
+\begin{htmlonly}
+ \param{h}{integration step, in simulation time units}
+\end{htmlonly}
+\begin{code}
+
+ public static void selectRungeKutta4(double h) \begin{hide} {
+ Simulator.getDefaultSimulator().continuousState().selectRungeKutta4(h);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Selects a Runge-Kutta method of order~4 as the integration
+ method to be used, with step size \texttt{h}.
+ The non-static method \method{selectRungeKutta4}{} in \class{ContinuousState}
+ can be used to set the integration method for any given simulator.
+ This method appears here only to keep compatibility with older versions of SSJ; using
+ a non-static \class{Simulator} instance rather than the default simulator is recommended.
+ \end{tabb}
+\begin{code}
+
+ public static void selectRungeKutta2(double h) \begin{hide} {
+ Simulator.getDefaultSimulator().continuousState().selectRungeKutta2(h);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Selects a Runge-Kutta method of order~2 as the integration
+ method to be used, with step size \texttt{h}.
+ The non-static method \method{selectRungeKutta2}{} in \class{ContinuousState}
+ can be used to set the integration method for any given simulator.
+ This method appears here only to keep compatibility with older versions of SSJ; using
+ a non-static \class{Simulator} instance rather than the default simulator is recommended.
+ \end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/ContinuousState.java b/source/umontreal/iro/lecuyer/simevents/ContinuousState.java
new file mode 100644
index 0000000..8e3dfd8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/ContinuousState.java
@@ -0,0 +1,271 @@
+
+
+/*
+ * Class: ContinuousState
+ * Description: Represents the portion of the simulator's state associated
+ with continuous-time simulation.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;
+
+
+/**
+ * Represents the portion of the simulator's state associated with
+ * continuous-time simulation.
+ * Any simulator, including the default static one, can have an associate continuous state
+ * which is obtained using the <TT>continuousState()</TT> method
+ * of the {@link Simulator} class.
+ * This state includes all active integration variables as well as the current integration method.
+ *
+ * <P>
+ * One of the methods {@link #selectEuler selectEuler}, {@link #selectRungeKutta2 selectRungeKutta2} or
+ * {@link #selectRungeKutta4 selectRungeKutta4} must be called before starting
+ * any integration.
+ * These methods permit one to select the numerical integration method
+ * and the step size <TT>h</TT> (in time units) that will be used
+ * for <SPAN CLASS="textit">all</SPAN> continuous-time variables linked to the simulator.
+ * For all the methods, an integration step at time <SPAN CLASS="MATH"><I>t</I></SPAN> changes
+ * the values of the variables from their old values at time <SPAN CLASS="MATH"><I>t</I> - <I>h</I></SPAN> to their
+ * new values at time <SPAN CLASS="MATH"><I>t</I></SPAN>.
+ *
+ * <P>
+ * Each integration step is scheduled as an event and added to the event list.
+ *
+ */
+public class ContinuousState {
+
+
+ // Integration methods
+ public enum IntegMethod{
+ EULER, // Euler integration method
+ RUNGEKUTTA2, // Runge-Kutta integration method of order 2
+ RUNGEKUTTA4 // Runge-Kutta integration method of order 4
+ }
+
+ private double stepSize; // Integration step size.
+ private IntegMethod integMethod; // Integration method in use.
+ private int order; // Order of the method in use.
+ private double[] A = new double[4];
+ private double[] B = new double[4];
+ private double[] C = new double[4];
+
+ // The event that actually executes integration steps.
+ private StepEvent stepEv = null;
+
+ // Class of event that executes an integration step.
+ private class StepEvent extends Event {
+ public StepEvent(Simulator sim) { super(sim); }
+ public void actions() {
+ switch (integMethod) {
+ case EULER: oneStepEuler(); break;
+ case RUNGEKUTTA2: oneStepRK(); break;
+ case RUNGEKUTTA4: oneStepRK(); break;
+ default: throw new IllegalArgumentException
+ ("Integration step with undefined method");
+ }
+ this.schedule (stepSize);
+ // if (afterInteg != null) afterInteg.actions();
+ }
+
+ public String toString() {
+ return "Integration step for continuous variable ";
+ }
+ }
+
+ private List<Continuous> list;
+ private Simulator sim;
+
+
+ /**
+ * Creates a new {@link ContinuousState} object linked to the given simulator.
+ * Usually, the user should not call this constructor directly since a new object
+ * is created automatically by the <TT>continuousState()</TT> method of
+ * class {@link Simulator}.
+ *
+ */
+ protected ContinuousState (Simulator sim) {
+ this.list = new ArrayList<Continuous>();
+ this.sim = sim;
+ assert sim != null;
+ }
+
+
+
+ /**
+ * Returns the list of continuous-time variables currently
+ * integrated by the simulator.
+ * The returned list is updated automatically as variables are added or removed, but it
+ * cannot be modified directly. One must instead use
+ * <TT>startInteg</TT> or <TT>stopInteg</TT> in class {@link Continuous} to add
+ * or remove variables.
+ *
+ *
+ */
+ public List<Continuous> getContinuousVariables() {
+ return Collections.unmodifiableList (list);
+ }
+
+
+ /**
+ * Starts the integration process that will change the state of
+ * {@link Continuous} variable at each integration step.
+ *
+ */
+ protected void startInteg(Continuous c) {
+ // The following is done only the first time this method is called.
+ if (stepEv == null)
+ stepEv = new StepEvent(sim);
+ c.active = true;
+ // Inserts this in list of active variables.
+ if (list.isEmpty()) {
+ stepEv.schedule (stepSize);
+ } // There was no active variable.
+ list.add(c);
+ }
+
+
+ protected void stopInteg(Continuous c) {
+ c.active = false;
+ list.remove(c);
+ if (list.isEmpty()) stepEv.cancel();
+ }
+
+
+ /**
+ * Return an integer that represent the integration method in use.
+ *
+ * @return Interger that represent the integration method in use.
+ *
+ */
+ public IntegMethod integMethod () {
+ return integMethod;
+ }
+
+
+ /**
+ * Selects the Euler method as the integration method,
+ * with the integration step size <TT>h</TT>, in time units.
+ *
+ * @param h integration step, in simulation time units
+ *
+ *
+ */
+ public void selectEuler (double h) {
+ integMethod = IntegMethod.EULER;
+ stepSize = h;
+ }
+
+
+ /**
+ * Selects a Runge-Kutta method of order 2 as the integration
+ * method to be used, with step size <TT>h</TT>.
+ *
+ * @param h integration step, in simulation time units
+ *
+ *
+ */
+ public void selectRungeKutta2 (double h) {
+ integMethod = IntegMethod.RUNGEKUTTA2;
+ stepSize = h;
+ order = 2;
+ A[0] = 1.0; A[1] = 0.0;
+ B[0] = 0.5; B[1] = 0.5;
+ C[0] = 0.0; C[1] = 1.0;
+ }
+
+
+ /**
+ * Selects a Runge-Kutta method of order 4 as the integration
+ * method to be used, with step size <TT>h</TT>.
+ *
+ * @param h integration step, in simulation time units
+ *
+ *
+ */
+ public void selectRungeKutta4 (double h) {
+ integMethod = IntegMethod.RUNGEKUTTA4;
+ stepSize = h;
+ order = 4;
+ A[0] = 0.5; A[1] = 0.5; A[2] = 1.0; A[3] = 0.0;
+ B[0] = 1.0/6.0; B[1] = 1.0/3.0;
+ B[2] = 1.0/3.0; B[3] = 1.0/6.0;
+ C[0] = 0.0; C[1] = 0.5; C[2] = 0.5; C[3] = 1.0;
+ }
+
+
+ private void oneStepEuler() {
+ Continuous v;
+ double t = sim.time() - stepSize;
+ int current;
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.phi = v.value + stepSize * v.derivative (t);
+ }
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.value = v.phi;
+ if (v.ev != null)
+ v.ev.scheduleNext();
+ v.afterEachStep();
+ }
+ }
+
+ private void oneStepRK() {
+ Continuous v;
+ double t = sim.time() - stepSize;
+ int current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.buffer = v.value;
+ v.sum = 0.0;
+ v.pi = 0.0;
+ }
+ for (int i = 1; i <= order-1; i++) {
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.pi = v.derivative (t + stepSize * C[i-1]);
+ v.sum = v.sum + v.pi * B[i-1];
+ v.phi = v.buffer + stepSize * v.pi * A[i-1];
+ }
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.value = v.phi;
+ }
+ }
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.pi = v.derivative (t + stepSize * C[order-1]);
+ v.value = v.buffer + stepSize * (v.sum + v.pi * B[order-1]);
+ if (v.ev != null) v.ev.scheduleNext();
+ v.afterEachStep();
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/ContinuousState.tex b/source/umontreal/iro/lecuyer/simevents/ContinuousState.tex
new file mode 100644
index 0000000..3041639
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/ContinuousState.tex
@@ -0,0 +1,278 @@
+\defmodule {ContinuousState}
+
+Represents the portion of the simulator's state associated with
+continuous-time simulation.
+Any simulator, including the default static one, can have an associate continuous state
+which is obtained using the \texttt{continuousState()} method
+of the \class{Simulator} class.
+This state includes all active integration variables as well as the current integration method.
+
+One of the methods \method{selectEuler}{}, \method{selectRungeKutta2}{} or
+\method{selectRungeKutta4}{} must be called before starting
+any integration.
+These methods permit one to select the numerical integration method
+and the step size \texttt{h} (in time units) that will be used
+for \emph{all} continuous-time variables linked to the simulator.
+For all the methods, an integration step at time $t$ changes
+the values of the variables from their old values at time $t-h$ to their
+new values at time $t$.
+
+Each integration step is scheduled as an event and added to the event list.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ContinuousState
+ * Description: Represents the portion of the simulator's state associated
+ with continuous-time simulation.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;\begin{hide}
+
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;\end{hide}
+
+
+public class ContinuousState \begin{hide} {
+\end{hide}
+
+ // Integration methods
+ public enum IntegMethod{
+ EULER, // Euler integration method
+ RUNGEKUTTA2, // Runge-Kutta integration method of order 2
+ RUNGEKUTTA4 // Runge-Kutta integration method of order 4
+ }\begin{hide}
+
+ private double stepSize; // Integration step size.
+ private IntegMethod integMethod; // Integration method in use.
+ private int order; // Order of the method in use.
+ private double[] A = new double[4];
+ private double[] B = new double[4];
+ private double[] C = new double[4];
+
+ // The event that actually executes integration steps.
+ private StepEvent stepEv = null;
+
+ // Class of event that executes an integration step.
+ private class StepEvent extends Event {
+ public StepEvent(Simulator sim) { super(sim); }
+ public void actions() {
+ switch (integMethod) {
+ case EULER: oneStepEuler(); break;
+ case RUNGEKUTTA2: oneStepRK(); break;
+ case RUNGEKUTTA4: oneStepRK(); break;
+ default: throw new IllegalArgumentException
+ ("Integration step with undefined method");
+ }
+ this.schedule (stepSize);
+ // if (afterInteg != null) afterInteg.actions();
+ }
+
+ public String toString() {
+ return "Integration step for continuous variable ";
+ }
+ }
+
+ private List<Continuous> list;
+ private Simulator sim;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+ protected ContinuousState (Simulator sim) \begin{hide} {
+ this.list = new ArrayList<Continuous>();
+ this.sim = sim;
+ assert sim != null;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb} Creates a new \class{ContinuousState} object linked to the given simulator.
+ Usually, the user should not call this constructor directly since a new object
+ is created automatically by the \texttt{continuousState()} method of
+ class \class{Simulator}.
+ \end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public List<Continuous> getContinuousVariables()\begin{hide} {
+ return Collections.unmodifiableList (list);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the list of continuous-time variables currently
+ integrated by the simulator.
+ The returned list is updated automatically as variables are added or removed, but it
+ cannot be modified directly. One must instead use
+ \texttt{startInteg} or \texttt{stopInteg} in class \class{Continuous} to add
+ or remove variables.
+\end{tabb}
+ \begin{hide} \begin{code}
+
+ protected void startInteg(Continuous c) {
+ // The following is done only the first time this method is called.
+ if (stepEv == null)
+ stepEv = new StepEvent(sim);
+ c.active = true;
+ // Inserts this in list of active variables.
+ if (list.isEmpty()) {
+ stepEv.schedule (stepSize);
+ } // There was no active variable.
+ list.add(c);
+ }
+\end{code}
+ \begin{tabb} Starts the integration process that will change the state of
+ \class{Continuous} variable at each integration step.
+ \end{tabb}
+ \begin{code}
+
+ protected void stopInteg(Continuous c) {
+ c.active = false;
+ list.remove(c);
+ if (list.isEmpty()) stepEv.cancel();
+ }
+\end{code}
+ \begin{tabb} Stops the integration process for \class{Continuous} variable.
+ The variable keeps the value it took at the last integration step
+ before calling \texttt{stopInteg}.
+ \end{tabb}\end{hide}
+ \begin{code}
+
+ public IntegMethod integMethod ()\begin{hide} {
+ return integMethod;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Return an integer that represent the integration method in use.
+ \end{tabb}
+\begin{htmlonly}
+ \return{Interger that represent the integration method in use.}
+\end{htmlonly}
+\begin{code}
+
+ public void selectEuler (double h)\begin{hide} {
+ integMethod = IntegMethod.EULER;
+ stepSize = h;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Selects the Euler method as the integration method,
+ with the integration step size \texttt{h}, in time units.
+ \end{tabb}
+\begin{htmlonly}
+ \param{h}{integration step, in simulation time units}
+\end{htmlonly}
+\begin{code}
+
+ public void selectRungeKutta2 (double h)\begin{hide} {
+ integMethod = IntegMethod.RUNGEKUTTA2;
+ stepSize = h;
+ order = 2;
+ A[0] = 1.0; A[1] = 0.0;
+ B[0] = 0.5; B[1] = 0.5;
+ C[0] = 0.0; C[1] = 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Selects a Runge-Kutta method of order~2 as the integration
+ method to be used, with step size \texttt{h}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{h}{integration step, in simulation time units}
+\end{htmlonly}
+\begin{code}
+
+ public void selectRungeKutta4 (double h)\begin{hide} {
+ integMethod = IntegMethod.RUNGEKUTTA4;
+ stepSize = h;
+ order = 4;
+ A[0] = 0.5; A[1] = 0.5; A[2] = 1.0; A[3] = 0.0;
+ B[0] = 1.0/6.0; B[1] = 1.0/3.0;
+ B[2] = 1.0/3.0; B[3] = 1.0/6.0;
+ C[0] = 0.0; C[1] = 0.5; C[2] = 0.5; C[3] = 1.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Selects a Runge-Kutta method of order~4 as the integration
+ method to be used, with step size \texttt{h}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{h}{integration step, in simulation time units}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+
+ private void oneStepEuler() {
+ Continuous v;
+ double t = sim.time() - stepSize;
+ int current;
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.phi = v.value + stepSize * v.derivative (t);
+ }
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.value = v.phi;
+ if (v.ev != null)
+ v.ev.scheduleNext();
+ v.afterEachStep();
+ }
+ }
+
+ private void oneStepRK() {
+ Continuous v;
+ double t = sim.time() - stepSize;
+ int current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.buffer = v.value;
+ v.sum = 0.0;
+ v.pi = 0.0;
+ }
+ for (int i = 1; i <= order-1; i++) {
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.pi = v.derivative (t + stepSize * C[i-1]);
+ v.sum = v.sum + v.pi * B[i-1];
+ v.phi = v.buffer + stepSize * v.pi * A[i-1];
+ }
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.value = v.phi;
+ }
+ }
+ current = list.size();
+ while (current > 0) {
+ v = list.get(--current);
+ v.pi = v.derivative (t + stepSize * C[order-1]);
+ v.value = v.buffer + stepSize * (v.sum + v.pi * B[order-1]);
+ if (v.ev != null) v.ev.scheduleNext();
+ v.afterEachStep();
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/Event.java b/source/umontreal/iro/lecuyer/simevents/Event.java
new file mode 100644
index 0000000..68462b7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Event.java
@@ -0,0 +1,369 @@
+
+
+/*
+ * Class: Event
+ * Description: provides event scheduling tools
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+
+/**
+ * This abstract class provides event scheduling tools.
+ * Each type of event should be defined as a subclass of the
+ * class <TT>Event</TT>, and should provide an implementation of the method
+ * {@link #actions actions} which is executed when an event of this type occurs.
+ * The instances of these subclasses are the actual events.
+ *
+ * <P>
+ * Each event is linked to a simulator represented by an instance of
+ * {@link Simulator} before it can be scheduled and processed.
+ * A default simulator, given by <TT>Simulator.getDefaultSimulator</TT>,
+ * is used if no simulator is linked explicitly with an event.
+ * When an event is constructed, it is not scheduled. It must be scheduled
+ * separately by calling one of the scheduling methods {@link #schedule schedule},
+ * {@link #scheduleNext scheduleNext}, {@link #scheduleBefore scheduleBefore}, etc.
+ * An event can also be cancelled before it occurs.
+ *
+ * <P>
+ * A scheduled event has an associated time at which it will happen and a priority,
+ * which can be queried using the methods {@link #time time} and {@link #priority priority}, respectively.
+ * By default, events occur in ascending order of time, and have priority 1.
+ * Events with the same time occur in ascending order of priority.
+ * For example, if events <TT>e1</TT> and <TT>e2</TT> occur at the same time
+ * with priority 2 and 1 respectively, then <TT>e2</TT> will occur before <TT>e1</TT>.
+ * Events with the same time and priority occur in the order they were scheduled.
+ *
+ */
+public abstract class Event implements Comparable<Event> {
+
+ protected Simulator sim;
+ //simulator linked with the current event
+
+ protected double priority;
+ //priority of the event. Priority is a second parameter (after eventTime)
+ // used to class events for their running order, in the EventList.
+
+ protected double eventTime;
+ // Planned time of occurence of this event. Negative if not planned.
+ // Is protected because it is used (changed) in Process.
+
+ // Replace that with instanceof simProcess to completely detach processes.
+ // protected boolean isProcess = false;
+ // Will be true for objects of the subclass Process of the class Event.
+ // (i.e., true if this event is a process.)
+
+ private int myra = 0;
+ // A new event must always occur after those with same time and
+ // same priority in the Event list. myra is used for that in
+ // SplayTree.java.
+
+ // For internal use
+ public final int getRa() { return myra; }
+ public final void setRa(int r) { myra = r; }
+
+
+
+ /**
+ * Constructs a new event instance, which can be placed afterwards
+ * into the event list of the default simulator.
+ * For example, if <TT>Bang</TT> is an <TT>Event</TT> subclass,
+ * the statement ``<TT>new Bang().scheduleNext();</TT>'' creates a new
+ * <TT>Bang</TT> event and places it at the beginning of the event list.
+ *
+ */
+ public Event() {
+ this (Simulator.getDefaultSimulator());
+ }
+
+
+ /**
+ * Construct a new event instance associated with the given simulator.
+ *
+ * @param sim Instance of class Simulator associated with the new Event
+ *
+ */
+ public Event (Simulator sim) {
+ if (sim == null)
+ throw new NullPointerException();
+ eventTime = -10.0;
+ priority = 1.0;
+ this.sim = sim;
+ }
+
+
+ /**
+ * Schedules this event to happen in <TT>delay</TT> time units,
+ * i.e., at time <TT>sim.time() + delay</TT>, by inserting it in the event list.
+ * When two or more events are scheduled to happen at the same time and
+ * with the same priority,
+ * they are placed in the event list (and executed) in the same order
+ * as they have been scheduled.
+ * Note that the priority of this event should be adjusted using
+ * {@link #setPriority setPriority} <SPAN CLASS="textit">before</SPAN> it is scheduled.
+ *
+ * @param delay simulation time that must pass before the event happens
+ *
+ *
+ */
+ public void schedule (double delay) {
+ if (delay < 0.0)
+ throw new IllegalArgumentException ("Cannot schedule in the past.");
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = sim.time() + delay;
+ sim.eventList.add (this);
+ }
+
+
+ /**
+ * Schedules this event as the <EM>first</EM> event in the event
+ * list, to be executed at the current time (as the next event).
+ *
+ */
+ public void scheduleNext() {
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = sim.time();
+ priority = 0.0;
+ sim.eventList.addFirst (this);
+ }
+
+
+ /**
+ * Schedules this event to happen just before, and at the same
+ * time, as the event <TT>other</TT>.
+ * For example, if <TT>Bing</TT> and <TT>Bang</TT> are <TT>Event</TT>
+ * subclasses, after the statements
+ *
+ * <P>
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ *
+ * <BR> Bang bigOne = new Bang().schedule(12.0);
+ * <BR> new Bing().scheduleBefore(bigOne);
+ * <BR> </TT>
+ * </DIV>
+ * the event list contains two new events scheduled to happen in 12
+ * units of time: a <TT>Bing</TT> event, followed by a <TT>Bang</TT> called
+ * <TT>bigOne</TT>.
+ *
+ * @param other event before which this event will be scheduled
+ *
+ *
+ */
+ public void scheduleBefore (Event other) {
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = other.eventTime;
+ priority = other.priority;
+ sim.eventList.addBefore (this, other);
+ }
+
+
+ /**
+ * Schedules this event to happen just after, and at the same
+ * time, as the event <TT>other</TT>.
+ *
+ * @param other event after which this event will be scheduled
+ *
+ *
+ */
+ public void scheduleAfter (Event other) {
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = other.eventTime;
+ priority = other.priority;
+ sim.eventList.addAfter (this, other);
+ }
+
+
+ /**
+ * Cancels this event and reschedules it to happen
+ * in <TT>delay</TT> time units.
+ *
+ * @param delay simulation time units that must elapse before the event happens
+ *
+ *
+ */
+ public void reschedule (double delay) {
+ if (delay < 0.0)
+ throw new IllegalArgumentException ("Cannot schedule in the past.");
+ if (eventTime < -1.0)
+ throw new IllegalStateException ("Event not scheduled");
+ sim.getEventList().remove (this);
+ eventTime = sim.time() + delay;
+ sim.getEventList().add (this);
+ }
+
+
+ /**
+ * Cancels this event before it occurs.
+ * Returns <TT>true</TT> if cancellation succeeds (this event was found
+ * in the event list), <TT>false</TT> otherwise.
+ *
+ * @return <TT>true</TT> if the event could be cancelled
+ *
+ */
+ public boolean cancel() {
+ boolean removed = false;
+ if (eventTime >= sim.time()) removed = sim.getEventList().remove (this);
+ eventTime = -10.0;
+ return removed;
+ }
+
+
+ /**
+ * Finds the first occurence of an event of class ``type''
+ * in the event list, and cancels it.
+ * Returns <TT>true</TT> if cancellation succeeds, <TT>false</TT> otherwise.
+ *
+ * @param type name of an event subclass
+ *
+ * @return <TT>true</TT> if an event of this class was found and cancelled
+ *
+ */
+ public final boolean cancel (String type) {
+ Event ev = sim.getEventList().getFirstOfClass (type);
+ return ev.cancel();
+ }
+
+
+ /**
+ * Returns the simulator linked to this event.
+ *
+ * @return the simulator linked to the event
+ *
+ */
+ public final Simulator simulator() {
+ return sim;
+ }
+
+
+ /**
+ * Sets the simulator associated with this event to
+ * <TT>sim</TT>.
+ * This method should not be called while this event is in an event list.
+ *
+ * @param sim the Simulator
+ *
+ *
+ */
+ public final void setSimulator (Simulator sim) {
+ if (sim == null)
+ throw new NullPointerException();
+ if (eventTime > -1.0)
+ throw new UnsupportedOperationException (
+ "Unable to set Simulator, current Event already scheduled");
+ this.sim = sim;
+ }
+
+
+ /**
+ * Returns the (planned) time of occurence of this event.
+ *
+ * @return the time of occurence of the event
+ *
+ */
+ public final double time() {
+ return eventTime;
+ }
+
+
+ /**
+ * Sets the (planned) time of occurence of this event to <TT>time</TT>.
+ * This method should never be called after the event was scheduled, otherwise
+ * the events would not execute in ascending time order anymore.
+ *
+ * @param time new time of occurence for the event
+ *
+ *
+ */
+ public final void setTime (double time) {
+ if (eventTime > -1.0)
+ throw new UnsupportedOperationException(
+ "Unable to set time, current Event already scheduled");
+ eventTime = time;
+ }
+
+
+ /**
+ * Returns the priority of this event.
+ *
+ * @return the priority of the event
+ *
+ */
+ public final double priority() {
+ return priority;
+ }
+
+
+ /**
+ * Sets the priority of this event to <TT>inPriority</TT>.
+ * This method should never be called after the event was scheduled, otherwise
+ * the events would not execute in ascending priority order anymore.
+ *
+ * @param priority new priority for the event
+ *
+ *
+ */
+ public final void setPriority (double priority) {
+ if(eventTime > -1.0)
+ throw new UnsupportedOperationException(
+ "Unable to set priority, current Event already scheduled");
+ this.priority = priority;
+ }
+
+
+ /**
+ * Compares this object with the specified object <TT>e</TT> for
+ * order. Returns <SPAN CLASS="MATH">-1</SPAN> or <SPAN CLASS="MATH">+1</SPAN> as this event occurs before or after the specified
+ * event <TT>e</TT>, respectively. If the two events occur at the same time, then
+ * returns <SPAN CLASS="MATH">-1</SPAN>, <SPAN CLASS="MATH">0</SPAN>, or <SPAN CLASS="MATH">+1</SPAN> as this event has a smaller, equal, or larger
+ * priority than event <TT>e</TT>.
+ *
+ */
+ public int compareTo (Event e) {
+ if (eventTime < e.time())
+ return -1;
+ if (eventTime > e.time())
+ return 1;
+ // Si le moment de declenchement des "Event" est identique, on
+ // examine leurs priorites.
+ if (priority < e.priority())
+ return -1;
+ if (priority > e.priority())
+ return 1;
+ return 0;
+ }
+
+
+ /**
+ * This is the method that is executed when this event occurs.
+ * Every subclass of <TT>Event</TT> that is to be instantiated must provide
+ * an implementation of this method.
+ *
+ */
+ public abstract void actions();
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/simevents/Event.tex b/source/umontreal/iro/lecuyer/simevents/Event.tex
new file mode 100644
index 0000000..477f62f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Event.tex
@@ -0,0 +1,423 @@
+\defmodule {Event}
+
+This abstract class provides event scheduling tools.
+Each type of event should be defined as a subclass of the
+class \texttt{Event}, and should provide an implementation of the method
+\method{actions}{} which is executed when an event of this type occurs.
+The instances of these subclasses are the actual events.
+
+Each event is linked to a simulator represented by an instance of
+\class{Simulator} before it can be scheduled and processed.
+A default simulator, given by \texttt{Simulator.getDefaultSimulator},
+is used if no simulator is linked explicitly with an event.
+When an event is constructed, it is not scheduled. It must be scheduled
+separately by calling one of the scheduling methods \method{schedule}{},
+\method{scheduleNext}{}, \method{scheduleBefore}{}, etc.
+An event can also be cancelled before it occurs.
+
+A scheduled event has an associated time at which it will happen and a priority,
+which can be queried using the methods \method{time}{} and \method{priority}{}, respectively.
+By default, events occur in ascending order of time, and have priority 1.
+Events with the same time occur in ascending order of priority.
+For example, if events \texttt{e1} and \texttt{e2} occur at the same time
+with priority 2 and 1 respectively, then \texttt{e2} will occur before \texttt{e1}.
+Events with the same time and priority occur in the order they were scheduled.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Event
+ * Description: provides event scheduling tools
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;
+
+public abstract class Event implements Comparable<Event>\begin{hide} {
+
+ protected Simulator sim;
+ //simulator linked with the current event
+
+ protected double priority;
+ //priority of the event. Priority is a second parameter (after eventTime)
+ // used to class events for their running order, in the EventList.
+
+ protected double eventTime;
+ // Planned time of occurence of this event. Negative if not planned.
+ // Is protected because it is used (changed) in Process.
+
+ // Replace that with instanceof simProcess to completely detach processes.
+ // protected boolean isProcess = false;
+ // Will be true for objects of the subclass Process of the class Event.
+ // (i.e., true if this event is a process.)
+
+ private int myra = 0;
+ // A new event must always occur after those with same time and
+ // same priority in the Event list. myra is used for that in
+ // SplayTree.java.
+
+ // For internal use
+ public final int getRa() { return myra; }
+ public final void setRa(int r) { myra = r; }
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+
+ public Event() \begin{hide} {
+ this (Simulator.getDefaultSimulator());
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new event instance, which can be placed afterwards
+ into the event list of the default simulator\latex{ by calling one of the
+ \texttt{schedule...} variants}.
+ For example, if \texttt{Bang} is an \texttt{Event} subclass,
+ the statement ``\texttt{new Bang().scheduleNext();}'' creates a new
+ \texttt{Bang} event and places it at the beginning of the event list.
+ \end{tabb}
+%%%%%%%%%%
+\begin{comment}
+\begin{vcode}
+
+ public Event (double delay) \begin{hide} {
+ this (Simulator.getDefaultSimulator(), delay);
+ } \end{hide}
+
+ public Event (Simulator sim, double delay)\begin{hide} {
+ if (sim == null)
+ throw new NullPointerException();
+ if (delay >= 0.0) {
+ priority = 1;
+ this.sim = sim;
+ eventTime = sim.time() + delay;
+ sim.eventList.add (this);
+ }
+ else
+ throw new IllegalArgumentException ("Cannot schedule in the past.");
+ }\end{hide}
+\end{vcode}
+ \begin{tabb} Constructs a new event and inserts it in the event
+ list of the default simulator.
+ If \texttt{delay >= 0.0}, the event is scheduled to happen in
+ \texttt{delay} units of simutated time.
+ If two or more events are scheduled to happen at the same time,
+ events with the highest priorities (lowest value of the
+ \texttt{priority}
+ field) occur first. If two or more events are schedule to the
+ same time, with the same priority,
+ they are placed in the event list (and executed) in the same order
+ as they have been scheduled.
+
+ We recall that such constructors with parameters are not inherited
+ automatically by the subclasses in Java, but they can be invoked
+ using \texttt{super}. For example, one can have
+\begin{vcode}
+ class Bang extends Event {
+ public Bang (double delay) { super (delay); }
+ public void actions() { \dots }
+\end{vcode}
+ and then invoke the constructor ``\texttt{new Bang (10.0)}'' to get
+ a \texttt{Bang} in 10 units of time.
+ This is equivalent to ``\texttt{new Bang().schedule(10.0)}.''
+ \end{tabb}
+\begin{htmlonly}
+ \param{delay}{simulation time that must pass before the event happens}
+\end{htmlonly}
+\end{comment}
+\begin{code}
+
+ public Event (Simulator sim) \begin{hide} {
+ if (sim == null)
+ throw new NullPointerException();
+ eventTime = -10.0;
+ priority = 1.0;
+ this.sim = sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Construct a new event instance associated with the given simulator.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{Instance of class Simulator associated with the new Event}
+\end{htmlonly}
+%%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public void schedule (double delay) \begin{hide} {
+ if (delay < 0.0)
+ throw new IllegalArgumentException ("Cannot schedule in the past.");
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = sim.time() + delay;
+ sim.eventList.add (this);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Schedules this event to happen in \texttt{delay} time units,
+ i.e., at time \texttt{sim.time() + delay}, by inserting it in the event list.
+ When two or more events are scheduled to happen at the same time and
+ with the same priority,
+ they are placed in the event list (and executed) in the same order
+ as they have been scheduled.
+ Note that the priority of this event should be adjusted using
+ \method{setPriority}{} \emph{before} it is scheduled.
+ \end{tabb}
+\begin{htmlonly}
+ \param{delay}{simulation time that must pass before the event happens}
+\end{htmlonly}
+\begin{code}
+
+ public void scheduleNext() \begin{hide} {
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = sim.time();
+ priority = 0.0;
+ sim.eventList.addFirst (this);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Schedules this event as the {\em first\/} event in the event
+ list, to be executed at the current time (as the next event).
+ \end{tabb}
+\begin{code}
+
+ public void scheduleBefore (Event other) \begin{hide} {
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = other.eventTime;
+ priority = other.priority;
+ sim.eventList.addBefore (this, other);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Schedules this event to happen just before, and at the same
+ time, as the event \texttt{other}.
+ For example, if \texttt{Bing} and \texttt{Bang} are \texttt{Event}
+ subclasses, after the statements
+ \begin{vcode}
+
+ Bang bigOne = new Bang().schedule(12.0);
+ new Bing().scheduleBefore(bigOne);
+ \end{vcode}
+ the event list contains two new events scheduled to happen in 12
+ units of time: a \texttt{Bing} event, followed by a \texttt{Bang} called
+ \texttt{bigOne}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{other}{event before which this event will be scheduled}
+\end{htmlonly}
+\begin{code}
+
+ public void scheduleAfter (Event other) \begin{hide} {
+ if (eventTime > -1.0)
+ throw new IllegalStateException ("Event already scheduled");
+ eventTime = other.eventTime;
+ priority = other.priority;
+ sim.eventList.addAfter (this, other);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Schedules this event to happen just after, and at the same
+ time, as the event \texttt{other}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{other}{event after which this event will be scheduled}
+\end{htmlonly}
+\begin{code}
+
+ public void reschedule (double delay) \begin{hide} {
+ if (delay < 0.0)
+ throw new IllegalArgumentException ("Cannot schedule in the past.");
+ if (eventTime < -1.0)
+ throw new IllegalStateException ("Event not scheduled");
+ sim.getEventList().remove (this);
+ eventTime = sim.time() + delay;
+ sim.getEventList().add (this);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Cancels this event and reschedules it to happen
+ in \texttt{delay} time units.
+ \end{tabb}
+\begin{htmlonly}
+ \param{delay}{simulation time units that must elapse before the event happens}
+\end{htmlonly}
+\begin{code}
+
+ public boolean cancel() \begin{hide} {
+ boolean removed = false;
+ if (eventTime >= sim.time()) removed = sim.getEventList().remove (this);
+ eventTime = -10.0;
+ return removed;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Cancels this event before it occurs.
+ Returns \texttt{true} if cancellation succeeds (this event was found
+ in the event list), \texttt{false} otherwise.
+ \end{tabb}
+\begin{htmlonly}
+ \return{\texttt{true} if the event could be cancelled}
+\end{htmlonly}
+\begin{code}
+
+ public final boolean cancel (String type) \begin{hide} {
+ Event ev = sim.getEventList().getFirstOfClass (type);
+ return ev.cancel();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Finds the first occurence of an event of class ``type''
+ in the event list, and cancels it.
+ Returns \texttt{true} if cancellation succeeds, \texttt{false} otherwise.
+ \end{tabb}
+\begin{htmlonly}
+ \param{type}{name of an event subclass}
+ \return{\texttt{true} if an event of this class was found and cancelled}
+\end{htmlonly}
+\begin{code}
+
+ public final Simulator simulator() \begin{hide} {
+ return sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the simulator linked to this event.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the simulator linked to the event}
+\end{htmlonly}
+\begin{code}
+
+ public final void setSimulator (Simulator sim) \begin{hide} {
+ if (sim == null)
+ throw new NullPointerException();
+ if (eventTime > -1.0)
+ throw new UnsupportedOperationException (
+ "Unable to set Simulator, current Event already scheduled");
+ this.sim = sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Sets the simulator associated with this event to
+ \texttt{sim}.
+ This method should not be called while this event is in an event list.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{the Simulator}
+\end{htmlonly}
+\begin{code}
+
+ public final double time() \begin{hide} {
+ return eventTime;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the (planned) time of occurence of this event.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the time of occurence of the event}
+\end{htmlonly}
+\begin{code}
+
+ public final void setTime (double time) \begin{hide} {
+ if (eventTime > -1.0)
+ throw new UnsupportedOperationException(
+ "Unable to set time, current Event already scheduled");
+ eventTime = time;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Sets the (planned) time of occurence of this event to \texttt{time}.
+ This method should never be called after the event was scheduled, otherwise
+ the events would not execute in ascending time order anymore.
+ \end{tabb}
+\heric{This is used internally by the event lists but it could be avoided
+ because the event time is updated in the Event class. Actually, the event
+ time is set twice.
+ It is also used in simprocs because some special (negative) values
+ of event time mark process states. If we want to remove setTime,
+ we would have to implement a different mechanism (e.g., a new variable in
+ simProcess) to maintain the process state.}
+\begin{htmlonly}
+ \param{time}{new time of occurence for the event}
+\end{htmlonly}
+\begin{code}
+
+ public final double priority() \begin{hide} {
+ return priority;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the priority of this event.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the priority of the event}
+\end{htmlonly}
+\begin{code}
+
+ public final void setPriority (double priority) \begin{hide} {
+ if(eventTime > -1.0)
+ throw new UnsupportedOperationException(
+ "Unable to set priority, current Event already scheduled");
+ this.priority = priority;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Sets the priority of this event to \texttt{inPriority}.
+ This method should never be called after the event was scheduled, otherwise
+ the events would not execute in ascending priority order anymore.
+ \end{tabb}
+\begin{htmlonly}
+ \param{priority}{new priority for the event}
+\end{htmlonly}
+\begin{code}
+
+ public int compareTo (Event e) \begin{hide} {
+ if (eventTime < e.time())
+ return -1;
+ if (eventTime > e.time())
+ return 1;
+ // Si le moment de declenchement des "Event" est identique, on
+ // examine leurs priorites.
+ if (priority < e.priority())
+ return -1;
+ if (priority > e.priority())
+ return 1;
+ return 0;
+ } \end{hide}
+\end{code}
+\begin{tabb} Compares this object with the specified object \texttt{e} for
+order. Returns $-1$ or $+1$ as this event occurs before or after the specified
+event \texttt{e}, respectively. If the two events occur at the same time, then
+returns $-1$, $0$, or $+1$ as this event has a smaller, equal, or larger
+priority than event \texttt{e}.
+\end{tabb}
+\begin{code}
+
+ public abstract void actions();
+\end{code}
+ \begin{tabb} This is the method that is executed when this event occurs.
+ Every subclass of \texttt{Event} that is to be instantiated must provide
+ an implementation of this method.
+ \end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/LinkedListStat.java b/source/umontreal/iro/lecuyer/simevents/LinkedListStat.java
new file mode 100644
index 0000000..63aff5e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/LinkedListStat.java
@@ -0,0 +1,190 @@
+
+
+/*
+ * Class: LinkedListStat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * This class extends {@link ListWithStat}, and
+ * uses a linked list as the internal data structure.
+ *
+ */
+public class LinkedListStat<E> extends ListWithStat<E> {
+
+
+ /**
+ * Constructs a new list, initially empty.
+ *
+ */
+ public LinkedListStat() {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>());
+ }
+
+
+ /**
+ * Constructs a new list, initially empty, and using the default simulator.
+ *
+ * @param inSim Simulator associate to the current variable.
+ *
+ *
+ */
+ public LinkedListStat(Simulator inSim) {
+ super (inSim, new LinkedList<Node<E>>());
+ }
+
+
+ /**
+ * Constructs a list containing the elements of the specified
+ * collection, using the default simulator.
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ *
+ */
+ public LinkedListStat (Collection<? extends E> c) {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>(), c);
+ }
+
+
+ /**
+ * Constructs a list containing the elements of the specified
+ * collection.
+ *
+ * @param inSim Simulator associate to the current variable.
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ *
+ */
+ public LinkedListStat (Simulator inSim, Collection<? extends E> c) {
+ super (inSim, new LinkedList<Node<E>>(), c);
+ }
+
+
+ /**
+ * Constructs a new list with name <TT>name</TT>, using the default simulator.
+ * This name can be used to identify the list in traces and reports.
+ *
+ * @param name name for the list object
+ *
+ *
+ */
+ public LinkedListStat (String name) {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>(), name);
+ }
+
+
+ /**
+ * Constructs a new list with name <TT>name</TT>.
+ * This name can be used to identify the list in traces and reports.
+ *
+ * @param inSim Simulator associate to the current variable.
+ *
+ * @param name name for the list object
+ *
+ *
+ */
+ public LinkedListStat (Simulator inSim, String name) {
+ super (inSim, new LinkedList<Node<E>>(), name);
+ }
+
+
+ /**
+ * Constructs a new list containing the elements of the
+ * specified collection <TT>c</TT> and with name <TT>name</TT>, using the default simulator.
+ * This name can be used to identify the list in traces and reports.
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ * @param name name for the list object
+ *
+ *
+ */
+ public LinkedListStat (Collection<? extends E> c, String name) {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>(), c, name);
+ }
+
+
+ /**
+ * Constructs a new list containing the elements of the
+ * specified collection <TT>c</TT> and with name <TT>name</TT>.
+ * This name can be used to identify the list in traces and reports.
+ *
+ * @param inSim Simulator associate to the current variable.
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ * @param name name for the list object
+ *
+ */
+ public LinkedListStat (Simulator inSim, Collection<? extends E> c,
+ String name) {
+ super (inSim, new LinkedList<Node<E>>(), c, name);
+ }
+
+
+ public void addFirst (E obj) {
+ add (0, obj);
+ }
+
+
+ public void addLast (E obj) {
+ add (size(), obj);
+ }
+
+
+ public E getFirst() {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return get (0);
+ }
+
+
+ public E getLast() {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return get (size() - 1);
+ }
+
+
+ public E removeFirst() {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return remove (0);
+ }
+
+
+ public E removeLast() {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return remove (size() - 1);
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/LinkedListStat.tex b/source/umontreal/iro/lecuyer/simevents/LinkedListStat.tex
new file mode 100644
index 0000000..874c6e3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/LinkedListStat.tex
@@ -0,0 +1,206 @@
+\defmodule {LinkedListStat}
+
+This class extends \class{ListWithStat}, and
+uses a linked list as the internal data structure.
+
+%The iterators returned by the \method{listIterator()}{} method are
+%{\em fail-fast}: if
+%the list is structurally modified at any time after the iterator is created,
+%in any way except through the iterator's own
+%\externalmethod{java.util}{ListIterator}{remove}{} or
+%\externalmethod{java.util}{ListIterator}{add}{}
+%methods, the iterator will throw a \class{ConcurrentModificationException}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: LinkedListStat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;
+\begin{hide}
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class LinkedListStat<E> extends ListWithStat<E>\begin{hide} {
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public LinkedListStat()\begin{hide} {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>());
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new list, initially empty.
+\end{tabb}
+\begin{code}
+
+ public LinkedListStat(Simulator inSim)\begin{hide} {
+ super (inSim, new LinkedList<Node<E>>());
+ } \end{hide}
+\end{code}
+\begin{tabb} Constructs a new list, initially empty, and using the default simulator.
+\end{tabb}
+\begin{htmlonly}
+ \param{inSim}{Simulator associate to the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat (Collection<? extends E> c)\begin{hide} {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>(), c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a list containing the elements of the specified
+ collection, using the default simulator.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{collection containing elements to fill in this list with}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat (Simulator inSim, Collection<? extends E> c)\begin{hide} {
+ super (inSim, new LinkedList<Node<E>>(), c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a list containing the elements of the specified
+ collection.
+\end{tabb}
+\begin{htmlonly}
+ \param{inSim}{Simulator associate to the current variable.}
+ \param{c}{collection containing elements to fill in this list with}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat (String name)\begin{hide} {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>(), name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list with name \texttt{name}, using the default simulator.
+ This name can be used to identify the list in traces and reports.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{name for the list object}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat (Simulator inSim, String name)\begin{hide} {
+ super (inSim, new LinkedList<Node<E>>(), name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list with name \texttt{name}.
+ This name can be used to identify the list in traces and reports.
+\end{tabb}
+\begin{htmlonly}
+ \param{inSim}{Simulator associate to the current variable.}
+ \param{name}{name for the list object}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat (Collection<? extends E> c, String name)\begin{hide} {
+ super (Simulator.getDefaultSimulator(), new LinkedList<Node<E>>(), c, name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list containing the elements of the
+ specified collection \texttt{c} and with name \texttt{name}, using the default simulator.
+ This name can be used to identify the list in traces and reports.
+\end{tabb}
+\begin{htmlonly}\
+ \param{c}{collection containing elements to fill in this list with}
+ \param{name}{name for the list object}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat (Simulator inSim, Collection<? extends E> c,
+ String name)\begin{hide} {
+ super (inSim, new LinkedList<Node<E>>(), c, name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list containing the elements of the
+ specified collection \texttt{c} and with name \texttt{name}.
+ This name can be used to identify the list in traces and reports.
+\end{tabb}
+\begin{htmlonly}
+ \param{inSim}{Simulator associate to the current variable.}
+ \param{c}{collection containing elements to fill in this list with}
+ \param{name}{name for the list object}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {\texttt{LinkedList} methods}
+
+See the JDK documentation for more information about these methods.
+
+\begin{code}
+
+ public void addFirst (E obj)\begin{hide} {
+ add (0, obj);
+ }\end{hide}
+\end{code}
+\begin{code}
+
+ public void addLast (E obj)\begin{hide} {
+ add (size(), obj);
+ }\end{hide}
+\end{code}
+\begin{code}
+
+ public E getFirst()\begin{hide} {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return get (0);
+ }\end{hide}
+\end{code}
+\begin{code}
+
+ public E getLast()\begin{hide} {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return get (size() - 1);
+ }\end{hide}
+\end{code}
+\begin{code}
+
+ public E removeFirst()\begin{hide} {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return remove (0);
+ }\end{hide}
+\end{code}
+\begin{code}
+
+ public E removeLast() \begin{hide} {
+ if (isEmpty())
+ throw new NoSuchElementException();
+ return remove (size() - 1);
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/ListWithStat.java b/source/umontreal/iro/lecuyer/simevents/ListWithStat.java
new file mode 100644
index 0000000..c0f107e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/ListWithStat.java
@@ -0,0 +1,627 @@
+
+
+/*
+ * Class: ListWithStat
+ * Description: Implements a list with integrated statistical probes to
+ provide automatic collection of statistics on the sojourn
+ times of objects in the list and on the size of the list
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+import java.util.Collection;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.TransformingList;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * Implements a list with integrated statistical
+ * probes to provide automatic collection of
+ * statistics on the sojourn times of objects in the list and on the
+ * size of the list as a function of time given by a simulator.
+ * The automatic statistical collection can be
+ * enabled or disabled for each list, to reduce overhead.
+ * This class extends {@link umontreal.iro.lecuyer.util.TransformingList TransformingList}
+ * and transforms elements into nodes associating insertion times with elements.
+ *
+ */
+public class ListWithStat<E>
+ extends TransformingList<E, ListWithStat.Node<E>> {
+ private boolean stats; // true si on a appele setStatCollecting
+ private double initTime; // temps de la derniere initialisation
+ private Accumulate blockSize; //block stat. sur la longueur de la liste
+ private Tally blockSojourn; // block stat. sur les durees de sejour
+ private String name;
+ private Simulator sim;
+
+
+
+ /**
+ * Constructs a new list with internal data structure using the default simulator and implemented by <TT>nodeList</TT>.
+ * The given list is cleared for the constructed list to be initially empty.
+ *
+ */
+ public ListWithStat (List<Node<E>> nodeList) {
+ super (nodeList);
+ nodeList.clear();
+ sim = Simulator.getDefaultSimulator();
+ stats = false;
+ }
+
+
+ /**
+ * Constructs a new list with internal data structure implemented by
+ * <TT>nodeList</TT>.
+ * The given list is cleared for the constructed list to be initially empty.
+ *
+ * @param nodeList the list containing the nodes
+ *
+ *
+ */
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList) {
+ super (nodeList);
+ if (inSim == null || nodeList == null)
+ throw new NullPointerException();
+ nodeList.clear();
+ sim = inSim;
+ stats = false;
+ }
+
+
+ /**
+ * Constructs a list containing the elements of the specified
+ * collection, whose elements are stored into <TT>nodeList</TT> and using the default simulator.
+ *
+ * @param nodeList the list containing the nodes
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ *
+ */
+ public ListWithStat (List<Node<E>> nodeList, Collection<? extends E> c) {
+ this (Simulator.getDefaultSimulator(), nodeList);
+ addAll (c);
+ }
+
+
+ /**
+ * Constructs a list containing the elements of the specified
+ * collection, whose elements are stored into <TT>nodeList</TT>.
+ *
+ * @param inSim simulator associate to the current variable
+ *
+ * @param nodeList the list containing the nodes
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ *
+ */
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList,
+ Collection<? extends E> c) {
+ this (inSim, nodeList);
+ addAll (c);
+ }
+
+
+ /**
+ * Constructs a new list with name <TT>name</TT>, internal
+ * list <TT>nodeList</TT>, and using the default simulator.
+ * This name can be used to identify the list in traces and reports.
+ * The given list is cleared for the constructed list to be initially empty.
+ *
+ * @param nodeList the list containing the nodes
+ *
+ * @param name name for the list object
+ *
+ *
+ */
+ public ListWithStat (List<Node<E>> nodeList, String name) {
+ this (Simulator.getDefaultSimulator(), nodeList);
+ this.name = name;
+ }
+
+
+ /**
+ * Constructs a new list with name <TT>name</TT>, and
+ * internal list <TT>nodeList</TT>.
+ * This name can be used to identify the list in traces and reports.
+ * The given list is cleared for the constructed list to be initially empty.
+ *
+ * @param inSim simulator associate to the current variable
+ *
+ * @param nodeList the list containing the nodes
+ *
+ * @param name name for the list object
+ *
+ *
+ */
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList,
+ String name) {
+ this (inSim, nodeList);
+ this.name = name;
+ }
+
+
+ /**
+ * Constructs a new list containing the elements of the
+ * specified collection <TT>c</TT>, with name <TT>name</TT>,
+ * internal list <TT>nodeList</TT>, and using the default simulator.
+ * This name can be used to identify the list in traces and reports.
+ *
+ * @param nodeList the list containing the nodes
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ * @param name name for the list object
+ *
+ *
+ */
+ public ListWithStat (List<Node<E>> nodeList, Collection<? extends E> c,
+ String name) {
+ this (Simulator.getDefaultSimulator(), nodeList);
+ this.name = name;
+ addAll (c);
+ }
+
+
+ /**
+ * Constructs a new list containing the elements of the
+ * specified collection <TT>c</TT>, with name <TT>name</TT>, and
+ * internal list <TT>nodeList</TT>.
+ * This name can be used to identify the list in traces and reports.
+ *
+ * @param inSim simulator associate to the current variable
+ *
+ * @param nodeList the list containing the nodes
+ *
+ * @param c collection containing elements to fill in this list with
+ *
+ * @param name name for the list object
+ *
+ */
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList,
+ Collection<? extends E> c, String name) {
+ this (inSim, nodeList);
+ this.name = name;
+ addAll (c);
+ }
+
+
+ public E convertFromInnerType (Node<E> node) {
+ return node.getElement();
+ }
+
+ public Node<E> convertToInnerType (E element) {
+ return new Node<E> (element, sim.time());
+ }
+
+ /**
+ * Returns the simulator associated with this list.
+ *
+ * @return the simulator associated with this list
+ *
+ */
+ public Simulator simulator() {
+ return sim;
+ }
+
+
+ /**
+ * Sets the simulator associated with this list.
+ * This list should be cleared after this method is called.
+ *
+ * @param sim the simulator of this list
+ *
+ *
+ */
+ public void setSimulator(Simulator sim) {
+ if (sim == null)
+ throw new NullPointerException();
+ this.sim = sim;
+ if (blockSize != null)
+ blockSize.setSimulator (sim);
+ }
+
+
+ @Override
+ public void clear() {
+ if (stats)
+ initStat();
+ super.clear();
+ }
+
+ @Override
+ public void add (int index, E obj) {
+ super.add (index, obj);
+ if (stats)
+ blockSize.update (size());
+ }
+
+ @Override
+ public E remove (int index) {
+ Node<E> node = getInnerList().get (index);
+ if (stats)
+ blockSojourn.add (sim.time() - node.getInsertionTime());
+ E e = super.remove (index);
+ if (stats)
+ blockSize.update (size());
+ return e;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new IteratorWithStat (getInnerList().iterator());
+ }
+
+ @Override
+ public ListIterator<E> listIterator() {
+ return new ListIteratorWithStat (getInnerList().listIterator());
+ }
+
+ @Override
+ public ListIterator<E> listIterator (int index) {
+ return new ListIteratorWithStat (getInnerList().listIterator (index));
+ }
+
+ @Override
+ public E set (int index, E element) {
+ Node<E> oldNode = getInnerList().get (index);
+ E oldElement = oldNode.getElement();
+ boolean equal;
+ if (oldElement == null || element == null)
+ equal = oldElement == element;
+ else
+ equal = oldElement.equals (element);
+ if (equal) {
+ getInnerList().set (index, new Node<E> (element, oldNode.getInsertionTime()));
+ return oldElement;
+ }
+ else {
+ if (stats)
+ blockSojourn.add (sim.time() - oldNode.getInsertionTime());
+ getInnerList().set (index, new Node<E> (element, sim.time()));
+ return oldElement;
+ }
+ }
+
+ private class IteratorWithStat implements Iterator<E> {
+ private Iterator<Node<E>> itr;
+ private Node<E> lastRet;
+
+ public IteratorWithStat (Iterator<Node<E>> itr) {
+ this.itr = itr;
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public E next() {
+ lastRet = itr.next();
+ return lastRet.getElement();
+ }
+
+ public void remove() {
+ itr.remove();
+ if (stats) {
+ blockSize.update (size());
+ blockSojourn.add (sim.time() - lastRet.getInsertionTime());
+ }
+ lastRet = null;
+ }
+ }
+
+ private class ListIteratorWithStat implements ListIterator<E> {
+ private ListIterator<Node<E>> itr;
+ private Node<E> lastRet;
+
+ public ListIteratorWithStat (ListIterator<Node<E>> itr) {
+ this.itr = itr;
+ }
+
+ public void add (E o) {
+ itr.add (new Node<E> (o, sim.time()));
+ lastRet = null;
+ if (stats)
+ blockSize.update (size());
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public boolean hasPrevious() {
+ return itr.hasPrevious();
+ }
+
+ public E next() {
+ lastRet = itr.next();
+ return lastRet.getElement();
+ }
+
+ public int nextIndex() {
+ return itr.nextIndex();
+ }
+
+ public E previous() {
+ lastRet = itr.previous();
+ return lastRet.getElement();
+ }
+
+ public int previousIndex() {
+ return itr.previousIndex();
+ }
+
+ public void remove() {
+ itr.remove();
+ if (stats) {
+ blockSize.update (size());
+ blockSojourn.add (sim.time() - lastRet.getInsertionTime());
+ }
+ lastRet = null;
+ }
+
+ public void set (E element) {
+ if (lastRet == null)
+ throw new NoSuchElementException();
+ Node<E> oldNode = lastRet;
+ E oldElement = oldNode.getElement();
+ boolean equal;
+ if (oldElement == null || element == null)
+ equal = oldElement == element;
+ else
+ equal = oldElement.equals (element);
+ if (equal) {
+ lastRet = new Node<E> (element, oldNode.getInsertionTime());
+ itr.set (lastRet);
+ }
+ else {
+ if (stats)
+ blockSojourn.add (sim.time() - oldNode.getInsertionTime());
+ lastRet = new Node<E> (element, sim.time());
+ itr.set (lastRet);
+ }
+ }
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the list collects statistics
+ * about its size and sojourn times of elements, and
+ * <TT>false</TT> otherwise.
+ * By default, statistical collecting is turned off.
+ *
+ * @return the status of statistical collecting
+ *
+ */
+ public boolean getStatCollecting() {
+ return stats;
+ }
+
+
+ /**
+ * Starts or stops collecting statistics on this list.
+ * If the statistical collection is turned ON, the method
+ * creates two statistical probes if they do not exist yet.
+ * The first one, of the class {@link Accumulate}, measures the evolution
+ * of the size of the list as a function of time.
+ * It can be accessed by the method {@link #statSize statSize}.
+ * The second one, of the class {@link Tally} and accessible via
+ * {@link #statSojourn statSojourn}, samples the sojourn times in the list of the
+ * objects removed during the observation period,
+ * i.e., between the last initialization time of this statistical
+ * probe and the current time.
+ * The method automatically calls {@link #initStat initStat} to
+ * initialize these two probes.
+ * When this method is used, it is normally invoked immediately after
+ * calling the constructor of the list.
+ *
+ * @exception IllegalStateException if the statistical collection
+ * is in the same state as the caller requires
+ *
+ *
+ */
+ public void setStatCollecting (boolean b) {
+ if (b && !stats) {
+ if (blockSize == null)
+ blockSize = new Accumulate(sim, "List Size " + name);
+ if (blockSojourn == null)
+ blockSojourn = new Tally("List Sojourn " + name);
+ blockSize.update (size());
+ stats = true;
+ initStat();
+ } else
+ stats = false;
+ }
+
+
+ /**
+ * Reinitializes the two statistical probes created by
+ * {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> and makes an update for the
+ * probe on the list size.
+ *
+ * @exception IllegalStateException if the statistical collection is disabled
+ *
+ *
+ */
+ public void initStat() {
+ if (!stats)
+ throw new IllegalStateException("initStat for a list that did not call setStatCollecting (true).");
+ blockSize.init();
+ blockSojourn.init();
+ blockSize.update (size());
+ initTime = sim.time();
+ }
+
+
+ /**
+ * Returns the last simulation time {@link #initStat initStat} was called.
+ *
+ * @return the last simulation time {@link #initStat initStat} was called
+ *
+ */
+ public double getInitTime() {
+ return initTime;
+ }
+
+
+ /**
+ * Returns the statistical probe on the evolution of the size of
+ * the list as a function of the simulation time. This probe
+ * exists only if {@link #setStatCollecting setStatCollecting} <TT>(true)</TT>
+ * has been called for this list.
+ *
+ * @return the statistical probe on the evolution of the size of the list
+ *
+ */
+ public Accumulate statSize() {
+ return blockSize;
+ }
+
+
+ /**
+ * Returns the statistical probe on the sojourn times of the objects in
+ * the list. This probe exists
+ * only if {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> has been called for this list.
+ *
+ * @return the statistical probe for the sojourn times in the list
+ *
+ */
+ public Tally statSojourn() {
+ return blockSojourn;
+ }
+
+
+ /**
+ * Returns a string containing a statistical report on the list,
+ * provided that {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> has been
+ * called before for this list.
+ * Even If {@link #setStatCollecting setStatCollecting} was called with <TT>false</TT>
+ * afterward, the report will be made for
+ * the collected observations.
+ * If the probes do not exist, i.e., {@link #setStatCollecting setStatCollecting} was never called
+ * for this object, an illegal state exception will be thrown.
+ *
+ * @return a statistical report, represented as a string
+ * @exception IllegalStateException if no statistical probes exist
+ *
+ *
+ */
+ public String report() {
+ if (blockSojourn == null || blockSize == null)
+ throw new IllegalStateException
+ ("Calling report when no statistics were collected");
+
+ PrintfFormat str = new PrintfFormat();
+ str.append (PrintfFormat.NEWLINE +
+ "REPORT ON LIST : ").append (name).append (PrintfFormat.NEWLINE);
+ str.append (" From time: ").append (7, 2, 2, initTime);
+ str.append (" to time: ").append (10, 2, 2, sim.time());
+ str.append (" min max average ");
+ str.append ("standard dev. nb. Obs");
+
+ str.append (" Size ");
+ str.append (9, (int)(blockSize.min()+0.5));
+ str.append (11, (int)(blockSize.max()+0.5));
+ str.append (14, 3, 2, blockSize.average()).append(PrintfFormat.NEWLINE);
+
+ str.append (" Sojourn ");
+ str.append ( 12, 3, 2, blockSojourn.min()).append (" ");
+ str.append (10, 3, 2, blockSojourn.max()).append (" ");
+ str.append (10, 3, 2, blockSojourn.average()).append (" ");
+ str.append (10, 3, 2, blockSojourn.standardDeviation()).append (" ");
+ str.append (11, blockSojourn.numberObs()).append (PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+
+
+ /**
+ * Returns the name associated to this list,
+ * or <TT>null</TT> if no name was assigned.
+ *
+ * @return the name associated to this list
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+
+ /**
+ * Represents a node that can be part of a list with
+ * statistical collecting.
+ *
+ */
+ public static class Node<E> {
+ private E element;
+ private double insertionTime;
+
+
+ /**
+ * Constructs a new node containing element
+ * <TT>element</TT>
+ * inserted into the list at time <TT>insertionTime</TT>.
+ *
+ * @param element the element to add into this new node
+ *
+ * @param insertionTime the insertion time of the element
+ *
+ *
+ */
+ public Node (E element, double insertionTime) {
+ this.element = element;
+ this.insertionTime = insertionTime;
+ }
+
+
+ /**
+ * Returns the element stored into this node.
+ *
+ * @return the element into this node
+ *
+ */
+ public E getElement() { return element; }
+
+
+ /**
+ * Returns the insertion time of the element in this node.
+ *
+ * @return the insertion time of the element
+ *
+ */
+ public double getInsertionTime() { return insertionTime; }
+
+
+ public String toString() {
+ String str = element == null ? "null" : element.toString();
+ str += " (inserted at time " + insertionTime + ")";
+ return str;
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/ListWithStat.tex b/source/umontreal/iro/lecuyer/simevents/ListWithStat.tex
new file mode 100644
index 0000000..212364f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/ListWithStat.tex
@@ -0,0 +1,627 @@
+\defmodule {ListWithStat}
+
+Implements a list with integrated statistical
+probes to provide automatic collection of
+statistics on the sojourn times of objects in the list and on the
+size of the list as a function of time given by a simulator.
+The automatic statistical collection can be
+enabled or disabled for each list, to reduce overhead.
+This class extends \externalclass{umontreal.iro.lecuyer.util}{TransformingList}
+and transforms elements into nodes associating insertion times with elements.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ListWithStat
+ * Description: Implements a list with integrated statistical probes to
+ provide automatic collection of statistics on the sojourn
+ times of objects in the list and on the size of the list
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;\begin{hide}
+import java.util.Collection;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.TransformingList;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class ListWithStat<E>
+ extends TransformingList<E, ListWithStat.Node<E>>\begin{hide} {
+ private boolean stats; // true si on a appele setStatCollecting
+ private double initTime; // temps de la derniere initialisation
+ private Accumulate blockSize; //block stat. sur la longueur de la liste
+ private Tally blockSojourn; // block stat. sur les durees de sejour
+ private String name;
+ private Simulator sim;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public ListWithStat (List<Node<E>> nodeList)\begin{hide} {
+ super (nodeList);
+ nodeList.clear();
+ sim = Simulator.getDefaultSimulator();
+ stats = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new list with internal data structure using the default simulator and implemented by \texttt{nodeList}.
+ The given list is cleared for the constructed list to be initially empty.
+\end{tabb}
+\begin{code}
+
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList)\begin{hide} {
+ super (nodeList);
+ if (inSim == null || nodeList == null)
+ throw new NullPointerException();
+ nodeList.clear();
+ sim = inSim;
+ stats = false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Constructs a new list with internal data structure implemented by
+ \texttt{nodeList}.
+ The given list is cleared for the constructed list to be initially empty.
+\end{tabb}
+\begin{htmlonly}
+ \param{nodeList}{the list containing the nodes}
+\end{htmlonly}
+\begin{code}
+
+ public ListWithStat (List<Node<E>> nodeList, Collection<? extends E> c)\begin{hide} {
+ this (Simulator.getDefaultSimulator(), nodeList);
+ addAll (c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a list containing the elements of the specified
+ collection, whose elements are stored into \texttt{nodeList} and using the default simulator.
+\end{tabb}
+\begin{htmlonly}
+ \param{nodeList}{the list containing the nodes}
+ \param{c}{collection containing elements to fill in this list with}
+\end{htmlonly}
+\begin{code}
+
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList,
+ Collection<? extends E> c)\begin{hide} {
+ this (inSim, nodeList);
+ addAll (c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a list containing the elements of the specified
+ collection, whose elements are stored into \texttt{nodeList}.
+\end{tabb}
+\begin{htmlonly}
+ \param{inSim}{simulator associate to the current variable}
+ \param{nodeList}{the list containing the nodes}
+ \param{c}{collection containing elements to fill in this list with}
+\end{htmlonly}
+\begin{code}
+
+ public ListWithStat (List<Node<E>> nodeList, String name)\begin{hide} {
+ this (Simulator.getDefaultSimulator(), nodeList);
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list with name \texttt{name}, internal
+ list \texttt{nodeList}, and using the default simulator.
+ This name can be used to identify the list in traces and reports.
+ The given list is cleared for the constructed list to be initially empty.
+\end{tabb}
+\begin{htmlonly}
+ \param{nodeList}{the list containing the nodes}
+ \param{name}{name for the list object}
+\end{htmlonly}
+\begin{code}
+
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList,
+ String name)\begin{hide} {
+ this (inSim, nodeList);
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list with name \texttt{name}, and
+ internal list \texttt{nodeList}.
+ This name can be used to identify the list in traces and reports.
+ The given list is cleared for the constructed list to be initially empty.
+\end{tabb}
+\begin{htmlonly}
+ \param{inSim}{simulator associate to the current variable}
+ \param{nodeList}{the list containing the nodes}
+ \param{name}{name for the list object}
+\end{htmlonly}
+\begin{code}
+
+ public ListWithStat (List<Node<E>> nodeList, Collection<? extends E> c,
+ String name)\begin{hide} {
+ this (Simulator.getDefaultSimulator(), nodeList);
+ this.name = name;
+ addAll (c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list containing the elements of the
+ specified collection \texttt{c}, with name \texttt{name},
+ internal list \texttt{nodeList}, and using the default simulator.
+ This name can be used to identify the list in traces and reports.
+\end{tabb}
+\begin{htmlonly}
+ \param{nodeList}{the list containing the nodes}
+ \param{c}{collection containing elements to fill in this list with}
+ \param{name}{name for the list object}
+\end{htmlonly}
+\begin{code}
+
+ public ListWithStat (Simulator inSim, List<Node<E>> nodeList,
+ Collection<? extends E> c, String name)\begin{hide} {
+ this (inSim, nodeList);
+ this.name = name;
+ addAll (c);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new list containing the elements of the
+ specified collection \texttt{c}, with name \texttt{name}, and
+ internal list \texttt{nodeList}.
+ This name can be used to identify the list in traces and reports.
+\end{tabb}
+\begin{htmlonly}
+ \param{inSim}{simulator associate to the current variable}
+ \param{nodeList}{the list containing the nodes}
+ \param{c}{collection containing elements to fill in this list with}
+ \param{name}{name for the list object}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public E convertFromInnerType (Node<E> node)\begin{hide} {
+ return node.getElement();
+ }\end{hide}
+
+ public Node<E> convertToInnerType (E element)\begin{hide} {
+ return new Node<E> (element, sim.time());
+ }\end{hide}
+
+ public Simulator simulator() \begin{hide} {
+ return sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the simulator associated with this list.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the simulator associated with this list}
+\end{htmlonly}
+\begin{code}
+
+ public void setSimulator(Simulator sim) \begin{hide} {
+ if (sim == null)
+ throw new NullPointerException();
+ this.sim = sim;
+ if (blockSize != null)
+ blockSize.setSimulator (sim);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Sets the simulator associated with this list.
+ This list should be cleared after this method is called.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{the simulator of this list}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ @Override
+ public void clear() {
+ if (stats)
+ initStat();
+ super.clear();
+ }
+
+ @Override
+ public void add (int index, E obj) {
+ super.add (index, obj);
+ if (stats)
+ blockSize.update (size());
+ }
+
+ @Override
+ public E remove (int index) {
+ Node<E> node = getInnerList().get (index);
+ if (stats)
+ blockSojourn.add (sim.time() - node.getInsertionTime());
+ E e = super.remove (index);
+ if (stats)
+ blockSize.update (size());
+ return e;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new IteratorWithStat (getInnerList().iterator());
+ }
+
+ @Override
+ public ListIterator<E> listIterator() {
+ return new ListIteratorWithStat (getInnerList().listIterator());
+ }
+
+ @Override
+ public ListIterator<E> listIterator (int index) {
+ return new ListIteratorWithStat (getInnerList().listIterator (index));
+ }
+
+ @Override
+ public E set (int index, E element) {
+ Node<E> oldNode = getInnerList().get (index);
+ E oldElement = oldNode.getElement();
+ boolean equal;
+ if (oldElement == null || element == null)
+ equal = oldElement == element;
+ else
+ equal = oldElement.equals (element);
+ if (equal) {
+ getInnerList().set (index, new Node<E> (element, oldNode.getInsertionTime()));
+ return oldElement;
+ }
+ else {
+ if (stats)
+ blockSojourn.add (sim.time() - oldNode.getInsertionTime());
+ getInnerList().set (index, new Node<E> (element, sim.time()));
+ return oldElement;
+ }
+ }
+
+ private class IteratorWithStat implements Iterator<E> {
+ private Iterator<Node<E>> itr;
+ private Node<E> lastRet;
+
+ public IteratorWithStat (Iterator<Node<E>> itr) {
+ this.itr = itr;
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public E next() {
+ lastRet = itr.next();
+ return lastRet.getElement();
+ }
+
+ public void remove() {
+ itr.remove();
+ if (stats) {
+ blockSize.update (size());
+ blockSojourn.add (sim.time() - lastRet.getInsertionTime());
+ }
+ lastRet = null;
+ }
+ }
+
+ private class ListIteratorWithStat implements ListIterator<E> {
+ private ListIterator<Node<E>> itr;
+ private Node<E> lastRet;
+
+ public ListIteratorWithStat (ListIterator<Node<E>> itr) {
+ this.itr = itr;
+ }
+
+ public void add (E o) {
+ itr.add (new Node<E> (o, sim.time()));
+ lastRet = null;
+ if (stats)
+ blockSize.update (size());
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public boolean hasPrevious() {
+ return itr.hasPrevious();
+ }
+
+ public E next() {
+ lastRet = itr.next();
+ return lastRet.getElement();
+ }
+
+ public int nextIndex() {
+ return itr.nextIndex();
+ }
+
+ public E previous() {
+ lastRet = itr.previous();
+ return lastRet.getElement();
+ }
+
+ public int previousIndex() {
+ return itr.previousIndex();
+ }
+
+ public void remove() {
+ itr.remove();
+ if (stats) {
+ blockSize.update (size());
+ blockSojourn.add (sim.time() - lastRet.getInsertionTime());
+ }
+ lastRet = null;
+ }
+
+ public void set (E element) {
+ if (lastRet == null)
+ throw new NoSuchElementException();
+ Node<E> oldNode = lastRet;
+ E oldElement = oldNode.getElement();
+ boolean equal;
+ if (oldElement == null || element == null)
+ equal = oldElement == element;
+ else
+ equal = oldElement.equals (element);
+ if (equal) {
+ lastRet = new Node<E> (element, oldNode.getInsertionTime());
+ itr.set (lastRet);
+ }
+ else {
+ if (stats)
+ blockSojourn.add (sim.time() - oldNode.getInsertionTime());
+ lastRet = new Node<E> (element, sim.time());
+ itr.set (lastRet);
+ }
+ }
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Statistic collection methods}
+
+\begin{code}
+
+ public boolean getStatCollecting()\begin{hide} {
+ return stats;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns \texttt{true} if the list collects statistics
+ about its size and sojourn times of elements, and
+ \texttt{false} otherwise.
+ By default, statistical collecting is turned off.
+\end{tabb}
+\begin{htmlonly}
+ \return{the status of statistical collecting}
+\end{htmlonly}
+\begin{code}
+
+ public void setStatCollecting (boolean b)\begin{hide} {
+ if (b && !stats) {
+ if (blockSize == null)
+ blockSize = new Accumulate(sim, "List Size " + name);
+ if (blockSojourn == null)
+ blockSojourn = new Tally("List Sojourn " + name);
+ blockSize.update (size());
+ stats = true;
+ initStat();
+ } else
+ stats = false;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Starts or stops collecting statistics on this list.
+ If the statistical collection is turned ON, the method
+ creates two statistical probes if they do not exist yet.
+ The first one, of the class \class{Accumulate}, measures the evolution
+ of the size of the list as a function of time.
+ It can be accessed by the method \method{statSize}{}.
+ The second one, of the class \class{Tally} and accessible via
+ \method{statSojourn}{}, samples the sojourn times in the list of the
+ objects removed during the observation period,
+ i.e., between the last initialization time of this statistical
+ probe and the current time.
+ The method automatically calls \method{initStat}{} to
+ initialize these two probes.
+ When this method is used, it is normally invoked immediately after
+ calling the constructor of the list.
+ \end{tabb}
+\begin{htmlonly}
+ \exception{IllegalStateException}{if the statistical collection
+ is in the same state as the caller requires}
+\end{htmlonly}
+\begin{code}
+
+ public void initStat() \begin{hide} {
+ if (!stats)
+ throw new IllegalStateException("initStat for a list that did not call setStatCollecting (true).");
+ blockSize.init();
+ blockSojourn.init();
+ blockSize.update (size());
+ initTime = sim.time();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Reinitializes the two statistical probes created by
+ \method{setStatCollecting}{}~\texttt{(true)} and makes an update for the
+ probe on the list size.
+ \end{tabb}
+\begin{htmlonly}
+ \exception{IllegalStateException}{if the statistical collection is disabled}
+\end{htmlonly}
+\begin{code}
+
+ public double getInitTime()\begin{hide} {
+ return initTime;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the last simulation time \method{initStat}{} was called.
+\end{tabb}
+\begin{htmlonly}
+ \return{the last simulation time \method{initStat}{} was called}
+\end{htmlonly}
+\begin{code}
+
+ public Accumulate statSize() \begin{hide} {
+ return blockSize;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the statistical probe on the evolution of the size of
+ the list as a function of the simulation time. This probe
+ exists only if \method{setStatCollecting}{}~\texttt{(true)}
+ has been called for this list.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the statistical probe on the evolution of the size of the list}
+\end{htmlonly}
+\begin{code}
+
+ public Tally statSojourn() \begin{hide} {
+ return blockSojourn;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the statistical probe on the sojourn times of the objects in
+ the list. This probe exists
+ only if \method{setStatCollecting}{}~\texttt{(true)} has been called for this list.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the statistical probe for the sojourn times in the list}
+\end{htmlonly}
+\begin{code}
+
+ public String report() \begin{hide} {
+ if (blockSojourn == null || blockSize == null)
+ throw new IllegalStateException
+ ("Calling report when no statistics were collected");
+
+ PrintfFormat str = new PrintfFormat();
+ str.append (PrintfFormat.NEWLINE +
+ "REPORT ON LIST : ").append (name).append (PrintfFormat.NEWLINE);
+ str.append (" From time: ").append (7, 2, 2, initTime);
+ str.append (" to time: ").append (10, 2, 2, sim.time());
+ str.append (" min max average ");
+ str.append ("standard dev. nb. Obs");
+
+ str.append (" Size ");
+ str.append (9, (int)(blockSize.min()+0.5));
+ str.append (11, (int)(blockSize.max()+0.5));
+ str.append (14, 3, 2, blockSize.average()).append(PrintfFormat.NEWLINE);
+
+ str.append (" Sojourn ");
+ str.append ( 12, 3, 2, blockSojourn.min()).append (" ");
+ str.append (10, 3, 2, blockSojourn.max()).append (" ");
+ str.append (10, 3, 2, blockSojourn.average()).append (" ");
+ str.append (10, 3, 2, blockSojourn.standardDeviation()).append (" ");
+ str.append (11, blockSojourn.numberObs()).append (PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a string containing a statistical report on the list,
+ provided that \method{setStatCollecting}{} \texttt{(true)} has been
+ called before for this list.
+ Even If \method{setStatCollecting}{} was called with \texttt{false}
+ afterward, the report will be made for
+ the collected observations.
+ If the probes do not exist, i.e., \method{setStatCollecting}{} was never called
+ for this object, an illegal state exception will be thrown.
+ \end{tabb}
+\begin{htmlonly}
+ \return{a statistical report, represented as a string}
+ \exception{IllegalStateException}{if no statistical probes exist}
+\end{htmlonly}
+\begin{code}
+
+ public String getName()\begin{hide} {
+ return name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the name associated to this list,
+ or \texttt{null} if no name was assigned.
+\end{tabb}
+\begin{htmlonly}
+ \return{the name associated to this list}
+\end{htmlonly}
+
+\subsubsection*{Inner class}
+
+\begin{code}
+
+ public static class Node<E>\begin{hide} {
+ private E element;
+ private double insertionTime;\end{hide}
+\end{code}
+\begin{tabb} Represents a node that can be part of a list with
+ statistical collecting.
+\end{tabb}
+\subsubsection*{Constructor}
+\begin{code}
+
+ public Node (E element, double insertionTime)\begin{hide} {
+ this.element = element;
+ this.insertionTime = insertionTime;
+ }\end{hide}
+\end{code}
+\begin{tabbb} Constructs a new node containing element
+ \texttt{element}
+ inserted into the list at time \texttt{insertionTime}.
+\end{tabbb}
+\begin{htmlonly}
+ \param{element}{the element to add into this new node}
+ \param{insertionTime}{the insertion time of the element}
+\end{htmlonly}
+\subsubsection*{Methods}
+\begin{code}
+
+ public E getElement()\begin{hide} { return element; }\end{hide}
+\end{code}
+\begin{tabbb} Returns the element stored into this node.
+\end{tabbb}
+\begin{htmlonly}
+ \return{the element into this node}
+\end{htmlonly}
+\begin{code}
+
+ public double getInsertionTime()\begin{hide} { return insertionTime; }\end{hide}
+\end{code}
+\begin{tabbb} Returns the insertion time of the element in this node.
+\end{tabbb}
+\begin{htmlonly}
+ \return{the insertion time of the element}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public String toString() {
+ String str = element == null ? "null" : element.toString();
+ str += " (inserted at time " + insertionTime + ")";
+ return str;
+ }
+ }\end{hide}
+\end{code}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/Sim.java b/source/umontreal/iro/lecuyer/simevents/Sim.java
new file mode 100644
index 0000000..6135913
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Sim.java
@@ -0,0 +1,156 @@
+
+
+/*
+ * Class: Sim
+ * Description: contains the executive of a discrete-event simulation
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.eventlist.SplayTree;
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.Simulator;
+
+/**
+ * This static class contains the executive of a discrete-event simulation.
+ * It maintains the simulation clock and starts executing the events
+ * in the appropriate order.
+ * Its methods permit one to start, stop, and (re)initialize the simulation,
+ * and read the simulation clock.
+ *
+ * <P>
+ * Starting from SSJ-2.0, the <TT>Sim</TT> class now uses
+ * the default simulator returned by the <TT>getDefaultSimulator()</TT>
+ * method in the {@link Simulator} class. Although the <TT>Sim</TT> class is
+ * perfectly adequate for simple simulations, the {@link Simulator} class
+ * is more general and supports more functionnalities.
+ * For example, if one needs to have more than one simulation clock and event list,
+ * one will have to use the {@link Simulator} class instead of the simpler
+ * <TT>Sim</TT> class.
+ *
+ */
+public final class Sim {
+
+ // Prevents construction of this object
+ private Sim() {}
+
+
+
+ /**
+ * Returns the current value of the simulation clock.
+ * @return the current simulation time
+ *
+ */
+ public static double time() {
+ return Simulator.getDefaultSimulator().time();
+ }
+
+
+ /**
+ * Reinitializes the simulation executive by clearing up the event
+ * list, and resetting the simulation clock to zero.
+ * This method must not be used to initialize process-driven
+ * simulation; {@link umontreal.iro.lecuyer.simprocs.SimProcess#init SimProcess.init}
+ * must be used instead.
+ *
+ */
+ public static void init() {
+ // This has to be done another way in order to separate events and processes.
+ Simulator.getDefaultSimulator().init();
+ }
+
+
+ /**
+ * Same as {@link #init init}, but also chooses <TT>evlist</TT> as the
+ * event list to be used. For example, calling
+ * <TT>init(new DoublyLinked())</TT> initializes the simulation
+ * with a doubly linked linear structure for the event list.
+ * This method must not be used to initialize process-driven
+ * simulation;
+ * {@link umontreal.iro.lecuyer.simprocs#DSOLProcessSimulator(init) umontreal.iro.lecuyer.simprocs.DSOLProcessSimulator} <TT>(EventList)</TT> or
+ * <BR>{@link umontreal.iro.lecuyer.simprocs#ThreadProcessSimulator(init) umontreal.iro.lecuyer.simprocs.ThreadProcessSimulator} <TT>(EventList)</TT>
+ * must be used instead.
+ *
+ * @param evlist selected event list implementation
+ *
+ *
+ */
+ public static void init (EventList evlist) {
+ Simulator.getDefaultSimulator().init(evlist);
+ }
+
+
+ /**
+ * Gets the currently used event list.
+ *
+ * @return the currently used event list
+ *
+ *
+ */
+ public static EventList getEventList() {
+ return Simulator.getDefaultSimulator().getEventList();
+ }
+
+ protected static Event removeFirstEvent() {
+ return Simulator.getDefaultSimulator().removeFirstEvent();
+ }
+
+
+ /**
+ * Starts the simulation executive.
+ * There must be at least one <TT>Event</TT> in the
+ * event list when this method is called.
+ *
+ */
+ public static void start() {
+ Simulator.getDefaultSimulator().start();
+ }
+
+
+ /**
+ * Tells the simulation executive to stop as soon as it takes control,
+ * and to return control to the program that called {@link #start start}.
+ * This program will then continue
+ * executing from the instructions right after its call to <TT>Sim.start</TT>.
+ * If an {@link Event} is currently executing (and this event has just called
+ * <TT>Sim.stop</TT>), the executive will take
+ * control when the event terminates its execution.
+ *
+ */
+ public static void stop() {
+ Simulator.getDefaultSimulator().stop();
+ }
+
+
+ // Used to passivate and activate the main process.
+ // See the SimProcess.dispatch() and SimThread.actions()
+
+// protected static void activate() {
+ // synchronized (eventList) {eventList.notify(); }
+ // }
+
+// protected static void passivate() {
+// synchronized (eventList) {
+ // try { eventList.wait(); } catch (InterruptedException e) {}
+ // }
+ // }
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/Sim.tex b/source/umontreal/iro/lecuyer/simevents/Sim.tex
new file mode 100644
index 0000000..2c5c62e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Sim.tex
@@ -0,0 +1,175 @@
+\defmodule {Sim}
+
+This static class contains the executive of a discrete-event simulation.
+It maintains the simulation clock and starts executing the events
+%and processes
+in the appropriate order.
+Its methods permit one to start, stop, and (re)initialize the simulation,
+and read the simulation clock.
+
+Starting from SSJ-2.0, the \texttt{Sim} class now uses
+the default simulator returned by the \texttt{getDefaultSimulator()}
+method in the \class{Simulator} class. Although the \texttt{Sim} class is
+perfectly adequate for simple simulations, the \class{Simulator} class
+is more general and supports more functionnalities.
+For example, if one needs to have more than one simulation clock and event list,
+one will have to use the \class{Simulator} class instead of the simpler
+\texttt{Sim} class.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Sim
+ * Description: contains the executive of a discrete-event simulation
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;\begin{hide}
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.eventlist.SplayTree;
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.Simulator;\end{hide}
+
+public final class Sim \begin{hide} {
+
+ // Prevents construction of this object
+ private Sim() {}
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static double time() \begin{hide} {
+ return Simulator.getDefaultSimulator().time();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the current value of the simulation clock. \end{tabb}
+\begin{htmlonly}
+ \return{the current simulation time}
+\end{htmlonly}
+\begin{code}
+
+ public static void init() \begin{hide} {
+ // This has to be done another way in order to separate events and processes.
+ Simulator.getDefaultSimulator().init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Reinitializes the simulation executive by clearing up the event
+ list, and resetting the simulation clock to zero.
+ This method must not be used to initialize process-driven
+ simulation; \clsexternalmethod{umontreal.iro.lecuyer.simprocs}{SimProcess}{init}{}
+ must be used instead.
+ \end{tabb}
+\begin{code}
+
+ public static void init (EventList evlist) \begin{hide} {
+ Simulator.getDefaultSimulator().init(evlist);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Same as \method{init}{}, but also chooses \texttt{evlist} as the
+ event list to be used. For example, calling
+ \texttt{init(new DoublyLinked())} initializes the simulation
+ with a doubly linked linear structure for the event list.
+ This method must not be used to initialize process-driven
+ simulation;
+\clsexternalmethod{}{umontreal.iro.lecuyer.simprocs}{DSOLProcessSimulator}{init}{}~\texttt{(EventList)} or \\
+\clsexternalmethod{}{umontreal.iro.lecuyer.simprocs}{ThreadProcessSimulator}{init}{}~\texttt{(EventList)}
+ must be used instead.
+ \end{tabb}
+\begin{htmlonly}
+ \param{evlist}{selected event list implementation}
+\end{htmlonly}
+\begin{code}
+
+ public static EventList getEventList()\begin{hide} {
+ return Simulator.getDefaultSimulator().getEventList();
+ }\end{hide}
+\end{code}
+\begin{tabb} Gets the currently used event list.
+\end{tabb}
+\begin{htmlonly}
+ \return{the currently used event list}
+\end{htmlonly}
+\begin{hide}
+\begin{code}
+ protected static Event removeFirstEvent() {
+ return Simulator.getDefaultSimulator().removeFirstEvent();
+ }
+\end{code}
+\begin{tabb} This method is used by the package
+ \externalclass{umontreal.iro.lecuyer}{simprocs};
+ \emph{it should not be used directly by a simulation program}.
+ It removes the first event from the event list and sets the simulation
+ clock to its event time.
+\end{tabb}
+\begin{htmlonly}
+ \return{the first planned event, or \texttt{null} if there is no such event}
+\end{htmlonly}
+\end{hide}
+\begin{code}
+
+ public static void start() \begin{hide} {
+ Simulator.getDefaultSimulator().start();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Starts the simulation executive.
+ There must be at least one \texttt{Event} in the
+ event list when this method is called.
+ \end{tabb}
+\begin{code}
+
+ public static void stop() \begin{hide} {
+ Simulator.getDefaultSimulator().stop();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Tells the simulation executive to stop as soon as it takes control,
+ and to return control to the program that called \method{start}{}.
+ This program will then continue
+ executing from the instructions right after its call to \texttt{Sim.start}.
+ If an \class{Event} is currently executing (and this event has just called
+ \texttt{Sim.stop}), the executive will take
+ control when the event terminates its execution.
+% If a \texttt{SimProcess} is currently executing, the executive will take
+% control when the process terminates or suspends itself by invoking
+% one of its methods such as \texttt{delay}, \texttt{suspend},
+% \texttt{request}, etc.
+ \end{tabb}
+\begin{code}\begin{hide}
+
+ // Used to passivate and activate the main process.
+ // See the SimProcess.dispatch() and SimThread.actions()
+
+// protected static void activate() {
+ // synchronized (eventList) {eventList.notify(); }
+ // }
+
+// protected static void passivate() {
+// synchronized (eventList) {
+ // try { eventList.wait(); } catch (InterruptedException e) {}
+ // }
+ // }
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/Simulator.java b/source/umontreal/iro/lecuyer/simevents/Simulator.java
new file mode 100644
index 0000000..65df652
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Simulator.java
@@ -0,0 +1,285 @@
+
+
+/*
+ * Class: Simulator
+ * Description: Represents the executive of a discrete-event simulator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents;
+
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.eventlist.SplayTree;
+import umontreal.iro.lecuyer.simprocs.*;
+ import java.util.ListIterator;
+
+/**
+ * Represents the executive of a discrete-event simulator.
+ * This class maintains a simulation
+ * clock, an event list, and starts executing the events
+ * in the appropriate order.
+ * Its methods permit one to start, stop, and (re)initialize the simulation,
+ * and read the simulation clock.
+ *
+ * <P>
+ * Usually, a simulation program uses a single simulation clock which is
+ * represented by an instance of this class.
+ * For more convenience and compatibility, this class therefore provides
+ * a mechanism to construct and return a default simulator
+ * which is used when an event is constructed without an explicit reference to a
+ * simulator, and when the simulator is accessed through
+ * the {@link Sim} class.
+ *
+ * <P>
+ * Note that this class is NOT thread-safe. Consequently, if a simulation program
+ * uses multiple threads, it should acquire a lock on a simulator (using a
+ * <TT>synchronized</TT> block) before accessing its state.
+ * Note however, that one can launch many simulations in parallel with as many
+ * threads, as long as <SPAN CLASS="textit">each thread has its own</SPAN> <TT>Simulator</TT>.
+ *
+ */
+public class Simulator {
+
+ protected double currentTime = 0.0;
+ // The current simulation time (clock).
+
+ protected EventList eventList;
+ // The list of future events.
+ // Can be changed by the method \texttt{init}.
+
+ protected boolean stopped = true;
+ // Becomes true when the simulation has ended
+ // (stopped has been called or the event list is empty).
+
+ protected boolean simulating = false;
+
+ protected ContinuousState continuousState = null;
+
+ /**
+ * Represents the default simulator being used by the
+ * class {@link Sim}, and the no-argument constructor of {@link Event}.
+ * This simulator is usually obtained with the
+ * {@link #getDefaultSimulator getDefaultSimulator} method, which initializes it if needed.
+ * But it might also be initialized
+ * differently, e.g., if process-driven simulation is required.
+ *
+ */
+ public static Simulator defaultSimulator = null;
+
+
+ /**
+ * Constructs a new simulator using a splay tree for the
+ * event list.
+ *
+ */
+ public Simulator() {
+ eventList = new SplayTree();
+ }
+
+
+ /**
+ * Constructs a new simulator using <TT>eventList</TT> for
+ * the event list.
+ *
+ */
+ public Simulator (EventList eventList) {
+ if (eventList == null)
+ throw new NullPointerException();
+ this.eventList = eventList;
+ }
+
+
+ /**
+ * Returns the current value of the simulation clock.
+ * @return the current simulation time
+ *
+ */
+ public double time()
+ {
+ return currentTime;
+ }
+
+
+ /**
+ * Reinitializes the simulation executive by clearing up the event
+ * list, and resetting the simulation clock to zero.
+ *
+ */
+ public void init() {
+ // This has to be done another way in order to separate events and processes.
+// SimProcess.killAll();
+ currentTime = 0.0; eventList.clear(); stopped = false; simulating = false;
+ }
+
+
+ /**
+ * Same as {@link #init init}, but also chooses <TT>evlist</TT> as the
+ * event list to be used.
+ * For example, <TT>init (new DoublyLinked())</TT> initializes the simulation
+ * with a doubly linked linear structure for the event list. To initialize the
+ * current <TT>Simulator</TT> with a not empty eventList is also possible, but
+ * the events scheduled in the eventList will be linked with the current simulator only.
+ *
+ * @param evlist selected event list implementation
+ *
+ *
+ */
+ public void init (EventList evlist) {
+ if (evlist == null)
+ throw new NullPointerException();
+ eventList = evlist;
+ ListIterator iter = eventList.listIterator();
+ while(iter.hasNext())
+ ((Event)iter.next()).setSimulator(this);
+ init();
+ }
+
+
+ /**
+ * Gets the currently used event list.
+ *
+ * @return the currently used event list
+ *
+ */
+ public EventList getEventList() {
+ return eventList;
+ }
+
+
+ /**
+ * Determines if this simulator is currently running, i.e.,
+ * executing scheduled events.
+ *
+ */
+ public boolean isSimulating() {
+ return simulating;
+ }
+
+
+ /**
+ * Determines if this simulator was stopped by
+ * an event. The simulator may still be processing the event which
+ * has called the {@link #stop stop} method; in this case,
+ * {@link #isSimulating isSimulating} returns <TT>true</TT>.
+ *
+ */
+ public boolean isStopped() {
+ return stopped;
+ }
+
+
+ /**
+ * Removes the first event from the event list and sets the simulation
+ * clock to its event time.
+ *
+ * @return the first planned event, or <TT>null</TT> if there is no such event
+ *
+ */
+ protected Event removeFirstEvent() {
+ if (stopped)
+ return null;
+ Event ev = eventList.removeFirst();
+ if (ev == null)
+ return null;
+ currentTime = ev.eventTime;
+ ev.eventTime = -10.0;
+ return ev;
+ }
+
+
+ /**
+ * Starts the simulation executive.
+ * There must be at least one <TT>Event</TT> in the
+ * event list when this method is called.
+ *
+ */
+ public void start () {
+ if (eventList.isEmpty())
+ throw new IllegalStateException ("start() called with an empty event list");
+ stopped = false;
+ simulating = true;
+ Event ev;
+ try {
+ while ((ev = removeFirstEvent()) != null && !stopped) {
+ // while (!stopped && (ev = eventList.removeFirst()) != null) {
+ // currentTime = ev.eventTime;
+ // ev.eventTime = -10.0;
+ ev.actions();
+ // if ev is a thread object associated to a process,
+ // the control will be transfered to this thread and the
+ // executive will be passivated in the actions() method.
+ }
+ }
+ finally {
+ stopped = true; simulating = false;
+ }
+ }
+
+
+ /**
+ * Tells the simulation executive to stop as soon as it takes control,
+ * and to return control to the program that called {@link #start start}.
+ * This program will then continue executing from the instructions right after
+ * its call to {@link #start(.) start} If an {@link Event} is currently executing (and
+ * this event has just called {@link #stop stop}), the executive will take
+ * control when the event terminates its execution.
+ *
+ */
+ public void stop()
+ {
+ stopped = true;
+ }
+
+
+ /**
+ * Returns the current state of continuous variables being
+ * integrated during the simulation.
+ * This state is used by the {@link Continuous} class when performing
+ * simulation of continuous variables; it defaults to an empty state,
+ * which is initialized only when this method is called.
+ *
+ * @return continuousState field
+ *
+ */
+ public ContinuousState continuousState()
+ {
+ if (continuousState == null)
+ continuousState = new ContinuousState(this);
+ return continuousState;
+ }
+
+
+ /**
+ * Returns the default simulator instance used by
+ * the deprecated class {@link Sim}.
+ * If this simulator does not exist yet, it is constructed using the
+ * no-argument constructor of this class.
+ * One can specify a different default simulator by setting
+ * the <TT>defaultSimulator</TT> field directly.
+ *
+ */
+ public static Simulator getDefaultSimulator() {
+ if (defaultSimulator == null)
+ defaultSimulator = new Simulator();
+ return defaultSimulator;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/Simulator.tex b/source/umontreal/iro/lecuyer/simevents/Simulator.tex
new file mode 100644
index 0000000..837ba77
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/Simulator.tex
@@ -0,0 +1,291 @@
+\defmodule {Simulator}
+
+Represents the executive of a discrete-event simulator.
+This class maintains a simulation
+clock, an event list, and starts executing the events
+%and processes
+in the appropriate order.
+Its methods permit one to start, stop, and (re)initialize the simulation,
+and read the simulation clock.
+
+Usually, a simulation program uses a single simulation clock which is
+represented by an instance of this class.
+For more convenience and compatibility, this class therefore provides
+a mechanism to construct and return a default simulator
+which is used when an event is constructed without an explicit reference to a
+simulator, and when the simulator is accessed through
+the \class{Sim} class.
+
+Note that this class is NOT thread-safe. Consequently, if a simulation program
+uses multiple threads, it should acquire a lock on a simulator (using a
+\texttt{synchronized} block) before accessing its state.
+Note however, that one can launch many simulations in parallel with as many
+threads, as long as \emph{each thread has its own} \texttt{Simulator}.
+
+%At last, this class provides tools to manage a default simulator variable thanks to its static methods.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Simulator
+ * Description: Represents the executive of a discrete-event simulator
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents;\begin{hide}
+
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.eventlist.SplayTree;
+import umontreal.iro.lecuyer.simprocs.*;\end{hide}
+ import java.util.ListIterator;
+
+public class Simulator \begin{hide} {
+
+ protected double currentTime = 0.0;
+ // The current simulation time (clock).
+
+ protected EventList eventList;
+ // The list of future events.
+ // Can be changed by the method \texttt{init}.
+
+ protected boolean stopped = true;
+ // Becomes true when the simulation has ended
+ // (stopped has been called or the event list is empty).
+
+ protected boolean simulating = false;
+
+ protected ContinuousState continuousState = null; \end{hide}
+
+ public static Simulator defaultSimulator\begin{hide} = null;\end{hide}
+\end{code}
+ \begin{tabb} Represents the default simulator being used by the
+ class \class{Sim}, and the no-argument constructor of \class{Event}.
+ This simulator is usually obtained with the
+ \method{getDefaultSimulator}{} method, which initializes it if needed.
+ But it might also be initialized
+ differently, e.g., if process-driven simulation is required.
+ \end{tabb}
+\subsubsection*{Constructors}
+\begin{code}
+
+ public Simulator()\begin{hide} {
+ eventList = new SplayTree();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new simulator using a splay tree for the
+ event list.
+\end{tabb}
+\begin{code}
+
+ public Simulator (EventList eventList)\begin{hide} {
+ if (eventList == null)
+ throw new NullPointerException();
+ this.eventList = eventList;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new simulator using \texttt{eventList} for
+ the event list.
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public double time() \begin{hide}
+ {
+ return currentTime;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the current value of the simulation clock. \end{tabb}
+\begin{htmlonly}
+ \return{the current simulation time}
+\end{htmlonly}
+\begin{code}
+
+ public void init() \begin{hide} {
+ // This has to be done another way in order to separate events and processes.
+// SimProcess.killAll();
+ currentTime = 0.0; eventList.clear(); stopped = false; simulating = false;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Reinitializes the simulation executive by clearing up the event
+ list, and resetting the simulation clock to zero.
+ \end{tabb}
+\begin{code}
+
+ public void init (EventList evlist) \begin{hide} {
+ if (evlist == null)
+ throw new NullPointerException();
+ eventList = evlist;
+ ListIterator iter = eventList.listIterator();
+ while(iter.hasNext())
+ ((Event)iter.next()).setSimulator(this);
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Same as \method{init}{}, but also chooses \texttt{evlist} as the
+ event list to be used.
+ For example, \texttt{init (new DoublyLinked())} initializes the simulation
+ with a doubly linked linear structure for the event list. To initialize the
+ current \texttt{Simulator} with a not empty eventList is also possible, but
+ the events scheduled in the eventList will be linked with the current simulator only.
+ \end{tabb}
+\begin{htmlonly}
+ \param{evlist}{selected event list implementation}
+\end{htmlonly}
+\begin{code}
+
+ public EventList getEventList()\begin{hide} {
+ return eventList;
+ }\end{hide}
+\end{code}
+\begin{tabb} Gets the currently used event list.
+\end{tabb}
+\begin{htmlonly}
+ \return{the currently used event list}
+\end{htmlonly}
+\begin{code}
+
+ public boolean isSimulating()\begin{hide} {
+ return simulating;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if this simulator is currently running, i.e.,
+ executing scheduled events.
+\end{tabb}
+\begin{code}
+
+ public boolean isStopped()\begin{hide} {
+ return stopped;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if this simulator was stopped by
+ an event. The simulator may still be processing the event which
+ has called the \method{stop}{} method; in this case,
+ \method{isSimulating}{} returns \texttt{true}.
+\end{tabb}
+\begin{code}
+
+ protected Event removeFirstEvent()\begin{hide} {
+ if (stopped)
+ return null;
+ Event ev = eventList.removeFirst();
+ if (ev == null)
+ return null;
+ currentTime = ev.eventTime;
+ ev.eventTime = -10.0;
+ return ev;
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes the first event from the event list and sets the simulation
+ clock to its event time.
+\end{tabb}
+\begin{htmlonly}
+ \return{the first planned event, or \texttt{null} if there is no such event}
+\end{htmlonly}
+\begin{code}
+
+ public void start () \begin{hide} {
+ if (eventList.isEmpty())
+ throw new IllegalStateException ("start() called with an empty event list");
+ stopped = false;
+ simulating = true;
+ Event ev;
+ try {
+ while ((ev = removeFirstEvent()) != null && !stopped) {
+ // while (!stopped && (ev = eventList.removeFirst()) != null) {
+ // currentTime = ev.eventTime;
+ // ev.eventTime = -10.0;
+ ev.actions();
+ // if ev is a thread object associated to a process,
+ // the control will be transfered to this thread and the
+ // executive will be passivated in the actions() method.
+ }
+ }
+ finally {
+ stopped = true; simulating = false;
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Starts the simulation executive.
+ There must be at least one \texttt{Event} in the
+ event list when this method is called.
+ \end{tabb}
+\begin{code}
+
+ public void stop() \begin{hide}
+ {
+ stopped = true;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Tells the simulation executive to stop as soon as it takes control,
+ and to return control to the program that called \method{start}{}.
+ This program will then continue executing from the instructions right after
+ its call to \method{start}. If an \class{Event} is currently executing (and
+ this event has just called \method{stop}{}), the executive will take
+ control when the event terminates its execution.
+% If a \texttt{SimProcess} is currently executing, the executive will take
+% control when the process terminates or suspends itself by invoking
+% one of its methods such as \texttt{delay}, \texttt{suspend},
+% \texttt{request}, etc.
+ \end{tabb}
+\begin{code}
+
+ public ContinuousState continuousState() \begin{hide}
+ {
+ if (continuousState == null)
+ continuousState = new ContinuousState(this);
+ return continuousState;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the current state of continuous variables being
+ integrated during the simulation.
+ This state is used by the \class{Continuous} class when performing
+ simulation of continuous variables; it defaults to an empty state,
+ which is initialized only when this method is called.
+ \end{tabb}
+ \begin{htmlonly}
+ \return{continuousState field}
+ \end{htmlonly}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Static methods}
+
+\begin{code}
+
+ public static Simulator getDefaultSimulator() \begin{hide} {
+ if (defaultSimulator == null)
+ defaultSimulator = new Simulator();
+ return defaultSimulator;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the default simulator instance used by
+ the deprecated class \class{Sim}.
+ If this simulator does not exist yet, it is constructed using the
+ no-argument constructor of this class.
+ One can specify a different default simulator by setting
+ the \texttt{defaultSimulator} field directly.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/BinaryTree.java b/source/umontreal/iro/lecuyer/simevents/eventlist/BinaryTree.java
new file mode 100644
index 0000000..614bea8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/BinaryTree.java
@@ -0,0 +1,772 @@
+
+
+/*
+ * Class: BinaryTree
+ * Description: implementation of class EventList using a binary search tree
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents.eventlist;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+/* *
+ * Implantation de l'interface EventList ayant comme structure de
+ * donnees un arbre binaire.
+ */
+
+
+/**
+ * An implementation of {@link EventList} using a binary search tree.
+ * Every event is stored into a tree node which has left and right children.
+ * Using the event time as a comparator the left child is always smaller
+ * than its parent whereas the right is greater or equal. This allows
+ * an average
+ * <SPAN CLASS="MATH"><I>O</I>(log(<I>n</I>))</SPAN> time for adding an event
+ * and searching the first event,
+ * where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number of events in the structure.
+ * There is less overhead for adding and removing events than splay tree
+ * or red black tree.
+ * However, in the worst case, adding or removing could
+ * be done in time proportional to <SPAN CLASS="MATH"><I>n</I></SPAN> because the binary search tree can be turned
+ * into a linked list.
+ *
+ */
+public class BinaryTree implements EventList {
+ // racine de l'arbre
+ private Entry root = null;
+
+ // liste d'objets qui peuvent etre reutilises
+ private Entry freeEntries = null;
+
+ // compteur de modifications sur l'iterateur.
+ private int modCount = 0;
+
+
+ public boolean isEmpty() {
+ return root == null;
+ }
+
+ public void clear() {
+ while (root != null)
+ remove (root);
+ }
+
+ public void add (Event ev) {
+ // fonction qui ajoute un evenement dans l'arbre
+ // note : si deux evenements ont le meme temps, alors il faut
+ // toujours faire en sorte que ces evenements se retrouvement
+ // comme les fils droits les uns des autres
+
+ Entry cursor = root;
+ boolean found = false;
+
+ if (cursor == null)
+ root = add (ev, null);
+ else {
+ while (!found) {
+ if (ev.compareTo(cursor.event) < 0) {
+ if (cursor.left == null) {
+ cursor.left = add (ev, cursor);
+ found = true;
+ }
+ cursor = cursor.left;
+
+ } else {
+ if (cursor.right == null) {
+ cursor.right = add (ev, cursor);
+ found = true;
+ }
+ cursor = cursor.right;
+ }
+ }
+ }
+ ++modCount;
+ }
+
+ public void addFirst (Event ev) {
+ /* *
+ * Ajoute "ev" comme premier evenement dans l'arbre.
+ * Donc completement a gauche
+ * On met l'ancien premier evenement a droite de ev.
+ * (Necessaire quand on a des evenements simultanes)
+ */
+ Entry cursor = root;
+
+ if (cursor != null) {
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ Entry e = add (ev, cursor.father);
+ e.right = cursor;
+ if (cursor == root)
+ root = e;
+ else
+ cursor.father.left = e;
+ cursor.father = e;
+ }
+ else
+ root = add (ev, null);
+
+ ++modCount;
+ }
+
+ public void addBefore (Event ev, Event other) {
+ Entry otherEntry = findEntry (other);
+ Entry evEntry = add (ev , null);
+
+ if (otherEntry == null)
+ throw new IllegalArgumentException("other not in the tree");
+
+ // insere evEntry a la place de otherEntry et otherEntry
+ // devient le fils droit de evEntry
+ if (otherEntry != root) {
+ if (otherEntry == otherEntry.father.right)
+ otherEntry.father.right = evEntry;
+ else
+ otherEntry.father.left = evEntry;
+ }
+ else
+ root = evEntry;
+
+ evEntry.father = otherEntry.father;
+ otherEntry.father = evEntry;
+ evEntry.right = otherEntry;
+
+ // le ss-arbre de droite de otherEntry devient le
+ // ss-arbre de droite de evEntry
+ // permet que evEntry soit exactement apres
+ // otherEntry qu'importe les operations effectuees
+ evEntry.left = otherEntry.left;
+
+ if (evEntry.left != null)
+ evEntry.left.father = evEntry;
+
+ otherEntry.left = null;
+
+ ++modCount;
+ }
+
+ public void addAfter (Event ev, Event other) {
+ // on va chercher le "Entry" de other
+ Entry otherEntry = findEntry (other);
+
+ if (otherEntry == null)
+ throw new IllegalArgumentException("other not in the tree");
+
+ // otherEntry est le parent de evEntry
+ Entry evEntry = add (ev, otherEntry);
+
+ evEntry.right = otherEntry.right;
+ otherEntry.right = evEntry;
+
+ if (evEntry.right != null)
+ evEntry.right.father = evEntry;
+
+ ++modCount;
+ }
+
+ public Event getFirst() {
+ if (root==null)
+ return null;
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ return cursor.event;
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ while (cursor != null) {
+ if (cursor.event.getClass().getName().equals (cl))
+ return cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ while (cursor != null) {
+ if (cursor.event.getClass() == cl)
+ return (E)cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new BTItr();
+ }
+
+ public boolean remove (Event ev) {
+ Entry evEntry = findEntry(ev);
+ if (evEntry == null)
+ return false;
+ else
+ return remove(evEntry);
+ }
+
+ public Event removeFirst() {
+ if (root == null)
+ return null;
+
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ Event first = cursor.event;
+ remove(cursor);
+
+ return first;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Contents of the event list BinaryTree:");
+ Entry cursor = root;
+
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ while (cursor != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, cursor.event.time()) + ", " +
+ PrintfFormat.g (8, 4, cursor.event.priority()) +
+ " : " + cursor.event.toString());
+ cursor = successor (cursor);
+ }
+
+ return sb.toString();
+ }
+
+ private Entry add (Event ev, Entry father) {
+ // On regarde la liste freeEntries
+ if (freeEntries != null) {
+ Entry tempo = freeEntries;
+ freeEntries = freeEntries.right;
+ tempo.event = ev;
+ tempo.left = null;
+ tempo.right = null;
+ tempo.father = father;
+ return tempo;
+ }
+ // on cree un nouvel objet
+ else
+ return new Entry(ev, null, null, father);
+ }
+
+ private boolean remove (Entry e) {
+ boolean filsGauche = false;
+ boolean isRoot = false;
+ Entry cursor;
+
+ if (e == root)
+ isRoot = true;
+ else {
+ if (e == e.father.left)
+ filsGauche = true;
+ else
+ filsGauche = false;
+ }
+
+ // Si condition vrai, a un fils droit ou rien
+ if (e.left == null) {
+ if (isRoot)
+ root = e.right;
+ else if (filsGauche)
+ e.father.left = e.right;
+ else
+ e.father.right = e.right;
+
+ if (e.right != null)
+ e.right.father = e.father;
+ }
+ else if (e.right == null) {
+ // Si condition vrai, a uniquement un fils gauche
+ if (isRoot)
+ root = e.left;
+ else if (filsGauche)
+ e.father.left = e.left;
+ else
+ e.father.right = e.left;
+ e.left.father = e.father;
+ }
+ else {
+ // a 2 fils
+ // recherche son descendant le plus petit dans le ss-arbre de droite
+ // et remplace "e" par ce descendant
+
+ cursor = e.right;
+ if (cursor.left == null) {
+ // c'est son fils de droite
+ if (isRoot)
+ root = cursor;
+ else {
+ if (filsGauche)
+ e.father.left = cursor;
+ else
+ e.father.right = cursor;
+ }
+ cursor.left = e.left;
+ }
+ else {
+ // recherche de la plus petite valeur dans le ss-arbre droit
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ // echange entre e et cursor et elimination de e
+ cursor.father.left = cursor.right;
+
+ if (isRoot)
+ root = cursor;
+ else if (filsGauche)
+ e.father.left = cursor;
+ else
+ e.father.right = cursor;
+
+ cursor.father.left = cursor.right;
+ if (cursor.right != null)
+ cursor.right.father = cursor.father;
+
+ cursor.right = e.right;
+ cursor.left = e.left;
+ e.right.father = cursor;
+ }
+
+ cursor.father = e.father;
+ e.left.father = cursor;
+ }
+
+ // recupere l'espace du noeud
+ e.right = freeEntries;
+ e.left = null;
+ e.event = null;
+ freeEntries = e;
+ e = null;
+ ++modCount;
+ return true;
+ }
+
+ private Entry successor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.right != null) {
+ cursor = cursor.right;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ }
+ else {
+ while (cursor.father != null && cursor.father.right == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ /* *
+ * fonction qui trouve le noeud (Entry) d'un evenement
+ * dans l'arbre
+ */
+ private Entry findEntry (Event ev) {
+ Entry cursor = root;
+ while (cursor != null) {
+ if (cursor.event == ev)
+ return cursor;
+ else if (ev.compareTo(cursor.event) < 0)
+ cursor = cursor.left;
+ else
+ cursor = cursor.right;
+ }
+ return null;
+ }
+
+ private Entry predecessor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.left != null) {
+ cursor = cursor.left;
+ while (cursor.right != null)
+ cursor = cursor.right;
+ }
+ else {
+ while (cursor.father != null && cursor.father.left == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ /* *
+ * Classe interne representant les noeuds de l'arbre
+ */
+ private static class Entry {
+ Event event;
+ Entry right;
+ Entry left;
+ Entry father;
+
+ Entry (Event event, Entry left, Entry right, Entry father) {
+ this.event = event;
+ this.left = left;
+ this.right = right;
+ this.father = father;
+ }
+ }
+
+ private class BTItr implements ListIterator<Event> {
+ private Entry prev;
+ private Entry next;
+ private Entry lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ BTItr() {
+ prev = null;
+ next = root;
+ if (next != null) {
+ while (next.left != null)
+ next = next.left;
+ }
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add(Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ // Check the event time and priority
+ if (next != null && ev.compareTo(next.event) > 0) {
+ ev.setTime (next.event.time());
+ ev.setPriority (next.event.priority());
+ }
+ if (prev != null && ev.compareTo(prev.event) < 0) {
+ ev.setTime (prev.event.time());
+ ev.setPriority (prev.event.priority());
+ }
+
+ Entry e = BinaryTree.this.add (ev, next);
+ if (prev != null) {
+ // Ajouter ev apr`es prev.
+ // insere e comme fils droit de prev
+ e.father = prev;
+ e.right = prev.right;
+ prev.right = e;
+ if (e.right != null)
+ e.right.father = e;
+ }
+ else {
+ // ajoute ev avant next.
+ // insere e a la place de eo et eo devient le fils droit de e
+ if (next != root) {
+ if (next == next.father.left)
+ next.father.left = e;
+ else
+ next.father.right = e;
+ }
+ else
+ root = e;
+ e.father = prev.father;
+ prev.father = e;
+ e.left = prev;
+ // le ss-arbre de droite de eo devient le ss-arbre de droite de e
+ // permet que e soit exactement apres eo qu'importe les
+ // operations effectuees
+ e.right = prev.right;
+ if (e.right != null)
+ e.right.father = e;
+ prev.right = null;
+ }
+
+ prev = e;
+ ++nextIndex;
+ lastRet = null;
+ ++modCount;
+ ++expectedModCount;
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != null;
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prev != null;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = next.event;
+ lastRet = next;
+ prev = next;
+ next = successor (next);
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = prev.event;
+ lastRet = prev;
+ next = prev;
+ prev = predecessor (prev);
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ if (lastRet == next) // Last call to previous
+ next = successor (next);
+ else { // Last call to next or no call
+ prev = predecessor (prev);
+ --nextIndex;
+ }
+ BinaryTree.this.remove (lastRet);
+ lastRet = null;
+ ++expectedModCount;
+ }
+
+ public void set (Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ Entry pred = predecessor (lastRet);
+ Entry succ = successor (lastRet);
+ if (pred != null && ev.compareTo(pred.event) < 0) {
+ ev.setTime (pred.event.time());
+ ev.setPriority (pred.event.priority());
+ }
+ if (succ != null && ev.compareTo(succ.event) > 0) {
+ ev.setTime (succ.event.time());
+ ev.setPriority (succ.event.priority());
+ }
+ lastRet.event = ev;
+ }
+ }
+
+/* public static void main (String[] args) {
+ BinaryTree sp = new BinaryTree();
+
+ Event1 e1 = new Event1(); e1.setTime(10.0);
+ Event1 e2 = new Event1(); e2.setTime(20.0);
+ Event1 e3 = new Event1(); e3.setTime(30.0);
+ Event1 e4 = new Event1(); e4.setTime(40.0);
+ Event1 e5 = new Event1(); e5.setTime(50.0);
+ Event1 e6 = new Event1(); e6.setTime(60.0);
+ Event1 e7 = new Event1(); e7.setTime(70.0);
+
+ sp.add(e1);
+ sp.add(e2);
+ sp.print2(sp.root);
+ sp.add(e3);
+ sp.print2(sp.root);
+ sp.add(e4);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+ sp.add(e6);
+ sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ // sp.add(e5);
+ // sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ // sp.add(e5);
+ // sp.print2(sp.root);
+
+ sp.getFirst();
+ System.out.println(".....after GetFirst" +
+ PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ sp.remove(e3);
+ System.out.println("Apres remove" + PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ }
+
+ private void print(Entry t) {
+ if (t != null){
+ print (t.left);
+ System.out.println ("===========> Event time "+t.event.time());
+ print (t.right);
+ }
+ }
+
+ private void print2(Entry t) {
+ System.out.println("=============================== "+
+ "print2 : pour ..... "+t.event.time());
+ if (t != null) {
+ System.out.println ("===========> ev time "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void gauche (Entry t) {
+ if (t != null) {
+ System.out.println ("===========> gauche "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void droite (Entry t) {
+ if (t != null){
+ System.out.println ("===========> droite "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ // System.out.println();
+ }
+ else
+ System.out.println ("===========> droite null ");
+ }
+
+ private static class Event1 extends Event {
+ public void actions() {}
+
+ public String toString() {
+ return "Event(" + eventTime + ")";
+ }
+ };
+
+
+
+
+
+*/
+ /*
+ public BinaryTree() {
+
+ Event1 e1 = new Event1(); e1.setTime(7.0);
+ Event1 e2 = new Event1(); e2.setTime(5.0);
+ Event1 e3 = new Event1(); e3.setPriority(2); e3.setTime(5.0);
+ Event1 e4 = new Event1(); e4.setTime(6.0);
+ Event1 e5 = new Event1(); e5.setPriority(2); e5.setTime(10.0);
+ Event1 e6 = new Event1(); e6.setTime(9.0);
+ Event1 e7 = new Event1(); e7.setTime(11.0);
+
+ add(e1);
+ add(e2);
+ add(e3);
+ add(e4);
+ add(e5);
+ add(e6);
+ add(e7);
+ print22(root);
+ remove (e5);
+ print22(root);
+
+ }
+
+
+
+ public void print22(Entry t) {
+ System.out.println("racine............ ..... "+t.event.time());
+ gauche2(t.left);
+ droite2(t.right);
+ System.out.println();
+
+ }
+
+
+
+ public void gauche2 (Entry t) {
+ if (t!=null){
+ System.out.println ("===========> gauche "+t.event.time());
+ gauche2(t.left);
+ droite2(t.right);
+ System.out.println();
+
+ }
+ else System.out.println ("===========> gauche null ");
+ }
+
+ public void droite2 (Entry t) {
+ if (t!=null){
+ System.out.println ("===========> droite "+t.event.time());
+ gauche2(t.left);
+ droite2(t.right);
+ // System.out.println();
+
+ }
+ else System.out.println ("===========> droite null ");
+ }
+ */
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/BinaryTree.tex b/source/umontreal/iro/lecuyer/simevents/eventlist/BinaryTree.tex
new file mode 100644
index 0000000..b342367
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/BinaryTree.tex
@@ -0,0 +1,777 @@
+\defmodule {BinaryTree}
+
+An implementation of \class{EventList} using a binary search tree.
+Every event is stored into a tree node which has left and right children.
+Using the event time as a comparator %(and also the priority),
+the left child is always smaller
+than its parent whereas the right is greater or equal. This allows
+an average $O(\log (n))$ time for adding an event
+and searching the first event,
+where $n$ is the number of events in the structure.
+There is less overhead for adding and removing events than splay tree
+or red black tree.
+However, in the worst case, adding or removing could
+be done in time proportional to $n$ because the binary search tree can be turned
+into a linked list.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BinaryTree
+ * Description: implementation of class EventList using a binary search tree
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents.eventlist;\begin{hide}
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+/**
+ * Implantation de l'interface EventList ayant comme structure de
+ * donnees un arbre binaire.
+ */
+\end{hide}
+
+public class BinaryTree implements EventList\begin{hide} {
+ // racine de l'arbre
+ private Entry root = null;
+
+ // liste d'objets qui peuvent etre reutilises
+ private Entry freeEntries = null;
+
+ // compteur de modifications sur l'iterateur.
+ private int modCount = 0;\end{hide}
+\end{code}\begin{hide}\begin{code}
+
+ public boolean isEmpty()\begin{hide} {
+ return root == null;
+ }\end{hide}
+
+ public void clear()\begin{hide} {
+ while (root != null)
+ remove (root);
+ }\end{hide}
+
+ public void add (Event ev)\begin{hide} {
+ // fonction qui ajoute un evenement dans l'arbre
+ // note : si deux evenements ont le meme temps, alors il faut
+ // toujours faire en sorte que ces evenements se retrouvement
+ // comme les fils droits les uns des autres
+
+ Entry cursor = root;
+ boolean found = false;
+
+ if (cursor == null)
+ root = add (ev, null);
+ else {
+ while (!found) {
+ if (ev.compareTo(cursor.event) < 0) {
+ if (cursor.left == null) {
+ cursor.left = add (ev, cursor);
+ found = true;
+ }
+ cursor = cursor.left;
+
+ } else {
+ if (cursor.right == null) {
+ cursor.right = add (ev, cursor);
+ found = true;
+ }
+ cursor = cursor.right;
+ }
+ }
+ }
+ ++modCount;
+ }\end{hide}
+
+ public void addFirst (Event ev)\begin{hide} {
+ /**
+ * Ajoute "ev" comme premier evenement dans l'arbre.
+ * Donc completement a gauche
+ * On met l'ancien premier evenement a droite de ev.
+ * (Necessaire quand on a des evenements simultanes)
+ */
+ Entry cursor = root;
+
+ if (cursor != null) {
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ Entry e = add (ev, cursor.father);
+ e.right = cursor;
+ if (cursor == root)
+ root = e;
+ else
+ cursor.father.left = e;
+ cursor.father = e;
+ }
+ else
+ root = add (ev, null);
+
+ ++modCount;
+ }\end{hide}
+
+ public void addBefore (Event ev, Event other)\begin{hide} {
+ Entry otherEntry = findEntry (other);
+ Entry evEntry = add (ev , null);
+
+ if (otherEntry == null)
+ throw new IllegalArgumentException("other not in the tree");
+
+ // insere evEntry a la place de otherEntry et otherEntry
+ // devient le fils droit de evEntry
+ if (otherEntry != root) {
+ if (otherEntry == otherEntry.father.right)
+ otherEntry.father.right = evEntry;
+ else
+ otherEntry.father.left = evEntry;
+ }
+ else
+ root = evEntry;
+
+ evEntry.father = otherEntry.father;
+ otherEntry.father = evEntry;
+ evEntry.right = otherEntry;
+
+ // le ss-arbre de droite de otherEntry devient le
+ // ss-arbre de droite de evEntry
+ // permet que evEntry soit exactement apres
+ // otherEntry qu'importe les operations effectuees
+ evEntry.left = otherEntry.left;
+
+ if (evEntry.left != null)
+ evEntry.left.father = evEntry;
+
+ otherEntry.left = null;
+
+ ++modCount;
+ }\end{hide}
+
+ public void addAfter (Event ev, Event other)\begin{hide} {
+ // on va chercher le "Entry" de other
+ Entry otherEntry = findEntry (other);
+
+ if (otherEntry == null)
+ throw new IllegalArgumentException("other not in the tree");
+
+ // otherEntry est le parent de evEntry
+ Entry evEntry = add (ev, otherEntry);
+
+ evEntry.right = otherEntry.right;
+ otherEntry.right = evEntry;
+
+ if (evEntry.right != null)
+ evEntry.right.father = evEntry;
+
+ ++modCount;
+ }\end{hide}
+
+ public Event getFirst()\begin{hide} {
+ if (root==null)
+ return null;
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ return cursor.event;
+ }\end{hide}
+
+ public Event getFirstOfClass (String cl)\begin{hide} {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ while (cursor != null) {
+ if (cursor.event.getClass().getName().equals (cl))
+ return cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ while (cursor != null) {
+ if (cursor.event.getClass() == cl)
+ return (E)cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new BTItr();
+ }\end{hide}
+
+ public boolean remove (Event ev)\begin{hide} {
+ Entry evEntry = findEntry(ev);
+ if (evEntry == null)
+ return false;
+ else
+ return remove(evEntry);
+ }\end{hide}
+
+ public Event removeFirst()\begin{hide} {
+ if (root == null)
+ return null;
+
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ Event first = cursor.event;
+ remove(cursor);
+
+ return first;
+ }\end{hide}
+
+ public String toString()\begin{hide} {
+ StringBuffer sb = new StringBuffer ("Contents of the event list BinaryTree:");
+ Entry cursor = root;
+
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ while (cursor != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, cursor.event.time()) + ", " +
+ PrintfFormat.g (8, 4, cursor.event.priority()) +
+ " : " + cursor.event.toString());
+ cursor = successor (cursor);
+ }
+
+ return sb.toString();
+ }
+
+ private Entry add (Event ev, Entry father) {
+ // On regarde la liste freeEntries
+ if (freeEntries != null) {
+ Entry tempo = freeEntries;
+ freeEntries = freeEntries.right;
+ tempo.event = ev;
+ tempo.left = null;
+ tempo.right = null;
+ tempo.father = father;
+ return tempo;
+ }
+ // on cree un nouvel objet
+ else
+ return new Entry(ev, null, null, father);
+ }
+
+ private boolean remove (Entry e) {
+ boolean filsGauche = false;
+ boolean isRoot = false;
+ Entry cursor;
+
+ if (e == root)
+ isRoot = true;
+ else {
+ if (e == e.father.left)
+ filsGauche = true;
+ else
+ filsGauche = false;
+ }
+
+ // Si condition vrai, a un fils droit ou rien
+ if (e.left == null) {
+ if (isRoot)
+ root = e.right;
+ else if (filsGauche)
+ e.father.left = e.right;
+ else
+ e.father.right = e.right;
+
+ if (e.right != null)
+ e.right.father = e.father;
+ }
+ else if (e.right == null) {
+ // Si condition vrai, a uniquement un fils gauche
+ if (isRoot)
+ root = e.left;
+ else if (filsGauche)
+ e.father.left = e.left;
+ else
+ e.father.right = e.left;
+ e.left.father = e.father;
+ }
+ else {
+ // a 2 fils
+ // recherche son descendant le plus petit dans le ss-arbre de droite
+ // et remplace "e" par ce descendant
+
+ cursor = e.right;
+ if (cursor.left == null) {
+ // c'est son fils de droite
+ if (isRoot)
+ root = cursor;
+ else {
+ if (filsGauche)
+ e.father.left = cursor;
+ else
+ e.father.right = cursor;
+ }
+ cursor.left = e.left;
+ }
+ else {
+ // recherche de la plus petite valeur dans le ss-arbre droit
+ while (cursor.left != null)
+ cursor = cursor.left;
+
+ // echange entre e et cursor et elimination de e
+ cursor.father.left = cursor.right;
+
+ if (isRoot)
+ root = cursor;
+ else if (filsGauche)
+ e.father.left = cursor;
+ else
+ e.father.right = cursor;
+
+ cursor.father.left = cursor.right;
+ if (cursor.right != null)
+ cursor.right.father = cursor.father;
+
+ cursor.right = e.right;
+ cursor.left = e.left;
+ e.right.father = cursor;
+ }
+
+ cursor.father = e.father;
+ e.left.father = cursor;
+ }
+
+ // recupere l'espace du noeud
+ e.right = freeEntries;
+ e.left = null;
+ e.event = null;
+ freeEntries = e;
+ e = null;
+ ++modCount;
+ return true;
+ }
+
+ private Entry successor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.right != null) {
+ cursor = cursor.right;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ }
+ else {
+ while (cursor.father != null && cursor.father.right == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ /**
+ * fonction qui trouve le noeud (Entry) d'un evenement
+ * dans l'arbre
+ */
+ private Entry findEntry (Event ev) {
+ Entry cursor = root;
+ while (cursor != null) {
+ if (cursor.event == ev)
+ return cursor;
+ else if (ev.compareTo(cursor.event) < 0)
+ cursor = cursor.left;
+ else
+ cursor = cursor.right;
+ }
+ return null;
+ }
+
+ private Entry predecessor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.left != null) {
+ cursor = cursor.left;
+ while (cursor.right != null)
+ cursor = cursor.right;
+ }
+ else {
+ while (cursor.father != null && cursor.father.left == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ /**
+ * Classe interne representant les noeuds de l'arbre
+ */
+ private static class Entry {
+ Event event;
+ Entry right;
+ Entry left;
+ Entry father;
+
+ Entry (Event event, Entry left, Entry right, Entry father) {
+ this.event = event;
+ this.left = left;
+ this.right = right;
+ this.father = father;
+ }
+ }
+
+ private class BTItr implements ListIterator<Event> {
+ private Entry prev;
+ private Entry next;
+ private Entry lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ BTItr() {
+ prev = null;
+ next = root;
+ if (next != null) {
+ while (next.left != null)
+ next = next.left;
+ }
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add(Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ // Check the event time and priority
+ if (next != null && ev.compareTo(next.event) > 0) {
+ ev.setTime (next.event.time());
+ ev.setPriority (next.event.priority());
+ }
+ if (prev != null && ev.compareTo(prev.event) < 0) {
+ ev.setTime (prev.event.time());
+ ev.setPriority (prev.event.priority());
+ }
+
+ Entry e = BinaryTree.this.add (ev, next);
+ if (prev != null) {
+ // Ajouter ev apr`es prev.
+ // insere e comme fils droit de prev
+ e.father = prev;
+ e.right = prev.right;
+ prev.right = e;
+ if (e.right != null)
+ e.right.father = e;
+ }
+ else {
+ // ajoute ev avant next.
+ // insere e a la place de eo et eo devient le fils droit de e
+ if (next != root) {
+ if (next == next.father.left)
+ next.father.left = e;
+ else
+ next.father.right = e;
+ }
+ else
+ root = e;
+ e.father = prev.father;
+ prev.father = e;
+ e.left = prev;
+ // le ss-arbre de droite de eo devient le ss-arbre de droite de e
+ // permet que e soit exactement apres eo qu'importe les
+ // operations effectuees
+ e.right = prev.right;
+ if (e.right != null)
+ e.right.father = e;
+ prev.right = null;
+ }
+
+ prev = e;
+ ++nextIndex;
+ lastRet = null;
+ ++modCount;
+ ++expectedModCount;
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != null;
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prev != null;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = next.event;
+ lastRet = next;
+ prev = next;
+ next = successor (next);
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = prev.event;
+ lastRet = prev;
+ next = prev;
+ prev = predecessor (prev);
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ if (lastRet == next) // Last call to previous
+ next = successor (next);
+ else { // Last call to next or no call
+ prev = predecessor (prev);
+ --nextIndex;
+ }
+ BinaryTree.this.remove (lastRet);
+ lastRet = null;
+ ++expectedModCount;
+ }
+
+ public void set (Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ Entry pred = predecessor (lastRet);
+ Entry succ = successor (lastRet);
+ if (pred != null && ev.compareTo(pred.event) < 0) {
+ ev.setTime (pred.event.time());
+ ev.setPriority (pred.event.priority());
+ }
+ if (succ != null && ev.compareTo(succ.event) > 0) {
+ ev.setTime (succ.event.time());
+ ev.setPriority (succ.event.priority());
+ }
+ lastRet.event = ev;
+ }
+ }
+
+/* public static void main (String[] args) {
+ BinaryTree sp = new BinaryTree();
+
+ Event1 e1 = new Event1(); e1.setTime(10.0);
+ Event1 e2 = new Event1(); e2.setTime(20.0);
+ Event1 e3 = new Event1(); e3.setTime(30.0);
+ Event1 e4 = new Event1(); e4.setTime(40.0);
+ Event1 e5 = new Event1(); e5.setTime(50.0);
+ Event1 e6 = new Event1(); e6.setTime(60.0);
+ Event1 e7 = new Event1(); e7.setTime(70.0);
+
+ sp.add(e1);
+ sp.add(e2);
+ sp.print2(sp.root);
+ sp.add(e3);
+ sp.print2(sp.root);
+ sp.add(e4);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+ sp.add(e6);
+ sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ // sp.add(e5);
+ // sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ // sp.add(e5);
+ // sp.print2(sp.root);
+
+ sp.getFirst();
+ System.out.println(".....after GetFirst" +
+ PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ sp.remove(e3);
+ System.out.println("Apres remove" + PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ }
+
+ private void print(Entry t) {
+ if (t != null){
+ print (t.left);
+ System.out.println ("===========> Event time "+t.event.time());
+ print (t.right);
+ }
+ }
+
+ private void print2(Entry t) {
+ System.out.println("=============================== "+
+ "print2 : pour ..... "+t.event.time());
+ if (t != null) {
+ System.out.println ("===========> ev time "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void gauche (Entry t) {
+ if (t != null) {
+ System.out.println ("===========> gauche "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void droite (Entry t) {
+ if (t != null){
+ System.out.println ("===========> droite "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ // System.out.println();
+ }
+ else
+ System.out.println ("===========> droite null ");
+ }
+
+ private static class Event1 extends Event {
+ public void actions() {}
+
+ public String toString() {
+ return "Event(" + eventTime + ")";
+ }
+ };
+
+
+
+
+
+*/
+ /*
+ public BinaryTree() {
+
+ Event1 e1 = new Event1(); e1.setTime(7.0);
+ Event1 e2 = new Event1(); e2.setTime(5.0);
+ Event1 e3 = new Event1(); e3.setPriority(2); e3.setTime(5.0);
+ Event1 e4 = new Event1(); e4.setTime(6.0);
+ Event1 e5 = new Event1(); e5.setPriority(2); e5.setTime(10.0);
+ Event1 e6 = new Event1(); e6.setTime(9.0);
+ Event1 e7 = new Event1(); e7.setTime(11.0);
+
+ add(e1);
+ add(e2);
+ add(e3);
+ add(e4);
+ add(e5);
+ add(e6);
+ add(e7);
+ print22(root);
+ remove (e5);
+ print22(root);
+
+ }
+
+
+
+ public void print22(Entry t) {
+ System.out.println("racine............ ..... "+t.event.time());
+ gauche2(t.left);
+ droite2(t.right);
+ System.out.println();
+
+ }
+
+
+
+ public void gauche2 (Entry t) {
+ if (t!=null){
+ System.out.println ("===========> gauche "+t.event.time());
+ gauche2(t.left);
+ droite2(t.right);
+ System.out.println();
+
+ }
+ else System.out.println ("===========> gauche null ");
+ }
+
+ public void droite2 (Entry t) {
+ if (t!=null){
+ System.out.println ("===========> droite "+t.event.time());
+ gauche2(t.left);
+ droite2(t.right);
+ // System.out.println();
+
+ }
+ else System.out.println ("===========> droite null ");
+ }
+ */
+}\end{hide}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/DoublyLinked.java b/source/umontreal/iro/lecuyer/simevents/eventlist/DoublyLinked.java
new file mode 100644
index 0000000..dc9da48
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/DoublyLinked.java
@@ -0,0 +1,491 @@
+
+
+/*
+ * Class: DoublyLinked
+ * Description: implementation of class EventList using a doubly-linked list
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents.eventlist;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+
+
+/**
+ * An implementation of {@link EventList} using a doubly linked linear list.
+ * Each event is stored into a list node that contains a pointer to its following
+ * and preceding events. Adding an event requires a linear search to keep the
+ * event list sorted by event time and priority. Removing the first event is
+ * done in constant time because it simply removes the first list node.
+ * List nodes are recycled for increased memory management efficiency.
+ *
+ */
+public class DoublyLinked implements EventList {
+ private int modCount = 0;
+
+ // First and last elements in the list.
+ private Node first = null, last = null;
+ private static Node free = null; // Pointer to stack of free nodes.
+
+
+
+ public boolean isEmpty() {
+ return first == null;
+ }
+
+ public void clear() {
+ if (first == null)
+ return;
+ /* Node p = first;
+ while (p != null) {
+ p.ev.setTime(-20.0);
+ p.ev = null;
+ p = p.succ;
+ }
+ */
+ synchronized (DoublyLinked.class) {
+ last.succ = free; free = first;
+ }
+ last = first = null;
+ ++modCount;
+ }
+
+ public void add (Event ev) {
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+ ++modCount;
+ if (last == null) { // Easy: the event list was empty.
+ first = last = newNode;
+ first.prec = first.succ = null;
+ return;
+ }
+
+ Node node = findPosition (ev); // Finds where to insert.
+ if (node == null) { // Must be inserted first.
+ newNode.succ = first;
+ newNode.succ.prec = newNode;
+ first = newNode;
+ newNode.prec = null;
+ }
+ else { // Insert after node.
+ newNode.prec = node;
+ newNode.succ = node.succ;
+ node.succ = newNode;
+ if (newNode.succ != null)
+ newNode.succ.prec = newNode;
+ else
+ last = newNode;
+ }
+ }
+
+ public void addFirst (Event ev) {
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+ newNode.prec = null;
+ if (first == null) {
+ first = last = newNode;
+ first.succ = null;
+ }
+ else {
+ newNode.succ = first;
+ first.prec = newNode;
+ first = newNode;
+ }
+ ++modCount;
+ }
+
+ public void addBefore (Event ev, Event other) {
+ Node node = last;
+ while (node != null && node.ev.compareTo(other) >= 0 && node.ev != other)
+ node = node.prec;
+ if (node.ev != other)
+ throw new IllegalArgumentException ("Event not in list.");
+
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+
+ newNode.prec = node.prec;
+ newNode.succ = node;
+ node.prec = newNode;
+ if (newNode.prec != null)
+ newNode.prec.succ = newNode;
+ else
+ first = newNode;
+ ++modCount;
+ }
+
+ public void addAfter (Event ev, Event other) {
+ Node node = last;
+ while (node != null && node.ev.compareTo(other) >= 0 && node.ev != other)
+ node = node.prec;
+ if (node.ev != other)
+ throw new IllegalArgumentException ("Event not in list.");
+
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+
+ newNode.prec = node;
+ newNode.succ = node.succ;
+ node.succ = newNode;
+ if (newNode.succ != null)
+ newNode.succ.prec = newNode;
+ else
+ last = newNode;
+ ++modCount;
+ }
+
+ public Event getFirst() {
+ return first == null ? null : first.ev;
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Node node = first;
+ while (node != null) {
+ if (node.ev.getClass().getName().equals (cl))
+ return node.ev;
+ node = node.succ;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Node node = first;
+ while (node != null) {
+ if (node.ev.getClass() == cl)
+ return (E)node.ev;
+ node = node.succ;
+ }
+ return null;
+ }
+
+ public boolean remove (Event ev) {
+ // Find the node corresponding to this event ev.
+ Node node = last;
+ while (node != null && node.ev.compareTo(ev) >= 0 && node.ev != ev)
+ node = node.prec;
+ if (node == null || node.ev != ev)
+ return false;
+
+ if (node == last && node == first)
+ last = first = null; // The list is now empty.
+ else {
+ if (node == last) {
+ last = node.prec;
+ last.succ = null;
+ }
+ else
+ node.succ.prec = node.prec;
+ if (node == first) {
+ first = node.succ;
+ first.prec = null;
+ }
+ else {
+ node.prec.succ = node.succ;
+ node.prec = null;
+ }
+ }
+ node.ev = null;
+ synchronized (DoublyLinked.class) {
+ node.succ = free; free = node; // Recycle node.
+ }
+ ++modCount;
+ return true;
+ }
+
+ public Event removeFirst() {
+ if (first == null)
+ return null;
+
+ Event ev = first.ev;
+ Node temp = first;
+ first = first.succ;
+ if (first == null)
+ last = null;
+ else
+ first.prec = null;
+
+ temp.ev = null;
+ synchronized (DoublyLinked.class) {
+ temp.succ = free; free = temp; // Recycle node.
+ }
+ ++modCount;
+ return ev;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new DLItr();
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Contents of the event list DoublyLinked:");
+ Node node = first;
+ while (node != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, node.ev.time()) + ", " +
+ PrintfFormat.g (8, 4, node.ev.priority()) + " : " +
+ node.ev.toString());
+ node = node.succ;
+ }
+ return sb.toString();
+ }
+
+ // A element of the event list. This node contains the event ev.
+ // His predecessor and successor are prec and succ.
+ private static class Node {
+ Event ev;
+ Node prec, succ;
+ }
+
+ private class DLItr implements ListIterator<Event> {
+ private Node prev;
+ private Node next;
+ private Node lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ DLItr() {
+ prev = null;
+ next = first;
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add(Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ // Check the event time and priority
+ if (next != null && ev.compareTo(next.ev) > 0) {
+ ev.setTime (next.ev.time());
+ ev.setPriority (next.ev.priority());
+ }
+ if (prev != null && ev.compareTo(prev.ev) < 0) {
+ ev.setTime (prev.ev.time());
+ ev.setPriority (prev.ev.priority());
+ }
+
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+ ++nextIndex;
+ ++modCount;
+ ++expectedModCount;
+ lastRet = null;
+ if (last == null) { // Easy: the event list was empty.
+ first = last = newNode;
+ first.prec = first.succ = null;
+ prev = newNode;
+ next = null;
+ nextIndex = 1;
+ }
+ else if (prev == null) { // Must be inserted first.
+ // next is non-null or the list would be empty.
+ newNode.succ = first;
+ newNode.succ.prec = newNode;
+ first = newNode;
+ newNode.prec = null;
+ prev = newNode;
+ }
+ else { // Insert after node.
+ // prev is non-null but next can be null.
+ newNode.prec = prev;
+ newNode.succ = next;
+ prev.succ = newNode;
+ if (newNode.succ != null)
+ newNode.succ.prec = newNode;
+ else
+ last = newNode;
+ prev = newNode;
+ }
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != null;
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prev != null;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = next.ev;
+ lastRet = next;
+ prev = next;
+ next = next.succ;
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = prev.ev;
+ lastRet = prev;
+ next = prev;
+ prev = prev.prec;
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ if (lastRet == next) // Last call to previous
+ next = next.succ;
+ else { // Last call to next
+ prev = prev.prec;
+ --nextIndex;
+ }
+ if (lastRet == last && lastRet == first) {
+ last = first = null; // The list is now empty.
+ next = prev = null;
+ }
+ else {
+ if (lastRet == last) {
+ last = lastRet.prec;
+ last.succ = null;
+ }
+ else
+ lastRet.succ.prec = lastRet.prec;
+ if (lastRet == first) {
+ first = lastRet.succ;
+ first.prec = null;
+ }
+ else {
+ lastRet.prec.succ = lastRet.succ;
+ lastRet.prec = null;
+ }
+ }
+ lastRet.ev = null;
+ synchronized (DoublyLinked.class) {
+ lastRet.succ = free; free = lastRet; // Recycle node.
+ }
+ lastRet = null;
+ ++modCount;
+ ++expectedModCount;
+ }
+
+ public void set (Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ // Check the event time and priority
+ if (lastRet.prec != null && ev.compareTo(lastRet.prec.ev) < 0) {
+ ev.setTime (lastRet.prec.ev.time());
+ ev.setPriority (lastRet.prec.ev.priority());
+ }
+ if (lastRet.succ != null && ev.compareTo(lastRet.succ.ev) > 0) {
+ ev.setTime (lastRet.succ.ev.time());
+ ev.setPriority (lastRet.succ.ev.priority());
+ }
+
+ lastRet.ev = ev;
+ }
+ }
+
+ private Node findPosition (Event ev) {
+ // This implementation appears quite inefficient !!!!
+ // Must try to improve.
+ Node node = last;
+
+ // Finds the occurrence time of the new event (evTime).
+ while (node != null && ev.compareTo(node.ev) < 0) {
+ node = node.prec;
+ }
+ return node;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/DoublyLinked.tex b/source/umontreal/iro/lecuyer/simevents/eventlist/DoublyLinked.tex
new file mode 100644
index 0000000..75d0a01
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/DoublyLinked.tex
@@ -0,0 +1,496 @@
+\defmodule{DoublyLinked}
+
+An implementation of \class{EventList} using a doubly linked linear list.
+Each event is stored into a list node that contains a pointer to its following
+and preceding events. Adding an event requires a linear search to keep the
+event list sorted by event time and priority. Removing the first event is
+done in constant time because it simply removes the first list node.
+List nodes are recycled for increased memory management efficiency.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: DoublyLinked
+ * Description: implementation of class EventList using a doubly-linked list
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents.eventlist; \begin{hide}
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+\end{hide}
+
+public class DoublyLinked implements EventList\begin{hide} {
+ private int modCount = 0;
+
+ // First and last elements in the list.
+ private Node first = null, last = null;
+ private static Node free = null; // Pointer to stack of free nodes.
+\end{hide}
+\end{code}\begin{hide}\begin{code}
+
+ public boolean isEmpty()\begin{hide} {
+ return first == null;
+ }\end{hide}
+
+ public void clear()\begin{hide} {
+ if (first == null)
+ return;
+ /* Node p = first;
+ while (p != null) {
+ p.ev.setTime(-20.0);
+ p.ev = null;
+ p = p.succ;
+ }
+ */
+ synchronized (DoublyLinked.class) {
+ last.succ = free; free = first;
+ }
+ last = first = null;
+ ++modCount;
+ }\end{hide}
+
+ public void add (Event ev)\begin{hide} {
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+ ++modCount;
+ if (last == null) { // Easy: the event list was empty.
+ first = last = newNode;
+ first.prec = first.succ = null;
+ return;
+ }
+
+ Node node = findPosition (ev); // Finds where to insert.
+ if (node == null) { // Must be inserted first.
+ newNode.succ = first;
+ newNode.succ.prec = newNode;
+ first = newNode;
+ newNode.prec = null;
+ }
+ else { // Insert after node.
+ newNode.prec = node;
+ newNode.succ = node.succ;
+ node.succ = newNode;
+ if (newNode.succ != null)
+ newNode.succ.prec = newNode;
+ else
+ last = newNode;
+ }
+ }\end{hide}
+
+ public void addFirst (Event ev)\begin{hide} {
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+ newNode.prec = null;
+ if (first == null) {
+ first = last = newNode;
+ first.succ = null;
+ }
+ else {
+ newNode.succ = first;
+ first.prec = newNode;
+ first = newNode;
+ }
+ ++modCount;
+ }\end{hide}
+
+ public void addBefore (Event ev, Event other)\begin{hide} {
+ Node node = last;
+ while (node != null && node.ev.compareTo(other) >= 0 && node.ev != other)
+ node = node.prec;
+ if (node.ev != other)
+ throw new IllegalArgumentException ("Event not in list.");
+
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+
+ newNode.prec = node.prec;
+ newNode.succ = node;
+ node.prec = newNode;
+ if (newNode.prec != null)
+ newNode.prec.succ = newNode;
+ else
+ first = newNode;
+ ++modCount;
+ }\end{hide}
+
+ public void addAfter (Event ev, Event other)\begin{hide} {
+ Node node = last;
+ while (node != null && node.ev.compareTo(other) >= 0 && node.ev != other)
+ node = node.prec;
+ if (node.ev != other)
+ throw new IllegalArgumentException ("Event not in list.");
+
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+
+ newNode.prec = node;
+ newNode.succ = node.succ;
+ node.succ = newNode;
+ if (newNode.succ != null)
+ newNode.succ.prec = newNode;
+ else
+ last = newNode;
+ ++modCount;
+ }\end{hide}
+
+ public Event getFirst()\begin{hide} {
+ return first == null ? null : first.ev;
+ }\end{hide}
+
+ public Event getFirstOfClass (String cl)\begin{hide} {
+ Node node = first;
+ while (node != null) {
+ if (node.ev.getClass().getName().equals (cl))
+ return node.ev;
+ node = node.succ;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Node node = first;
+ while (node != null) {
+ if (node.ev.getClass() == cl)
+ return (E)node.ev;
+ node = node.succ;
+ }
+ return null;
+ }\end{hide}
+
+ public boolean remove (Event ev)\begin{hide} {
+ // Find the node corresponding to this event ev.
+ Node node = last;
+ while (node != null && node.ev.compareTo(ev) >= 0 && node.ev != ev)
+ node = node.prec;
+ if (node == null || node.ev != ev)
+ return false;
+
+ if (node == last && node == first)
+ last = first = null; // The list is now empty.
+ else {
+ if (node == last) {
+ last = node.prec;
+ last.succ = null;
+ }
+ else
+ node.succ.prec = node.prec;
+ if (node == first) {
+ first = node.succ;
+ first.prec = null;
+ }
+ else {
+ node.prec.succ = node.succ;
+ node.prec = null;
+ }
+ }
+ node.ev = null;
+ synchronized (DoublyLinked.class) {
+ node.succ = free; free = node; // Recycle node.
+ }
+ ++modCount;
+ return true;
+ }\end{hide}
+
+ public Event removeFirst()\begin{hide} {
+ if (first == null)
+ return null;
+
+ Event ev = first.ev;
+ Node temp = first;
+ first = first.succ;
+ if (first == null)
+ last = null;
+ else
+ first.prec = null;
+
+ temp.ev = null;
+ synchronized (DoublyLinked.class) {
+ temp.succ = free; free = temp; // Recycle node.
+ }
+ ++modCount;
+ return ev;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new DLItr();
+ }\end{hide}
+
+ public String toString()\begin{hide} {
+ StringBuffer sb = new StringBuffer ("Contents of the event list DoublyLinked:");
+ Node node = first;
+ while (node != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, node.ev.time()) + ", " +
+ PrintfFormat.g (8, 4, node.ev.priority()) + " : " +
+ node.ev.toString());
+ node = node.succ;
+ }
+ return sb.toString();
+ }
+
+ // A element of the event list. This node contains the event ev.
+ // His predecessor and successor are prec and succ.
+ private static class Node {
+ Event ev;
+ Node prec, succ;
+ }
+
+ private class DLItr implements ListIterator<Event> {
+ private Node prev;
+ private Node next;
+ private Node lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ DLItr() {
+ prev = null;
+ next = first;
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add(Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ // Check the event time and priority
+ if (next != null && ev.compareTo(next.ev) > 0) {
+ ev.setTime (next.ev.time());
+ ev.setPriority (next.ev.priority());
+ }
+ if (prev != null && ev.compareTo(prev.ev) < 0) {
+ ev.setTime (prev.ev.time());
+ ev.setPriority (prev.ev.priority());
+ }
+
+ Node newNode;
+ synchronized (DoublyLinked.class) {
+ if (free == null)
+ newNode = new Node();
+ else {
+ newNode = free;
+ free = free.succ;
+ }
+ }
+ newNode.ev = ev;
+ ++nextIndex;
+ ++modCount;
+ ++expectedModCount;
+ lastRet = null;
+ if (last == null) { // Easy: the event list was empty.
+ first = last = newNode;
+ first.prec = first.succ = null;
+ prev = newNode;
+ next = null;
+ nextIndex = 1;
+ }
+ else if (prev == null) { // Must be inserted first.
+ // next is non-null or the list would be empty.
+ newNode.succ = first;
+ newNode.succ.prec = newNode;
+ first = newNode;
+ newNode.prec = null;
+ prev = newNode;
+ }
+ else { // Insert after node.
+ // prev is non-null but next can be null.
+ newNode.prec = prev;
+ newNode.succ = next;
+ prev.succ = newNode;
+ if (newNode.succ != null)
+ newNode.succ.prec = newNode;
+ else
+ last = newNode;
+ prev = newNode;
+ }
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != null;
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prev != null;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = next.ev;
+ lastRet = next;
+ prev = next;
+ next = next.succ;
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = prev.ev;
+ lastRet = prev;
+ next = prev;
+ prev = prev.prec;
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ if (lastRet == next) // Last call to previous
+ next = next.succ;
+ else { // Last call to next
+ prev = prev.prec;
+ --nextIndex;
+ }
+ if (lastRet == last && lastRet == first) {
+ last = first = null; // The list is now empty.
+ next = prev = null;
+ }
+ else {
+ if (lastRet == last) {
+ last = lastRet.prec;
+ last.succ = null;
+ }
+ else
+ lastRet.succ.prec = lastRet.prec;
+ if (lastRet == first) {
+ first = lastRet.succ;
+ first.prec = null;
+ }
+ else {
+ lastRet.prec.succ = lastRet.succ;
+ lastRet.prec = null;
+ }
+ }
+ lastRet.ev = null;
+ synchronized (DoublyLinked.class) {
+ lastRet.succ = free; free = lastRet; // Recycle node.
+ }
+ lastRet = null;
+ ++modCount;
+ ++expectedModCount;
+ }
+
+ public void set (Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ // Check the event time and priority
+ if (lastRet.prec != null && ev.compareTo(lastRet.prec.ev) < 0) {
+ ev.setTime (lastRet.prec.ev.time());
+ ev.setPriority (lastRet.prec.ev.priority());
+ }
+ if (lastRet.succ != null && ev.compareTo(lastRet.succ.ev) > 0) {
+ ev.setTime (lastRet.succ.ev.time());
+ ev.setPriority (lastRet.succ.ev.priority());
+ }
+
+ lastRet.ev = ev;
+ }
+ }
+
+ private Node findPosition (Event ev) {
+ // This implementation appears quite inefficient !!!!
+ // Must try to improve.
+ Node node = last;
+
+ // Finds the occurrence time of the new event (evTime).
+ while (node != null && ev.compareTo(node.ev) < 0) {
+ node = node.prec;
+ }
+ return node;
+ }
+}\end{hide}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/EventList.java b/source/umontreal/iro/lecuyer/simevents/eventlist/EventList.java
new file mode 100644
index 0000000..ad061b1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/EventList.java
@@ -0,0 +1,203 @@
+
+
+/*
+ * Class: EventList
+ * Description: interface for implementations of event lists
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents.eventlist;
+
+import java.util.ListIterator;
+import umontreal.iro.lecuyer.simevents.Event;
+
+
+/**
+ * An interface for implementations of event lists.
+ * Different implementations are provided in SSJ:
+ * doubly-linked list, splay tree, Henricksen's method, etc.
+ * The <EM>events</EM> in the event list are objects of the class
+ * {@link umontreal.iro.lecuyer.simevents.Event Event}.
+ * The method {@link umontreal.iro.lecuyer.simevents.Sim#init(EventList) Sim.init}
+ * permits one to select
+ * the actual implementation used in a simulation.
+ *
+ * <P>
+ * To allow the user to print the event list, the
+ * {@link java.lang.Object#toString toString} method
+ * from the {@link Object} class should be reimplemented in all <TT>EventList</TT>
+ * implementations. It will return a string in the following format:
+ * ``<TT>Contents of the event list </TT><SPAN CLASS="textit">event list class</SPAN><TT>:</TT>'' for the first line and
+ * each subsequent line has format
+ * ``<SPAN CLASS="textit">scheduled event time</SPAN><TT>, </TT><SPAN CLASS="textit">event priority</SPAN> <TT>:</TT> <SPAN CLASS="textit">event string</SPAN>''.
+ * The <SPAN CLASS="textit">event string</SPAN> is obtained by calling the
+ * <TT>toString</TT> method of the event objects.
+ * The string should not end with the end-of-line character.
+ *
+ * <P>
+ * The following example is the event list of the bank example,
+ * printed at 10h30. See <TT>examples.pdf</TT> for more information.
+ *
+ * <P>
+ * <PRE>
+ * Contents of the event list SplayTree:
+ * 10.51, 1 : BankEv$Arrival at cfb549
+ * 10.54, 1 : BankEv$Departure at 8a7efd
+ * 11, 1 : BankEv$3 at 86d4c1
+ * 14, 1 : BankEv$4 at f9f9d8
+ * 15, 1 : BankEv$5 at 820dda
+ * </PRE>
+ *
+ */
+public interface EventList extends Iterable<Event> {
+
+
+ /**
+ * Returns <TT>true</TT> if and only if the event list is empty
+ * (no event is scheduled).
+ *
+ * @return <TT>true</TT> if the event list is empty
+ *
+ */
+ public boolean isEmpty();
+
+
+ /**
+ * Empties the event list, i.e., cancels all events.
+ *
+ */
+ public void clear();
+
+
+ /**
+ * Adds a new event in the event list, according to
+ * the time of <TT>ev</TT>.
+ * If the event list contains events scheduled to happen at the same time as
+ * <TT>ev</TT>, <TT>ev</TT> must be added after all these events.
+ *
+ * @param ev event to be added
+ *
+ *
+ */
+ public void add (Event ev);
+
+
+ /**
+ * Adds a new event at the beginning of the event list. The given
+ * event <TT>ev</TT> will occur at the current simulation time.
+ *
+ * @param ev event to be added
+ *
+ *
+ */
+ public void addFirst (Event ev);
+
+
+ /**
+ * Same as {@link #add add}, but adds the new event <TT>ev</TT>
+ * immediately before the event <TT>other</TT> in the list.
+ *
+ * @param ev event to be added
+ *
+ * @param other reference event before which <TT>ev</TT> will be added
+ *
+ *
+ */
+ public void addBefore (Event ev, Event other);
+
+
+ /**
+ * Same as {@link #add add}, but adds the new event <TT>ev</TT>
+ * immediately after the event <TT>other</TT> in the list.
+ *
+ * @param ev event to be added
+ *
+ * @param other reference event after which <TT>ev</TT> will be added
+ *
+ *
+ */
+ public void addAfter (Event ev, Event other);
+
+
+ /**
+ * Returns the first event in the event list.
+ * If the event list is empty, returns <TT>null</TT>.
+ *
+ * @return the first event in the event list, or <TT>null</TT> if the list is empty
+ *
+ */
+ public Event getFirst();
+
+
+ /**
+ * Returns the first event of the class <TT>cl</TT> (a subclass of
+ * <TT>Event</TT>) in the event list. If no such event is found, returns
+ * <TT>null</TT>.
+ *
+ * @return the first event of class <TT>cl</TT>, or <TT>null</TT> if no such event exists in the list
+ *
+ */
+ public Event getFirstOfClass (String cl);
+
+
+ /**
+ * Returns the first event of the class <TT>E</TT> (a subclass of
+ * <TT>Event</TT>) in the event list. If no such event is found, returns
+ * <TT>null</TT>.
+ *
+ * @return the first event of class <TT>cl</TT>, or <TT>null</TT> if no such event exists in the list
+ *
+ */
+ public <E extends Event> E getFirstOfClass (Class<E> cl);
+
+
+ /**
+ * Returns a list iterator over the elements of the class <TT>Event</TT> in this list.
+ *
+ * @return a list iterator over the elements of the class <TT>Event</TT> in this list
+ *
+ */
+ public ListIterator<Event> listIterator();
+
+
+ /**
+ * Removes the event <TT>ev</TT> from the event list (cancels this event).
+ * Returns <TT>true</TT> if and only if the event removal has succeeded.
+ *
+ * @param ev event to be removed
+ *
+ * @return <TT>true</TT> if the event was successfully removed from the list
+ *
+ */
+ public boolean remove (Event ev);
+
+
+ /**
+ * Removes the first event from the event list (to cancel or
+ * execute this event). Returns the removed event. If the list is empty,
+ * then <TT>null</TT> is returned.
+ *
+ * @return the first event removed from the list, or <TT>null</TT> if the list is empty
+ *
+ */
+ public Event removeFirst();
+
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/EventList.tex b/source/umontreal/iro/lecuyer/simevents/eventlist/EventList.tex
new file mode 100644
index 0000000..ecf360a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/EventList.tex
@@ -0,0 +1,203 @@
+\defmodule {EventList}
+
+An interface for implementations of event lists.
+Different implementations are provided in SSJ:
+doubly-linked list, splay tree, Henricksen's method, etc.
+The {\em events\/} in the event list are objects of the class
+\externalclass{umontreal.iro.lecuyer.simevents}{Event}.
+The method \clsexternalmethod{umontreal.iro.lecuyer.simevents}{Sim}{init}{EventList}
+permits one to select
+the actual implementation used in a simulation \cite{sKIN85a}.
+
+To allow the user to print the event list, the
+\externalmethod{java.lang}{Object}{toString}{} method
+from the \class{Object} class should be reimplemented in all {\tt EventList}
+implementations. It will return a string in the following format:
+``{\tt Contents of the event list }\emph{event list class}{\tt :}'' for the first line and
+each subsequent line has format
+``\emph{scheduled event time}{\tt , }\emph{event priority}~{\tt :}~\emph{event string}''.
+The \emph{event string} is obtained by calling the
+{\tt toString} method of the event objects.
+The string should not end with the end-of-line character.
+
+The following example is the event list of the bank example,
+printed at 10h30. See {\tt examples.pdf} for more information.
+
+\begin{verbatim}
+Contents of the event list SplayTree:
+ 10.51, 1 : BankEv$Arrival at cfb549
+ 10.54, 1 : BankEv$Departure at 8a7efd
+ 11, 1 : BankEv$3 at 86d4c1
+ 14, 1 : BankEv$4 at f9f9d8
+ 15, 1 : BankEv$5 at 820dda
+\end{verbatim}
+%$ Without that, Emacs believes we are in math mode.
+
+% The event time (obtained by calling the
+% \clsexternalmethod{}{Event}{time}{}) must not be modified
+% by implementations of this interface because the \class{Event} class
+% already takes care of that.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: EventList
+ * Description: interface for implementations of event lists
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents.eventlist; \begin{hide}
+
+import java.util.ListIterator;
+import umontreal.iro.lecuyer.simevents.Event;
+\end{hide}
+
+public interface EventList extends Iterable<Event>\begin{hide} {
+\end{hide}
+
+ public boolean isEmpty();
+\end{code}
+ \begin{tabb} Returns {\tt true} if and only if the event list is empty
+ (no event is scheduled).
+ \end{tabb}
+\begin{htmlonly}
+ \return{{\tt true} if the event list is empty}
+\end{htmlonly}
+\begin{code}
+
+ public void clear();
+\end{code}
+ \begin{tabb} Empties the event list, i.e., cancels all events.
+ \end{tabb}
+\begin{code}
+
+ public void add (Event ev);
+\end{code}
+ \begin{tabb} Adds a new event in the event list, according to
+ the time of {\tt ev}.
+ If the event list contains events scheduled to happen at the same time as
+ {\tt ev}, {\tt ev} must be added after all these events.
+ \end{tabb}
+\begin{htmlonly}
+ \param{ev}{event to be added}
+\end{htmlonly}
+\begin{code}
+
+ public void addFirst (Event ev);
+\end{code}
+ \begin{tabb} Adds a new event at the beginning of the event list. The given
+ event {\tt ev} will occur at the current simulation time.
+ \end{tabb}
+\begin{htmlonly}
+ \param{ev}{event to be added}
+\end{htmlonly}
+\begin{code}
+
+ public void addBefore (Event ev, Event other);
+\end{code}
+ \begin{tabb} Same as \method{add}{}, but adds the new event {\tt ev}
+ immediately before the event {\tt other} in the list.
+ \end{tabb}
+\begin{htmlonly}
+ \param{ev}{event to be added}
+ \param{other}{reference event before which {\tt ev} will be added}
+\end{htmlonly}
+\begin{code}
+
+ public void addAfter (Event ev, Event other);
+\end{code}
+ \begin{tabb} Same as \method{add}{}, but adds the new event {\tt ev}
+ immediately after the event {\tt other} in the list.
+ \end{tabb}
+\begin{htmlonly}
+ \param{ev}{event to be added}
+ \param{other}{reference event after which {\tt ev} will be added}
+\end{htmlonly}
+\begin{code}
+
+ public Event getFirst();
+\end{code}
+ \begin{tabb} Returns the first event in the event list.
+ If the event list is empty, returns {\tt null}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the first event in the event list, or {\tt null} if the list is empty}
+\end{htmlonly}
+\begin{code}
+
+ public Event getFirstOfClass (String cl);
+\end{code}
+ \begin{tabb} Returns the first event of the class {\tt cl} (a subclass of
+ {\tt Event}) in the event list. If no such event is found, returns
+ {\tt null}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the first event of class {\tt cl}, or {\tt null} if no such event exists in the list}
+\end{htmlonly}
+\begin{code}
+
+ public <E extends Event> E getFirstOfClass (Class<E> cl);
+\end{code}
+ \begin{tabb} Returns the first event of the class {\tt E} (a subclass of
+ {\tt Event}) in the event list. If no such event is found, returns
+ {\tt null}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the first event of class {\tt cl}, or {\tt null} if no such event exists in the list}
+\end{htmlonly}
+\begin{code}
+
+ public ListIterator<Event> listIterator();
+\end{code}
+\begin{tabb}
+ Returns a list iterator over the elements of the class {\tt Event} in this list.
+\end{tabb}
+\begin{htmlonly}
+ \return{a list iterator over the elements of the class {\tt Event} in this list}
+\end{htmlonly}
+\begin{code}
+
+ public boolean remove (Event ev);
+\end{code}
+ \begin{tabb} Removes the event {\tt ev} from the event list (cancels this event).
+ Returns {\tt true} if and only if the event removal has succeeded.
+ \end{tabb}
+\begin{htmlonly}
+ \param{ev}{event to be removed}
+ \return{{\tt true} if the event was successfully removed from the list}
+\end{htmlonly}
+\begin{code}
+
+ public Event removeFirst();
+\end{code}
+ \begin{tabb} Removes the first event from the event list (to cancel or
+ execute this event). Returns the removed event. If the list is empty,
+ then {\tt null} is returned.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the first event removed from the list, or {\tt null} if the list is empty}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/Henriksen.java b/source/umontreal/iro/lecuyer/simevents/eventlist/Henriksen.java
new file mode 100644
index 0000000..09f2f27
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/Henriksen.java
@@ -0,0 +1,526 @@
+
+
+/*
+ * Class: Henriksen
+ * Description: implementation of class EventList using the doubly-linked
+ indexed list of Henriksen
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents.eventlist;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+
+
+/**
+ * An implementation of {@link EventList} using the doubly-linked
+ * indexed list of Henriksen (see also).
+ *
+ * <P>
+ * Events are stored in a normal doubly-linked list. An additionnal
+ * index array is added to the structure to allow quick access to the
+ * events.
+ *
+ */
+public class Henriksen implements EventList {
+ /*
+ Fonctionnement de l'algorithme :
+
+ On maintient une liste doublement chainee contenant les evenements.
+ Les deux extremites de cette liste sont occupees par des bornes qui
+ ont des temps qui ne peuvent pas etre depasses.
+
+ Au-dessus de cette liste se retrouve un index (trie en ordre decroissant)
+ qui contient des pointeurs vers certaines des entrees de la liste
+ chainee. Le premier element de l'index est toujours la borne superieure.
+ On se sert de cet index pour faire des recherches binaires pour
+ retrouver rapidement un element dans la liste. On fait d'abord une
+ recherche binaire dans l'index avant de faire une recherche lineaire
+ dans la liste. On compte aussi le nombre d'elements qui doivent etre
+ parcourus dans la recherche lineaire. Si ce nombre atteint une certaine
+ valeur (4 dans cette implantation), alors on fait en sorte qu'un
+ element de l'index pointe vers l'element de la liste que l'on parcourt.
+ Ceci permet un certain balancement des elements presents dans l'index,
+ et donc une meilleure efficacite de la recherche binaire sans avoir
+ a mettre tous les elements dans l'index.
+
+ */
+
+ private static final double MIN_VALUE = -10E38; //n'importe quoi < 0
+ private static final double MAX_VALUE = 10E38; //le plus gros possible
+
+ private static final int ARRAY_LENGTH_INIT = 256;
+
+
+ private int modCount = 0;
+
+ private Entry firstEntry;
+
+ //for the binary search
+ private Entry[] entryVec;
+
+ private int vectSize;
+ private int arrayLength;
+
+
+ public Henriksen() {
+ //creation des bornes
+ Entry lastEntry = new Entry(null, null, null, MAX_VALUE);
+ firstEntry = new Entry(null, null, lastEntry, MIN_VALUE);
+ lastEntry.left = firstEntry;
+
+ arrayLength = ARRAY_LENGTH_INIT;
+ entryVec = new Entry[arrayLength];
+
+ changeSize(1);
+ entryVec[0] = lastEntry;
+ }
+
+ public boolean isEmpty() {
+ return firstEntry.right == entryVec[0];
+ }
+
+ public void clear() {
+ if(isEmpty())
+ return;
+
+ firstEntry.right = entryVec[0];
+ entryVec[0].left = firstEntry;
+ changeSize(1);
+
+ //on enleve tous les liens menant aux entrees supprimees :
+ for(int i = 1; i < arrayLength; i++)
+ entryVec[i] = null;
+
+ modCount++;
+ }
+
+
+ public void add (Event ev) {
+ Entry prec = findEntry(ev, false);
+
+ Entry e = new Entry(ev, prec, prec.right, ev.time());
+ e.right.left = e;
+ prec.right = e;
+ modCount++;
+ }
+
+ public void addFirst (Event ev) {
+ Entry e = new Entry(ev, firstEntry, firstEntry.right, ev.time());
+ firstEntry.right.left = e;
+ firstEntry.right = e;
+
+ modCount++;
+ }
+
+ public void addBefore (Event ev, Event other) {
+ Entry otherEntry = findEntry(other, true);
+ if(otherEntry == null)
+ throw new IllegalArgumentException("Event not in list.");
+ Entry e = new Entry(ev, otherEntry.left, otherEntry, ev.time());
+ otherEntry.left.right = e;
+ otherEntry.left = e;
+
+ modCount++;
+ }
+
+ public void addAfter (Event ev, Event other) {
+ Entry otherEntry = findEntry(other, true);
+ if(otherEntry == null)
+ throw new IllegalArgumentException("Event not in list.");
+ Entry e = new Entry(ev, otherEntry, otherEntry.right, ev.time());
+ otherEntry.right.left = e;
+ otherEntry.right = e;
+
+ modCount++;
+ }
+
+
+ public Event getFirst() {
+ return firstEntry.right.event;
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Entry e = firstEntry.right;
+ while(e.right != null) {
+ if(e.event.getClass().getName().equals(cl))
+ return e.event;
+ e = e.right;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Entry e = firstEntry.right;
+ while(e.right != null) {
+ if(e.event.getClass() == cl)
+ return (E)e.event;
+ e = e.right;
+ }
+ return null;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new HItr();
+ }
+
+ public boolean remove (Event ev) {
+ Entry e = findEntry (ev, true);
+ if (e == null)
+ return false;
+
+ //on l'enleve de l'index
+ int i = findIndex (ev.time());
+ i++;
+
+ while (i < vectSize && entryVec[i].event != null &&
+ ev.compareTo(entryVec[i].event) == 0) {
+ if(entryVec[i].event == ev)
+ entryVec[i] = e.left;
+
+ i++;
+ }
+
+ //on l'enleve de la liste
+ e.left.right = e.right;
+ e.right.left = e.left;
+ e.right = null;
+ e.left = null;
+ e.event = null;
+
+ modCount++;
+
+ return true;
+ }
+
+ public Event removeFirst() {
+ // si la premiere moitie de l'index est composee d'entrees perimees,
+ // on coupe de moitie l'index
+ if (entryVec[vectSize/2].time <= firstEntry.right.time && vectSize > 1)
+ changeSize(vectSize / 2);
+
+ Entry e = firstEntry.right;
+
+ //borne superieure
+ if (e == entryVec[0])
+ return null;
+
+ firstEntry.right = e.right;
+ e.right.left = firstEntry;
+
+ e.right = null;
+ e.left = null;
+
+ Event ev = e.event;
+ e.event = null;
+
+ modCount++;
+
+ return ev;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer
+ ("Contents of the event list Henriksen:");
+ Entry e = firstEntry.right;
+ while (e.right != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, e.event.time()) + ", " +
+ PrintfFormat.g (8, 4, e.event.priority()) + " : " +
+ e.event.toString());
+ e = e.right;
+ }
+ return sb.toString();
+ }
+
+
+ private static class Entry {
+ public Event event;
+ public Entry left;
+ public Entry right;
+ public double time;
+
+ Entry (Event event, Entry left, Entry right, double time) {
+ this.event = event;
+ this.left = left;
+ this.right = right;
+ this.time = time;
+ }
+
+ public String toString() {
+ return "[" + event + " |" + time + "|]";
+ }
+ }
+
+
+ private class HItr implements ListIterator<Event> {
+ private Entry prev;
+ private Entry next;
+ private Entry lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ private HItr() {
+ prev = firstEntry;
+ next = firstEntry.right;
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add (Event ev) {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ //make sure the time is in the right order
+ if (ev.time() > next.time) {
+ ev.setTime (next.time);
+ ev.setPriority (next.event.priority());
+ }
+ if (ev.time() < prev.time) {
+ ev.setTime (prev.time);
+ ev.setPriority (prev.event.priority());
+ }
+
+ Entry e = new Entry(ev, prev, next, ev.time());
+ prev.right = e;
+ next.left = e;
+ prev = e;
+
+ nextIndex++;
+ lastRet = null;
+ modCount++;
+ expectedModCount++;
+ }
+
+ public boolean hasNext() {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != entryVec[0];
+ }
+
+ public boolean hasPrevious() {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != firstEntry;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ nextIndex++;
+ Event ev = next.event;
+ lastRet = next;
+ next = next.right;
+ prev = prev.right;
+ return ev;
+ }
+
+ public int nextIndex() {
+ if(!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if(!hasPrevious())
+ throw new NoSuchElementException();
+
+ nextIndex--;
+ Event ev = prev.event;
+ lastRet = prev;
+ prev = prev.left;
+ next = next.left;
+ return ev;
+ }
+
+ public int previousIndex() {
+ if(!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+
+ public void remove() {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if(lastRet == null)
+ throw new IllegalStateException();
+
+ if(lastRet == next) { //last call was to previous, not next
+ if(next != entryVec[0])
+ next = next.right;
+ } else { //last call was to next or nothing
+ if(prev != firstEntry) {
+ prev = prev.left;
+ nextIndex--;
+ }
+ }
+
+ //remove the deleted entry in the vector
+ double evtime = lastRet.time;
+ int i = findIndex (evtime);
+ i++;
+
+ while(i < vectSize && entryVec[i].time == evtime) {
+ if(entryVec[i].event == lastRet.event)
+ entryVec[i] = lastRet.left;
+ i++;
+ }
+
+ lastRet.event = null;
+ lastRet.left.right = lastRet.right;
+ lastRet.right.left = lastRet.left;
+ lastRet.left = null;
+ lastRet.right = null;
+ lastRet = null;
+ modCount++;
+ expectedModCount++;
+ }
+
+ public void set (Event ev) {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if(lastRet == null)
+ throw new IllegalStateException();
+
+ // Check for a good time
+ if (ev.time() < lastRet.left.time) {
+ ev.setTime (lastRet.left.time);
+ ev.setPriority (lastRet.left.event.priority());
+ }
+ if (ev.time() > lastRet.right.time) {
+ ev.setTime (lastRet.right.time);
+ ev.setPriority (lastRet.right.event.priority());
+ }
+
+ lastRet.event = ev;
+ }
+ }
+
+ /*
+ On change la taille de l'index.
+ Le changement de taille se fait du cote des entrees de temps inferieures.
+ Ces entrees se retrouvent a la fin de l'index pour simplifier ce
+ travail.
+ */
+ private void changeSize(int newSize) {
+ // si on grossit le vecteur reel
+ if(newSize > arrayLength) {
+ Entry[] newVec = new Entry[newSize];
+ for(int i = 0; i < vectSize; i++)
+ newVec[i] = entryVec[i];
+ entryVec = newVec;
+ arrayLength = newSize;
+ }
+
+ // les nouveaux emplacements sont remplis par la borne min
+ for(int i = vectSize; i < newSize; i++)
+ entryVec[i] = firstEntry;
+
+ vectSize = newSize;
+ }
+
+ /*
+ Fait une recherche binaire a l'interieur de l'index pour trouve
+ l'entree dans l'index qui a la plus petite valeur de temps, mais
+ dont la valeur est superieure a evtime.
+ Les entrees sont triees en ordre inverse dans l'index, pour simplifie
+ le changement de taille de l'index.
+ */
+ private int findIndex (double evtime) {
+ int i = vectSize / 2;
+ int j = vectSize / 4;
+
+ // recherche binaire dans le vecteur d'index
+ while(j > 0) {
+ // note : entryVec est trie a l'envers
+ if(evtime >= entryVec[i].time)
+ i -= j;
+ else
+ i += j;
+ j /= 2;
+ }
+
+ if (evtime >= entryVec[i].time)
+ i--;
+
+ return i;
+ }
+
+ /*
+ Si findEvent est false, trouve la derniere entree qui a un temps
+ egal ou inferieur au temps de l'evenement ev.
+ Sinon, trouve l'entree contenant ev dans la liste.
+ */
+ private Entry findEntry (Event ev, boolean findEvent) {
+ double evtime = ev.time();
+ int i = findIndex(evtime);
+
+ Entry e = entryVec[i].left;
+ if (null == e) return null;
+ int count = 0;
+
+ while (e.time >= evtime/* && e.event != null*/ && ev.compareTo(e.event) < 0) {
+ ++count;
+ if (count == 4) {
+ //operation pull
+ if (i+1 >= vectSize)
+ changeSize(vectSize * 2);
+
+ i++;
+ count = 0;
+ entryVec[i] = e;
+ }
+
+ e = e.left;
+ }
+
+ if (findEvent) {
+ // on cherche l'evenement identique
+ Entry start = e;
+
+ while (e != firstEntry && e.time == evtime && e.event != ev)
+ e = e.left;
+
+ // on ne l'a pas trouve
+ if (e.event != ev)
+ return null;
+ }
+
+ return e;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/Henriksen.tex b/source/umontreal/iro/lecuyer/simevents/eventlist/Henriksen.tex
new file mode 100644
index 0000000..30e10ca
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/Henriksen.tex
@@ -0,0 +1,529 @@
+\defmodule {Henriksen}
+
+An implementation of \class{EventList} using the doubly-linked
+indexed list of Henriksen \cite{sKIN86a} (see also \cite[p. 207]{sFIS01a}).
+
+Events are stored in a normal doubly-linked list. An additionnal
+index array is added to the structure to allow quick access to the
+events.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Henriksen
+ * Description: implementation of class EventList using the doubly-linked
+ indexed list of Henriksen
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents.eventlist;\begin{hide}
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+\end{hide}
+
+public class Henriksen implements EventList \begin{hide} {
+ /*
+ Fonctionnement de l'algorithme :
+
+ On maintient une liste doublement chainee contenant les evenements.
+ Les deux extremites de cette liste sont occupees par des bornes qui
+ ont des temps qui ne peuvent pas etre depasses.
+
+ Au-dessus de cette liste se retrouve un index (trie en ordre decroissant)
+ qui contient des pointeurs vers certaines des entrees de la liste
+ chainee. Le premier element de l'index est toujours la borne superieure.
+ On se sert de cet index pour faire des recherches binaires pour
+ retrouver rapidement un element dans la liste. On fait d'abord une
+ recherche binaire dans l'index avant de faire une recherche lineaire
+ dans la liste. On compte aussi le nombre d'elements qui doivent etre
+ parcourus dans la recherche lineaire. Si ce nombre atteint une certaine
+ valeur (4 dans cette implantation), alors on fait en sorte qu'un
+ element de l'index pointe vers l'element de la liste que l'on parcourt.
+ Ceci permet un certain balancement des elements presents dans l'index,
+ et donc une meilleure efficacite de la recherche binaire sans avoir
+ a mettre tous les elements dans l'index.
+
+ */
+
+ private static final double MIN_VALUE = -10E38; //n'importe quoi < 0
+ private static final double MAX_VALUE = 10E38; //le plus gros possible
+
+ private static final int ARRAY_LENGTH_INIT = 256;
+
+
+ private int modCount = 0;
+
+ private Entry firstEntry;
+
+ //for the binary search
+ private Entry[] entryVec;
+
+ private int vectSize;
+ private int arrayLength;
+
+
+ public Henriksen() {
+ //creation des bornes
+ Entry lastEntry = new Entry(null, null, null, MAX_VALUE);
+ firstEntry = new Entry(null, null, lastEntry, MIN_VALUE);
+ lastEntry.left = firstEntry;
+
+ arrayLength = ARRAY_LENGTH_INIT;
+ entryVec = new Entry[arrayLength];
+
+ changeSize(1);
+ entryVec[0] = lastEntry;
+ }
+
+ public boolean isEmpty() {
+ return firstEntry.right == entryVec[0];
+ }
+
+ public void clear() {
+ if(isEmpty())
+ return;
+
+ firstEntry.right = entryVec[0];
+ entryVec[0].left = firstEntry;
+ changeSize(1);
+
+ //on enleve tous les liens menant aux entrees supprimees :
+ for(int i = 1; i < arrayLength; i++)
+ entryVec[i] = null;
+
+ modCount++;
+ }
+
+
+ public void add (Event ev) {
+ Entry prec = findEntry(ev, false);
+
+ Entry e = new Entry(ev, prec, prec.right, ev.time());
+ e.right.left = e;
+ prec.right = e;
+ modCount++;
+ }
+
+ public void addFirst (Event ev) {
+ Entry e = new Entry(ev, firstEntry, firstEntry.right, ev.time());
+ firstEntry.right.left = e;
+ firstEntry.right = e;
+
+ modCount++;
+ }
+
+ public void addBefore (Event ev, Event other) {
+ Entry otherEntry = findEntry(other, true);
+ if(otherEntry == null)
+ throw new IllegalArgumentException("Event not in list.");
+ Entry e = new Entry(ev, otherEntry.left, otherEntry, ev.time());
+ otherEntry.left.right = e;
+ otherEntry.left = e;
+
+ modCount++;
+ }
+
+ public void addAfter (Event ev, Event other) {
+ Entry otherEntry = findEntry(other, true);
+ if(otherEntry == null)
+ throw new IllegalArgumentException("Event not in list.");
+ Entry e = new Entry(ev, otherEntry, otherEntry.right, ev.time());
+ otherEntry.right.left = e;
+ otherEntry.right = e;
+
+ modCount++;
+ }
+
+
+ public Event getFirst() {
+ return firstEntry.right.event;
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Entry e = firstEntry.right;
+ while(e.right != null) {
+ if(e.event.getClass().getName().equals(cl))
+ return e.event;
+ e = e.right;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Entry e = firstEntry.right;
+ while(e.right != null) {
+ if(e.event.getClass() == cl)
+ return (E)e.event;
+ e = e.right;
+ }
+ return null;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new HItr();
+ }
+
+ public boolean remove (Event ev) {
+ Entry e = findEntry (ev, true);
+ if (e == null)
+ return false;
+
+ //on l'enleve de l'index
+ int i = findIndex (ev.time());
+ i++;
+
+ while (i < vectSize && entryVec[i].event != null &&
+ ev.compareTo(entryVec[i].event) == 0) {
+ if(entryVec[i].event == ev)
+ entryVec[i] = e.left;
+
+ i++;
+ }
+
+ //on l'enleve de la liste
+ e.left.right = e.right;
+ e.right.left = e.left;
+ e.right = null;
+ e.left = null;
+ e.event = null;
+
+ modCount++;
+
+ return true;
+ }
+
+ public Event removeFirst() {
+ // si la premiere moitie de l'index est composee d'entrees perimees,
+ // on coupe de moitie l'index
+ if (entryVec[vectSize/2].time <= firstEntry.right.time && vectSize > 1)
+ changeSize(vectSize / 2);
+
+ Entry e = firstEntry.right;
+
+ //borne superieure
+ if (e == entryVec[0])
+ return null;
+
+ firstEntry.right = e.right;
+ e.right.left = firstEntry;
+
+ e.right = null;
+ e.left = null;
+
+ Event ev = e.event;
+ e.event = null;
+
+ modCount++;
+
+ return ev;
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer
+ ("Contents of the event list Henriksen:");
+ Entry e = firstEntry.right;
+ while (e.right != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, e.event.time()) + ", " +
+ PrintfFormat.g (8, 4, e.event.priority()) + " : " +
+ e.event.toString());
+ e = e.right;
+ }
+ return sb.toString();
+ }
+
+
+ private static class Entry {
+ public Event event;
+ public Entry left;
+ public Entry right;
+ public double time;
+
+ Entry (Event event, Entry left, Entry right, double time) {
+ this.event = event;
+ this.left = left;
+ this.right = right;
+ this.time = time;
+ }
+
+ public String toString() {
+ return "[" + event + " |" + time + "|]";
+ }
+ }
+
+
+ private class HItr implements ListIterator<Event> {
+ private Entry prev;
+ private Entry next;
+ private Entry lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ private HItr() {
+ prev = firstEntry;
+ next = firstEntry.right;
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add (Event ev) {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ //make sure the time is in the right order
+ if (ev.time() > next.time) {
+ ev.setTime (next.time);
+ ev.setPriority (next.event.priority());
+ }
+ if (ev.time() < prev.time) {
+ ev.setTime (prev.time);
+ ev.setPriority (prev.event.priority());
+ }
+
+ Entry e = new Entry(ev, prev, next, ev.time());
+ prev.right = e;
+ next.left = e;
+ prev = e;
+
+ nextIndex++;
+ lastRet = null;
+ modCount++;
+ expectedModCount++;
+ }
+
+ public boolean hasNext() {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != entryVec[0];
+ }
+
+ public boolean hasPrevious() {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != firstEntry;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ nextIndex++;
+ Event ev = next.event;
+ lastRet = next;
+ next = next.right;
+ prev = prev.right;
+ return ev;
+ }
+
+ public int nextIndex() {
+ if(!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if(!hasPrevious())
+ throw new NoSuchElementException();
+
+ nextIndex--;
+ Event ev = prev.event;
+ lastRet = prev;
+ prev = prev.left;
+ next = next.left;
+ return ev;
+ }
+
+ public int previousIndex() {
+ if(!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+
+ public void remove() {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if(lastRet == null)
+ throw new IllegalStateException();
+
+ if(lastRet == next) { //last call was to previous, not next
+ if(next != entryVec[0])
+ next = next.right;
+ } else { //last call was to next or nothing
+ if(prev != firstEntry) {
+ prev = prev.left;
+ nextIndex--;
+ }
+ }
+
+ //remove the deleted entry in the vector
+ double evtime = lastRet.time;
+ int i = findIndex (evtime);
+ i++;
+
+ while(i < vectSize && entryVec[i].time == evtime) {
+ if(entryVec[i].event == lastRet.event)
+ entryVec[i] = lastRet.left;
+ i++;
+ }
+
+ lastRet.event = null;
+ lastRet.left.right = lastRet.right;
+ lastRet.right.left = lastRet.left;
+ lastRet.left = null;
+ lastRet.right = null;
+ lastRet = null;
+ modCount++;
+ expectedModCount++;
+ }
+
+ public void set (Event ev) {
+ if(modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if(lastRet == null)
+ throw new IllegalStateException();
+
+ // Check for a good time
+ if (ev.time() < lastRet.left.time) {
+ ev.setTime (lastRet.left.time);
+ ev.setPriority (lastRet.left.event.priority());
+ }
+ if (ev.time() > lastRet.right.time) {
+ ev.setTime (lastRet.right.time);
+ ev.setPriority (lastRet.right.event.priority());
+ }
+
+ lastRet.event = ev;
+ }
+ }
+
+ /*
+ On change la taille de l'index.
+ Le changement de taille se fait du cote des entrees de temps inferieures.
+ Ces entrees se retrouvent a la fin de l'index pour simplifier ce
+ travail.
+ */
+ private void changeSize(int newSize) {
+ // si on grossit le vecteur reel
+ if(newSize > arrayLength) {
+ Entry[] newVec = new Entry[newSize];
+ for(int i = 0; i < vectSize; i++)
+ newVec[i] = entryVec[i];
+ entryVec = newVec;
+ arrayLength = newSize;
+ }
+
+ // les nouveaux emplacements sont remplis par la borne min
+ for(int i = vectSize; i < newSize; i++)
+ entryVec[i] = firstEntry;
+
+ vectSize = newSize;
+ }
+
+ /*
+ Fait une recherche binaire a l'interieur de l'index pour trouve
+ l'entree dans l'index qui a la plus petite valeur de temps, mais
+ dont la valeur est superieure a evtime.
+ Les entrees sont triees en ordre inverse dans l'index, pour simplifie
+ le changement de taille de l'index.
+ */
+ private int findIndex (double evtime) {
+ int i = vectSize / 2;
+ int j = vectSize / 4;
+
+ // recherche binaire dans le vecteur d'index
+ while(j > 0) {
+ // note : entryVec est trie a l'envers
+ if(evtime >= entryVec[i].time)
+ i -= j;
+ else
+ i += j;
+ j /= 2;
+ }
+
+ if (evtime >= entryVec[i].time)
+ i--;
+
+ return i;
+ }
+
+ /*
+ Si findEvent est false, trouve la derniere entree qui a un temps
+ egal ou inferieur au temps de l'evenement ev.
+ Sinon, trouve l'entree contenant ev dans la liste.
+ */
+ private Entry findEntry (Event ev, boolean findEvent) {
+ double evtime = ev.time();
+ int i = findIndex(evtime);
+
+ Entry e = entryVec[i].left;
+ if (null == e) return null;
+ int count = 0;
+
+ while (e.time >= evtime/* && e.event != null*/ && ev.compareTo(e.event) < 0) {
+ ++count;
+ if (count == 4) {
+ //operation pull
+ if (i+1 >= vectSize)
+ changeSize(vectSize * 2);
+
+ i++;
+ count = 0;
+ entryVec[i] = e;
+ }
+
+ e = e.left;
+ }
+
+ if (findEvent) {
+ // on cherche l'evenement identique
+ Entry start = e;
+
+ while (e != firstEntry && e.time == evtime && e.event != ev)
+ e = e.left;
+
+ // on ne l'a pas trouve
+ if (e.event != ev)
+ return null;
+ }
+
+ return e;
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/RedblackTree.java b/source/umontreal/iro/lecuyer/simevents/eventlist/RedblackTree.java
new file mode 100644
index 0000000..45b4f2e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/RedblackTree.java
@@ -0,0 +1,419 @@
+
+
+/*
+ * Class: RedblackTree
+ * Description: implementation of class EventList using a red-black tree
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents.eventlist;
+
+import java.util.TreeMap;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.LinkedList;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.Sim;
+
+
+/**
+ * An implementation of {@link EventList} using a <SPAN CLASS="textit">red black</SPAN> tree,
+ * which is similar to a binary search tree except that
+ * every node is colored red or black. When modifying the structure,
+ * the tree is reorganized for the colors to satisfy rules that
+ * give an average
+ * <SPAN CLASS="MATH"><I>O</I>(log(<I>n</I>))</SPAN> time for removing the first event
+ * or inserting a new event,
+ * where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number of elements in the structure.
+ * However, adding or removing events imply reorganizing
+ * the tree and requires more overhead than a binary search tree.
+ *
+ * <P>
+ * The present implementation uses the Java 2
+ * {@link java.util.TreeMap TreeMap} class
+ * which implements a red black tree for general usage.
+ * This event list implementation is not efficient.
+ *
+ */
+public class RedblackTree implements EventList {
+ private TreeMap<Event, Node> tree = new TreeMap<Event, Node>(new EventComparator());
+ private static Node free = null;
+ private int modCount = 0;
+
+
+ public void clear() {
+ Iterator<Node> itr = tree.values().iterator();
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ node.events.clear();
+ itr.remove();
+ synchronized (RedblackTree.class) {
+ node.nextNode = free;
+ free = node;
+ }
+ }
+ ++modCount;
+ }
+
+ public void add (Event ev) {
+ Node node = tree.get (ev);
+ if (node != null)
+ node.events.add (ev);
+ else
+ tree.put (new EventMapKey (ev), newNode (ev));
+ ++modCount;
+ }
+
+ public void addFirst (Event ev) {
+ // ev.setTime (Sim.time()); // necessaire si eventime n'est pas deja a 0
+ Node node = tree.get (ev);
+ if (node != null)
+ node.events.add (ev);
+ else
+ tree.put (new EventMapKey(ev), newNode (ev));
+ ++modCount;
+ }
+
+ public void addBefore (Event ev, Event other) {
+ Node node = tree.get (other);
+ if (node == null)
+ throw new IllegalArgumentException ("Event not in list.");
+ // ev.setTime (other.time());
+ node.addBefore (ev, other);
+ ++modCount;
+ }
+
+ public void addAfter (Event ev, Event other) {
+ Node node = tree.get (other);
+ if (node == null)
+ throw new IllegalArgumentException ("Event not in list.");
+ // ev.setTime (other.time());
+ node.addAfter (ev, other);
+ ++modCount;
+ }
+
+ public Event getFirst() {
+ return isEmpty() ? null :
+ tree.get (tree.firstKey()).events.get (0);
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Iterator<Node> itr = tree.values().iterator();
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ Event ev = node.getFirstOfClass (cl);
+ if (ev != null)
+ return ev;
+ }
+ return null;
+ }
+
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Iterator<Node> itr = tree.values().iterator();
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ E ev = node.getFirstOfClass (cl);
+ if (ev != null)
+ return ev;
+ }
+ return null;
+ }
+
+ public boolean remove (Event ev) {
+ Node node = tree.get (ev);
+ if (node == null)
+ return false;
+ if (node.remove (ev)) {
+ tree.remove (ev);
+ synchronized (RedblackTree.class) {
+ node.nextNode = free; free = node;
+ }
+ }
+ ++modCount;
+ return true;
+ }
+
+ public Event removeFirst() {
+ if (tree.isEmpty())
+ return null;
+ Event evKey = tree.firstKey();
+ Node node = tree.get (evKey);
+ Event first = node.events.get (0);
+ node.events.remove (0);
+ if (node.events.isEmpty()) {
+ tree.remove (evKey);
+ synchronized (RedblackTree.class) {
+ node.nextNode = free; free = node;
+ }
+ }
+ ++modCount;
+ return first;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer ("Contents of the event list RedblackTree:");
+ for (Node node : tree.values()) {
+ for (Event ev : node.events)
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, ev.time()) + ", " +
+ PrintfFormat.g (8, 4, ev.priority()) + " : " +
+ ev.toString());
+ }
+ return sb.toString();
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new RBItr();
+ }
+
+ public boolean isEmpty() {
+ return tree.isEmpty();
+ }
+
+ private static class Node {
+ // For the iterator
+ public Node prevNode = null;
+ public Node nextNode = null;
+
+ public java.util.List<Event> events = new LinkedList<Event>();
+
+ public Node (Event ev) {
+ events.add (ev);
+ }
+
+ public void addAfter (Event ev, Event other) {
+ ListIterator<Event> itr = events.listIterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev == other) {
+ itr.add (ev);
+ return;
+ }
+ }
+ throw new IllegalArgumentException ("Event not in node.");
+ }
+
+ public void addBefore (Event ev, Event other) {
+ ListIterator<Event> itr = events.listIterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev == other) {
+ itr.previous();
+ itr.add (ev);
+ return;
+ }
+ }
+ throw new IllegalArgumentException ("Event not in node.");
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev.getClass().getName().equals (cl))
+ return listev;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev.getClass() == cl)
+ return (E)listev;
+ }
+ return null;
+ }
+
+ /* *
+ * Remove an event from a node.
+ * Returns true if the node becomes empty.
+ */
+ public boolean remove (Event ev) {
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev == ev) {
+ itr.remove();
+ return events.isEmpty();
+ }
+ }
+ throw new IllegalArgumentException ("Event not in node.");
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ boolean first = true;
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (itr.next());
+ }
+ return sb.toString();
+ }
+ }
+
+ private static class EventComparator implements Comparator<Event> {
+ public int compare (Event ev1, Event ev2) {
+ return ev1.compareTo(ev2);
+ }
+
+ public boolean equals (Object obj) { return true; }
+ }
+
+ private class RBItr implements ListIterator<Event> {
+ private int expectedModCount;
+ private Node prevNode;
+ private Node nextNode;
+ private int prevNodeIndex;
+ private int nextNodeIndex;
+ private int nextIndex;
+
+ RBItr() {
+ expectedModCount = modCount;
+ prevNode = null;
+ nextNode = tree.isEmpty() ? null :
+ (Node)tree.get (tree.firstKey());
+ prevNodeIndex = 0;
+ nextNodeIndex = 0;
+ nextIndex = 0;
+
+ Iterator<Node> itr = tree.values().iterator();
+ Node lastNode = null;
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ node.prevNode = lastNode;
+ if (lastNode != null)
+ lastNode.nextNode = node;
+ node.nextNode = null;
+ lastNode = node;
+ }
+ }
+
+ public void add(Event ev) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return nextNode != null &&
+ nextNodeIndex < nextNode.events.size();
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prevNode != null &&
+ prevNodeIndex >= 0;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = (Event)nextNode.events.get(nextNodeIndex);
+ prevNode = nextNode;
+ prevNodeIndex = nextNodeIndex;
+ ++nextNodeIndex;
+ if (nextNodeIndex >= nextNode.events.size()) {
+ nextNode = nextNode.nextNode;
+ nextNodeIndex = 0;
+ }
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = (Event)prevNode.events.get(prevNodeIndex);
+ nextNode = prevNode;
+ nextNodeIndex = prevNodeIndex;
+ --prevNodeIndex;
+ if (prevNodeIndex < 0) {
+ prevNode = prevNode.prevNode;
+ if (prevNode != null)
+ prevNodeIndex = prevNode.events.size() - 1;
+ }
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void set (Event ev) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private Node newNode (Event ev) {
+ Node temp;
+ synchronized (RedblackTree.class) {
+ if (free == null)
+ return new Node (ev);
+
+ temp = free;
+ free = free.nextNode;
+ }
+ temp.events.add (ev);
+ return temp;
+ }
+
+ private class EventMapKey extends Event {
+ public EventMapKey(Event ev) {
+ this.eventTime = ev.time();
+ this.priority = ev.priority();
+ }
+
+ public void actions() { }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/RedblackTree.tex b/source/umontreal/iro/lecuyer/simevents/eventlist/RedblackTree.tex
new file mode 100644
index 0000000..f8edb26
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/RedblackTree.tex
@@ -0,0 +1,422 @@
+\defmodule {RedblackTree}
+
+An implementation of \class{EventList} using a \emph{red black} tree,
+which is similar to a binary search tree except that
+every node is colored red or black. When modifying the structure,
+the tree is reorganized for the colors to satisfy rules that
+give an average $O(\log (n))$ time for removing the first event
+or inserting a new event,
+where $n$ is the number of elements in the structure.
+However, adding or removing events imply reorganizing
+the tree and requires more overhead than a binary search tree.
+
+The present implementation uses the Java 2
+\externalclass{java.util}{TreeMap} class
+which implements a red black tree for general usage.
+This event list implementation is not efficient.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: RedblackTree
+ * Description: implementation of class EventList using a red-black tree
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents.eventlist;\begin{hide}
+
+import java.util.TreeMap;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.LinkedList;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.Sim;
+\end{hide}
+
+public class RedblackTree implements EventList\begin{hide} {
+ private TreeMap<Event, Node> tree = new TreeMap<Event, Node>(new EventComparator());
+ private static Node free = null;
+ private int modCount = 0;\end{hide}
+\end{code}\begin{hide}\begin{code}
+
+ public void clear()\begin{hide} {
+ Iterator<Node> itr = tree.values().iterator();
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ node.events.clear();
+ itr.remove();
+ synchronized (RedblackTree.class) {
+ node.nextNode = free;
+ free = node;
+ }
+ }
+ ++modCount;
+ }\end{hide}
+
+ public void add (Event ev)\begin{hide} {
+ Node node = tree.get (ev);
+ if (node != null)
+ node.events.add (ev);
+ else
+ tree.put (new EventMapKey (ev), newNode (ev));
+ ++modCount;
+ }\end{hide}
+
+ public void addFirst (Event ev)\begin{hide} {
+ // ev.setTime (Sim.time()); // necessaire si eventime n'est pas deja a 0
+ Node node = tree.get (ev);
+ if (node != null)
+ node.events.add (ev);
+ else
+ tree.put (new EventMapKey(ev), newNode (ev));
+ ++modCount;
+ }\end{hide}
+
+ public void addBefore (Event ev, Event other)\begin{hide} {
+ Node node = tree.get (other);
+ if (node == null)
+ throw new IllegalArgumentException ("Event not in list.");
+ // ev.setTime (other.time());
+ node.addBefore (ev, other);
+ ++modCount;
+ }\end{hide}
+
+ public void addAfter (Event ev, Event other)\begin{hide} {
+ Node node = tree.get (other);
+ if (node == null)
+ throw new IllegalArgumentException ("Event not in list.");
+ // ev.setTime (other.time());
+ node.addAfter (ev, other);
+ ++modCount;
+ }\end{hide}
+
+ public Event getFirst()\begin{hide} {
+ return isEmpty() ? null :
+ tree.get (tree.firstKey()).events.get (0);
+ }\end{hide}
+
+ public Event getFirstOfClass (String cl)\begin{hide} {
+ Iterator<Node> itr = tree.values().iterator();
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ Event ev = node.getFirstOfClass (cl);
+ if (ev != null)
+ return ev;
+ }
+ return null;
+ }
+
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Iterator<Node> itr = tree.values().iterator();
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ E ev = node.getFirstOfClass (cl);
+ if (ev != null)
+ return ev;
+ }
+ return null;
+ }\end{hide}
+
+ public boolean remove (Event ev)\begin{hide} {
+ Node node = tree.get (ev);
+ if (node == null)
+ return false;
+ if (node.remove (ev)) {
+ tree.remove (ev);
+ synchronized (RedblackTree.class) {
+ node.nextNode = free; free = node;
+ }
+ }
+ ++modCount;
+ return true;
+ }\end{hide}
+
+ public Event removeFirst()\begin{hide} {
+ if (tree.isEmpty())
+ return null;
+ Event evKey = tree.firstKey();
+ Node node = tree.get (evKey);
+ Event first = node.events.get (0);
+ node.events.remove (0);
+ if (node.events.isEmpty()) {
+ tree.remove (evKey);
+ synchronized (RedblackTree.class) {
+ node.nextNode = free; free = node;
+ }
+ }
+ ++modCount;
+ return first;
+ }\end{hide}
+
+ public String toString()\begin{hide} {
+ StringBuffer sb = new StringBuffer ("Contents of the event list RedblackTree:");
+ for (Node node : tree.values()) {
+ for (Event ev : node.events)
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, ev.time()) + ", " +
+ PrintfFormat.g (8, 4, ev.priority()) + " : " +
+ ev.toString());
+ }
+ return sb.toString();
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new RBItr();
+ }\end{hide}
+
+ public boolean isEmpty()\begin{hide} {
+ return tree.isEmpty();
+ }
+
+ private static class Node {
+ // For the iterator
+ public Node prevNode = null;
+ public Node nextNode = null;
+
+ public java.util.List<Event> events = new LinkedList<Event>();
+
+ public Node (Event ev) {
+ events.add (ev);
+ }
+
+ public void addAfter (Event ev, Event other) {
+ ListIterator<Event> itr = events.listIterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev == other) {
+ itr.add (ev);
+ return;
+ }
+ }
+ throw new IllegalArgumentException ("Event not in node.");
+ }
+
+ public void addBefore (Event ev, Event other) {
+ ListIterator<Event> itr = events.listIterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev == other) {
+ itr.previous();
+ itr.add (ev);
+ return;
+ }
+ }
+ throw new IllegalArgumentException ("Event not in node.");
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev.getClass().getName().equals (cl))
+ return listev;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev.getClass() == cl)
+ return (E)listev;
+ }
+ return null;
+ }
+
+ /**
+ * Remove an event from a node.
+ * Returns true if the node becomes empty.
+ */
+ public boolean remove (Event ev) {
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ Event listev = itr.next();
+ if (listev == ev) {
+ itr.remove();
+ return events.isEmpty();
+ }
+ }
+ throw new IllegalArgumentException ("Event not in node.");
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ boolean first = true;
+ Iterator<Event> itr = events.iterator();
+ while (itr.hasNext()) {
+ if (first)
+ first = false;
+ else
+ sb.append (", ");
+ sb.append (itr.next());
+ }
+ return sb.toString();
+ }
+ }
+
+ private static class EventComparator implements Comparator<Event> {
+ public int compare (Event ev1, Event ev2) {
+ return ev1.compareTo(ev2);
+ }
+
+ public boolean equals (Object obj) { return true; }
+ }
+
+ private class RBItr implements ListIterator<Event> {
+ private int expectedModCount;
+ private Node prevNode;
+ private Node nextNode;
+ private int prevNodeIndex;
+ private int nextNodeIndex;
+ private int nextIndex;
+
+ RBItr() {
+ expectedModCount = modCount;
+ prevNode = null;
+ nextNode = tree.isEmpty() ? null :
+ (Node)tree.get (tree.firstKey());
+ prevNodeIndex = 0;
+ nextNodeIndex = 0;
+ nextIndex = 0;
+
+ Iterator<Node> itr = tree.values().iterator();
+ Node lastNode = null;
+ while (itr.hasNext()) {
+ Node node = itr.next();
+ node.prevNode = lastNode;
+ if (lastNode != null)
+ lastNode.nextNode = node;
+ node.nextNode = null;
+ lastNode = node;
+ }
+ }
+
+ public void add(Event ev) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return nextNode != null &&
+ nextNodeIndex < nextNode.events.size();
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prevNode != null &&
+ prevNodeIndex >= 0;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = (Event)nextNode.events.get(nextNodeIndex);
+ prevNode = nextNode;
+ prevNodeIndex = nextNodeIndex;
+ ++nextNodeIndex;
+ if (nextNodeIndex >= nextNode.events.size()) {
+ nextNode = nextNode.nextNode;
+ nextNodeIndex = 0;
+ }
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = (Event)prevNode.events.get(prevNodeIndex);
+ nextNode = prevNode;
+ nextNodeIndex = prevNodeIndex;
+ --prevNodeIndex;
+ if (prevNodeIndex < 0) {
+ prevNode = prevNode.prevNode;
+ if (prevNode != null)
+ prevNodeIndex = prevNode.events.size() - 1;
+ }
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void set (Event ev) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private Node newNode (Event ev) {
+ Node temp;
+ synchronized (RedblackTree.class) {
+ if (free == null)
+ return new Node (ev);
+
+ temp = free;
+ free = free.nextNode;
+ }
+ temp.events.add (ev);
+ return temp;
+ }
+
+ private class EventMapKey extends Event {
+ public EventMapKey(Event ev) {
+ this.eventTime = ev.time();
+ this.priority = ev.priority();
+ }
+
+ public void actions() { }
+ }
+}\end{hide}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/SplayTree.java b/source/umontreal/iro/lecuyer/simevents/eventlist/SplayTree.java
new file mode 100644
index 0000000..47f8a2c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/SplayTree.java
@@ -0,0 +1,878 @@
+
+
+/*
+ * Class: SplayTree
+ * Description: implementation of class EventList using a splay tree
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simevents.eventlist;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+
+
+/**
+ * An implementation of {@link EventList} using a splay tree.
+ * This tree is like a binary search tree except that when it
+ * is modified, the affected node is moved to the top.
+ * The rebalancing scheme is simpler than for a <SPAN CLASS="textit">red black</SPAN>
+ * tree and can avoid the worst case of the linked list.
+ * This gives a
+ * <SPAN CLASS="MATH"><I>O</I>(log(<I>n</I>))</SPAN> average time for adding or removing
+ * an event, where <SPAN CLASS="MATH"><I>n</I></SPAN> is the size of the event list.
+ *
+ */
+public class SplayTree implements EventList {
+ private Entry root = null;
+ private static Entry free = null;
+ private int modCount = 0;
+
+ private int myCompareTo (Event ev, Event other) {
+ // A new event must always occur after those with the same time and
+ // same priority in the Event list. getRa is used to ensure that.
+ int j = ev.compareTo(other);
+ if (0 != j)
+ return j;
+ if (ev.getRa() < other.getRa())
+ return -1;
+ if (ev.getRa() > other.getRa())
+ return 1;
+ return 0;
+ }
+
+
+ public boolean isEmpty() {
+ return root == null;
+ }
+
+ public void clear() {
+ // Simply root = null would be more efficient but the
+ // entries would not be recuperated.
+ while (root != null)
+ remove (root);
+ }
+
+ public void add (Event ev) {
+ //ajoute un element dans l'arbre en faisant un "splay"
+
+ //reference pour le splay :
+ //WEISS, Mark Allen, Data Structures & Problem Solving using JAVA
+ //section 22.5 Top-Down Splay Tree
+
+ //"zig" : rotation entre un element et son parent
+ //"zig-zag" simplifie : rotation entre un element et son parent
+ //"zig-zig" : rotation entre le parent et le grand-parent,
+ // suivis par une rotation entre le parent et l'element
+
+ //NOTE : on considere toujours qu'un nouvel element doit etre place
+ // apres les evenements de meme temps et meme priorite
+
+ ev.setRa(1); // Temporary; make sure ev is after other events with
+ // the same time and priority. Others have ra = 0.
+
+ Entry next = root;
+ // le nouvel evenement devient la racine
+ Entry e = add (ev, null);
+ root = e;
+ if (next != null) {
+ Entry left = root;
+ Entry right = root;
+ Entry temp = null;
+ boolean end_splay = false;
+ while (!end_splay) {
+ if (myCompareTo(ev, next.event) > 0) {
+ temp = next.right;
+ if (temp == null) {
+ //cas "zig"
+ left.right = next;
+ next.father = left;
+ right.left = null;
+ end_splay = true;
+ }
+ else if (myCompareTo(ev, temp.event) < 0) {
+ //cas "zig-zag" simplifie
+ left.right = next;
+ next.father = left;
+ left = next;
+ next = temp;
+ }
+ else {
+ //cas "zig-zig"
+ next.right = temp.left;
+ if (temp.left != null)
+ temp.left.father = next;
+ left.right = temp;
+ temp.father = left;
+ temp.left = next;
+ next.father = temp;
+ left = temp;
+ next = temp.right;
+ if (next == null) {
+ right.left = null;
+ end_splay = true;
+ }
+ }
+
+ } else {
+ temp = next.left;
+ if (temp == null) {
+ //cas "zig"
+ right.left = next;
+ next.father = right;
+ left.right = null;
+ end_splay = true;
+ }
+ else if (myCompareTo(ev, temp.event) > 0) {
+ //cas "zig-zag" simplifie
+ right.left = next;
+ next.father = right;
+ right = next;
+ next = temp;
+ }
+ else {
+ //cas "zig-zig"
+ next.left = temp.right;
+ if (temp.right != null)
+ temp.right.father = next;
+ right.left = temp;
+ temp.father = right;
+ temp.right = next;
+ next.father = temp;
+ right = temp;
+ next = temp.left;
+ if (next == null) {
+ left.right = null;
+ end_splay = true;
+ }
+ }
+ }
+ }
+ temp = e.left;
+ e.left = e.right;
+ e.right = temp;
+ }
+ ev.setRa(0);
+ ++modCount;
+ }
+
+ public void addFirst (Event ev) {
+ if (root == null)
+ root = add (ev, null);
+ else {
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ cursor.left = add (ev, cursor);
+ }
+ ++modCount;
+ }
+
+ public void addBefore (Event ev, Event other) {
+ Entry otherEntry = findEntry (other);
+ if (otherEntry == null)
+ throw new IllegalArgumentException ("Event not in list.");
+
+ Entry e = add (ev, null);
+ // insere e a la place de otherEntry et otherEntry
+ // devient le fils droit de e
+ if (otherEntry != root) {
+ if (otherEntry == otherEntry.father.left)
+ otherEntry.father.left = e;
+ else
+ otherEntry.father.right = e;
+ }
+ else
+ root = e;
+ e.father = otherEntry.father;
+ otherEntry.father = e;
+ e.right = otherEntry;
+ // le sous-arbre de droite de otherEntry devient le sous-arbre de
+ // droite de e.
+ // permet que e soit exactement apres otherEntry qu'importe
+ // les operations effectuees
+ e.left = otherEntry.left;
+ if (e.left != null)
+ e.left.father = e;
+ otherEntry.left = null;
+ ++modCount;
+ }
+
+ public void addAfter (Event ev, Event other) {
+ Entry otherEntry = findEntry (other);
+ if (otherEntry == null)
+ throw new IllegalArgumentException ("Event not in list.");
+ Entry e = add (ev, otherEntry);
+ // insere e comme fils droit de otherEntry
+ e.right = otherEntry.right;
+ otherEntry.right = e;
+ if (e.right != null)
+ e.right.father = e;
+ ++modCount;
+ }
+
+ public Event getFirst() {
+ if (root == null)
+ return null;
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ return cursor.event;
+ }
+
+ public Event getFirstOfClass (String cl) {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+ while (cursor != null) {
+ if (cursor.event.getClass().getName().equals (cl))
+ return cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+ while (cursor != null) {
+ if (cursor.event.getClass() == cl)
+ return (E)cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new SPItr();
+ }
+
+ public boolean remove (Event ev) {
+ //on trouve le noeud correspondant a l'evenement pour l'enlever
+ if (root == null)
+ return false;
+ Entry e = findEntry (ev);
+ if (e == null)
+ return false;
+ return remove (e);
+ }
+
+ public Event removeFirst() {
+ if (root == null)
+ return null;
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ Event first = cursor.event;
+ remove (cursor);
+ return first;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer
+ ("Contents of the event list SplayTree:");
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+ while (cursor != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, cursor.event.time()) + ", " +
+ PrintfFormat.g (8, 4, cursor.event.priority()) + " : " +
+ cursor.event.toString());
+ cursor = successor (cursor);
+ }
+ return sb.toString();
+ }
+
+ private static class Entry {
+ Event event;
+ Entry father;
+ Entry left;
+ Entry right;
+
+ Entry (Event event, Entry left, Entry right, Entry father) {
+ this.event = event;
+ this.left = left;
+ this.right = right;
+ this.father = father;
+ }
+ }
+
+ private class SPItr implements ListIterator<Event> {
+ private Entry prev;
+ private Entry next;
+ private Entry lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ SPItr() {
+ prev = null;
+ next = root;
+ if (next != null) {
+ while (next.left != null)
+ next = next.left;
+ }
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add(Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ // Check the event time and priority
+ if (next != null && ev.compareTo(next.event) > 0) {
+ ev.setTime (next.event.time());
+ ev.setPriority (next.event.priority());
+ }
+ if (prev != null && ev.compareTo(prev.event) < 0) {
+ ev.setTime (prev.event.time());
+ ev.setPriority (prev.event.priority());
+ }
+
+ Entry e = SplayTree.this.add (ev, null);
+ if (prev != null) {
+ // Ajouter ev apr`es prev.
+ // insere e comme fils droit de prev
+ e.father = prev;
+ e.right = prev.right;
+ prev.right = e;
+ if (e.right != null)
+ e.right.father = e;
+ }
+ else {
+ // ajoute ev avant next.
+ // insere e a la place de otherEntry et
+ // otherEntry devient le fils droit de e
+ if (next != root) {
+ if (next == next.father.left)
+ next.father.left = e;
+ else
+ next.father.right = e;
+ }
+ else
+ root = e;
+ e.father = next.father;
+ next.father = e;
+ e.right = next;
+ // le sous-arbre de droite de otherEntry
+ // devient le sous-arbre de droite de e.
+ // permet que e soit exactement apres otherEntry qu'importe les
+ // operations effectuees
+ e.left = next.left;
+ if (e.left != null)
+ e.left.father = e;
+ next.left = null;
+ }
+
+ prev = e;
+ ++nextIndex;
+ lastRet = null;
+ ++modCount;
+ ++expectedModCount;
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != null;
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prev != null;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = next.event;
+ lastRet = next;
+ prev = next;
+ next = successor (next);
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = prev.event;
+ lastRet = prev;
+ next = prev;
+ prev = predecessor (prev);
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ if (lastRet == next) // Last call to previous
+ next = successor (next);
+ else { // Last call to next or no call
+ prev = predecessor (prev);
+ --nextIndex;
+ }
+ SplayTree.this.remove (lastRet);
+ lastRet = null;
+ ++expectedModCount;
+ }
+
+ public void set (Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ Entry pred = predecessor (lastRet);
+ Entry succ = successor (lastRet);
+
+ if (pred != null && ev.compareTo(pred.event) < 0) {
+ ev.setTime (pred.event.time());
+ ev.setPriority (pred.event.priority());
+ }
+ if (succ != null && ev.compareTo(succ.event) > 0) {
+ ev.setTime (succ.event.time());
+ ev.setPriority (succ.event.priority());
+ }
+
+ lastRet.event = ev;
+ }
+ }
+
+ /* *
+ * Creates a new entry object.
+ */
+ private Entry add (Event ev, Entry father) {
+ Entry e;
+ synchronized (SplayTree.class) {
+ if (free == null)
+ return new Entry (ev, null, null, father);
+
+ e = free;
+ free = free.right;
+ }
+ e.event = ev;
+ e.left = e.right = null;
+ e.father = father;
+ return e;
+ }
+
+
+ /*
+ Remonte a la racine une entree avec un splay de type "bottom-up",
+ qui prend pour parametre le noeud a remonter.
+
+ Fonctionnement du splay :
+ Jusqu'a ce que l'element devienne la racine, on fait :
+ - Si le pere de l'element est la racine, on fait la rotation
+ entre le pere et l'element (cas "zig").
+ - Si le pere de l'element et l'element ne sont pas des fils du
+ meme cote (gauche-droite ou droite-gauche), alors on fait
+ la rotation entre le pere et l'element (cas "zig-zag" simplifie).
+ - Si le pere de l'element et l'element sont des fils du meme cote
+ (gauche-gauche ou droite-droite), alors on fait la rotation
+ entre le grand-pere et le pere avant de faire la rotation entre
+ le pere et l'element (cas "zig-zig").
+ */
+ private void splay(Entry e)
+ {
+ boolean left;
+
+ Entry f; // = father le pere de l'element e
+ Entry gf; // = grandFather le grand-pere de l'element e
+ Entry ggf; // = grandGrandFather l'arriere-grand-pere de l'element e
+
+ while(e.father != null) {
+ f = e.father;
+ gf = f.father;
+ left = e == f.left;
+
+ if (left)
+ // cas du fils gauche
+ if (gf == null) {
+ // cas "zig", on fait la rotation de f (la racine) et e
+ f.father = e;
+ f.left = e.right;
+ if(f.left != null)
+ f.left.father = f;
+ e.right = f;
+ e.father = null;
+ }
+ else if (gf.right == f) {
+ // cas "zig-zag", simplifie, pareil que le cas "zig"
+ gf.right = e;
+
+ f.father = e;
+ f.left = e.right;
+ if(f.left != null)
+ f.left.father = f;
+ e.right = f;
+ e.father = gf;
+ }
+ else {
+ // cas "zig-zig", on fait la rotation de gf avec
+ // f, suivis de la rotation de e avec f
+ ggf = gf.father;
+
+ gf.left = f.right;
+ if(gf.left != null)
+ gf.left.father = gf;
+ f.right = gf;
+ gf.father = f;
+
+ f.left = e.right;
+ if(f.left != null)
+ f.left.father = f;
+ f.father = e;
+ e.right = f;
+
+ // on rattache e a son nouveau pere
+ e.father = ggf;
+ if(ggf != null)
+ if(ggf.left == gf)
+ ggf.left = e;
+ else
+ ggf.right = e;
+ }
+ else
+ //cas du fils droit
+ if(gf == null) {
+ // cas "zig", on fait la rotation de f (la racine) et e
+
+ f.father = e;
+ f.right = e.left;
+ if(f.right != null)
+ f.right.father = f;
+ e.left = f;
+ e.father = null;
+ }
+ else if(gf.left == f) {
+ // cas "zig-zag", simplifie, pareil que le cas "zig"
+ gf.left = e;
+
+ f.father = e;
+ f.right = e.left;
+ if(f.right != null)
+ f.right.father = f;
+ e.left = f;
+ e.father = gf;
+ }
+ else {
+ // cas "zig-zig", on fait la rotation de gf avec
+ // f, suivis de la rotation de e avec f
+ ggf = gf.father;
+
+ gf.right = f.left;
+ if(gf.right != null)
+ gf.right.father = gf;
+ f.left = gf;
+ gf.father = f;
+
+ f.right = e.left;
+ if(f.right != null)
+ f.right.father = f;
+ f.father = e;
+ e.left = f;
+
+ // on rattache e a son nouveau pere
+ e.father = ggf;
+ if(ggf != null)
+ if(ggf.left == gf)
+ ggf.left = e;
+ else
+ ggf.right = e;
+ }
+ }
+ }
+
+
+ /* *
+ Enleve l'entree e de l'arbre.
+
+ Procedure :
+ On fait d'abord un splay sur l'entree a enlever pour la faire monter
+ a la racine. Une fois a la racine, on l'enleve. On cherche ensuite
+ l'element minimal du sous-arbre droit (qui contient les elements
+ qui sont plus grand que e) et, avec un splay, on le remonte a la
+ racine de son sous-arbre. Cet element, puisqu'il est le minimum de
+ son sous-arbre, n'a pas de fils gauche. On lui rattache donc comme
+ fils gauche le sous-arbre gauche.
+
+ La raison de cette procedure plus compliquee que l'equivalent dans
+ un arbre binaire ordinaire est que, pour que la garantie de
+ temps d'acces amorti en O(log n) du splay tree s'applique, il
+ faut que chaque acces (qui necessite une recherche) d'un element
+ s'accompagne du splay de cet element a la racine.
+ */
+ private boolean remove(Entry e)
+ {
+ if (root == null || e == null)
+ return false;
+
+ splay(e);
+ // e est implicitement la racine, meme si root != e
+
+ Entry leftTree = e.left;
+ Entry rightTree = e.right;
+
+ if(leftTree != null)
+ leftTree.father = null;
+ if(rightTree != null)
+ rightTree.father = null;
+
+ // Recupere l'espace de l'avis d'evenement
+ e.left = null;
+ e.event = null;
+ synchronized (SplayTree.class) {
+ e.right = free;
+ free = e;
+ }
+
+
+ if(rightTree == null)
+ root = leftTree;
+ else if(leftTree == null)
+ root = rightTree;
+ else {
+ // on cherche le plus petit element du sous-arbre de droite
+ Entry newRoot = rightTree;
+ while(newRoot.left != null)
+ newRoot = newRoot.left;
+
+ // on monte newRoot en haut du sous-arbre de droite
+ splay(newRoot);
+
+ // on rattache les deux sous-arbres
+ newRoot.left = leftTree;
+ leftTree.father = newRoot;
+ root = newRoot;
+ }
+
+
+ ++modCount;
+
+ return true;
+ }
+
+
+ /* *
+ * Finds the corresponding entry for an event.
+ */
+ private Entry findEntry (Event ev) {
+ Entry cursor = root;
+ while (cursor != null) {
+ if (cursor.event == ev)
+ return cursor;
+ else if (ev.compareTo(cursor.event) > 0)
+ cursor = cursor.right;
+ else if (ev.compareTo(cursor.event) < 0)
+ cursor = cursor.left;
+ else {
+ // on a trouve un element "egal" (compareTo() revoie 0 ), on ne peut donc plus
+ // utiliser la recherche binaire et il faut donc regarder
+ // tous les elements "egaux" qui se retrouvent soit apres,
+ // soit avant l'element trouve
+ Entry center = cursor;
+
+ // recherche avant center
+ while (cursor != null && ev.compareTo(cursor.event) == 0)
+ if(cursor.event == ev)
+ return cursor;
+ else
+ cursor = predecessor(cursor);
+
+ cursor = center;
+
+ // recherche apres center
+ while (cursor != null && ev.compareTo(cursor.event) == 0)
+ if(cursor.event == ev)
+ return cursor;
+ else
+ cursor = successor(cursor);
+
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private Entry successor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.right != null) {
+ cursor = cursor.right;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ }
+ else {
+ while (cursor.father != null && cursor.father.right == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ private Entry predecessor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.left != null) {
+ cursor = cursor.left;
+ while (cursor.right != null)
+ cursor = cursor.right;
+ }
+ else {
+
+ while (cursor.father != null && cursor.father.left == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ /*
+ public static void main (String[] args) {
+ SplayTree sp = new SplayTree();
+ Event1 e1 = new Event1(); e1.setTime(10.0);
+ Event1 e2 = new Event1(); e2.setTime(20.0);
+ Event1 e3 = new Event1(); e3.setTime(30.0);
+ Event1 e4 = new Event1(); e4.setTime(40.0);
+ Event1 e5 = new Event1(); e5.setTime(50.0);
+ Event1 e6 = new Event1(); e6.setTime(60.0);
+ Event1 e7 = new Event1(); e7.setTime(70.0);
+ sp.add(e1);
+ sp.add(e2);
+ sp.print2(sp.root);
+ sp.add(e3);
+ sp.print2(sp.root);
+ sp.add(e4);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+ sp.add(e6);
+ sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+
+ sp.getFirst();
+ System.out.println(".....after Get" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ sp.remove(e3);
+ System.out.println("Apres remove" + PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ }
+
+ private void print(Entry t) {
+ if (t != null){
+ print (t.left);
+ System.out.println ("===========> Event time "+t.event.time());
+ print (t.right);
+ }
+ }
+
+ private void print2(Entry t) {
+ System.out.println("=============================== "+
+ "print2 : pour ..... "+t.event.time());
+ if (t != null) {
+ System.out.println ("===========> ev time "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void gauche (Entry t) {
+ if (t != null) {
+ System.out.println ("===========> gauche "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void droite (Entry t) {
+ if (t != null){
+ System.out.println ("===========> droite "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ // System.out.println();
+ }
+ else
+ System.out.println ("===========> droite null ");
+ }
+
+ private static class Event1 extends Event {
+ public void actions() {}
+
+ public String toString() {
+ return "Event(" + eventTime + ")";
+ }
+ }
+*/
+}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/SplayTree.tex b/source/umontreal/iro/lecuyer/simevents/eventlist/SplayTree.tex
new file mode 100644
index 0000000..7705ac4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/SplayTree.tex
@@ -0,0 +1,882 @@
+\defmodule {SplayTree}
+
+An implementation of \class{EventList} using a splay tree \cite{iSLE85a}.
+This tree is like a binary search tree except that when it
+is modified, the affected node is moved to the top.
+The rebalancing scheme is simpler than for a \emph{red black}
+tree and can avoid the worst case of the linked list.
+This gives a $O(\log (n))$ average time for adding or removing
+an event, where $n$ is the size of the event list.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: SplayTree
+ * Description: implementation of class EventList using a splay tree
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simevents.eventlist; \begin{hide}
+
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.simevents.Event;
+\end{hide}
+
+public class SplayTree implements EventList\begin{hide} {
+ private Entry root = null;
+ private static Entry free = null;
+ private int modCount = 0;
+
+ private int myCompareTo (Event ev, Event other) {
+ // A new event must always occur after those with the same time and
+ // same priority in the Event list. getRa is used to ensure that.
+ int j = ev.compareTo(other);
+ if (0 != j)
+ return j;
+ if (ev.getRa() < other.getRa())
+ return -1;
+ if (ev.getRa() > other.getRa())
+ return 1;
+ return 0;
+ }\end{hide}
+\end{code}\begin{hide}\begin{code}
+
+ public boolean isEmpty()\begin{hide} {
+ return root == null;
+ }\end{hide}
+
+ public void clear()\begin{hide} {
+ // Simply root = null would be more efficient but the
+ // entries would not be recuperated.
+ while (root != null)
+ remove (root);
+ }\end{hide}
+
+ public void add (Event ev)\begin{hide} {
+ //ajoute un element dans l'arbre en faisant un "splay"
+
+ //reference pour le splay :
+ //WEISS, Mark Allen, Data Structures & Problem Solving using JAVA
+ //section 22.5 Top-Down Splay Tree
+
+ //"zig" : rotation entre un element et son parent
+ //"zig-zag" simplifie : rotation entre un element et son parent
+ //"zig-zig" : rotation entre le parent et le grand-parent,
+ // suivis par une rotation entre le parent et l'element
+
+ //NOTE : on considere toujours qu'un nouvel element doit etre place
+ // apres les evenements de meme temps et meme priorite
+
+ ev.setRa(1); // Temporary; make sure ev is after other events with
+ // the same time and priority. Others have ra = 0.
+
+ Entry next = root;
+ // le nouvel evenement devient la racine
+ Entry e = add (ev, null);
+ root = e;
+ if (next != null) {
+ Entry left = root;
+ Entry right = root;
+ Entry temp = null;
+ boolean end_splay = false;
+ while (!end_splay) {
+ if (myCompareTo(ev, next.event) > 0) {
+ temp = next.right;
+ if (temp == null) {
+ //cas "zig"
+ left.right = next;
+ next.father = left;
+ right.left = null;
+ end_splay = true;
+ }
+ else if (myCompareTo(ev, temp.event) < 0) {
+ //cas "zig-zag" simplifie
+ left.right = next;
+ next.father = left;
+ left = next;
+ next = temp;
+ }
+ else {
+ //cas "zig-zig"
+ next.right = temp.left;
+ if (temp.left != null)
+ temp.left.father = next;
+ left.right = temp;
+ temp.father = left;
+ temp.left = next;
+ next.father = temp;
+ left = temp;
+ next = temp.right;
+ if (next == null) {
+ right.left = null;
+ end_splay = true;
+ }
+ }
+
+ } else {
+ temp = next.left;
+ if (temp == null) {
+ //cas "zig"
+ right.left = next;
+ next.father = right;
+ left.right = null;
+ end_splay = true;
+ }
+ else if (myCompareTo(ev, temp.event) > 0) {
+ //cas "zig-zag" simplifie
+ right.left = next;
+ next.father = right;
+ right = next;
+ next = temp;
+ }
+ else {
+ //cas "zig-zig"
+ next.left = temp.right;
+ if (temp.right != null)
+ temp.right.father = next;
+ right.left = temp;
+ temp.father = right;
+ temp.right = next;
+ next.father = temp;
+ right = temp;
+ next = temp.left;
+ if (next == null) {
+ left.right = null;
+ end_splay = true;
+ }
+ }
+ }
+ }
+ temp = e.left;
+ e.left = e.right;
+ e.right = temp;
+ }
+ ev.setRa(0);
+ ++modCount;
+ }\end{hide}
+
+ public void addFirst (Event ev)\begin{hide} {
+ if (root == null)
+ root = add (ev, null);
+ else {
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ cursor.left = add (ev, cursor);
+ }
+ ++modCount;
+ }\end{hide}
+
+ public void addBefore (Event ev, Event other)\begin{hide} {
+ Entry otherEntry = findEntry (other);
+ if (otherEntry == null)
+ throw new IllegalArgumentException ("Event not in list.");
+
+ Entry e = add (ev, null);
+ // insere e a la place de otherEntry et otherEntry
+ // devient le fils droit de e
+ if (otherEntry != root) {
+ if (otherEntry == otherEntry.father.left)
+ otherEntry.father.left = e;
+ else
+ otherEntry.father.right = e;
+ }
+ else
+ root = e;
+ e.father = otherEntry.father;
+ otherEntry.father = e;
+ e.right = otherEntry;
+ // le sous-arbre de droite de otherEntry devient le sous-arbre de
+ // droite de e.
+ // permet que e soit exactement apres otherEntry qu'importe
+ // les operations effectuees
+ e.left = otherEntry.left;
+ if (e.left != null)
+ e.left.father = e;
+ otherEntry.left = null;
+ ++modCount;
+ }\end{hide}
+
+ public void addAfter (Event ev, Event other)\begin{hide} {
+ Entry otherEntry = findEntry (other);
+ if (otherEntry == null)
+ throw new IllegalArgumentException ("Event not in list.");
+ Entry e = add (ev, otherEntry);
+ // insere e comme fils droit de otherEntry
+ e.right = otherEntry.right;
+ otherEntry.right = e;
+ if (e.right != null)
+ e.right.father = e;
+ ++modCount;
+ }\end{hide}
+
+ public Event getFirst()\begin{hide} {
+ if (root == null)
+ return null;
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ return cursor.event;
+ }\end{hide}
+
+ public Event getFirstOfClass (String cl)\begin{hide} {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+ while (cursor != null) {
+ if (cursor.event.getClass().getName().equals (cl))
+ return cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <E extends Event> E getFirstOfClass (Class<E> cl) {
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+ while (cursor != null) {
+ if (cursor.event.getClass() == cl)
+ return (E)cursor.event;
+ cursor = successor (cursor);
+ }
+ return null;
+ }
+
+ public Iterator<Event> iterator() {
+ return listIterator();
+ }
+
+ public ListIterator<Event> listIterator() {
+ return new SPItr();
+ }\end{hide}
+
+ public boolean remove (Event ev)\begin{hide} {
+ //on trouve le noeud correspondant a l'evenement pour l'enlever
+ if (root == null)
+ return false;
+ Entry e = findEntry (ev);
+ if (e == null)
+ return false;
+ return remove (e);
+ }\end{hide}
+
+ public Event removeFirst()\begin{hide} {
+ if (root == null)
+ return null;
+ Entry cursor = root;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ Event first = cursor.event;
+ remove (cursor);
+ return first;
+ }\end{hide}
+
+ public String toString()\begin{hide} {
+ StringBuffer sb = new StringBuffer
+ ("Contents of the event list SplayTree:");
+ Entry cursor = root;
+ if (root != null)
+ while (cursor.left != null)
+ cursor = cursor.left;
+ while (cursor != null) {
+ sb.append (PrintfFormat.NEWLINE +
+ PrintfFormat.g (12, 7, cursor.event.time()) + ", " +
+ PrintfFormat.g (8, 4, cursor.event.priority()) + " : " +
+ cursor.event.toString());
+ cursor = successor (cursor);
+ }
+ return sb.toString();
+ }
+
+ private static class Entry {
+ Event event;
+ Entry father;
+ Entry left;
+ Entry right;
+
+ Entry (Event event, Entry left, Entry right, Entry father) {
+ this.event = event;
+ this.left = left;
+ this.right = right;
+ this.father = father;
+ }
+ }
+
+ private class SPItr implements ListIterator<Event> {
+ private Entry prev;
+ private Entry next;
+ private Entry lastRet;
+ private int expectedModCount;
+ private int nextIndex;
+
+ SPItr() {
+ prev = null;
+ next = root;
+ if (next != null) {
+ while (next.left != null)
+ next = next.left;
+ }
+ expectedModCount = modCount;
+ lastRet = null;
+ nextIndex = 0;
+ }
+
+ public void add(Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ // Check the event time and priority
+ if (next != null && ev.compareTo(next.event) > 0) {
+ ev.setTime (next.event.time());
+ ev.setPriority (next.event.priority());
+ }
+ if (prev != null && ev.compareTo(prev.event) < 0) {
+ ev.setTime (prev.event.time());
+ ev.setPriority (prev.event.priority());
+ }
+
+ Entry e = SplayTree.this.add (ev, null);
+ if (prev != null) {
+ // Ajouter ev apr`es prev.
+ // insere e comme fils droit de prev
+ e.father = prev;
+ e.right = prev.right;
+ prev.right = e;
+ if (e.right != null)
+ e.right.father = e;
+ }
+ else {
+ // ajoute ev avant next.
+ // insere e a la place de otherEntry et
+ // otherEntry devient le fils droit de e
+ if (next != root) {
+ if (next == next.father.left)
+ next.father.left = e;
+ else
+ next.father.right = e;
+ }
+ else
+ root = e;
+ e.father = next.father;
+ next.father = e;
+ e.right = next;
+ // le sous-arbre de droite de otherEntry
+ // devient le sous-arbre de droite de e.
+ // permet que e soit exactement apres otherEntry qu'importe les
+ // operations effectuees
+ e.left = next.left;
+ if (e.left != null)
+ e.left.father = e;
+ next.left = null;
+ }
+
+ prev = e;
+ ++nextIndex;
+ lastRet = null;
+ ++modCount;
+ ++expectedModCount;
+ }
+
+ public boolean hasNext() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return next != null;
+ }
+
+ public boolean hasPrevious() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ return prev != null;
+ }
+
+ public Event next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ ++nextIndex;
+ Event ev = next.event;
+ lastRet = next;
+ prev = next;
+ next = successor (next);
+ return ev;
+ }
+
+ public int nextIndex() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return nextIndex;
+ }
+
+ public Event previous() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ --nextIndex;
+ Event ev = prev.event;
+ lastRet = prev;
+ next = prev;
+ prev = predecessor (prev);
+ return ev;
+ }
+
+ public int previousIndex() {
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ return nextIndex - 1;
+ }
+
+ public void remove() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ if (lastRet == next) // Last call to previous
+ next = successor (next);
+ else { // Last call to next or no call
+ prev = predecessor (prev);
+ --nextIndex;
+ }
+ SplayTree.this.remove (lastRet);
+ lastRet = null;
+ ++expectedModCount;
+ }
+
+ public void set (Event ev) {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (lastRet == null)
+ throw new IllegalStateException();
+
+ Entry pred = predecessor (lastRet);
+ Entry succ = successor (lastRet);
+
+ if (pred != null && ev.compareTo(pred.event) < 0) {
+ ev.setTime (pred.event.time());
+ ev.setPriority (pred.event.priority());
+ }
+ if (succ != null && ev.compareTo(succ.event) > 0) {
+ ev.setTime (succ.event.time());
+ ev.setPriority (succ.event.priority());
+ }
+
+ lastRet.event = ev;
+ }
+ }
+
+ /**
+ * Creates a new entry object.
+ */
+ private Entry add (Event ev, Entry father) {
+ Entry e;
+ synchronized (SplayTree.class) {
+ if (free == null)
+ return new Entry (ev, null, null, father);
+
+ e = free;
+ free = free.right;
+ }
+ e.event = ev;
+ e.left = e.right = null;
+ e.father = father;
+ return e;
+ }
+
+
+ /*
+ Remonte a la racine une entree avec un splay de type "bottom-up",
+ qui prend pour parametre le noeud a remonter.
+
+ Fonctionnement du splay :
+ Jusqu'a ce que l'element devienne la racine, on fait :
+ - Si le pere de l'element est la racine, on fait la rotation
+ entre le pere et l'element (cas "zig").
+ - Si le pere de l'element et l'element ne sont pas des fils du
+ meme cote (gauche-droite ou droite-gauche), alors on fait
+ la rotation entre le pere et l'element (cas "zig-zag" simplifie).
+ - Si le pere de l'element et l'element sont des fils du meme cote
+ (gauche-gauche ou droite-droite), alors on fait la rotation
+ entre le grand-pere et le pere avant de faire la rotation entre
+ le pere et l'element (cas "zig-zig").
+ */
+ private void splay(Entry e)
+ {
+ boolean left;
+
+ Entry f; // = father le pere de l'element e
+ Entry gf; // = grandFather le grand-pere de l'element e
+ Entry ggf; // = grandGrandFather l'arriere-grand-pere de l'element e
+
+ while(e.father != null) {
+ f = e.father;
+ gf = f.father;
+ left = e == f.left;
+
+ if (left)
+ // cas du fils gauche
+ if (gf == null) {
+ // cas "zig", on fait la rotation de f (la racine) et e
+ f.father = e;
+ f.left = e.right;
+ if(f.left != null)
+ f.left.father = f;
+ e.right = f;
+ e.father = null;
+ }
+ else if (gf.right == f) {
+ // cas "zig-zag", simplifie, pareil que le cas "zig"
+ gf.right = e;
+
+ f.father = e;
+ f.left = e.right;
+ if(f.left != null)
+ f.left.father = f;
+ e.right = f;
+ e.father = gf;
+ }
+ else {
+ // cas "zig-zig", on fait la rotation de gf avec
+ // f, suivis de la rotation de e avec f
+ ggf = gf.father;
+
+ gf.left = f.right;
+ if(gf.left != null)
+ gf.left.father = gf;
+ f.right = gf;
+ gf.father = f;
+
+ f.left = e.right;
+ if(f.left != null)
+ f.left.father = f;
+ f.father = e;
+ e.right = f;
+
+ // on rattache e a son nouveau pere
+ e.father = ggf;
+ if(ggf != null)
+ if(ggf.left == gf)
+ ggf.left = e;
+ else
+ ggf.right = e;
+ }
+ else
+ //cas du fils droit
+ if(gf == null) {
+ // cas "zig", on fait la rotation de f (la racine) et e
+
+ f.father = e;
+ f.right = e.left;
+ if(f.right != null)
+ f.right.father = f;
+ e.left = f;
+ e.father = null;
+ }
+ else if(gf.left == f) {
+ // cas "zig-zag", simplifie, pareil que le cas "zig"
+ gf.left = e;
+
+ f.father = e;
+ f.right = e.left;
+ if(f.right != null)
+ f.right.father = f;
+ e.left = f;
+ e.father = gf;
+ }
+ else {
+ // cas "zig-zig", on fait la rotation de gf avec
+ // f, suivis de la rotation de e avec f
+ ggf = gf.father;
+
+ gf.right = f.left;
+ if(gf.right != null)
+ gf.right.father = gf;
+ f.left = gf;
+ gf.father = f;
+
+ f.right = e.left;
+ if(f.right != null)
+ f.right.father = f;
+ f.father = e;
+ e.left = f;
+
+ // on rattache e a son nouveau pere
+ e.father = ggf;
+ if(ggf != null)
+ if(ggf.left == gf)
+ ggf.left = e;
+ else
+ ggf.right = e;
+ }
+ }
+ }
+
+
+ /**
+ Enleve l'entree e de l'arbre.
+
+ Procedure :
+ On fait d'abord un splay sur l'entree a enlever pour la faire monter
+ a la racine. Une fois a la racine, on l'enleve. On cherche ensuite
+ l'element minimal du sous-arbre droit (qui contient les elements
+ qui sont plus grand que e) et, avec un splay, on le remonte a la
+ racine de son sous-arbre. Cet element, puisqu'il est le minimum de
+ son sous-arbre, n'a pas de fils gauche. On lui rattache donc comme
+ fils gauche le sous-arbre gauche.
+
+ La raison de cette procedure plus compliquee que l'equivalent dans
+ un arbre binaire ordinaire est que, pour que la garantie de
+ temps d'acces amorti en O(log n) du splay tree s'applique, il
+ faut que chaque acces (qui necessite une recherche) d'un element
+ s'accompagne du splay de cet element a la racine.
+ */
+ private boolean remove(Entry e)
+ {
+ if (root == null || e == null)
+ return false;
+
+ splay(e);
+ // e est implicitement la racine, meme si root != e
+
+ Entry leftTree = e.left;
+ Entry rightTree = e.right;
+
+ if(leftTree != null)
+ leftTree.father = null;
+ if(rightTree != null)
+ rightTree.father = null;
+
+ // Recupere l'espace de l'avis d'evenement
+ e.left = null;
+ e.event = null;
+ synchronized (SplayTree.class) {
+ e.right = free;
+ free = e;
+ }
+
+
+ if(rightTree == null)
+ root = leftTree;
+ else if(leftTree == null)
+ root = rightTree;
+ else {
+ // on cherche le plus petit element du sous-arbre de droite
+ Entry newRoot = rightTree;
+ while(newRoot.left != null)
+ newRoot = newRoot.left;
+
+ // on monte newRoot en haut du sous-arbre de droite
+ splay(newRoot);
+
+ // on rattache les deux sous-arbres
+ newRoot.left = leftTree;
+ leftTree.father = newRoot;
+ root = newRoot;
+ }
+
+
+ ++modCount;
+
+ return true;
+ }
+
+
+ /**
+ * Finds the corresponding entry for an event.
+ */
+ private Entry findEntry (Event ev) {
+ Entry cursor = root;
+ while (cursor != null) {
+ if (cursor.event == ev)
+ return cursor;
+ else if (ev.compareTo(cursor.event) > 0)
+ cursor = cursor.right;
+ else if (ev.compareTo(cursor.event) < 0)
+ cursor = cursor.left;
+ else {
+ // on a trouve un element "egal" (compareTo() revoie 0 ), on ne peut donc plus
+ // utiliser la recherche binaire et il faut donc regarder
+ // tous les elements "egaux" qui se retrouvent soit apres,
+ // soit avant l'element trouve
+ Entry center = cursor;
+
+ // recherche avant center
+ while (cursor != null && ev.compareTo(cursor.event) == 0)
+ if(cursor.event == ev)
+ return cursor;
+ else
+ cursor = predecessor(cursor);
+
+ cursor = center;
+
+ // recherche apres center
+ while (cursor != null && ev.compareTo(cursor.event) == 0)
+ if(cursor.event == ev)
+ return cursor;
+ else
+ cursor = successor(cursor);
+
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private Entry successor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.right != null) {
+ cursor = cursor.right;
+ while (cursor.left != null)
+ cursor = cursor.left;
+ }
+ else {
+ while (cursor.father != null && cursor.father.right == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ private Entry predecessor (Entry cursor) {
+ if (cursor == null)
+ return null;
+
+ if (cursor.left != null) {
+ cursor = cursor.left;
+ while (cursor.right != null)
+ cursor = cursor.right;
+ }
+ else {
+
+ while (cursor.father != null && cursor.father.left == cursor)
+ cursor = cursor.father;
+ cursor = cursor.father;
+ }
+ return cursor;
+ }
+
+ /*
+ public static void main (String[] args) {
+ SplayTree sp = new SplayTree();
+ Event1 e1 = new Event1(); e1.setTime(10.0);
+ Event1 e2 = new Event1(); e2.setTime(20.0);
+ Event1 e3 = new Event1(); e3.setTime(30.0);
+ Event1 e4 = new Event1(); e4.setTime(40.0);
+ Event1 e5 = new Event1(); e5.setTime(50.0);
+ Event1 e6 = new Event1(); e6.setTime(60.0);
+ Event1 e7 = new Event1(); e7.setTime(70.0);
+ sp.add(e1);
+ sp.add(e2);
+ sp.print2(sp.root);
+ sp.add(e3);
+ sp.print2(sp.root);
+ sp.add(e4);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+ sp.add(e6);
+ sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+ sp.add(e7);
+ sp.print2(sp.root);
+ sp.add(e5);
+ sp.print2(sp.root);
+
+ sp.getFirst();
+ System.out.println(".....after Get" + PrintfFormat.NEWLINE +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ sp.remove(e3);
+ System.out.println("Apres remove" + PrintfFormat.NEWLINE);
+ sp.print2(sp.root);
+ }
+
+ private void print(Entry t) {
+ if (t != null){
+ print (t.left);
+ System.out.println ("===========> Event time "+t.event.time());
+ print (t.right);
+ }
+ }
+
+ private void print2(Entry t) {
+ System.out.println("=============================== "+
+ "print2 : pour ..... "+t.event.time());
+ if (t != null) {
+ System.out.println ("===========> ev time "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void gauche (Entry t) {
+ if (t != null) {
+ System.out.println ("===========> gauche "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ System.out.println();
+ }
+ else
+ System.out.println ("===========> gauche null ");
+ }
+
+ private void droite (Entry t) {
+ if (t != null){
+ System.out.println ("===========> droite "+t.event.time());
+ gauche (t.left);
+ droite (t.right);
+ // System.out.println();
+ }
+ else
+ System.out.println ("===========> droite null ");
+ }
+
+ private static class Event1 extends Event {
+ public void actions() {}
+
+ public String toString() {
+ return "Event(" + eventTime + ")";
+ }
+ }
+*/
+}\end{hide}
+\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/simevents/eventlist/overview.tex b/source/umontreal/iro/lecuyer/simevents/eventlist/overview.tex
new file mode 100644
index 0000000..07dc9bd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/eventlist/overview.tex
@@ -0,0 +1,10 @@
+\latex{\section{overview of package \texttt{eventlist}}}
+
+This package provides different kinds of event list implementations.
+One can change the default
+\externalclass{umontreal.iro.lecuyer.simevents.eventlist}{SplayTree}
+event list implemntation via the method
+\clsexternalmethod{umontreal.iro.lecuyer.simevents}{Sim}{init}{umontreal.iro.lecuyer.simevents.eventlist.EventList}.
+
+\pierre{Should add the appropriate references for the different
+ event list implementations.}
diff --git a/source/umontreal/iro/lecuyer/simevents/guidesimevents.bbl b/source/umontreal/iro/lecuyer/simevents/guidesimevents.bbl
new file mode 100644
index 0000000..80605be
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/guidesimevents.bbl
@@ -0,0 +1,25 @@
+\begin{thebibliography}{1}
+
+\bibitem{sFIS01a}
+G.~S. Fishman.
+\newblock {\em Discrete Event Simulation: Modeling, Programming, and Analysis}.
+\newblock Springer Series in Operations Research. Springer-Verlag, New York,
+ NY, 2001.
+
+\bibitem{sKIN85a}
+J.~H. Kingston.
+\newblock Analysis of tree algorithms for the simulation event lists.
+\newblock {\em Acta Informatica}, 22:15--33, 1985.
+
+\bibitem{sKIN86a}
+J.~H. Kingston.
+\newblock Analysis of {H}enriksen's algorithm for the simulation event set.
+\newblock {\em SIAM Journal on Computing}, 15:887--902, 1986.
+
+\bibitem{iSLE85a}
+D.~D. Sleator and R.~E. Tarjan.
+\newblock Self-adjusting binary search trees.
+\newblock {\em Journal of the Association for Computing Machinery},
+ 32(3):652--686, 1985.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/simevents/guidesimevents.tex b/source/umontreal/iro/lecuyer/simevents/guidesimevents.tex
new file mode 100644
index 0000000..a03a337
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/guidesimevents.tex
@@ -0,0 +1,52 @@
+\documentclass [twoside,12pt]{article}
+\usepackage{amssymb}
+\usepackage{ssj}
+
+% This could confuse LaTeX2HTML
+%begin{latexonly}
+%%%%%%%%%%%%%%
+\def\fiverm {}
+\input prepictex.tex \input pictex.tex \input postpictex.tex
+\valuestolabelleading = -0.1\baselineskip
+%end{latexonly}
+
+% \dateheadtrue
+
+% \includeonly {exemples/Atelier}
+% \includeonly {Process}
+
+% \fulltrue %% Writes all the hidden code.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+\include{title}
+\pagenumbering{roman}
+\tableofcontents
+% \include{avantpro}
+%
+\pagenumbering{arabic}
+%\include{intro}
+\include{overview}
+
+%\include{events}
+\include{Sim}
+\include{Simulator}
+\include{Event}
+\include{Continuous}
+\include{ContinuousState}
+\include{ListWithStat}
+\include{LinkedListStat}
+\include{Accumulate}
+
+%\include{eventlists}
+\include{eventlist/EventList}
+\include{eventlist/DoublyLinked}
+\include{eventlist/SplayTree}
+\include{eventlist/BinaryTree}
+\include{eventlist/Henriksen}
+\include{eventlist/RedblackTree}
+
+\bibliography{simul,random,ift,stat,prob} %,temp2} % Dans texmac.
+\bibliographystyle{plain}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/simevents/overview.tex b/source/umontreal/iro/lecuyer/simevents/overview.tex
new file mode 100644
index 0000000..fcb308c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/overview.tex
@@ -0,0 +1,36 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+\latex{\label {sec:overview}}
+
+\begin {htmlonly}
+This package provides the simulation clock and tools to manage the
+future events list. These are the basic tools for discrete-event
+simulation. Several different implementations of the event list are
+offered. Some basic tools for continuous simulation (i.e., solving
+differential equations with respect to time) are also provided.
+\end {htmlonly}
+
+The scheduling part of discrete-event simulations is managed by
+the ``chief-executive'' class \externalclass{umontreal.iro.lecuyer.simevents}{Simulator},
+which contains the simulation clock and the central monitor.
+The event list is taken from one of the implementations of the
+interface \externalclass{umontreal.iro.lecuyer.simevents.eventlist}{EventList},
+which provide different kinds of
+event list implementations. One can change the default
+\externalclass{umontreal.iro.lecuyer.simevents.eventlist}{SplayTree} event list
+implementation via the method
+\externalmethod{umontreal.iro.lecuyer.simevents}{Sim}{init}{EventList}.
+The class \externalclass{umontreal.iro.lecuyer.simevents}{Event} provides the facilities
+for creating and scheduling events in the simulation.
+Each type of event must be defined by extending the class
+\externalclass{umontreal.iro.lecuyer.simevents}{Event}.
+The class \externalclass{umontreal.iro.lecuyer.simevents}{Continuous}
+provides elementary tools for continuous simulation,
+where certain variables vary continuously in time according to ordinary
+differential equations.
+
+The class \externalclass{umontreal.iro.lecuyer.simevents}{LinkedListStat}
+ implements {\em doubly linked\/} lists,
+with tools for inserting, removing, and viewing objects in the list,
+and automatic statistical collection.
+These lists can contain any kind of \externalclass{java.lang}{Object}.
+
diff --git a/source/umontreal/iro/lecuyer/simevents/title.tex b/source/umontreal/iro/lecuyer/simevents/title.tex
new file mode 100644
index 0000000..2c0521a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simevents/title.tex
@@ -0,0 +1,44 @@
+\begin{titlepage}
+
+\iffalse %%%%%%
+
+Other potential names:
+
+ Simja, Simjava, Javasim, Javarand, Javaran, Javas
+ flexim, simflex, SIM-J
+
+ {\sc Pierre L'Ecuyer} %% and Lakhdar Meliani
+\footnote {\normalsize
+ Most of the implementation of SSJ was done by Lakhdar Meliani
+ for his master's thesis.} \\
+\medskip
+ D\'epartement d'Informatique et de Recherche Op\'erationnelle \\
+ Universit\'e de Montr\'eal \
+\vfill\vfill
+\end {center}
+
+\fi %%%%%%%%%%%%%%
+
+\title{simevents}{Simulation Clock and Event List Management}
+
+\begin {quote}
+This package provides the simulation clock and tools to manage the
+future events list. These are the basic tools for discrete-event
+simulation. Several different implementations of the event list are
+offered. Some basic tools for continuous simulation (i.e., solving
+differential equations with respect to time) are also provided.
+\end {quote}
+
+\begin {comment}
+SSJ (an acronym for {\em Stochastic Simulation in Java\/}) is
+a library of classes, implemented in the Java programming language,
+offering general-purpose facilities for simulation programming.
+It supports the event view, process view, continuous simulation,
+and arbitrary mixtures of these.
+% It provides random number generators, clock and event list management,
+% general list management facilities, etc.
+\end {comment}
+
+\vfill\vfill
+
+\end{titlepage}
diff --git a/source/umontreal/iro/lecuyer/simprocs/Bin.java b/source/umontreal/iro/lecuyer/simprocs/Bin.java
new file mode 100644
index 0000000..80089e3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/Bin.java
@@ -0,0 +1,420 @@
+
+
+/*
+ * Class: Bin
+ * Description: corresponds to a pile of identical tokens, and a list of
+ processes waiting for the tokens when the list is empty
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import java.util.ListIterator;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simevents.Accumulate;
+import umontreal.iro.lecuyer.simevents.LinkedListStat;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * A <TT>Bin</TT> corresponds to a pile of identical tokens, and a list of
+ * processes waiting for the tokens when the list is empty.
+ * It is a producer/consumer process synchronization device.
+ * Tokens can be added to the pile (i.e., <EM>produced</EM>) by the
+ * method {@link #put put}.
+ * A process can request tokens from the pile (i.e., <EM>consume</EM>)
+ * by calling {@link #take take}.
+ *
+ * <P>
+ * The behavior of a <TT>Bin</TT> is somewhat similar to that of a
+ * {@link Resource}. Each <TT>Bin</TT> has a single queue of waiting
+ * processes, with FIFO or LIFO service policy, and
+ * which can be accessed via the method {@link #waitList waitList}.
+ * This list actually contains objects of the class {@link UserRecord}.
+ * Each {@link UserRecord} points to a process
+ * and contains some additional information.
+ *
+ */
+public class Bin {
+
+ private static final int FIFO = 1;
+ private static final int LIFO = 2;
+
+ // Variables
+
+ private ProcessSimulator sim;
+ private String name;
+ private int available = 0;
+ private int policy = FIFO;
+ private LinkedListStat<UserRecord> waitingList;
+ private ListIterator<UserRecord> iter;
+ private Accumulate statAvail;
+ private boolean stats;
+ private double initStatTime;
+
+
+
+ /**
+ * Constructs a new bin, initially empty, with
+ * service policy FIFO and linked with the default simulator.
+ *
+ */
+ public Bin() {
+ this ("");
+ }
+
+
+ /**
+ * Constructs a new bin, initially empty, with
+ * service policy FIFO and linked with simulator sim.
+ *
+ * @param sim Simulator associated to the current variable.
+ *
+ *
+ */
+ public Bin(ProcessSimulator sim) {
+ this (sim, "");
+ }
+
+
+ /**
+ * Constructs a new bin, initially empty, with service policy FIFO,
+ * identifier <TT>name</TT> and linked with the default simulator.
+ *
+ * @param name name associated to the bin
+ *
+ *
+ */
+ public Bin (String name) {
+ try {
+ ProcessSimulator.initDefault();
+ this.sim = (ProcessSimulator)Simulator.getDefaultSimulator();
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ iter = waitingList.listIterator();
+ stats = false;
+ }
+ catch (ClassCastException e) {
+ throw new IllegalArgumentException("Wrong default Simulator type");
+ }
+ }
+
+
+ /**
+ * Constructs a new bin, initially empty, with
+ * service policy FIFO, identifier <TT>name</TT> and linked with simulator sim.
+ *
+ * @param sim Simulator associated to the current variable.
+ *
+ * @param name name associated to the bin
+ *
+ */
+ public Bin (ProcessSimulator sim, String name) {
+ this.sim = sim;
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ iter = waitingList.listIterator();
+ stats = false;
+ }
+
+
+ /**
+ * Reinitializes this bin by clearing up its pile of tokens and
+ * its waiting list.
+ * The processes in this list (if any) remain in the same states.
+ *
+ */
+ public void init() {
+ int oldAvail = available;
+ waitingList.clear();
+ available = 0;
+ if (stats) initStat();
+ }
+
+
+ /**
+ * Starts or stops collecting statistics on the list returned
+ * by {@link #waitList waitList} for this bin.
+ * If the statistical collection is turned ON, It also constructs (if not yet done)
+ * and initializes an additional statistical
+ * collector of class
+ * {@link umontreal.iro.lecuyer.simevents.Accumulate Accumulate}
+ * for this bin. This collector will be updated
+ * automatically. It can be accessed via {@link #statOnAvail statOnAvail}, and
+ * monitors the evolution of the
+ * available tokens of the bin
+ * as a function of time.
+ *
+ * @param b <TT>true</TT> to turn statistical collection ON, <TT>false</TT> to turn it OFF
+ *
+ *
+ */
+ public void setStatCollecting (boolean b) {
+ if (b) {
+ if (stats)
+ throw new IllegalStateException ("Already collecting statistics for this resource");
+ stats = true;
+ waitingList.setStatCollecting (true);
+ if (statAvail != null)
+ initStat();
+ else {
+ statAvail = new Accumulate (sim, "StatOnAvail");
+ statAvail.update (available);
+ }
+ }
+ else {
+ if (stats)
+ throw new IllegalStateException ("Not collecting statistics for this resource");
+ stats = false;
+ waitingList.setStatCollecting (false);
+ }
+ }
+
+
+ /**
+ * Reinitializes all the statistical collectors for this
+ * bin. These collectors must exist, i.e.,
+ * {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> must have been invoked earlier for
+ * this bin.
+ *
+ */
+ public void initStat() {
+ if (!stats) throw new IllegalStateException (
+ "Not collecting statistics for this resource");
+ statAvail.init();
+ statAvail.update (available);
+ waitingList.initStat();
+ initStatTime = sim.time();
+ }
+
+
+ /**
+ * Sets the service policy for ordering processes waiting
+ * for tokens on the bin to FIFO (<SPAN CLASS="textit">first in, first out</SPAN>):
+ * the processes are placed in the
+ * list (and served) according to their order of arrival.
+ *
+ */
+ public void setPolicyFIFO() {
+ policy = FIFO;
+ }
+
+
+ /**
+ * Sets the service policy for ordering processes waiting
+ * for tokens on the bin to LIFO (<SPAN CLASS="textit">last in, first out</SPAN>):
+ * the processes are placed in the
+ * list (and served) according to their inverse order of arrival:
+ * the last arrived are served first.
+ *
+ */
+ public void setPolicyLIFO() {
+ policy = LIFO;
+ }
+
+
+ /**
+ * Returns the number of available tokens for this bin.
+ *
+ * @return the number of available tokens
+ *
+ */
+ public int getAvailable() {
+ return available;
+ }
+
+
+ /**
+ * The executing process invoking this method requests
+ * <TT>n</TT> tokens from this bin. If enough tokens are available,
+ * the number of tokens in the bin is reduced by <TT>n</TT> and the
+ * process continues its execution.
+ * Otherwise, the executing process is placed into the
+ * {@link #waitList waitList} list (the queue) for this bin and is suspended
+ * until it can obtain the requested number of tokens.
+ *
+ * @param n number of requested tokens
+ *
+ *
+ */
+ public void take (int n) {
+ SimProcess p = sim.currentProcess();
+ if (n <= available) {
+ // The process gets the resource right away.
+ available -= n;
+ if (stats) {
+ statAvail.update (available);
+ waitingList.statSojourn().add (0.0);
+ }
+ }
+ else {
+ // Not enough units of the resource are available.
+ // The process joins the queue waitingList;
+ UserRecord record = new UserRecord (n, p, sim.time());
+ switch (policy) {
+ case FIFO : waitingList.addLast (record); break;
+ case LIFO : waitingList.addFirst (record); break;
+ default : throw new IllegalStateException (
+ "the policy must be LIFO or FIFO");
+ }
+ p.suspend();
+ }
+ }
+
+
+ /**
+ * Adds <TT>n</TT> tokens to this bin.
+ * If there are processes waiting for tokens from this bin and
+ * whose requests can now be satisfied, they obtain the tokens and
+ * resume their execution.
+ *
+ * @param n number of tokens to put into the bin
+ *
+ *
+ */
+ public void put (int n) {
+ available+=n;
+ if (stats) statAvail.update (available);
+ if (waitingList.size()>0) wakeProcess();
+ }
+
+ private void wakeProcess() {
+
+ UserRecord record;
+ ListIterator<UserRecord> iter = waitingList.listIterator();
+ while (iter.hasNext() && available > 0) {
+ record=iter.next();
+ if (!record.process.isAlive())
+ throw new IllegalStateException(
+ "process not alive");
+ // The thread for this process is still alive.
+
+ if (record.numUnits <= available) {
+ // This request can now be satisfied
+ record.process.resume();
+ available -= record.numUnits;
+ if (stats) statAvail.update (available);
+ iter.remove();
+ }
+ }
+ }
+
+
+ /**
+ * Returns the list of {@link UserRecord}
+ * for the processes waiting for tokens from this bin.
+ *
+ * @return the list of waiting process user records
+ *
+ */
+ public LinkedListStat waitList() {
+ return waitingList;
+ }
+
+
+ /**
+ * Returns the statistical collector for the available tokens
+ * on the bin as a function of time.
+ * This collector exists only if {@link #setStatCollecting setStatCollecting} <TT>(true)</TT>
+ * has been invoked previously.
+ *
+ * @return the probe for available tokens
+ *
+ */
+ public Accumulate statOnAvail() {
+ return statAvail;
+ }
+
+
+ /**
+ * Returns a string containing a complete statistical report on
+ * this bin. The method
+ * <BR>{@link #setStatCollecting setStatCollecting} <TT>(true)</TT> must have
+ * been invoked previously, otherwise no statistics will have been collected.
+ * The report contains statistics on the available tokens, queue size and
+ * waiting time for this bin.
+ *
+ * @return a statistical report for this bin, represented as a string
+ *
+ */
+ public String report() {
+
+ if (statAvail == null) throw new IllegalStateException ("Asking a report for a bin "
+ +"for which setStatCollecting (true) has not been called");
+
+ Accumulate sizeWait = waitingList.statSize();
+ Tally sojWait = waitingList.statSojourn();
+
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("REPORT ON BIN : ").append (name).append (PrintfFormat.NEWLINE);
+ str.append (" From time : ").append (7, 2, 2, initStatTime);
+ str.append (" to time : ");
+ str.append (10,2, 2, sim.time());
+ str.append (PrintfFormat.NEWLINE + " min max average ");
+ str.append ("standard dev. nb. obs.");
+
+ str.append (PrintfFormat.NEWLINE + " Available tokens ");
+ str.append (8, (int)(statAvail.min()+0.5));
+ str.append (11, (int)(statAvail.max()+0.5));
+ str.append (12, 3, 2, statAvail.average());
+
+ str.append (PrintfFormat.NEWLINE + " Queue Size ");
+ str.append (8, (int)(sizeWait.min()+0.5));
+ str.append (11, (int)(sizeWait.max()+0.5));
+ str.append (12, 3, 2, sizeWait.average());
+
+ str.append (PrintfFormat.NEWLINE + " Wait ");
+ str.append (12, 3, 2, sojWait.min()).append (' ');
+ str.append (10, 3, 2, sojWait.max()).append (' ');
+ str.append (11, 3, 2, sojWait.average()).append (' ');
+ str.append (10, 3, 2, sojWait.standardDeviation());
+ str.append (10, sojWait.numberObs());
+
+ return str.toString();
+ }
+
+
+ /**
+ * Returns the current simulator of this continuous-time variable.
+ *
+ * @return the current simulator of the variable
+ *
+ */
+ public Simulator simulator() {
+ return sim;
+ }
+
+
+ /**
+ * Set the current simulator of this continuous-time variable.
+ * This method must not be called while a simulator is running.
+ *
+ * @param sim the simulator of the current variable
+ *
+ *
+ */
+ public void setSimulator (ProcessSimulator sim) {
+ this.sim = sim;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/Bin.tex b/source/umontreal/iro/lecuyer/simprocs/Bin.tex
new file mode 100644
index 0000000..acc458c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/Bin.tex
@@ -0,0 +1,418 @@
+\defmodule {Bin}
+
+A \texttt{Bin} corresponds to a pile of identical tokens, and a list of
+processes waiting for the tokens when the list is empty.
+It is a producer/consumer process synchronization device.
+Tokens can be added to the pile (i.e., {\em produced\/}) by the
+method \method{put}{}.
+A process can request tokens from the pile (i.e., {\em consume\/})
+by calling \method{take}{}.
+
+The behavior of a \texttt{Bin} is somewhat similar to that of a
+\class{Resource}. Each \texttt{Bin} has a single queue of waiting
+processes, with FIFO or LIFO service policy, and
+which can be accessed via the method \method{waitList}{}.
+This list actually contains objects of the class \class{UserRecord}.
+Each \class{UserRecord} points to a process
+and contains some additional information.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Bin
+ * Description: corresponds to a pile of identical tokens, and a list of
+ processes waiting for the tokens when the list is empty
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;
+\begin{hide}
+import java.util.ListIterator;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simevents.Accumulate;
+import umontreal.iro.lecuyer.simevents.LinkedListStat;
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public class Bin \begin{hide} {
+
+ private static final int FIFO = 1;
+ private static final int LIFO = 2;
+
+ // Variables
+
+ private ProcessSimulator sim;
+ private String name;
+ private int available = 0;
+ private int policy = FIFO;
+ private LinkedListStat<UserRecord> waitingList;
+ private ListIterator<UserRecord> iter;
+ private Accumulate statAvail;
+ private boolean stats;
+ private double initStatTime;
+
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Bin() \begin{hide} {
+ this ("");
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new bin, initially empty, with
+ service policy FIFO and linked with the default simulator.
+ \end{tabb}
+\begin{code}
+
+ public Bin(ProcessSimulator sim) \begin{hide} {
+ this (sim, "");
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new bin, initially empty, with
+ service policy FIFO and linked with simulator sim.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{Simulator associated to the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public Bin (String name) \begin{hide} {
+ try {
+ ProcessSimulator.initDefault();
+ this.sim = (ProcessSimulator)Simulator.getDefaultSimulator();
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ iter = waitingList.listIterator();
+ stats = false;
+ }
+ catch (ClassCastException e) {
+ throw new IllegalArgumentException("Wrong default Simulator type");
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new bin, initially empty, with service policy FIFO,
+ identifier \texttt{name} and linked with the default simulator.
+ \end{tabb}
+\begin{htmlonly}
+ \param{name}{name associated to the bin}
+\end{htmlonly}
+\begin{code}
+
+ public Bin (ProcessSimulator sim, String name) \begin{hide} {
+ this.sim = sim;
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ iter = waitingList.listIterator();
+ stats = false;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new bin, initially empty, with
+ service policy FIFO, identifier \texttt{name} and linked with simulator sim.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{Simulator associated to the current variable.}
+ \param{name}{name associated to the bin}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void init() \begin{hide} {
+ int oldAvail = available;
+ waitingList.clear();
+ available = 0;
+ if (stats) initStat();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Reinitializes this bin by clearing up its pile of tokens and
+ its waiting list.
+ The processes in this list (if any) remain in the same states.
+ \end{tabb}
+\begin{code}
+
+ public void setStatCollecting (boolean b) \begin{hide} {
+ if (b) {
+ if (stats)
+ throw new IllegalStateException ("Already collecting statistics for this resource");
+ stats = true;
+ waitingList.setStatCollecting (true);
+ if (statAvail != null)
+ initStat();
+ else {
+ statAvail = new Accumulate (sim, "StatOnAvail");
+ statAvail.update (available);
+ }
+ }
+ else {
+ if (stats)
+ throw new IllegalStateException ("Not collecting statistics for this resource");
+ stats = false;
+ waitingList.setStatCollecting (false);
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Starts or stops collecting statistics on the list returned
+ by \method{waitList}{} for this bin.
+ If the statistical collection is turned ON, It also constructs (if not yet done)
+ and initializes an additional statistical
+ collector of class
+ \externalclass{umontreal.iro.lecuyer.simevents}{Accumulate}
+ for this bin. This collector will be updated
+ automatically. It can be accessed via \method{statOnAvail}{}, and
+ monitors the evolution of the
+ available tokens of the bin
+ as a function of time.
+ \end{tabb}
+\begin{htmlonly}
+ \param{b}{\texttt{true} to turn statistical collection ON, \texttt{false} to turn it OFF}
+\end{htmlonly}
+\begin{code}
+
+ public void initStat()\begin{hide} {
+ if (!stats) throw new IllegalStateException (
+ "Not collecting statistics for this resource");
+ statAvail.init();
+ statAvail.update (available);
+ waitingList.initStat();
+ initStatTime = sim.time();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Reinitializes all the statistical collectors for this
+ bin. These collectors must exist, i.e.,
+ \method{setStatCollecting}{}~\texttt{(true)} must have been invoked earlier for
+ this bin.
+ \end{tabb}
+\begin{code}
+
+ public void setPolicyFIFO()\begin{hide} {
+ policy = FIFO;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the service policy for ordering processes waiting
+ for tokens on the bin to FIFO (\emph{first in, first out}):
+ the processes are placed in the
+ list (and served) according to their order of arrival.
+ \end{tabb}
+\begin{code}
+
+ public void setPolicyLIFO()\begin{hide} {
+ policy = LIFO;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the service policy for ordering processes waiting
+ for tokens on the bin to LIFO (\emph{last in, first out}):
+ the processes are placed in the
+ list (and served) according to their inverse order of arrival:
+ the last arrived are served first.
+ \end{tabb}
+\begin{code}
+
+ public int getAvailable() \begin{hide} {
+ return available;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the number of available tokens for this bin.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the number of available tokens}
+\end{htmlonly}
+\begin{code}
+
+ public void take (int n) \begin{hide} {
+ SimProcess p = sim.currentProcess();
+ if (n <= available) {
+ // The process gets the resource right away.
+ available -= n;
+ if (stats) {
+ statAvail.update (available);
+ waitingList.statSojourn().add (0.0);
+ }
+ }
+ else {
+ // Not enough units of the resource are available.
+ // The process joins the queue waitingList;
+ UserRecord record = new UserRecord (n, p, sim.time());
+ switch (policy) {
+ case FIFO : waitingList.addLast (record); break;
+ case LIFO : waitingList.addFirst (record); break;
+ default : throw new IllegalStateException (
+ "the policy must be LIFO or FIFO");
+ }
+ p.suspend();
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} The executing process invoking this method requests
+ \texttt{n} tokens from this bin. If enough tokens are available,
+ the number of tokens in the bin is reduced by \texttt{n} and the
+ process continues its execution.
+ Otherwise, the executing process is placed into the
+ \method{waitList}{} list (the queue) for this bin and is suspended
+ until it can obtain the requested number of tokens.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{number of requested tokens}
+\end{htmlonly}
+\begin{code}
+
+ public void put (int n) \begin{hide} {
+ available+=n;
+ if (stats) statAvail.update (available);
+ if (waitingList.size()>0) wakeProcess();
+ }
+
+ private void wakeProcess() {
+
+ UserRecord record;
+ ListIterator<UserRecord> iter = waitingList.listIterator();
+ while (iter.hasNext() && available > 0) {
+ record=iter.next();
+ if (!record.process.isAlive())
+ throw new IllegalStateException(
+ "process not alive");
+ // The thread for this process is still alive.
+
+ if (record.numUnits <= available) {
+ // This request can now be satisfied
+ record.process.resume();
+ available -= record.numUnits;
+ if (stats) statAvail.update (available);
+ iter.remove();
+ }
+ }
+ } \end{hide}
+\end{code}
+ \begin{tabb} Adds \texttt{n} tokens to this bin.
+ If there are processes waiting for tokens from this bin and
+ whose requests can now be satisfied, they obtain the tokens and
+ resume their execution.
+ \end{tabb}
+\begin{htmlonly}
+ \param{n}{number of tokens to put into the bin}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat waitList() \begin{hide} {
+ return waitingList;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the list of \class{UserRecord}
+ for the processes waiting for tokens from this bin.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the list of waiting process user records}
+\end{htmlonly}
+\begin{code}
+
+ public Accumulate statOnAvail() \begin{hide} {
+ return statAvail;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the statistical collector for the available tokens
+ on the bin as a function of time.
+ This collector exists only if \method{setStatCollecting}{}~\texttt{(true)}
+ has been invoked previously.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the probe for available tokens}
+\end{htmlonly}
+\begin{code}
+
+ public String report() \begin{hide} {
+
+ if (statAvail == null) throw new IllegalStateException ("Asking a report for a bin "
+ +"for which setStatCollecting (true) has not been called");
+
+ Accumulate sizeWait = waitingList.statSize();
+ Tally sojWait = waitingList.statSojourn();
+
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("REPORT ON BIN : ").append (name).append (PrintfFormat.NEWLINE);
+ str.append (" From time : ").append (7, 2, 2, initStatTime);
+ str.append (" to time : ");
+ str.append (10,2, 2, sim.time());
+ str.append (PrintfFormat.NEWLINE + " min max average ");
+ str.append ("standard dev. nb. obs.");
+
+ str.append (PrintfFormat.NEWLINE + " Available tokens ");
+ str.append (8, (int)(statAvail.min()+0.5));
+ str.append (11, (int)(statAvail.max()+0.5));
+ str.append (12, 3, 2, statAvail.average());
+
+ str.append (PrintfFormat.NEWLINE + " Queue Size ");
+ str.append (8, (int)(sizeWait.min()+0.5));
+ str.append (11, (int)(sizeWait.max()+0.5));
+ str.append (12, 3, 2, sizeWait.average());
+
+ str.append (PrintfFormat.NEWLINE + " Wait ");
+ str.append (12, 3, 2, sojWait.min()).append (' ');
+ str.append (10, 3, 2, sojWait.max()).append (' ');
+ str.append (11, 3, 2, sojWait.average()).append (' ');
+ str.append (10, 3, 2, sojWait.standardDeviation());
+ str.append (10, sojWait.numberObs());
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a string containing a complete statistical report on
+ this bin. The method \\ \method{setStatCollecting}{}~\texttt{(true)} must have
+ been invoked previously, otherwise no statistics will have been collected.
+ The report contains statistics on the available tokens, queue size and
+ waiting time for this bin.
+ \end{tabb}
+\begin{htmlonly}
+ \return{a statistical report for this bin, represented as a string}
+\end{htmlonly}
+\begin{code}
+
+ public Simulator simulator() \begin{hide} {
+ return sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the current simulator of this continuous-time variable.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the current simulator of the variable}
+\end{htmlonly}
+\begin{code}
+
+ public void setSimulator (ProcessSimulator sim) \begin{hide} {
+ this.sim = sim;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Set the current simulator of this continuous-time variable.
+ This method must not be called while a simulator is running.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{the simulator of the current variable}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simprocs/Condition.java b/source/umontreal/iro/lecuyer/simprocs/Condition.java
new file mode 100644
index 0000000..ec7197e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/Condition.java
@@ -0,0 +1,248 @@
+
+
+/*
+ * Class: Condition
+ * Description: boolean indicator with a list of processes waiting for
+ the indicator to be true
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+import java.util.Observable;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simevents.LinkedListStat;
+
+/**
+ * A <TT>Condition</TT> is a boolean indicator, with a list of processes
+ * waiting for the indicator to be <TT>true</TT> (when it is <TT>false</TT>).
+ * A process calling {@link #waitFor waitFor} on a condition that is currently
+ * <TT>false</TT> is suspended until the condition becomes <TT>true</TT>.
+ * The list of waiting processes can be accessed via {@link #waitList waitList}.
+ *
+ */
+public class Condition extends Observable {
+
+ private String name;
+ private LinkedListStat<UserRecord> waitingList;
+ private boolean state;
+ private boolean broadcasting;
+ private ProcessSimulator sim;
+
+
+ /**
+ * Constructs a new <TT>Condition</TT> with initial value <TT>val</TT>, linked with the default simulator.
+ *
+ * @param val initial state of the condition
+ *
+ *
+ */
+ public Condition (boolean val) {
+ this (val, "");
+ }
+
+
+ /**
+ * Constructs a new <TT>Condition</TT> with initial value <TT>val</TT>, linked with simulator <TT>sim</TT>.
+ *
+ * @param sim current simulator of the variable
+ *
+ * @param val initial state of the condition
+ *
+ *
+ */
+ public Condition (ProcessSimulator sim, boolean val) {
+ this (sim, val, "");
+ }
+
+
+ /**
+ * Constructs a new <TT>Condition</TT> with initial value <TT>val</TT>,
+ * identifier <TT>name</TT> and linked with the default simulator.
+ *
+ * @param val initial state of the condition
+ *
+ * @param name identifier or name for the condition
+ *
+ *
+ */
+ public Condition (boolean val, String name) {
+ super();
+ try {
+ ProcessSimulator.initDefault();
+ this.sim = (ProcessSimulator)Simulator.getDefaultSimulator();
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ broadcasting = false;
+ state = val;
+ }
+ catch (ClassCastException e) {
+ throw new IllegalArgumentException("Wrong default Simulator type");
+ }
+ }
+
+
+ /**
+ * Constructs a new <TT>Condition</TT> with initial value <TT>val</TT>,
+ * identifier <TT>name</TT> and linked with simulator <TT>sim</TT>.
+ *
+ * @param sim current simulator of the variable
+ *
+ * @param val initial state of the condition
+ *
+ * @param name identifier or name for the condition
+ *
+ */
+ public Condition (ProcessSimulator sim, boolean val, String name) {
+ super();
+ this.sim = sim;
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ broadcasting = false;
+ state = val;
+ }
+
+
+ /**
+ * Reinitializes this <TT>Condition</TT> by clearing up
+ * its waiting list and resetting its state to <TT>val</TT>.
+ * The processes in this list (if any) remain in the same states.
+ *
+ * @param val initial state of the condition
+ *
+ *
+ */
+ public void init (boolean val) {
+ waitingList.clear();
+ state = val;
+ if (broadcasting) {
+ setChanged();
+ notifyObservers (new Boolean (state));
+ }
+ }
+
+
+ /**
+ * Sets the condition to <TT>val</TT>.
+ * If <TT>val</TT> is <TT>true</TT>, all the processes waiting
+ * for this condition now resume their execution, in the same order as
+ * they have called {@link #waitFor waitFor} for this condition.
+ * (Note that a process can never wait for more than one condition at
+ * a time, because it cannot call {@link #waitFor waitFor} while it is suspended.
+ *
+ * @param val new state of the condition
+ *
+ *
+ */
+ public void set (boolean val) {
+ state = val;
+ if (state) {
+ UserRecord record;
+ while (!waitingList.isEmpty()) {
+ record= waitingList.removeLast();
+ record.process.resume();
+ }
+ }
+ if (broadcasting) {
+ setChanged();
+ notifyObservers (new Boolean (val));
+ }
+ }
+
+
+ /**
+ * Returns the state (<TT>true</TT> or <TT>false</TT>) of the condition.
+ *
+ * @return the current state of the condition
+ *
+ */
+ public boolean state() {
+ return state;
+ }
+
+
+ /**
+ * The executing process invoking this method
+ * must wait for this condition to be <TT>true</TT>.
+ * If it is already <TT>true</TT>, the
+ * process continues its execution immediately
+ * Otherwise, the executing process is placed into the
+ * {@link #waitList waitList} list for this condition and is suspended
+ * until the condition becomes <TT>true</TT>.
+ *
+ */
+ public void waitFor() {
+ SimProcess p = sim.currentProcess();
+ if (state == true) {
+ // The process can continus right away.
+ if (waitingList.statSojourn() != null)
+ waitingList.statSojourn().add (0.0);
+ return;
+ }
+ else {
+ // The process joins the waitingList;
+ UserRecord record = new UserRecord (1, p, sim.time());
+ waitingList.addLast (record);
+ p.suspend();
+ }
+ }
+
+
+ /**
+ * Returns the list of {@link UserRecord}
+ * for the processes waiting for this condition.
+ *
+ * @return the list of processes user records waiting for the condition
+ *
+ */
+ public LinkedListStat waitList() {
+ return waitingList;
+ }
+
+
+ /**
+ * Returns the name (or identifier) associated
+ * to this condition.
+ *
+ * @return the name associated to this object
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+
+ /**
+ * Instructs the condition to start or stop observation broadcasting.
+ * When this is turned ON, a {@link Boolean} observation is notified to
+ * registered observers when the state of the condition changes. This
+ * boolean gives the new state of the condition.
+ *
+ * <P>
+ * Warning: Since this can reduce program performance, this should be
+ * turned on only when there are registered observers.
+ *
+ * @param b <TT>true</TT> to turn broadcasting ON, <TT>false</TT> to turn it OFF
+ *
+ */
+ public void setBroadcasting (boolean b) {
+ broadcasting = b;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/Condition.tex b/source/umontreal/iro/lecuyer/simprocs/Condition.tex
new file mode 100644
index 0000000..152ca1e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/Condition.tex
@@ -0,0 +1,247 @@
+\defmodule {Condition}
+
+A \texttt{Condition} is a boolean indicator, with a list of processes
+waiting for the indicator to be \texttt{true} (when it is \texttt{false}).
+A process calling \method{waitFor}{} on a condition that is currently
+\texttt{false} is suspended until the condition becomes \texttt{true}.
+The list of waiting processes can be accessed via \method{waitList}{}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Condition
+ * Description: boolean indicator with a list of processes waiting for
+ the indicator to be true
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;\begin{hide}
+import java.util.Observable;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simevents.LinkedListStat;\end{hide}
+
+public class Condition extends Observable \begin{hide} {
+
+ private String name;
+ private LinkedListStat<UserRecord> waitingList;
+ private boolean state;
+ private boolean broadcasting;
+ private ProcessSimulator sim;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Condition (boolean val) \begin{hide} {
+ this (val, "");
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new \texttt{Condition} with initial value \texttt{val}, linked with the default simulator.
+ \end{tabb}
+\begin{htmlonly}
+ \param{val}{initial state of the condition}
+\end{htmlonly}
+\begin{code}
+
+ public Condition (ProcessSimulator sim, boolean val) \begin{hide} {
+ this (sim, val, "");
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new \texttt{Condition} with initial value \texttt{val}, linked with simulator \texttt{sim}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{current simulator of the variable}
+ \param{val}{initial state of the condition}
+\end{htmlonly}
+\begin{code}
+
+ public Condition (boolean val, String name) \begin{hide} {
+ super();
+ try {
+ ProcessSimulator.initDefault();
+ this.sim = (ProcessSimulator)Simulator.getDefaultSimulator();
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ broadcasting = false;
+ state = val;
+ }
+ catch (ClassCastException e) {
+ throw new IllegalArgumentException("Wrong default Simulator type");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{Condition} with initial value \texttt{val},
+ identifier \texttt{name} and linked with the default simulator.
+\end{tabb}
+\begin{htmlonly}
+ \param{val}{initial state of the condition}
+ \param{name}{identifier or name for the condition}
+\end{htmlonly}
+\begin{code}
+
+ public Condition (ProcessSimulator sim, boolean val, String name) \begin{hide} {
+ super();
+ this.sim = sim;
+ waitingList = new LinkedListStat<UserRecord>(sim);
+ this.name = name;
+ broadcasting = false;
+ state = val;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new \texttt{Condition} with initial value \texttt{val},
+ identifier \texttt{name} and linked with simulator \texttt{sim}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{sim}{current simulator of the variable}
+ \param{val}{initial state of the condition}
+ \param{name}{identifier or name for the condition}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void init (boolean val) \begin{hide} {
+ waitingList.clear();
+ state = val;
+ if (broadcasting) {
+ setChanged();
+ notifyObservers (new Boolean (state));
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Reinitializes this \texttt{Condition} by clearing up
+ its waiting list and resetting its state to \texttt{val}.
+ The processes in this list (if any) remain in the same states.
+ \end{tabb}
+\begin{htmlonly}
+ \param{val}{initial state of the condition}
+\end{htmlonly}
+\begin{code}
+
+ public void set (boolean val) \begin{hide} {
+ state = val;
+ if (state) {
+ UserRecord record;
+ while (!waitingList.isEmpty()) {
+ record= waitingList.removeLast();
+ record.process.resume();
+ }
+ }
+ if (broadcasting) {
+ setChanged();
+ notifyObservers (new Boolean (val));
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Sets the condition to \texttt{val}.
+ If \texttt{val} is \texttt{true}, all the processes waiting
+ for this condition now resume their execution, in the same order as
+ they have called \method{waitFor}{} for this condition.
+ (Note that a process can never wait for more than one condition at
+ a time, because it cannot call \method{waitFor}{} while it is suspended.
+ \end{tabb}
+\begin{htmlonly}
+ \param{val}{new state of the condition}
+\end{htmlonly}
+\begin{code}
+
+ public boolean state() \begin{hide} {
+ return state;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the state (\texttt{true} or \texttt{false}) of the condition.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the current state of the condition}
+\end{htmlonly}
+\begin{code}
+
+ public void waitFor() \begin{hide} {
+ SimProcess p = sim.currentProcess();
+ if (state == true) {
+ // The process can continus right away.
+ if (waitingList.statSojourn() != null)
+ waitingList.statSojourn().add (0.0);
+ return;
+ }
+ else {
+ // The process joins the waitingList;
+ UserRecord record = new UserRecord (1, p, sim.time());
+ waitingList.addLast (record);
+ p.suspend();
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} The executing process invoking this method
+ must wait for this condition to be \texttt{true}.
+ If it is already \texttt{true}, the
+ process continues its execution immediately
+ Otherwise, the executing process is placed into the
+ \method{waitList}{} list for this condition and is suspended
+ until the condition becomes \texttt{true}.
+ \end{tabb}
+\begin{code}
+
+ public LinkedListStat waitList() \begin{hide} {
+ return waitingList;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the list of \class{UserRecord}
+ for the processes waiting for this condition.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the list of processes user records waiting for the condition}
+\end{htmlonly}
+\begin{code}
+
+ public String getName()\begin{hide} {
+ return name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the name (or identifier) associated
+ to this condition.
+\end{tabb}
+\begin{htmlonly}
+ \return{the name associated to this object}
+\end{htmlonly}
+\begin{code}
+
+ public void setBroadcasting (boolean b)\begin{hide} {
+ broadcasting = b;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Instructs the condition to start or stop observation broadcasting.
+ When this is turned ON, a \class{Boolean} observation is notified to
+ registered observers when the state of the condition changes. This
+ boolean gives the new state of the condition.
+
+Warning: Since this can reduce program performance, this should be
+turned on only when there are registered observers.
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{\texttt{true} to turn broadcasting ON, \texttt{false} to turn it OFF}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/simprocs/DSOLProcessSimulator.java b/source/umontreal/iro/lecuyer/simprocs/DSOLProcessSimulator.java
new file mode 100644
index 0000000..d315cf0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/DSOLProcessSimulator.java
@@ -0,0 +1,210 @@
+
+
+/*
+ * Class: DSOLProcessSimulator
+ * Description: simulation process whose actions method is interpreted by
+ the DSOL interpreter
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import nl.tudelft.simulation.dsol.interpreter.process.Process;
+
+/*
+ * (c) copyright 2004 <a href="http://www.simulation.tudelft.nl/dsol/">Delft
+ * University of Technology </a>, the Netherlands. <br>
+ * See for project information <a href="http://www.simulation.tudelft.nl/dsol/">
+ * www.simulation.tudelft.nl/dsol </a> <br>
+ * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
+ * License (GPL) </a>, no warranty <br>
+ *
+ * @author <a href="http://www.tbm.tudelft.nl/webstaf/peterja/index.htm"> Peter
+ * Jacobs </a>
+ * @version $Revision$ $Date$
+ * @since 1.2
+ */
+
+
+/**
+ * Represents a simulation process whose <TT>actions</TT> method is
+ * interpreted by the DSOL interpreter, written by
+ * Peter Jacobs (<TT><A NAME="tex2html1"
+ * HREF="http://www.tbm.tudelft.nl/webstaf/peterja/index.htm">http://www.tbm.tudelft.nl/webstaf/peterja/index.htm</A></TT>).
+ * When a process executes, a virtual machine
+ * implemented in Java is invoked to interpret the byte-code.
+ * The processes are then simulated in a single Java thread, which allows
+ * a much larger number of threads than when each process has its own
+ * native thread, at the expense of a slower execution time.
+ *
+ */
+public class DSOLProcessSimulator extends ProcessSimulator {
+
+
+
+ /**
+ * Constructs a new DSOLProcessSimulator variable without initialization.
+ *
+ */
+ public DSOLProcessSimulator() {
+ }
+
+
+ /**
+ * Initializes the process-driven simulation. Calls super.init().
+ *
+ */
+ public void init() {
+ super.init();
+ }
+
+
+ /**
+ * Initializes the simulation and sets the given event list <TT>evlist</TT>
+ * to be used by the simulation executive. Calls super.init(evlist).
+ *
+ * @param evlist selected event list implementation
+ *
+ *
+ */
+ public void init (EventList evlist) {
+ super.init (evlist);
+ }
+
+ public ResumeEvent createControlEvent (SimProcess process) {
+ return new ResumeEvent (this, new InterpretedThread (process));
+ }
+
+ public void delay (SimProcess process, double delay) {
+ if (process.scheduledEvent() == null) // DEAD state
+ throw new IllegalStateException
+ ("Calling delay() for a dead process");
+ if (currentProcess != process)
+ throw new IllegalStateException
+ ("Calling delay() for a process not in EXECUTING state");
+ if (delay < 0.0)
+ throw new IllegalArgumentException
+ ("Calling delay() with negative delay");
+ process.scheduledEvent().schedule (delay);
+ process.suspend();
+ }
+
+ public void suspend(SimProcess process) {
+ if (process.scheduledEvent() == null) // DEAD state
+ throw new IllegalStateException
+ ("Calling suspend() for a dead process");
+ if (currentProcess == process) {
+ ((ResumeEvent)process.scheduledEvent()).target().suspend(); /// attention, pas le meme process
+ return;
+ }
+
+ if (process.scheduledEvent().time() >= 0.0) {
+ process.scheduledEvent().cancel();
+ process.scheduledEvent().setTime (SimProcess.WAITING);
+ return;
+ }
+
+ if (process.scheduledEvent().time() == SimProcess.STARTING)
+ // INITIAL state
+ throw new IllegalStateException
+ ("Calling suspend() for a process in INITIAL state");
+ // SUSPENDED state
+ throw new IllegalStateException
+ ("Calling suspend() for a suspended process");
+ }
+
+ public void kill(SimProcess process) {
+ if (process.scheduledEvent() == null)
+ throw new IllegalStateException
+ ("Calling kill() on a DEAD process");
+ if (currentProcess == process) {
+ process.setScheduledEvent(null);
+ InterpretedThread pr = ((ResumeEvent)process.scheduledEvent()).target();
+ ((ResumeEvent)process.scheduledEvent()).setTarget(null);
+ pr.suspend();
+ return;
+ }
+
+ if (process.scheduledEvent().time() >= 0.0)
+ process.scheduledEvent().cancel();
+
+ process.setScheduledEvent(null);
+ ((ResumeEvent)process.scheduledEvent()).setTarget(null);
+ }
+
+ public void killAll() {}
+
+
+}
+
+ final class ResumeEvent extends Event {
+ // the process to resume */
+ private InterpretedThread target = null;
+
+ public ResumeEvent (DSOLProcessSimulator sim, final InterpretedThread target) {
+ super(sim);
+ this.target = target;
+ setTime (SimProcess.STARTING);
+ }
+
+ public InterpretedThread target() {
+ return target;
+ }
+
+ public void setTarget(InterpretedThread target) {
+ this.target = target;
+ }
+
+ public void actions() {
+ ((ProcessSimulator)sim).setCurrentProcess(target.getProcess());
+ target.resume();
+ }
+
+
+ public String toString() {
+ // To get something useful when printing the event list
+ return "Start or resume process ";
+ }
+ }
+
+
+// The InterpretedThread is used by SSJ as basis for the DSOL SimProcess.
+final class InterpretedThread extends Process {
+ // the process to execute
+ private SimProcess simProcess = null;
+
+ public InterpretedThread(final SimProcess simProcess) {
+ this.simProcess = simProcess;
+ }
+
+ public SimProcess getProcess() {
+ return simProcess;
+ }
+
+ public void process() {
+ simProcess.actions();
+ simProcess.setScheduledEvent (null);
+ //((ResumeEvent)simProcess.scheduledEvent()).setTarget( null );
+ simProcess = null;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/DSOLProcessSimulator.tex b/source/umontreal/iro/lecuyer/simprocs/DSOLProcessSimulator.tex
new file mode 100644
index 0000000..f492671
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/DSOLProcessSimulator.tex
@@ -0,0 +1,217 @@
+\defmodule{DSOLProcessSimulator}
+
+Represents a simulation process whose \texttt{actions} method is
+interpreted by the DSOL interpreter \cite{iJAC05a}, written by
+Peter Jacobs (\url{http://www.tbm.tudelft.nl/webstaf/peterja/index.htm}).
+When a process executes, a virtual machine
+implemented in Java is invoked to interpret the byte-code.
+The processes are then simulated in a single Java thread, which allows
+a much larger number of threads than when each process has its own
+native thread, at the expense of a slower execution time.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DSOLProcessSimulator
+ * Description: simulation process whose actions method is interpreted by
+ the DSOL interpreter
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;\begin{hide}
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import nl.tudelft.simulation.dsol.interpreter.process.Process;
+
+/*
+ * (c) copyright 2004 <a href="http://www.simulation.tudelft.nl/dsol/">Delft
+ * University of Technology </a>, the Netherlands. <br>
+ * See for project information <a href="http://www.simulation.tudelft.nl/dsol/">
+ * www.simulation.tudelft.nl/dsol </a> <br>
+ * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
+ * License (GPL) </a>, no warranty <br>
+ *
+ * @author <a href="http://www.tbm.tudelft.nl/webstaf/peterja/index.htm"> Peter
+ * Jacobs </a>
+ * @version $Revision$ $Date$
+ * @since 1.2
+ */ \end{hide}
+
+
+public class DSOLProcessSimulator extends ProcessSimulator\begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public DSOLProcessSimulator() \begin{hide} {
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new DSOLProcessSimulator variable without initialization.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void init()\begin{hide} {
+ super.init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Initializes the process-driven simulation. Calls super.init().
+\end{tabb}
+\begin{code}
+
+ public void init (EventList evlist)\begin{hide} {
+ super.init (evlist);
+ }\end{hide}
+\end{code}
+\begin{tabb} Initializes the simulation and sets the given event list \texttt{evlist}
+ to be used by the simulation executive. Calls super.init(evlist).
+\end{tabb}
+\begin{htmlonly}
+ \param{evlist}{selected event list implementation}
+\end{htmlonly}
+\begin{code}\begin{hide}
+ public ResumeEvent createControlEvent (SimProcess process) {
+ return new ResumeEvent (this, new InterpretedThread (process));
+ }
+
+ public void delay (SimProcess process, double delay) {
+ if (process.scheduledEvent() == null) // DEAD state
+ throw new IllegalStateException
+ ("Calling delay() for a dead process");
+ if (currentProcess != process)
+ throw new IllegalStateException
+ ("Calling delay() for a process not in EXECUTING state");
+ if (delay < 0.0)
+ throw new IllegalArgumentException
+ ("Calling delay() with negative delay");
+ process.scheduledEvent().schedule (delay);
+ process.suspend();
+ }
+
+ public void suspend(SimProcess process) {
+ if (process.scheduledEvent() == null) // DEAD state
+ throw new IllegalStateException
+ ("Calling suspend() for a dead process");
+ if (currentProcess == process) {
+ ((ResumeEvent)process.scheduledEvent()).target().suspend(); /// attention, pas le meme process
+ return;
+ }
+
+ if (process.scheduledEvent().time() >= 0.0) {
+ process.scheduledEvent().cancel();
+ process.scheduledEvent().setTime (SimProcess.WAITING);
+ return;
+ }
+
+ if (process.scheduledEvent().time() == SimProcess.STARTING)
+ // INITIAL state
+ throw new IllegalStateException
+ ("Calling suspend() for a process in INITIAL state");
+ // SUSPENDED state
+ throw new IllegalStateException
+ ("Calling suspend() for a suspended process");
+ }
+
+ public void kill(SimProcess process) {
+ if (process.scheduledEvent() == null)
+ throw new IllegalStateException
+ ("Calling kill() on a DEAD process");
+ if (currentProcess == process) {
+ process.setScheduledEvent(null);
+ InterpretedThread pr = ((ResumeEvent)process.scheduledEvent()).target();
+ ((ResumeEvent)process.scheduledEvent()).setTarget(null);
+ pr.suspend();
+ return;
+ }
+
+ if (process.scheduledEvent().time() >= 0.0)
+ process.scheduledEvent().cancel();
+
+ process.setScheduledEvent(null);
+ ((ResumeEvent)process.scheduledEvent()).setTarget(null);
+ }
+
+ public void killAll() {}
+
+
+}
+
+ final class ResumeEvent extends Event {
+ // the process to resume */
+ private InterpretedThread target = null;
+
+ public ResumeEvent (DSOLProcessSimulator sim, final InterpretedThread target) {
+ super(sim);
+ this.target = target;
+ setTime (SimProcess.STARTING);
+ }
+
+ public InterpretedThread target() {
+ return target;
+ }
+
+ public void setTarget(InterpretedThread target) {
+ this.target = target;
+ }
+
+ public void actions() {
+ ((ProcessSimulator)sim).setCurrentProcess(target.getProcess());
+ target.resume();
+ }
+
+
+ public String toString() {
+ // To get something useful when printing the event list
+ return "Start or resume process ";
+ }
+ }
+
+
+// The InterpretedThread is used by SSJ as basis for the DSOL SimProcess.
+final class InterpretedThread extends Process {
+ // the process to execute
+ private SimProcess simProcess = null;
+
+ public InterpretedThread(final SimProcess simProcess) {
+ this.simProcess = simProcess;
+ }
+
+ public SimProcess getProcess() {
+ return simProcess;
+ }
+
+ public void process() {
+ simProcess.actions();
+ simProcess.setScheduledEvent (null);
+ //((ResumeEvent)simProcess.scheduledEvent()).setTarget( null );
+ simProcess = null;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simprocs/ProcessSimulator.java b/source/umontreal/iro/lecuyer/simprocs/ProcessSimulator.java
new file mode 100644
index 0000000..c987c2b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/ProcessSimulator.java
@@ -0,0 +1,205 @@
+
+
+
+/*
+ * Class: ProcessSimulator
+ * Description: Special type of simulator capable of managing processes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.Simulator;
+
+
+/**
+ * Defines a special type of simulator capable of managing processes.
+ * This class extends the {@link Simulator} class
+ * with methods providing basic facilities used by process simulators
+ * to suspend, resume, and kill processes.
+ * It also specifies a mechanism for creating control events used for process synchronization.
+ * These methods are not called directly by the user, but they
+ * need to be implemented by any framework managing processes.
+ *
+ * <P>
+ * A process simulator is usually constructed by the {@link #newInstance newInstance} method which
+ * selects the appropriate implementation based on system properties.
+ * It can also be constructed manually by calling the appropriate constructor explicitly.
+ * A default, implicit, simulator can also be used for compatibility with older programs.
+ * This can be done by calling the {@link #initDefault initDefault} method or setting the
+ * <TT>ssj.processes</TT> property.
+ *
+ */
+public abstract class ProcessSimulator extends Simulator {
+
+ protected SimProcess currentProcess;
+ // The process who has control right now (current process).
+ // If no process has control (an event is executing), current = null.
+
+
+
+ /**
+ * Returns the currently active process for this simulator.
+ *
+ */
+ public SimProcess currentProcess() {
+ return currentProcess;
+ }
+
+
+ protected void setCurrentProcess (SimProcess currentProcess) {
+ this.currentProcess = currentProcess;
+ }
+
+
+ /**
+ * Constructs and returns a new {@link Event} object used for synchronization.
+ * Such control events are used by process simulator to start a process or to resume it if it is already
+ * started.
+ *
+ */
+ public abstract Event createControlEvent (SimProcess process);
+
+
+ /**
+ * Suspends the execution of <TT>process</TT> and
+ * schedules it to resume its execution in <TT>delay</TT> units of simulation
+ * time. The state of the process also changes to <TT>DELAYED</TT>.
+ *
+ * @param process SimProcess variable to delay.
+ *
+ * @param delay delay value, in simulation time units.
+ *
+ *
+ */
+ public abstract void delay (SimProcess process, double delay);
+
+
+ /**
+ * Suspends <TT>process</TT>.
+ * If the process is <TT>EXECUTING</TT>, this suspends its execution.
+ * If the process is <TT>DELAYED</TT>, this cancels its control event.
+ * This method also places the process in the <TT>SUSPENDED</TT> state.
+ *
+ * @param process SimProcess variable to suspend.
+ *
+ *
+ */
+ public abstract void suspend (SimProcess process);
+
+
+ /**
+ * Terminates the life of <TT>process</TT> and sets its state to
+ * <TT>DEAD</TT>, after canceling its control event if there is one.
+ *
+ */
+ public abstract void kill (SimProcess process);
+
+
+ /**
+ * Kills all currently living (active, delayed, or suspended) processes managed by
+ * this simulator.
+ *
+ */
+ public abstract void killAll();
+
+
+ /**
+ * Initializes the default simulator to use processes.
+ * If the field <TT>Simulator.defaultSimulator</TT> is already initialized to a class
+ * extending <TT>ProcessSimulator</TT>, this
+ * method does nothing.
+ * Otherwise, it initializes the field to the return value of
+ * {@link #newInstance newInstance}.
+ *
+ * <P>
+ * Warning: <SPAN CLASS="textbf">this method must be called before any event or process is
+ * constructed</SPAN>, otherwise the program could return some exceptions like
+ * {@link ClassCastException} or {@link NullPointerException}.
+ * Alternatively, one can set the <TT>ssj.processes</TT> system property, which
+ * instructs this class to call this method at the time it is loaded.
+ *
+ * <P>
+ * The aim of this method is to allow the user to use default process-oriented
+ * simulation without giving options to the Java Virtual Machine.
+ * See package
+ * <TT>simevents</TT> and class {@link Simulator} for more information about the default simulator.
+ *
+ */
+ public static void initDefault() {
+ if (defaultSimulator instanceof ProcessSimulator) {
+ defaultSimulator.init();
+ return;
+ }
+ if (defaultSimulator instanceof Simulator) {
+ Simulator temp = defaultSimulator;
+ defaultSimulator = newInstance();
+ defaultSimulator.init(temp.getEventList());
+ return;
+ }
+ defaultSimulator = newInstance();
+ defaultSimulator.init();
+ }
+
+ static {
+ if (System.getProperty ("ssj.processes") != null)
+ initDefault();
+ }
+
+
+ /**
+ * Constructs and returns a new process-oriented simulator.
+ * This method selects the concrete subclass of process simulator as follows.
+ * If the <TT>ssj.processSimulator</TT> system property is set, this gives the
+ * fully qualified class name of the process simulator to be instantiated.
+ * The given class must not be abstract, and must have a no-argument constructor.
+ * Otherwise, if the <TT>ssj.withThread</TT> system property is set, this returns a
+ * {@link ThreadProcessSimulator}.
+ * Otherwise, if the <TT>ssj.withDSOL</TT> system property is set, this
+ * returns a {@link DSOLProcessSimulator} instance.
+ * If no system property is set, this returns a {@link ThreadProcessSimulator}.
+ *
+ * <P>
+ * For example, if a program is called using <TT>java -Dssj.withDSOL ...</TT>, it will use DSOL
+ * for process simulation.
+ *
+ */
+ public static ProcessSimulator newInstance() {
+ if(System.getProperty("ssj.processSimulator") != null) {
+ ProcessSimulator myProcessSimulator = null;
+ try{
+ myProcessSimulator = (ProcessSimulator)(Class.forName(System.getProperty("ssj.processSimulator"))).newInstance();
+ }
+ catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); }
+ catch (IllegalAccessException iae) { iae.printStackTrace(); }
+ catch (InstantiationException ie) { ie.printStackTrace(); }
+ return myProcessSimulator;
+ }
+ else if(System.getProperty("ssj.withThread") != null)
+ return new ThreadProcessSimulator();
+ else if (System.getProperty("ssj.withDSOL") != null)
+ return new DSOLProcessSimulator();
+ else
+ return new ThreadProcessSimulator();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/ProcessSimulator.tex b/source/umontreal/iro/lecuyer/simprocs/ProcessSimulator.tex
new file mode 100644
index 0000000..c476c7c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/ProcessSimulator.tex
@@ -0,0 +1,210 @@
+\defmodule{ProcessSimulator}
+
+Defines a special type of simulator capable of managing processes.
+This class extends the \class{Simulator} class
+with methods providing basic facilities used by process simulators
+to suspend, resume, and kill processes.
+It also specifies a mechanism for creating control events used for process synchronization.
+These methods are not called directly by the user, but they
+need to be implemented by any framework managing processes.
+
+A process simulator is usually constructed by the \method{newInstance}{} method which
+selects the appropriate implementation based on system properties.
+It can also be constructed manually by calling the appropriate constructor explicitly.
+A default, implicit, simulator can also be used for compatibility with older programs.
+This can be done by calling the \method{initDefault}{} method or setting the
+\texttt{ssj.processes} property.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+
+\begin{hide}
+/*
+ * Class: ProcessSimulator
+ * Description: Special type of simulator capable of managing processes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;\begin{hide}
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.Simulator;
+\end{hide}
+
+public abstract class ProcessSimulator extends Simulator \begin{hide} {
+
+ protected SimProcess currentProcess;
+ // The process who has control right now (current process).
+ // If no process has control (an event is executing), current = null.
+\end{hide}
+\end{code}
+
+% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \subsubsection* {Constructor}
+% \begin{code}
+% public ProcessSimulator() \begin{hide} {
+% currentProcess = null;
+% }\end{hide}
+% \end{code}
+% \begin{tabb} Creates a new Simulator for process-oriented simulation
+% \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public SimProcess currentProcess()\begin{hide} {
+ return currentProcess;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the currently active process for this simulator.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void setCurrentProcess (SimProcess currentProcess) {
+ this.currentProcess = currentProcess;
+ }
+\end{hide}
+
+ public abstract Event createControlEvent (SimProcess process);
+\end{code}
+\begin{tabb} Constructs and returns a new \class{Event} object used for synchronization.
+Such control events are used by process simulator to start a process or to resume it if it is already
+started.
+\end{tabb}
+\begin{code}
+
+ public abstract void delay (SimProcess process, double delay);
+\end{code}
+\begin{tabb} Suspends the execution of \texttt{process} and
+ schedules it to resume its execution in \texttt{delay} units of simulation
+ time. The state of the process also changes to \texttt{DELAYED}.
+\end{tabb}
+\begin{htmlonly}
+ \param{process}{SimProcess variable to delay.}
+ \param{delay}{delay value, in simulation time units.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract void suspend (SimProcess process);
+\end{code}
+\begin{tabb} Suspends \texttt{process}.
+ If the process is \texttt{EXECUTING}, this suspends its execution.
+ If the process is \texttt{DELAYED}, this cancels its control event.
+ This method also places the process in the \texttt{SUSPENDED} state.
+\end{tabb}
+\begin{htmlonly}
+ \param{process}{SimProcess variable to suspend.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract void kill (SimProcess process);
+\end{code}
+ \begin{tabb} Terminates the life of \texttt{process} and sets its state to
+ \texttt{DEAD}, after canceling its control event if there is one.
+ \end{tabb}
+\begin{code}
+
+ public abstract void killAll();
+\end{code}
+\begin{tabb} Kills all currently living (active, delayed, or suspended) processes managed by
+ this simulator.
+\end{tabb}
+\begin{code}
+
+ public static void initDefault() \begin{hide} {
+ if (defaultSimulator instanceof ProcessSimulator) {
+ defaultSimulator.init();
+ return;
+ }
+ if (defaultSimulator instanceof Simulator) {
+ Simulator temp = defaultSimulator;
+ defaultSimulator = newInstance();
+ defaultSimulator.init(temp.getEventList());
+ return;
+ }
+ defaultSimulator = newInstance();
+ defaultSimulator.init();
+ }
+
+ static {
+ if (System.getProperty ("ssj.processes") != null)
+ initDefault();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Initializes the default simulator to use processes.
+ If the field \texttt{Simulator.defaultSimulator} is already initialized to a class
+ extending \texttt{ProcessSimulator}, this
+ method does nothing.
+ Otherwise, it initializes the field to the return value of
+ \method{newInstance}{}.
+
+ Warning: \textbf{this method must be called before any event or process is
+ constructed}, otherwise the program could return some exceptions like
+ \class{ClassCastException} or \class{NullPointerException}.
+ Alternatively, one can set the \texttt{ssj.processes} system property, which
+ instructs this class to call this method at the time it is loaded.
+
+ The aim of this method is to allow the user to use default process-oriented
+ simulation without giving options to the Java Virtual Machine.
+ See package
+ \texttt{simevents} and class \class{Simulator} for more information about the default simulator.
+ \end{tabb}
+\begin{code}
+
+ public static ProcessSimulator newInstance()\begin{hide} {
+ if(System.getProperty("ssj.processSimulator") != null) {
+ ProcessSimulator myProcessSimulator = null;
+ try{
+ myProcessSimulator = (ProcessSimulator)(Class.forName(System.getProperty("ssj.processSimulator"))).newInstance();
+ }
+ catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); }
+ catch (IllegalAccessException iae) { iae.printStackTrace(); }
+ catch (InstantiationException ie) { ie.printStackTrace(); }
+ return myProcessSimulator;
+ }
+ else if(System.getProperty("ssj.withThread") != null)
+ return new ThreadProcessSimulator();
+ else if (System.getProperty("ssj.withDSOL") != null)
+ return new DSOLProcessSimulator();
+ else
+ return new ThreadProcessSimulator();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs and returns a new process-oriented simulator.
+ This method selects the concrete subclass of process simulator as follows.
+ If the \texttt{ssj.processSimulator} system property is set, this gives the
+ fully qualified class name of the process simulator to be instantiated.
+ The given class must not be abstract, and must have a no-argument constructor.
+ Otherwise, if the \texttt{ssj.withThread} system property is set, this returns a
+ \class{ThreadProcessSimulator}.
+ Otherwise, if the \texttt{ssj.withDSOL} system property is set, this
+ returns a \class{DSOLProcessSimulator} instance.
+ If no system property is set, this returns a \class{ThreadProcessSimulator}.
+
+ For example, if a program is called using \texttt{java -Dssj.withDSOL ...}, it will use DSOL
+ for process simulation.
+\end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/simprocs/Resource.java b/source/umontreal/iro/lecuyer/simprocs/Resource.java
new file mode 100644
index 0000000..9856150
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/Resource.java
@@ -0,0 +1,636 @@
+
+
+/*
+ * Class: Resource
+ * Description: resources with limited capacity which can be requested
+ and released by Process objects.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import java.util.ListIterator;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+// import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simevents.LinkedListStat;
+import umontreal.iro.lecuyer.simevents.Accumulate;
+import umontreal.iro.lecuyer.simprocs.ProcessSimulator;
+import umontreal.iro.lecuyer.stat.Tally;
+
+
+/**
+ * Objects of this class are resources having limited capacity,
+ * and which can be requested and released by {@link Process} objects.
+ * These resources act indirectly as synchonization devices for
+ * processes.
+ *
+ * <P>
+ * A resource is created with a finite capacity, specified when
+ * invoking the {@link #Resource(int) Resource} constructor, and can be changed
+ * later on. A resource also has an infinite-capacity queue
+ * (waiting line) and a service policy that defaults to FIFO and
+ * can be changed later on.
+ *
+ * <P>
+ * A process must ask for a certain number of units of the resource
+ * ({@link #request request}), and obtain it before using it.
+ * When it is done with the resource, the process must release
+ * it so that others can use it ({@link #release release}).
+ * A process does not have to request [release] all the resource
+ * units that it needs by a single call to the {@link #request request}
+ * [{@link #release release}] method. It can make several successive requests
+ * or releases, and can also hold different resources simultaneously.
+ *
+ * <P>
+ * Each resource maintains two lists: one contains the processes
+ * waiting for the resource (the waiting queue) and the other contains
+ * the processes currently holding units of this resource.
+ * The methods {@link #waitList waitList} and {@link #servList servList} permit one to
+ * access these two lists.
+ * These lists actually contain objects of the class {@link UserRecord}
+ * instead of {@link SimProcess} objects.
+ *
+ */
+public class Resource {
+
+ private static final int FIFO = 1;
+ private static final int LIFO = 2;
+
+ private ProcessSimulator sim;
+
+ private String name;
+ private int capacity = 0;
+ private int available = 0;
+ private int policy = FIFO;
+
+ private LinkedListStat<UserRecord> serviceList;
+ private LinkedListStat<UserRecord> waitingList;
+
+ private boolean stats = false;
+ private double initStatTime;
+ private Accumulate statUtil;
+ private Accumulate statCapacity;
+ private Tally statSojourn;
+
+
+ /**
+ * Constructs a new resource linked with the default simulator,
+ * with initial capacity <TT>capacity</TT>, and service policy FIFO.
+ *
+ * @param capacity initial capacity of the resource
+ *
+ *
+ */
+ public Resource (int capacity) {
+ this (capacity, "");
+ }
+
+
+ /**
+ * Constructs a new resource linked with the simulator
+ * <TT>sim</TT>, with initial capacity <TT>capacity</TT>, and service policy FIFO.
+ *
+ * @param sim current simulator of the variable
+ *
+ * @param capacity initial capacity of the resource
+ *
+ *
+ */
+ public Resource (ProcessSimulator sim, int capacity) {
+ this (sim, capacity, "");
+ }
+
+
+ /**
+ * Constructs a new resource linked with the default simulator, with
+ * initial capacity <TT>capacity</TT>, service policy FIFO, and identifier
+ * <TT>name</TT>.
+ *
+ * @param capacity initial capacity of the resource
+ *
+ * @param name identifier or name of the resource
+ *
+ *
+ */
+ public Resource (int capacity, String name) {
+ try {
+ ProcessSimulator.initDefault();
+ this.sim = (ProcessSimulator)ProcessSimulator.getDefaultSimulator();
+ this.capacity = available = capacity;
+ this.name = name;
+ serviceList = new LinkedListStat<UserRecord> (
+ sim," Service List for " + name);
+ waitingList = new LinkedListStat<UserRecord> (
+ sim," Waiting List for " + name);
+ }
+ catch (ClassCastException e) {
+ throw new IllegalArgumentException("Wrong default Simulator type");
+ }
+ }
+
+
+ /**
+ * Constructs a new resource linked with the simulator <TT>sim</TT>,
+ * with initial capacity <TT>capacity</TT>,
+ * service policy FIFO, and identifier (or name) <TT>name</TT>.
+ *
+ * @param sim current simulator of the variable
+ *
+ * @param capacity initial capacity of the resource
+ *
+ * @param name identifier or name of the resource
+ *
+ */
+ public Resource (ProcessSimulator sim, int capacity, String name) {
+ this.capacity = available = capacity;
+ this.name = name;
+ this.sim = sim;
+ serviceList = new LinkedListStat<UserRecord> (
+ sim," Service List for " + name);
+ waitingList = new LinkedListStat<UserRecord> (
+ sim," Waiting List for " + name);
+ }
+
+
+ /**
+ * Starts or stops collecting statistics on the lists returned
+ * by {@link #waitList waitList} and {@link #servList servList} for this resource.
+ * If the statistical collection is turned ON, the method
+ * also constructs (if not yet done)
+ * and initializes three additional statistical
+ * collectors for this resource. These collectors will be updated
+ * automatically. They can be accessed via {@link #statOnCapacity statOnCapacity},
+ * {@link #statOnUtil statOnUtil}, and {@link #statOnSojourn statOnSojourn}, respectively.
+ * The first two, of class
+ * {@link umontreal.iro.lecuyer.simevents.Accumulate Accumulate},
+ * monitor the evolution of the
+ * capacity and of the utilization (number of units busy) of the resource
+ * as a function of time.
+ * The third one, of class {@link umontreal.iro.lecuyer.stat.Tally Tally},
+ * collects statistics on the
+ * processes sojourn times (wait + service); it samples a new value
+ * each time a process releases all the units of this resource that it
+ * holds (i.e., when its {@link UserRecord} disappears).
+ *
+ * @param b <TT>true</TT> to turn statistical collecting ON, <TT>false</TT> to turn it OFF
+ *
+ *
+ */
+ public void setStatCollecting (boolean b) {
+ if (b) {
+ if (stats)
+ throw new IllegalStateException ("Already collecting statistics for this resource");
+ stats = true;
+ waitingList.setStatCollecting (true);
+ serviceList.setStatCollecting (true);
+
+ if (statUtil != null)
+ initStat();
+ else {
+ statUtil = new Accumulate (sim, "StatOnUtil");
+ statUtil.update (capacity - available);
+ statCapacity = new Accumulate (sim, "StatOnCapacity");
+ statCapacity.update (capacity);
+ statSojourn = new Tally ("StatOnSojourn");
+ }
+ }
+ else {
+ if (stats)
+ throw new IllegalStateException ("Not collecting statistics for this resource");
+ stats = false;
+ waitingList.setStatCollecting (false);
+ serviceList.setStatCollecting (false);
+ }
+ }
+
+
+ /**
+ * Reinitializes all the statistical collectors for this
+ * resource. These collectors must exist, i.e.,
+ * {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> must have been invoked earlier for
+ * this resource.
+ *
+ */
+ public void initStat() {
+ if (!stats) throw new IllegalStateException (
+ "Not collecting statistics for this resource");
+ statUtil.init();
+ statUtil.update (capacity - available);
+ statCapacity.init();
+ statCapacity.update (capacity);
+ statSojourn.init();
+ serviceList.initStat();
+ waitingList.initStat();
+ initStatTime = sim.time();
+ }
+
+
+ /**
+ * Reinitializes this resource by clearing up its waiting list
+ * and service list. The processes that were in these lists (if any)
+ * remain in the same states. If statistical collection is ``on'',
+ * {@link #initStat initStat} is invoked as well.
+ *
+ */
+ public void init() {
+ serviceList.clear();
+ waitingList.clear();
+ available = capacity;
+ if (stats) initStat();
+ }
+
+
+ /**
+ * Returns the current capacity of the resource.
+ *
+ * @return the capacity of the resource
+ *
+ */
+ public int getCapacity() {
+ return capacity;
+ }
+
+
+ /**
+ * Set the service policy to FIFO (<SPAN CLASS="textit">first in, first out</SPAN>):
+ * the processes are placed in the
+ * list (and served) according to their order of arrival.
+ *
+ */
+ public void setPolicyFIFO() {
+ policy = FIFO;
+ }
+
+
+ /**
+ * Set the service policy to LIFO (<SPAN CLASS="textit">last in, first out</SPAN>):
+ * the processes are placed in the
+ * list (and served) according to their inverse order of arrival,
+ * the last arrived are served first.
+ *
+ */
+ public void setPolicyLIFO() {
+ policy = LIFO;
+ }
+
+
+ /**
+ * Modifies by <TT>diff</TT> units (increases if <TT>diff > 0</TT>,
+ * decreases if <TT>diff < 0</TT>) the capacity (i.e., the number of units)
+ * of the resource.
+ * If <TT>diff > 0</TT> and there are processes in the waiting list whose
+ * request can now be satisfied, they obtain the resource.
+ * If <TT>diff < 0</TT>, there must be at least <TT>diff</TT> units of this
+ * resource available, otherwise an illegal argument exception will be thrown,
+ * printing an error message (this is not a strong limitation, because one
+ * can check first and release some units, if needed, before invoking
+ * <TT>changeCapacity</TT>).
+ * In particular, the capacity of a resource can never become negative.
+ *
+ * @param diff number of capacity units to add to the actual capacity,
+ * can be negative to reduce the capacity
+ *
+ * @exception IllegalArgumentException if <TT>diff</TT> is negative and
+ * the capacity cannot be reduced as required
+ *
+ * @exception IllegalStateException if <TT>diff</TT> is negative and
+ * the capacity could not be reduced due to a lack of available units
+ *
+ *
+ */
+ public void changeCapacity (int diff) {
+ if (diff > 0) {
+ available += diff;
+ capacity += diff;
+ if (waitingList.size() > 0) startNewCust();
+ }
+ else {
+ if (-diff > available)
+ throw new IllegalArgumentException("Trying to diminish the capacity "
+ + "of a resource more than its current availability");
+ available -= -diff;
+ capacity -= -diff;
+ }
+ if (stats) statCapacity.update (capacity);
+ }
+
+
+ /**
+ * Sets the capacity to <TT>newcap</TT>.
+ * Equivalent to {@link #changeCapacity changeCapacity} <TT>(newcap - old)</TT> if <TT>old</TT> is the
+ * current capacity.
+ *
+ * @param newcap new capacity of the resource
+ *
+ * @exception IllegalArgumentException if the passed capacity is negative
+ *
+ * @exception IllegalStateException if <TT>diff</TT> is negative and
+ * the capacity could not be reduced due to a lack of available units
+ *
+ *
+ */
+ public void setCapacity (int newcap) {
+ if (newcap < 0)
+ throw new IllegalArgumentException ("capacity cannot be negative");
+ changeCapacity (newcap - capacity);
+ }
+
+
+ /**
+ * Returns the number of available units, i.e., the capacity
+ * minus the number of units busy.
+ *
+ * @return the number of available units
+ *
+ */
+ public int getAvailable() {
+ return available;
+ }
+
+
+ /**
+ * The executing process invoking this method requests for
+ * <TT>n</TT> units of this resource. If there are enough units available
+ * to fill up the request immediately, the process obtains the desired
+ * number of units and holds them until it invokes {@link #release release}
+ * for this same resource. The process is also inserted into the
+ * {@link #servList servList} list for this resource.
+ * On the other hand, if there are less than <TT>n</TT> units of this
+ * resource available, the executing process is placed into the
+ * {@link #waitList waitList} list (the queue) for this resource and is suspended
+ * until it can obtain the requested number of units of the resource.
+ *
+ * @param n number of required units
+ *
+ */
+ public void request (int n) {
+ SimProcess p = sim.currentProcess();
+ UserRecord record = new UserRecord (n, p, sim.time());
+ if (n <= available) {
+ // The process gets the resource right away.
+ available -= n;
+ serviceList.addLast (record);
+ if (stats) {
+ waitingList.statSojourn().add (0.0);
+ statUtil.update (capacity - available);
+ }
+ }
+ else {
+ // Not enough units of the resource are available.
+ // The process joins the queue waitingList;
+ switch (policy) {
+ case FIFO : waitingList.addLast (record); break;
+ case LIFO : waitingList.addFirst (record); break;
+ default : throw new IllegalStateException(
+ "policy must be FIFO or LIFO");
+ }
+ p.suspend();
+ }
+ }
+
+
+ // Called by \texttt{release}.
+ private void startNewCust() {
+ UserRecord record;
+ ListIterator<UserRecord> iterWait = waitingList.listIterator();
+ while (iterWait.hasNext() && available > 0) {
+ record = iterWait.next();
+ if (record.process.getState() == SimProcess.DEAD) iterWait.remove();
+ // the process was killed, so we remove it from the waiting list.
+ // or maybe we stop the program by throwing IllegalStateException
+ //"Resource.startNewCust: process not alive");
+
+ // The thread for this process is still alive.
+ else if (record.numUnits <= available) {
+ // This request can now be satisfied.
+ serviceList.addLast (record);
+ record.process.resume();
+ available -= record.numUnits;
+ iterWait.remove();
+ }
+ }
+ }
+
+ /**
+ * The executing process that invokes this method releases
+ * <TT>n</TT> units of the resource. If this process is holding exactly
+ * <TT>n</TT> units, it is removed from the service list of this resource
+ * and its {@link UserRecord} object disappears.
+ * If this process is holding less than <TT>n</TT> units,
+ * the program throws an illegal argument exception.
+ * If there are other processes waiting for this resource whose requests
+ * can now be satisfied, they obtain the resource.
+ *
+ * @param n number of released units
+ *
+ * @exception IllegalArgumentException if the process did not request <TT>n</TT>
+ * units before releasing them
+ *
+ *
+ */
+ public void release (int n) {
+ SimProcess p = sim.currentProcess();
+ int temp = 0;
+ UserRecord record;
+ ListIterator<UserRecord> iterServ = serviceList.listIterator();
+ while (temp<n && iterServ.hasNext()) {
+ record = iterServ.next();
+ if (p == record.process) {
+ temp = temp + record.numUnits;
+ if (temp <= n) {
+ iterServ.remove();
+ if (stats) statSojourn.add
+ (sim.time() - record.requestTime);
+ }
+ else {
+ record.numUnits = temp - n;
+ temp = n;
+ }
+ }
+ }
+ if (temp < n) throw new IllegalArgumentException ("trying to release "
+ +"more units of a Resource than the process currently holds");
+ available += temp;
+ if (waitingList.size() > 0) startNewCust();
+ if (stats) statUtil.update (capacity - available);
+ }
+
+
+ /**
+ * Returns the list that contains the
+ * {@link UserRecord} objects
+ * for the processes in the waiting list for this resource.
+ *
+ * @return the list of process user records waiting for the resource
+ *
+ */
+ public LinkedListStat waitList() {
+ return waitingList;
+ }
+
+
+ /**
+ * Returns the list that contains the
+ * {@link UserRecord} objects
+ * for the processes in the service list for this resource.
+ *
+ * @return the list of process user records using this resource
+ *
+ */
+ public LinkedListStat servList() {
+ return serviceList;
+ }
+
+
+ /**
+ * Returns the statistical collector that measures the evolution of
+ * the capacity of the resource as a function of time.
+ * This collector exists only if {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> has been invoked
+ * previously.
+ *
+ * @return the probe for resource capacity
+ *
+ */
+ public Accumulate statOnCapacity() {
+ return statCapacity;
+ }
+
+
+ /**
+ * Returns the statistical collector for the utilization
+ * of the resource (number of units busy) as a function of time.
+ * This collector exists only if {@link #setStatCollecting setStatCollecting} <TT>(true)</TT>
+ * has been invoked previously.
+ * The <EM>utilization rate</EM> of a resource can be obtained as the
+ * <EM>time average</EM> computed by this collector, divided by the
+ * capacity of the resource.
+ * The collector returned by {@link #servList() servList()}<TT>.</TT>{@link umontreal.iro.lecuyer.simevents.LinkedListStat#statSize() statSize()}
+ * tracks the number of {@link UserRecord}
+ * in the service list;
+ * it differs from this collector because a process may hold more than one
+ * unit of the resource by given {@link UserRecord}.
+ *
+ * @return the probe for resource utilization
+ *
+ */
+ public Accumulate statOnUtil() {
+ return statUtil;
+ }
+
+
+ /**
+ * Returns the statistical collector for the sojourn times of
+ * the {@link UserRecord} for this resource.
+ * This collector exists only if {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> has been invoked
+ * previously.
+ * It gets a new observation each time a process releases all the units
+ * of this resource that it had requested by a single {@link #request request}
+ * call.
+ *
+ * @return the probe for the sojourn times
+ *
+ */
+ public Tally statOnSojourn() {
+ return statSojourn;
+ }
+
+
+ /**
+ * Returns the name (or identifier) associated to this
+ * resource. If it was not given upon resource construction, this returns
+ * <TT>null</TT>.
+ *
+ * @return the name associated to this resource, or <TT>null</TT> if it was not given
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+
+ /**
+ * Returns a string containing a complete statistical report on this
+ * resource. The method {@link #setStatCollecting setStatCollecting} <TT>(true)</TT> must have been invoked
+ * previously, otherwise no statistics have been collected.
+ * The report contains statistics on the waiting times, service
+ * times, and waiting times for this resource, on the capacity,
+ * number of units busy, and size of the queue as a function of time,
+ * and on the utilization rate.
+ *
+ * @return a statistical report for this resource, represented as a string
+ */
+ public String report() {
+
+ if (statUtil == null) throw new IllegalStateException ("Asking a report for a resource "
+ +"for which setStatCollecting (true) has not been called");
+
+ Accumulate sizeWait = waitingList.statSize();
+ Tally sojWait = waitingList.statSojourn();
+ Tally sojServ = serviceList.statSojourn();
+
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("REPORT ON RESOURCE : ").append (name).append (PrintfFormat.NEWLINE);
+ str.append (" From time : ").append (7, 2, 2, initStatTime);
+ str.append (" to time : ");
+ str.append (10,2, 2, sim.time());
+ str.append (PrintfFormat.NEWLINE + " min max average ");
+ str.append ("standard dev. nb. obs.");
+
+ str.append (PrintfFormat.NEWLINE + " Capacity ");
+ str.append (8, (int)(statCapacity.min()+0.5));
+ str.append (11, (int)(statCapacity.max()+0.5));
+ str.append (12, 3, 2, statCapacity.average());
+
+ str.append (PrintfFormat.NEWLINE + " Utilization ");
+ str.append (8, (int)(statUtil.min()+0.5));
+ str.append (11, (int)(statUtil.max()+0.5));
+ str.append (12, 3, 2, statUtil.average());
+
+ str.append (PrintfFormat.NEWLINE + " Queue Size ");
+ str.append (8, (int)(sizeWait.min()+0.5));
+ str.append (11, (int)(sizeWait.max()+0.5));
+ str.append (12, 3, 2, sizeWait.average());
+
+ str.append (PrintfFormat.NEWLINE + " Wait ");
+ str.append (12, 3, 2, sojWait.min()).append (' ');
+ str.append (10, 3, 2, sojWait.max()).append (' ');
+ str.append (11, 3, 2, sojWait.average()).append (' ');
+ str.append (10, 3, 2, sojWait.standardDeviation());
+ str.append (10, sojWait.numberObs());
+
+ str.append (PrintfFormat.NEWLINE + " Service ");
+ str.append (12, 3, 2, sojServ.min()).append (' ');
+ str.append (10, 3, 2, sojServ.max()).append (' ');
+ str.append (11, 3, 2, sojServ.average()).append (' ');
+ str.append (10, 3, 2, sojServ.standardDeviation());
+ str.append (10, sojServ.numberObs());
+
+ str.append (PrintfFormat.NEWLINE + " Sojourn ");
+ str.append (12, 3, 2, statSojourn.min()).append (' ');
+ str.append (10, 3, 2, statSojourn.max()).append (' ');
+ str.append (11, 3, 2, statSojourn.average()).append (' ');
+ str.append (10, 3, 2, statSojourn.standardDeviation());
+ str.append (10, statSojourn.numberObs()).append (PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/Resource.tex b/source/umontreal/iro/lecuyer/simprocs/Resource.tex
new file mode 100644
index 0000000..6a455ab
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/Resource.tex
@@ -0,0 +1,646 @@
+\defmodule {Resource}
+
+Objects of this class are resources having limited capacity,
+and which can be requested and released by \class{Process} objects.
+These resources act indirectly as synchonization devices for
+processes.
+
+A resource is created with a finite capacity, specified when
+invoking the \method{Resource}{int} constructor, and can be changed
+later on. A resource also has an infinite-capacity queue
+(waiting line) and a service policy that defaults to FIFO and
+can be changed later on.
+
+A process must ask for a certain number of units of the resource
+(\method{request}{}), and obtain it before using it.
+When it is done with the resource, the process must release
+it so that others can use it (\method{release}{}).
+A process does not have to request [release] all the resource
+units that it needs by a single call to the \method{request}{}
+[\method{release}{}] method. It can make several successive requests
+or releases, and can also hold different resources simultaneously.
+
+Each resource maintains two lists: one contains the processes
+waiting for the resource (the waiting queue) and the other contains
+the processes currently holding units of this resource.
+The methods \method{waitList}{} and \method{servList}{} permit one to
+access these two lists.
+These lists actually contain objects of the class \class{UserRecord}
+instead of \class{SimProcess} objects.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Resource
+ * Description: resources with limited capacity which can be requested
+ and released by Process objects.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;
+\begin{hide}
+import java.util.ListIterator;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+// import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simevents.LinkedListStat;
+import umontreal.iro.lecuyer.simevents.Accumulate;
+import umontreal.iro.lecuyer.simprocs.ProcessSimulator;
+import umontreal.iro.lecuyer.stat.Tally;
+\end{hide}
+
+public class Resource \begin{hide} {
+
+ private static final int FIFO = 1;
+ private static final int LIFO = 2;
+
+ private ProcessSimulator sim;
+
+ private String name;
+ private int capacity = 0;
+ private int available = 0;
+ private int policy = FIFO;
+
+ private LinkedListStat<UserRecord> serviceList;
+ private LinkedListStat<UserRecord> waitingList;
+
+ private boolean stats = false;
+ private double initStatTime;
+ private Accumulate statUtil;
+ private Accumulate statCapacity;
+ private Tally statSojourn;
+\end{hide}\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Resource (int capacity) \begin{hide} {
+ this (capacity, "");
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new resource linked with the default simulator,
+ with initial capacity \texttt{capacity}, and service policy FIFO.
+\end{tabb}
+\begin{htmlonly}
+ \param{capacity}{initial capacity of the resource}
+\end{htmlonly}
+\begin{code}
+
+ public Resource (ProcessSimulator sim, int capacity) \begin{hide} {
+ this (sim, capacity, "");
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new resource linked with the simulator
+ \texttt{sim}, with initial capacity \texttt{capacity}, and service policy FIFO.
+\end{tabb}
+\begin{htmlonly}
+ \param{sim}{current simulator of the variable}
+ \param{capacity}{initial capacity of the resource}
+\end{htmlonly}
+\begin{code}
+
+ public Resource (int capacity, String name) \begin{hide} {
+ try {
+ ProcessSimulator.initDefault();
+ this.sim = (ProcessSimulator)ProcessSimulator.getDefaultSimulator();
+ this.capacity = available = capacity;
+ this.name = name;
+ serviceList = new LinkedListStat<UserRecord> (
+ sim," Service List for " + name);
+ waitingList = new LinkedListStat<UserRecord> (
+ sim," Waiting List for " + name);
+ }
+ catch (ClassCastException e) {
+ throw new IllegalArgumentException("Wrong default Simulator type");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new resource linked with the default simulator, with
+ initial capacity \texttt{capacity}, service policy FIFO, and identifier
+ \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{capacity}{initial capacity of the resource}
+ \param{name}{identifier or name of the resource}
+\end{htmlonly}
+\begin{code}
+
+ public Resource (ProcessSimulator sim, int capacity, String name) \begin{hide} {
+ this.capacity = available = capacity;
+ this.name = name;
+ this.sim = sim;
+ serviceList = new LinkedListStat<UserRecord> (
+ sim," Service List for " + name);
+ waitingList = new LinkedListStat<UserRecord> (
+ sim," Waiting List for " + name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new resource linked with the simulator \texttt{sim},
+ with initial capacity \texttt{capacity},
+ service policy FIFO, and identifier (or name) \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{sim}{current simulator of the variable}
+ \param{capacity}{initial capacity of the resource}
+ \param{name}{identifier or name of the resource}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void setStatCollecting (boolean b) \begin{hide} {
+ if (b) {
+ if (stats)
+ throw new IllegalStateException ("Already collecting statistics for this resource");
+ stats = true;
+ waitingList.setStatCollecting (true);
+ serviceList.setStatCollecting (true);
+
+ if (statUtil != null)
+ initStat();
+ else {
+ statUtil = new Accumulate (sim, "StatOnUtil");
+ statUtil.update (capacity - available);
+ statCapacity = new Accumulate (sim, "StatOnCapacity");
+ statCapacity.update (capacity);
+ statSojourn = new Tally ("StatOnSojourn");
+ }
+ }
+ else {
+ if (stats)
+ throw new IllegalStateException ("Not collecting statistics for this resource");
+ stats = false;
+ waitingList.setStatCollecting (false);
+ serviceList.setStatCollecting (false);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Starts or stops collecting statistics on the lists returned
+ by \method{waitList}{} and \method{servList}{} for this resource.
+ If the statistical collection is turned ON, the method
+ also constructs (if not yet done)
+ and initializes three additional statistical
+ collectors for this resource. These collectors will be updated
+ automatically. They can be accessed via \method{statOnCapacity}{},
+ \method{statOnUtil}{}, and \method{statOnSojourn}{}, respectively.
+ The first two, of class
+ \externalclass{umontreal.iro.lecuyer.simevents}{Accumulate},
+ monitor the evolution of the
+ capacity and of the utilization (number of units busy) of the resource
+ as a function of time.
+ The third one, of class \externalclass{umontreal.iro.lecuyer.stat}{Tally},
+ collects statistics on the
+ processes sojourn times (wait + service); it samples a new value
+ each time a process releases all the units of this resource that it
+ holds (i.e., when its \class{UserRecord} disappears).
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{\texttt{true} to turn statistical collecting ON, \texttt{false} to turn it OFF}
+\end{htmlonly}
+\begin{code}
+
+ public void initStat() \begin{hide} {
+ if (!stats) throw new IllegalStateException (
+ "Not collecting statistics for this resource");
+ statUtil.init();
+ statUtil.update (capacity - available);
+ statCapacity.init();
+ statCapacity.update (capacity);
+ statSojourn.init();
+ serviceList.initStat();
+ waitingList.initStat();
+ initStatTime = sim.time();
+ }\end{hide}
+\end{code}
+\begin{tabb} Reinitializes all the statistical collectors for this
+ resource. These collectors must exist, i.e.,
+ \method{setStatCollecting}{}~\texttt{(true)} must have been invoked earlier for
+ this resource.
+\end{tabb}
+\begin{code}
+
+ public void init() \begin{hide} {
+ serviceList.clear();
+ waitingList.clear();
+ available = capacity;
+ if (stats) initStat();
+ }\end{hide}
+\end{code}
+\begin{tabb} Reinitializes this resource by clearing up its waiting list
+ and service list. The processes that were in these lists (if any)
+ remain in the same states. If statistical collection is ``on'',
+ \method{initStat}{} is invoked as well.
+\end{tabb}
+\begin{code}
+
+ public int getCapacity() \begin{hide} {
+ return capacity;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the current capacity of the resource.
+\end{tabb}
+\begin{htmlonly}
+ \return{the capacity of the resource}
+\end{htmlonly}
+\begin{code}
+
+ public void setPolicyFIFO()\begin{hide} {
+ policy = FIFO;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Set the service policy to FIFO (\emph{first in, first out}):
+ the processes are placed in the
+ list (and served) according to their order of arrival.
+\end{tabb}
+\begin{code}
+
+ public void setPolicyLIFO()\begin{hide} {
+ policy = LIFO;
+ }\end{hide}
+\end{code}
+\begin{tabb} Set the service policy to LIFO (\emph{last in, first out}):
+ the processes are placed in the
+ list (and served) according to their inverse order of arrival,
+ the last arrived are served first.
+% \texttt{PRIOR} (priority): the processes must give a priority number
+% when they ask for the resource \texttt{requestWithPriority}, and they are
+% ordered in the waiting list by order of priority (those with
+% highest priority are served first).
+% \pierre {Not yet implemented. Resources with priorities should be
+% implemented as a subclass of \texttt{Resource}.}
+\end{tabb}
+\begin{code}
+
+ public void changeCapacity (int diff) \begin{hide} {
+ if (diff > 0) {
+ available += diff;
+ capacity += diff;
+ if (waitingList.size() > 0) startNewCust();
+ }
+ else {
+ if (-diff > available)
+ throw new IllegalArgumentException("Trying to diminish the capacity "
+ + "of a resource more than its current availability");
+ available -= -diff;
+ capacity -= -diff;
+ }
+ if (stats) statCapacity.update (capacity);
+ }\end{hide}
+\end{code}
+\begin{tabb} Modifies by \texttt{diff} units (increases if \texttt{diff > 0},
+ decreases if \texttt{diff < 0}) the capacity (i.e., the number of units)
+ of the resource.
+ If \texttt{diff > 0} and there are processes in the waiting list whose
+ request can now be satisfied, they obtain the resource.
+ If \texttt{diff < 0}, there must be at least \texttt{diff} units of this
+ resource available, otherwise an illegal argument exception will be thrown,
+ printing an error message (this is not a strong limitation, because one
+ can check first and release some units, if needed, before invoking
+ \texttt{changeCapacity}).
+ In particular, the capacity of a resource can never become negative.
+\end{tabb}
+\begin{htmlonly}
+ \param{diff}{number of capacity units to add to the actual capacity,
+ can be negative to reduce the capacity}
+ \exception{IllegalArgumentException}{if \texttt{diff} is negative and
+ the capacity cannot be reduced as required}
+ \exception{IllegalStateException}{if \texttt{diff} is negative and
+ the capacity could not be reduced due to a lack of available units}
+\end{htmlonly}
+\begin{code}
+
+ public void setCapacity (int newcap) \begin{hide} {
+ if (newcap < 0)
+ throw new IllegalArgumentException ("capacity cannot be negative");
+ changeCapacity (newcap - capacity);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the capacity to \texttt{newcap}.
+ Equivalent to \method{changeCapacity}{}~\texttt{(newcap - old)} if \texttt{old} is the
+ current capacity.
+\end{tabb}
+\begin{htmlonly}
+ \param{newcap}{new capacity of the resource}
+ \exception{IllegalArgumentException}{if the passed capacity is negative}
+ \exception{IllegalStateException}{if \texttt{diff} is negative and
+ the capacity could not be reduced due to a lack of available units}
+\end{htmlonly}
+\begin{code}
+
+ public int getAvailable() \begin{hide} {
+ return available;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the number of available units, i.e., the capacity
+ minus the number of units busy.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of available units}
+\end{htmlonly}
+\begin{code}
+
+ public void request (int n) \begin{hide} {
+ SimProcess p = sim.currentProcess();
+ UserRecord record = new UserRecord (n, p, sim.time());
+ if (n <= available) {
+ // The process gets the resource right away.
+ available -= n;
+ serviceList.addLast (record);
+ if (stats) {
+ waitingList.statSojourn().add (0.0);
+ statUtil.update (capacity - available);
+ }
+ }
+ else {
+ // Not enough units of the resource are available.
+ // The process joins the queue waitingList;
+ switch (policy) {
+ case FIFO : waitingList.addLast (record); break;
+ case LIFO : waitingList.addFirst (record); break;
+ default : throw new IllegalStateException(
+ "policy must be FIFO or LIFO");
+ }
+ p.suspend();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} The executing process invoking this method requests for
+ \texttt{n} units of this resource. If there are enough units available
+ to fill up the request immediately, the process obtains the desired
+ number of units and holds them until it invokes \method{release}{}
+ for this same resource. The process is also inserted into the
+ \method{servList}{} list for this resource.
+ On the other hand, if there are less than \texttt{n} units of this
+ resource available, the executing process is placed into the
+ \method{waitList}{} list (the queue) for this resource and is suspended
+ until it can obtain the requested number of units of the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{number of required units}
+\end{htmlonly}
+\iffalse %%%%%%%%
+ public void requestWithPriority (int n, int prior) {
+ Throw new UnsupportedOperationException
+ ("requestWithPriority not yet implemented");
+ }
+\begin{tabb} Similar to \texttt{request}, except that the resource is requested
+ with a priority value \texttt{prior}. This method must be used when the
+ service policy for this resource is of type \texttt{Priority}.
+ Higher priorities are ahead in the waiting queue, and processes with
+ equal priorities are served according to their order of arrival.
+ \pierre {This method should appear only in a subclass of \texttt{Resource}
+ called, say, \texttt{ResourceWithPrior}. }
+\end{tabb}
+\fi %%%%%%%%%
+\begin{code}\begin{hide}
+
+ // Called by \texttt{release}.
+ private void startNewCust() {
+ UserRecord record;
+ ListIterator<UserRecord> iterWait = waitingList.listIterator();
+ while (iterWait.hasNext() && available > 0) {
+ record = iterWait.next();
+ if (record.process.getState() == SimProcess.DEAD) iterWait.remove();
+ // the process was killed, so we remove it from the waiting list.
+ // or maybe we stop the program by throwing IllegalStateException
+ //"Resource.startNewCust: process not alive");
+
+ // The thread for this process is still alive.
+ else if (record.numUnits <= available) {
+ // This request can now be satisfied.
+ serviceList.addLast (record);
+ record.process.resume();
+ available -= record.numUnits;
+ iterWait.remove();
+ }
+ }
+ }\end{hide}
+
+ public void release (int n) \begin{hide} {
+ SimProcess p = sim.currentProcess();
+ int temp = 0;
+ UserRecord record;
+ ListIterator<UserRecord> iterServ = serviceList.listIterator();
+ while (temp<n && iterServ.hasNext()) {
+ record = iterServ.next();
+ if (p == record.process) {
+ temp = temp + record.numUnits;
+ if (temp <= n) {
+ iterServ.remove();
+ if (stats) statSojourn.add
+ (sim.time() - record.requestTime);
+ }
+ else {
+ record.numUnits = temp - n;
+ temp = n;
+ }
+ }
+ }
+ if (temp < n) throw new IllegalArgumentException ("trying to release "
+ +"more units of a Resource than the process currently holds");
+ available += temp;
+ if (waitingList.size() > 0) startNewCust();
+ if (stats) statUtil.update (capacity - available);
+ }\end{hide}
+\end{code}
+\begin{tabb} The executing process that invokes this method releases
+ \texttt{n} units of the resource. If this process is holding exactly
+ \texttt{n} units, it is removed from the service list of this resource
+ and its \class{UserRecord} object disappears.
+ If this process is holding less than \texttt{n} units,
+ the program throws an illegal argument exception.
+ If there are other processes waiting for this resource whose requests
+ can now be satisfied, they obtain the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{number of released units}
+ \exception{IllegalArgumentException}{if the process did not request \texttt{n}
+ units before releasing them}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat waitList() \begin{hide} {
+ return waitingList;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the list that contains the
+ \class{UserRecord} objects
+ for the processes in the waiting list for this resource.
+\end{tabb}
+\begin{htmlonly}
+ \return{the list of process user records waiting for the resource}
+\end{htmlonly}
+\begin{code}
+
+ public LinkedListStat servList() \begin{hide} {
+ return serviceList;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the list that contains the
+ \class{UserRecord} objects
+ for the processes in the service list for this resource.
+\end{tabb}
+\begin{htmlonly}
+ \return{the list of process user records using this resource}
+\end{htmlonly}
+\begin{code}
+
+ public Accumulate statOnCapacity() \begin{hide} {
+ return statCapacity;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the statistical collector that measures the evolution of
+ the capacity of the resource as a function of time.
+ This collector exists only if \method{setStatCollecting}{}~\texttt{(true)} has been invoked
+ previously.
+\end{tabb}
+\begin{htmlonly}
+ \return{the probe for resource capacity}
+\end{htmlonly}
+\begin{code}
+
+ public Accumulate statOnUtil() \begin{hide} {
+ return statUtil;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the statistical collector for the utilization
+ of the resource (number of units busy) as a function of time.
+ This collector exists only if \method{setStatCollecting}{}~\texttt{(true)}
+ has been invoked previously.
+ The {\em utilization rate\/} of a resource can be obtained as the
+ {\em time average\/} computed by this collector, divided by the
+ capacity of the resource.
+ The collector returned by \method{servList()}{}\texttt{.}%
+\externalmethod{umontreal.iro.lecuyer.simevents}{LinkedListStat}{statSize()}{}
+ tracks the number of \class{UserRecord}
+ in the service list;
+ it differs from this collector because a process may hold more than one
+ unit of the resource by given \class{UserRecord}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the probe for resource utilization}
+\end{htmlonly}
+\begin{code}
+
+ public Tally statOnSojourn() \begin{hide} {
+ return statSojourn;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the statistical collector for the sojourn times of
+ the \class{UserRecord} for this resource.
+ This collector exists only if \method{setStatCollecting}{}~\texttt{(true)} has been invoked
+ previously.
+ It gets a new observation each time a process releases all the units
+ of this resource that it had requested by a single \method{request}{}
+ call.
+\end{tabb}
+\begin{htmlonly}
+ \return{the probe for the sojourn times}
+\end{htmlonly}
+\begin{code}
+
+ public String getName()\begin{hide} {
+ return name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the name (or identifier) associated to this
+ resource. If it was not given upon resource construction, this returns
+ \texttt{null}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the name associated to this resource, or \texttt{null} if it was not given}
+\end{htmlonly}
+\begin{code}
+
+ public String report() \begin{hide} {
+
+ if (statUtil == null) throw new IllegalStateException ("Asking a report for a resource "
+ +"for which setStatCollecting (true) has not been called");
+
+ Accumulate sizeWait = waitingList.statSize();
+ Tally sojWait = waitingList.statSojourn();
+ Tally sojServ = serviceList.statSojourn();
+
+ PrintfFormat str = new PrintfFormat();
+
+ str.append ("REPORT ON RESOURCE : ").append (name).append (PrintfFormat.NEWLINE);
+ str.append (" From time : ").append (7, 2, 2, initStatTime);
+ str.append (" to time : ");
+ str.append (10,2, 2, sim.time());
+ str.append (PrintfFormat.NEWLINE + " min max average ");
+ str.append ("standard dev. nb. obs.");
+
+ str.append (PrintfFormat.NEWLINE + " Capacity ");
+ str.append (8, (int)(statCapacity.min()+0.5));
+ str.append (11, (int)(statCapacity.max()+0.5));
+ str.append (12, 3, 2, statCapacity.average());
+
+ str.append (PrintfFormat.NEWLINE + " Utilization ");
+ str.append (8, (int)(statUtil.min()+0.5));
+ str.append (11, (int)(statUtil.max()+0.5));
+ str.append (12, 3, 2, statUtil.average());
+
+ str.append (PrintfFormat.NEWLINE + " Queue Size ");
+ str.append (8, (int)(sizeWait.min()+0.5));
+ str.append (11, (int)(sizeWait.max()+0.5));
+ str.append (12, 3, 2, sizeWait.average());
+
+ str.append (PrintfFormat.NEWLINE + " Wait ");
+ str.append (12, 3, 2, sojWait.min()).append (' ');
+ str.append (10, 3, 2, sojWait.max()).append (' ');
+ str.append (11, 3, 2, sojWait.average()).append (' ');
+ str.append (10, 3, 2, sojWait.standardDeviation());
+ str.append (10, sojWait.numberObs());
+
+ str.append (PrintfFormat.NEWLINE + " Service ");
+ str.append (12, 3, 2, sojServ.min()).append (' ');
+ str.append (10, 3, 2, sojServ.max()).append (' ');
+ str.append (11, 3, 2, sojServ.average()).append (' ');
+ str.append (10, 3, 2, sojServ.standardDeviation());
+ str.append (10, sojServ.numberObs());
+
+ str.append (PrintfFormat.NEWLINE + " Sojourn ");
+ str.append (12, 3, 2, statSojourn.min()).append (' ');
+ str.append (10, 3, 2, statSojourn.max()).append (' ');
+ str.append (11, 3, 2, statSojourn.average()).append (' ');
+ str.append (10, 3, 2, statSojourn.standardDeviation());
+ str.append (10, statSojourn.numberObs()).append (PrintfFormat.NEWLINE);
+
+ return str.toString();
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Returns a string containing a complete statistical report on this
+ resource. The method \method{setStatCollecting}{}~\texttt{(true)} must have been invoked
+ previously, otherwise no statistics have been collected.
+ The report contains statistics on the waiting times, service
+ times, and waiting times for this resource, on the capacity,
+ number of units busy, and size of the queue as a function of time,
+ and on the utilization rate.
+\end{tabb}
+\begin{htmlonly}
+ \return{a statistical report for this resource, represented as a string}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/simprocs/SSJInterpretationOracle.java b/source/umontreal/iro/lecuyer/simprocs/SSJInterpretationOracle.java
new file mode 100644
index 0000000..571a91b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/SSJInterpretationOracle.java
@@ -0,0 +1,98 @@
+
+
+/*
+ * Class: SSJInterpretationOracle
+ * Description: Determines which classes should be interpreted by the DSOL
+ interpreter during process simulation
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import java.lang.reflect.Method;
+import nl.tudelft.simulation.dsol.interpreter.operations.custom.InterpreterOracleInterface;
+
+/*
+ *
+ * <p>
+ * (c) copyright 2004 <a href="http://www.simulation.tudelft.nl/dsol/">Delft
+ * University of Technology </a>, the Netherlands. <br>
+ * See for project information <a href="http://www.simulation.tudelft.nl/dsol/">
+ * www.simulation.tudelft.nl/dsol </a> <br>
+ * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
+ * License (GPL) </a>, no warranty <br>
+ *
+ * @author <a href="http://www.tbm.tudelft.nl/webstaf/peterja/index.htm"> Peter
+ * Jacobs </a>
+ * @version $Revision$ $Date$
+ * @since 1.2
+ */
+
+
+/**
+ * Determines which classes should be interpreted by the DSOL
+ * interpreter during process simulation.
+ *
+ */
+public class SSJInterpretationOracle implements InterpreterOracleInterface {
+ public boolean shouldBeInterpreted (final Method method) {
+ Class declClass = method.getDeclaringClass();
+ if (declClass == null)
+ // Should not happen
+ return true;
+ Package pack = declClass.getPackage();
+ String packName = pack == null ? null : pack.getName();
+ if (packName != null) {
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.simevents"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.stat"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.util"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.rng"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.hups"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.gof"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.probdist"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.randvar"))
+ return false;
+ if (packName.startsWith
+ ("java"))
+ return false;
+ if (packName.startsWith
+ ("nl.tudelft.simulation"))
+ return false;
+ }
+ if (declClass.equals (System.class))
+ return false;
+ return true;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/SSJInterpretationOracle.tex b/source/umontreal/iro/lecuyer/simprocs/SSJInterpretationOracle.tex
new file mode 100644
index 0000000..816575e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/SSJInterpretationOracle.tex
@@ -0,0 +1,101 @@
+\defmodule{SSJInterpretationOracle}
+
+Determines which classes should be interpreted by the DSOL
+interpreter during process simulation.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: SSJInterpretationOracle
+ * Description: Determines which classes should be interpreted by the DSOL
+ interpreter during process simulation
+ * Environment: Java
+ * Software: SSJ
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;\begin{hide}
+
+import java.lang.reflect.Method;
+import nl.tudelft.simulation.dsol.interpreter.operations.custom.InterpreterOracleInterface;
+
+/*
+ *
+ * <p>
+ * (c) copyright 2004 <a href="http://www.simulation.tudelft.nl/dsol/">Delft
+ * University of Technology </a>, the Netherlands. <br>
+ * See for project information <a href="http://www.simulation.tudelft.nl/dsol/">
+ * www.simulation.tudelft.nl/dsol </a> <br>
+ * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
+ * License (GPL) </a>, no warranty <br>
+ *
+ * @author <a href="http://www.tbm.tudelft.nl/webstaf/peterja/index.htm"> Peter
+ * Jacobs </a>
+ * @version $Revision$ $Date$
+ * @since 1.2
+ */ \end{hide}
+
+
+public class SSJInterpretationOracle implements InterpreterOracleInterface\begin{hide} {
+ public boolean shouldBeInterpreted (final Method method) {
+ Class declClass = method.getDeclaringClass();
+ if (declClass == null)
+ // Should not happen
+ return true;
+ Package pack = declClass.getPackage();
+ String packName = pack == null ? null : pack.getName();
+ if (packName != null) {
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.simevents"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.stat"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.util"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.rng"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.hups"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.gof"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.probdist"))
+ return false;
+ if (packName.startsWith
+ ("umontreal.iro.lecuyer.randvar"))
+ return false;
+ if (packName.startsWith
+ ("java"))
+ return false;
+ if (packName.startsWith
+ ("nl.tudelft.simulation"))
+ return false;
+ }
+ if (declClass.equals (System.class))
+ return false;
+ return true;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simprocs/SimProcess.java b/source/umontreal/iro/lecuyer/simprocs/SimProcess.java
new file mode 100644
index 0000000..8b3ae81
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/SimProcess.java
@@ -0,0 +1,409 @@
+
+
+/*
+ * Class: SimProcess
+ * Description: Abstract class providing process scheduling tools
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simprocs.ProcessSimulator;
+
+/**
+ * This abstract class provides process scheduling tools.
+ * Each type of process should be defined as a subclass of the
+ * class <TT>SimProcess</TT>, and must provide an implementation of the method
+ * {@link #actions actions} which describes the life of a process of this class.
+ * Whenever a process instance starts, its {@link #actions actions} method
+ * begins executing.
+ *
+ * <P>
+ * Just like an event, a process must first be constructed, then scheduled.
+ * Scheduling a process is equivalent to placing an event in the event list
+ * that will start this process when
+ * the simulation clock reaches the specified time.
+ * The {@link #toString toString} method can be overridden to return
+ * information about the process. This information will
+ * be returned when formating the event list as a string, if the
+ * process is delayed.
+ *
+ * <P>
+ * A process can be in one of the following states: <TT>INITIAL</TT>,
+ * <TT>EXECUTING</TT>, <TT>DELAYED</TT>, <TT>SUSPENDED</TT>, and <TT>DEAD</TT>
+ * .
+ * At most <EM>one</EM> process can be in the <TT>EXECUTING</TT> state at
+ * any given time, and when there is one, this executing process
+ * (called the <EM>current process</EM>) is said to <EM>have control</EM>
+ * and is executing one of the instructions of its {@link #actions actions} method.
+ * A process that has been constructed but not yet scheduled is in
+ * the <TT>INITIAL</TT> state.
+ * A process is in the <TT>DELAYED</TT> state if there is a planned event
+ * in the event list to activate it (give it control).
+ * When a process is scheduled, it is placed in the <TT>DELAYED</TT> state.
+ * It is in the <TT>SUSPENDED</TT> state if it is waiting to be reactivated
+ * (i.e., obtain control) without having an event to do so in the event list.
+ * A process can suspends itself by calling {@link #suspend suspend} directly or
+ * indirectly (e.g., by requesting a busy {@link Resource}).
+ * Usually, a <TT>SUSPENDED</TT> process must be reactivated by another process
+ * or event via the {@link #resume resume} method.
+ * A process in the <TT>DEAD</TT> state no longer exists.
+ *
+ * <P>
+ * To construct new processes, the user needs to extend {@link SimProcess}.
+ *
+ * <P>
+ * Note: the user needs to ensure that the <TT>actions</TT>
+ * method of any process can be terminated, i.e., no infinite loops.
+ * For example, using threads process-oriented simulator,
+ * if such a method never terminates, threads will not be recycled,
+ * causing memory problems.
+ *
+ */
+public class SimProcess {
+
+
+
+ /**
+ * The process has been created but not yet scheduled.
+ *
+ */
+ public static final int INITIAL = 0;
+
+
+ /**
+ * The process is the one currently executing its
+ * {@link #actions actions} method.
+ *
+ */
+ public static final int EXECUTING = 1;
+
+
+ /**
+ * The process is not executing but has an event in the event list to
+ * reactivate it later on.
+ *
+ */
+ public static final int DELAYED = 2;
+
+
+ /**
+ * The process is not executing and will have to be reactivated by another
+ * process or event later on.
+ *
+ */
+ public static final int SUSPENDED = 3;
+
+
+ /**
+ * The process has terminated its execution.
+ *
+ */
+ public static final int DEAD = 4;
+
+ // The state of a process is not kept explicitly, but
+ // can be recovered from its eventTime variable.
+ public static final double STARTING = -20.0;
+ public static final double WAITING = -10.0;
+ // Variables
+
+ protected ProcessSimulator sim;
+ // Simulator linked with this simProcess.
+
+ protected Event scheduledEvent = null;
+ // the next scheduled event
+
+
+
+ /**
+ * Constructs a new process without scheduling it
+ * and associates this new process with the default simulator; one
+ * can get additional knowledge with
+ * {@link umontreal.iro.lecuyer.simevents.Simulator Simulator} static methods.
+ * It will have to be scheduled later on.
+ * The process is in the <TT>INITIAL</TT> state.
+ *
+ */
+ public SimProcess() {
+ this((ProcessSimulator)Simulator.getDefaultSimulator());
+ }
+
+
+ /**
+ * Constructs a new process associated with <TT>sim</TT>
+ * without scheduling it. It will have to be scheduled later on.
+ * The process is in the <TT>INITIAL</TT> state.
+ *
+ * @param sim Link the current variable to ProcessSimulator <TT>sim</TT>
+ *
+ */
+ public SimProcess (ProcessSimulator sim) {
+ this.sim = sim;
+ scheduledEvent = sim.createControlEvent(this);
+ }
+
+
+ /**
+ * Schedules the process to start in <TT>delay</TT> time units.
+ * This puts the process in the <TT>DELAYED</TT> state.
+ *
+ * @param delay delay, in simulation time, before the process starts
+ *
+ *
+ */
+ public void schedule (double delay) {
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("Cannot schedule a dead process");
+ if (scheduledEvent.time() != STARTING)
+ throw new IllegalStateException ("Only a process in INITIAL state can call schedule");
+
+ scheduledEvent.schedule (delay);
+ }
+
+
+ /**
+ * Schedules this process to start at the current time, by placing
+ * it at the beginning of the event list.
+ * This puts the process in the <TT>DELAYED</TT> state.
+ *
+ */
+ public void scheduleNext() {
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("Cannot schedule a dead process");
+ if (scheduledEvent.time() != STARTING)
+ throw new IllegalStateException
+ ("Only a process in INITIAL state can call scheduleNext");
+ scheduledEvent.scheduleNext();
+ }
+
+
+ /**
+ * Returns the <TT>Event</TT> associated with the current variable.
+ *
+ */
+ public Event scheduledEvent() {
+ return scheduledEvent;
+ }
+
+
+ /**
+ * Sets the event associated to the current variable.
+ *
+ * @param scheduledEvent new scheduledEvent for the current variable
+ *
+ *
+ */
+ public void setScheduledEvent (Event scheduledEvent) {
+ this.scheduledEvent = scheduledEvent;
+ }
+
+
+ /**
+ * Returns the priority of the current variable.
+ *
+ * @return priority of the current variable.
+ *
+ */
+ public double priority() {
+ return scheduledEvent.priority();
+ }
+
+
+ /**
+ * Sets the priority assigned to the current variable in the simulation.
+ * This method should never be called after the event has been scheduled, otherwise
+ * the events will not execute in ascending priority order anymore.
+ *
+ * @param priority priority assigned to the current variable in the simulation
+ *
+ *
+ */
+ public void setPriority (double priority) {
+ scheduledEvent.setPriority(priority);
+ }
+
+
+ /**
+ * Returns <TT>true</TT> if the process is alive, otherwise <TT>false</TT>.
+ *
+ * @return <TT>true</TT> if the process is not in the <TT>DEAD</TT> state
+ *
+ */
+ public final boolean isAlive() {
+ return scheduledEvent != null;
+ }
+
+
+ /**
+ * Returns the state of the process.
+ *
+ * @return one of the process states {@link #INITIAL INITIAL},
+ * {@link #EXECUTING EXECUTING}, {@link #DELAYED DELAYED}, {@link #SUSPENDED SUSPENDED}, or
+ * {@link #DEAD DEAD}.
+ *
+ */
+ public int getState() {
+ if (scheduledEvent == null) return DEAD;
+ else if (sim.currentProcess() == this) return EXECUTING;
+ else if (scheduledEvent.time() >= 0.0) return DELAYED;
+ else if (scheduledEvent.time() == STARTING) return INITIAL;
+ else return SUSPENDED;
+ }
+
+
+ /**
+ * If the process is in the <TT>DELAYED</TT> state, returns
+ * the remaining time until the planned occurrence of its
+ * activating event.
+ * Otherwise, an illegal state exception will be thrown printing an error message.
+ *
+ * @return remaining delay before the process starts
+ * @exception IllegalStateException if the process is not in <TT>DELAYED</TT> state
+ *
+ *
+ */
+ public double getDelay() {
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("Trying to getDelay() on a dead process");
+ if (scheduledEvent.time() < 0.0)
+ throw new IllegalStateException
+ ("Calling getDelay for a process not in DELAYED state");
+ return scheduledEvent.time() - sim.time();
+ }
+
+
+ /**
+ * If the process is in the <TT>DELAYED</TT> state, removes it from
+ * the event list and reschedules it in <TT>delay</TT> units of time.
+ * Example: If the process <TT>p</TT> has called {@link #delay delay}<TT>(5.0)</TT>
+ * at time 10.0, and another process invokes {@link #reschedule reschedule}<TT>(p, 6.2)</TT>
+ * at time 13.5, then the process <TT>p</TT> will resume at time
+ *
+ * <SPAN CLASS="MATH">13.5 + 6.2 = 19.7</SPAN>.
+ *
+ * @param delay new delay, in simulation time units, before the process starts or is resumed
+ *
+ *
+ */
+ public void reschedule (double delay) {
+ if (sim.currentProcess() == this)
+ throw new IllegalStateException
+ ("reschedule() for a process in EXECUTING state");
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("reschedule() for a dead process ");
+ if (delay < 0.0) throw new IllegalArgumentException
+ ("Calling reschedule() with negative delay");
+ scheduledEvent.reschedule (delay);
+ }
+
+
+ /**
+ * Places this process at the beginning of the event list
+ * to resume its execution. If the process was <TT>DELAYED</TT>, cancels
+ * its earlier activating event.
+ *
+ */
+ public void resume() {
+ if (scheduledEvent == null) // DEAD state
+ throw new IllegalStateException ("calling resume() for a dead process");
+ if (scheduledEvent.time() >= 0.0) scheduledEvent.cancel();
+ scheduledEvent.scheduleNext();
+ }
+
+
+ /**
+ * Cancels the activating event that was supposed to resume
+ * this process, and places the process in the <TT>SUSPENDED</TT> state.
+ * This method can be invoked only for a process in the <TT>DELAYED</TT>
+ * state.
+ *
+ * @return <TT>true</TT> if the process was canceled successfully
+ *
+ */
+ public boolean cancel() {
+ if (scheduledEvent == null) // DEAD state
+ throw new IllegalStateException ("calling cancel() for a dead process");
+ if (scheduledEvent.time() < 0.0 )
+ throw new IllegalStateException
+ ("cancel() for a process not in DELAYED state");
+ boolean removed = scheduledEvent.cancel();
+ scheduledEvent.setTime (WAITING);
+ return removed;
+ }
+
+
+ /**
+ * Suspends the execution of the currently executing process and
+ * schedules it to resume its execution in <TT>delay</TT> units of simulation
+ * time. It moves in the <TT>DELAYED</TT> state.
+ * Only the process in the <TT>EXECUTING</TT> state can call this method.
+ *
+ */
+ public void delay (double delay) {
+ sim.delay(this, delay);
+ }
+
+
+ /**
+ * This method can only be invoked for the <TT>EXECUTING</TT>
+ * or a <TT>DELAYED</TT> process.
+ * If the process is <TT>EXECUTING</TT>, suspends execution.
+ * If the process is <TT>DELAYED</TT>, cancels its activating event.
+ * This places the process in the <TT>SUSPENDED</TT> state.
+ *
+ */
+ public void suspend() {
+ sim.suspend(this);
+ }
+
+
+ /**
+ * Terminates the life of this process and sets its state to
+ * <TT>DEAD</TT>, after canceling its activating event if there is one.
+ *
+ */
+ public void kill() {
+ sim.kill(this);
+ }
+
+
+ /**
+ * This is the method that is called when this process is
+ * executing. Every subclass of <TT>SimProcess</TT> that is to be
+ * instantiated must provide an implementation of this method.
+ *
+ */
+ public void actions() { };
+
+
+ /**
+ * This method calls <TT>ProcessSimulator.initDefault()</TT>,
+ * which initializes the default simulator to use processes.
+ *
+ */
+ public static void init() {
+ ProcessSimulator.initDefault();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/SimProcess.tex b/source/umontreal/iro/lecuyer/simprocs/SimProcess.tex
new file mode 100644
index 0000000..c640102
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/SimProcess.tex
@@ -0,0 +1,462 @@
+\defmodule{SimProcess}
+
+This abstract class provides process scheduling tools.
+Each type of process should be defined as a subclass of the
+class \texttt{SimProcess}, and must provide an implementation of the method
+\method{actions}{} which describes the life of a process of this class.
+Whenever a process instance starts, its \method{actions}{} method
+begins executing.
+
+Just like an event, a process must first be constructed, then scheduled.
+Scheduling a process is equivalent to placing an event in the event list
+that will start this process when
+the simulation clock reaches the specified time.
+The \method{toString}{} method can be overridden to return
+information about the process. This information will
+be returned when formating the event list as a string, if the
+process is delayed.
+
+A process can be in one of the following states: \texttt{INITIAL},
+\texttt{EXECUTING}, \texttt{DELAYED}, \texttt{SUSPENDED}, and \texttt{DEAD}
+\latex{(see the diagram)}.
+At most {\em one\/} process can be in the \texttt{EXECUTING} state at
+any given time, and when there is one, this executing process
+(called the {\em current process\/}) is said to {\em have control\/}
+and is executing one of the instructions of its \method{actions}{} method.
+% Note that the simulation executive (in the class \texttt{Sim}) is also
+% a process and it is the one who has control, e.g, when an \texttt{Event}
+% is being executed.
+% ^^^^ No longer true.
+\begin{latexonly}
+\begin{figure}[htb]
+\caption{The possible states and state transitions of a \texttt{SimProcess}}
+%
+\setlength{\unitlength}{1in}
+\begin{picture}(6.4,5.5)(0.0,0.0)\thicklines\tt
+% Les boites et le texte:
+\put(2.0,0.5){\makebox(0,0)\texttt{EXECUTING}}
+\put(3.5,2.0){\makebox(0,0)\texttt{SUSPENDED}}
+\put(2.0,3.5){\makebox(0,0)\texttt{DELAYED}}
+\put(2.0,5.0){\makebox(0,0)\texttt{INITIAL}}
+\put(2.0,0.5){\oval(3.0,0.4)}
+\put(3.5,2.0){\oval(3.0,0.4)}
+\put(2.0,3.5){\oval(3.0,0.4)}
+\put(2.0,5.0){\oval(3.0,0.4)}
+\put(0.0,5.0){\vector(1,0){0.5}}
+\put(0.25,5.2){\makebox(0,0){\rm \texttt{new}}}
+\put(1.0,4.25){\makebox(0,0){\rm \texttt{schedule}}}
+\put(1.0,4.05){\vector(0,-1){.34}}
+\put(1.0,4.80){\line(0,-1){.40}}
+\put(3.0,4.25){\makebox(0,0){\rm \texttt{scheduleNext}}}
+\put(3.0,4.05){\vector(0,-1){.34}}
+\put(3.0,4.80){\line(0,-1){.40}}
+\put(0.8,2.5){\makebox(0,0){\rm ({\em event})}}
+\put(0.8,2.35){\vector(0,-1){1.64}}
+\put(0.8,2.65){\line(0,1){.65}}
+\put(1.6,1.5){\makebox(0,0){delay}}
+\put(1.6,1.65){\vector(0,1){1.64}}
+\put(1.6,1.35){\line(0,-1){.65}}
+\put(3.2,1.4){\makebox(0,0){suspend}}
+\put(3.2,0.7){\line(0,1){0.53}}
+\put(3.2,1.55){\vector(0,1){.24}}
+\put(3.55,2.55){\makebox(0,0){suspend, cancel}}
+\put(3.3,3.3){\line(0,-1){0.6}}
+\put(3.3,2.4){\vector(0,-1){.19}}
+\put(2.4,2.95){\makebox(0,0){reschedule}}
+\put(2.4,2.75){\makebox(0,0){resume}}
+\put(2.3,2.2){\line(0,1){0.40}}
+\put(2.3,3.1){\vector(0,1){.19}}
+\put(2.9,3.3){\line(0,-1){0.7}}
+\put(2.7,2.6){\oval(0.4,0.2)[b]}
+\put(2.5,3.1){\vector(0,1){.19}}
+\put(5.5,0.5){\makebox(0,0){kill}}
+\put(3.5,0.5){\line(1,0){1.8}}
+\put(5.7,0.5){\vector(1,0){0.25}}
+\put(5.5,2.0){\makebox(0,0){kill}}
+\put(5.0,2.0){\line(1,0){0.3}}
+\put(5.7,2.0){\vector(1,0){.25}}
+\put(5.5,3.5){\makebox(0,0){kill}}
+\put(3.5,3.5){\line(1,0){1.8}}
+\put(5.7,3.5){\vector(1,0){.25}}
+\put(5.5,5.0){\makebox(0,0){kill}}
+\put(3.5,5.0){\line(1,0){1.8}}
+\put(5.7,5.0){\vector(1,0){.25}}
+\end{picture}
+\end{figure}
+\end{latexonly}
+A process that has been constructed but not yet scheduled is in
+the \texttt{INITIAL} state.
+A process is in the \texttt{DELAYED} state if there is a planned event
+in the event list to activate it (give it control).
+When a process is scheduled, it is placed in the \texttt{DELAYED} state.
+It is in the \texttt{SUSPENDED} state if it is waiting to be reactivated
+(i.e., obtain control) without having an event to do so in the event list.
+A process can suspends itself by calling \method{suspend}{} directly or
+indirectly (e.g., by requesting a busy \class{Resource}).
+Usually, a \texttt{SUSPENDED} process must be reactivated by another process
+or event via the \method{resume}{} method.
+A process in the \texttt{DEAD} state no longer exists.
+
+To construct new processes, the user needs to extend \class{SimProcess}.
+
+Note: the user needs to ensure that the \texttt{actions}
+method of any process can be terminated, i.e., no infinite loops.
+For example, using threads process-oriented simulator,
+if such a method never terminates, threads will not be recycled,
+causing memory problems.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: SimProcess
+ * Description: Abstract class providing process scheduling tools
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;\begin{hide}
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simprocs.ProcessSimulator;\end{hide}
+
+public class SimProcess \begin{hide} {
+\end{hide}
+\end{code}
+\subsubsection*{Possible states of a process}
+
+\begin{code}
+
+ public static final int INITIAL = 0;
+\end{code}
+\begin{tabb} The process has been created but not yet scheduled. \end{tabb}
+\begin{code}
+
+ public static final int EXECUTING = 1;
+\end{code}
+\begin{tabb} The process is the one currently executing its
+ \method{actions}{} method. \end{tabb}
+\begin{code}
+
+ public static final int DELAYED = 2;
+\end{code}
+\begin{tabb} The process is not executing but has an event in the event list to
+ reactivate it later on. \end{tabb}
+\begin{code}
+
+ public static final int SUSPENDED = 3;
+\end{code}
+\begin{tabb} The process is not executing and will have to be reactivated by another
+ process or event later on. \end{tabb}
+\begin{code}
+
+ public static final int DEAD = 4;
+\end{code}
+\begin{tabb} The process has terminated its execution. \end{tabb}
+\begin{code}\begin{hide}
+ // The state of a process is not kept explicitly, but
+ // can be recovered from its eventTime variable.
+ public static final double STARTING = -20.0;
+ public static final double WAITING = -10.0;
+ // Variables
+
+ protected ProcessSimulator sim;
+ // Simulator linked with this simProcess.
+
+ protected Event scheduledEvent = null;
+ // the next scheduled event
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public SimProcess() \begin{hide} {
+ this((ProcessSimulator)Simulator.getDefaultSimulator());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new process without scheduling it
+ and associates this new process with the default simulator; one
+ can get additional knowledge with
+ \externalclass{umontreal.iro.lecuyer.simevents}{Simulator} static methods.
+ It will have to be scheduled later on.
+ The process is in the \texttt{INITIAL} state.
+ \end{tabb}
+\begin{code}
+
+ public SimProcess (ProcessSimulator sim) \begin{hide} {
+ this.sim = sim;
+ scheduledEvent = sim.createControlEvent(this);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new process associated with \texttt{sim}
+ without scheduling it. It will have to be scheduled later on.
+ The process is in the \texttt{INITIAL} state.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{sim}{Link the current variable to ProcessSimulator \texttt{sim}}
+\end{htmlonly}
+
+%%%%%%
+%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void schedule (double delay) \begin{hide} {
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("Cannot schedule a dead process");
+ if (scheduledEvent.time() != STARTING)
+ throw new IllegalStateException ("Only a process in INITIAL state can call schedule");
+
+ scheduledEvent.schedule (delay);
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Schedules the process to start in \texttt{delay} time units.
+ This puts the process in the \texttt{DELAYED} state.
+ \end{tabb}
+\begin{htmlonly}
+ \param{delay}{delay, in simulation time, before the process starts}
+\end{htmlonly}
+\begin{code}
+
+ public void scheduleNext() \begin{hide} {
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("Cannot schedule a dead process");
+ if (scheduledEvent.time() != STARTING)
+ throw new IllegalStateException
+ ("Only a process in INITIAL state can call scheduleNext");
+ scheduledEvent.scheduleNext();
+ } \end{hide}
+\end{code}
+\begin{tabb} Schedules this process to start at the current time, by placing
+ it at the beginning of the event list.
+ This puts the process in the \texttt{DELAYED} state.
+ \end{tabb}
+\begin{code}
+
+ public Event scheduledEvent() \begin{hide} {
+ return scheduledEvent;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Returns the \texttt{Event} associated with the current variable.
+ \end{tabb}
+\begin{code}
+
+ public void setScheduledEvent (Event scheduledEvent) \begin{hide} {
+ this.scheduledEvent = scheduledEvent;
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the event associated to the current variable.
+ \end{tabb}
+\begin{htmlonly}
+ \param{scheduledEvent}{new scheduledEvent for the current variable}
+\end{htmlonly}
+\begin{code}
+
+ public double priority() \begin{hide} {
+ return scheduledEvent.priority();
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Returns the priority of the current variable.
+\end{tabb}
+\begin{htmlonly}
+ \return{priority of the current variable.}
+\end{htmlonly}
+\begin{code}
+
+ public void setPriority (double priority) \begin{hide} {
+ scheduledEvent.setPriority(priority);
+ } \end{hide}
+\end{code}
+\begin{tabb}
+ Sets the priority assigned to the current variable in the simulation.
+ This method should never be called after the event has been scheduled, otherwise
+ the events will not execute in ascending priority order anymore.
+ \end{tabb}
+\begin{htmlonly}
+ \param{priority}{priority assigned to the current variable in the simulation}
+\end{htmlonly}
+\begin{code}
+
+ public final boolean isAlive() \begin{hide} {
+ return scheduledEvent != null;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns \texttt{true} if the process is alive, otherwise \texttt{false}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{\texttt{true} if the process is not in the \texttt{DEAD} state}
+\end{htmlonly}
+\begin{code}
+
+ public int getState() \begin{hide} {
+ if (scheduledEvent == null) return DEAD;
+ else if (sim.currentProcess() == this) return EXECUTING;
+ else if (scheduledEvent.time() >= 0.0) return DELAYED;
+ else if (scheduledEvent.time() == STARTING) return INITIAL;
+ else return SUSPENDED;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the state of the process.
+ \end{tabb}
+\begin{htmlonly}
+ \return{one of the process states \method{INITIAL}{},
+ \method{EXECUTING}{}, \method{DELAYED}{}, \method{SUSPENDED}{}, or
+ \method{DEAD}{}.}
+\end{htmlonly}
+\begin{code}
+
+ public double getDelay() \begin{hide} {
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("Trying to getDelay() on a dead process");
+ if (scheduledEvent.time() < 0.0)
+ throw new IllegalStateException
+ ("Calling getDelay for a process not in DELAYED state");
+ return scheduledEvent.time() - sim.time();
+ } \end{hide}
+\end{code}
+ \begin{tabb} If the process is in the \texttt{DELAYED} state, returns
+ the remaining time until the planned occurrence of its
+ activating event.
+ Otherwise, an illegal state exception will be thrown printing an error message.
+ \end{tabb}
+\begin{htmlonly}
+ \return{remaining delay before the process starts}
+ \exception{IllegalStateException}{if the process is not in \texttt{DELAYED} state}
+\end{htmlonly}
+\begin{code}
+
+ public void reschedule (double delay) \begin{hide} {
+ if (sim.currentProcess() == this)
+ throw new IllegalStateException
+ ("reschedule() for a process in EXECUTING state");
+ if (scheduledEvent == null)
+ throw new IllegalStateException ("reschedule() for a dead process ");
+ if (delay < 0.0) throw new IllegalArgumentException
+ ("Calling reschedule() with negative delay");
+ scheduledEvent.reschedule (delay);
+ } \end{hide}
+\end{code}
+ \begin{tabb} If the process is in the \texttt{DELAYED} state, removes it from
+ the event list and reschedules it in \texttt{delay} units of time.
+ Example: If the process \texttt{p} has called \method{delay}{}\texttt{(5.0)}
+ at time 10.0, and another process invokes \method{reschedule}{}\texttt{(p, 6.2)}
+ at time 13.5, then the process \texttt{p} will resume at time
+ $13.5 + 6.2 = 19.7$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{delay}{new delay, in simulation time units, before the process starts or is resumed}
+\end{htmlonly}
+\begin{code}
+
+ public void resume() \begin{hide} {
+ if (scheduledEvent == null) // DEAD state
+ throw new IllegalStateException ("calling resume() for a dead process");
+ if (scheduledEvent.time() >= 0.0) scheduledEvent.cancel();
+ scheduledEvent.scheduleNext();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Places this process at the beginning of the event list
+ to resume its execution. If the process was \texttt{DELAYED}, cancels
+ its earlier activating event.
+ \end{tabb}
+\begin{code}
+
+ public boolean cancel() \begin{hide} {
+ if (scheduledEvent == null) // DEAD state
+ throw new IllegalStateException ("calling cancel() for a dead process");
+ if (scheduledEvent.time() < 0.0 )
+ throw new IllegalStateException
+ ("cancel() for a process not in DELAYED state");
+ boolean removed = scheduledEvent.cancel();
+ scheduledEvent.setTime (WAITING);
+ return removed;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Cancels the activating event that was supposed to resume
+ this process, and places the process in the \texttt{SUSPENDED} state.
+ This method can be invoked only for a process in the \texttt{DELAYED}
+ state.
+ \end{tabb}
+\begin{htmlonly}
+ \return{\texttt{true} if the process was canceled successfully}
+\end{htmlonly}
+\begin{code}
+
+ public void delay (double delay) \begin{hide} {
+ sim.delay(this, delay);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Suspends the execution of the currently executing process and
+ schedules it to resume its execution in \texttt{delay} units of simulation
+ time. It moves in the \texttt{DELAYED} state.
+ Only the process in the \texttt{EXECUTING} state can call this method.
+ \end{tabb}
+\begin{code}
+
+ public void suspend() \begin{hide} {
+ sim.suspend(this);
+ } \end{hide}
+\end{code}
+ \begin{tabb} This method can only be invoked for the \texttt{EXECUTING}
+ or a \texttt{DELAYED} process.
+ If the process is \texttt{EXECUTING}, suspends execution.
+ If the process is \texttt{DELAYED}, cancels its activating event.
+ This places the process in the \texttt{SUSPENDED} state.
+ \end{tabb}
+\begin{code}
+
+ public void kill() \begin{hide} {
+ sim.kill(this);
+ } \end{hide}
+\end{code}
+ \begin{tabb} Terminates the life of this process and sets its state to
+ \texttt{DEAD}, after canceling its activating event if there is one.
+ \end{tabb}
+\begin{code}
+
+ public void actions()\begin{hide} { }; \end{hide}
+\end{code}
+ \begin{tabb} This is the method that is called when this process is
+ executing. Every subclass of \texttt{SimProcess} that is to be
+ instantiated must provide an implementation of this method.
+ \end{tabb}
+\begin{code}
+
+ public static void init() \begin{hide} {
+ ProcessSimulator.initDefault();
+ } \end{hide}
+\end{code}
+ \begin{tabb} This method calls \texttt{ProcessSimulator.initDefault()},
+ which initializes the default simulator to use processes.
+ \end{tabb}
+\begin{code} \begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simprocs/ThreadProcessSimulator.java b/source/umontreal/iro/lecuyer/simprocs/ThreadProcessSimulator.java
new file mode 100644
index 0000000..744167c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/ThreadProcessSimulator.java
@@ -0,0 +1,314 @@
+
+
+/*
+ * Class: ThreadProcessSimulator
+ * Description: process simulator using Java threads for process synchronization
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simprocs.SimProcess;
+
+/**
+ * Represents a process simulator using Java threads for process synchronization.
+ * The simulation process threads are synchronized so only one process runs
+ * at a time.
+ *
+ */
+public class ThreadProcessSimulator extends ProcessSimulator {
+
+ private SimThread threadAllHead = null;
+ //Tete de la liste des SimThread associated to this ThreadProcessSimulator
+
+
+ /**
+ * Creates a new {@link ThreadProcessSimulator} variable.
+ *
+ */
+ public ThreadProcessSimulator() {
+ }
+
+ /**
+ * Initializes the thread process-driven simulation using
+ * {@link umontreal.iro.lecuyer.simevents.eventlist.SplayTree SplayTree} algorithm
+ * as {@link umontreal.iro.lecuyer.simevents.eventlist.EventList EventList}.
+ * This kills all processes already associated with the current variable.
+ *
+ */
+ public void init() {
+ super.init();
+ /*if(threadAllHead != null)
+ killAll();
+ threadAllHead = null; //!!!!! utilisation de killAll instable !!!!!*/
+ }
+
+
+ /**
+ * Initializes the thread process-driven simulation
+ * using <TT>evlist</TT> variable as {@link EventList}.
+ * This kills all processes already associated with the current variable.
+ *
+ * @param evlist EventList assigned to the current variable eventlist field
+ *
+ *
+ */
+ public void init (EventList evlist) {
+ super.init (evlist);
+ /*if(threadAllHead != null)
+ killAll(); //!!!!! utilisation de killAll instable !!!!!*/
+ }
+
+
+ public SimThread createControlEvent (SimProcess process) {
+ return SimThread.getThread(process, this);
+ }
+
+ public void delay (SimProcess process, double delay) {
+ if (currentProcess != process)
+ throw new IllegalStateException ("Calling delay() for a process not in EXECUTING state");
+ if (delay < 0.0)
+ throw new IllegalArgumentException ("Calling delay() with negative delay");
+ process.scheduledEvent().schedule (delay);
+ dispatch();
+ ((SimThread)process.scheduledEvent()).passivate();
+ }
+
+ public void suspend(SimProcess process) {
+ SimThread ev = (SimThread)process.scheduledEvent();
+
+ if (ev == null) // DEAD state
+ throw new IllegalStateException ("Calling suspend() for a dead process");
+
+ if (currentProcess == process) { // EXECUTING state
+ dispatch();
+ ev.passivate();
+ return;
+ }
+
+ if (ev.time() >= 0.0 ) { // DELAYED state
+ ev.cancel();
+ ev.setTime (SimProcess.WAITING);
+ return;
+ }
+
+ if (ev.time() == SimProcess.STARTING ) // INITIAL state
+ throw new IllegalStateException
+ ("Calling suspend() for a process in INITIAL state");
+
+ // SUSPENDED state
+ throw new IllegalStateException ("Calling suspend() for a suspended process");
+ }
+
+ public void kill(SimProcess process) {
+ if (process.scheduledEvent() == null)
+ throw new IllegalStateException ("cannot kill a DEAD process");
+ ((SimThread)process.scheduledEvent()).kill();
+ }
+
+ /**
+ * Kills all threads linked to the current variable.
+ *
+ */
+ public void killAll() {
+ SimThread.killAll(this);
+ }
+
+
+
+ // This method looks for the next process to give it the control.
+ // If there are some event in the eventList, they will be executed
+ // This method doesn't reactivate the executive.
+ // Its behavior is the same of the executive (see the Sim.start method)
+ protected void dispatch() {
+ Event ev;
+ while ((ev = removeFirstEvent()) != null) {
+ if (ev instanceof SimThread) {
+ // This is a process, the control will transfered to it.
+ currentProcess = ((SimThread)ev).myProcess;
+ ((SimThread)ev).activate();
+ return;
+ }
+ else ev.actions();
+ // This event is executed by the calling process.
+ }
+ SimThread.simActivate(this); // Simulation is over.
+ }
+
+ protected SimThread threadAllHead() {
+ return threadAllHead;
+ }
+
+ protected void setThreadAllHead(SimThread thread) {
+ threadAllHead = thread;
+ }
+}
+
+
+
+
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%% SimThread %%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// thrown by the passivate() method and caught by the run() method.
+// used to kill a process and recycle its associated thread.
+// This empty class allows to control which Errors are thrown.
+// If the JVM happens to throw an Error, it must not be caught
+// by the SimThread class.
+final class SimThreadError extends Error {
+ public SimThreadError() {}
+}
+
+
+final class SimThread extends Event implements Runnable {
+ static SimThreadError error = new SimThreadError();
+ // thrown by the passivate() method and caught by the run() method.
+ // used to kill a process and recycle its associated thread.
+
+ private int n = 0;
+ // Semaphore: if n < 0 this thread must wait.
+
+ SimProcess myProcess;
+ // The Process to which this thread is associated.
+
+ private Thread myThread;
+ // The Thread in which this SimThread object will run.
+
+ // link with the next thread in the list headed by sim.threadAllHead, used by killAll().
+ private SimThread nextAll = null;
+
+ // Head of the free SimThread list
+ private static SimThread threadFreeHead = null;
+ // link with the next thread in the list
+ private SimThread nextFree = null;
+
+ // Constructor.
+ private SimThread (SimProcess p, ThreadProcessSimulator inSim) {
+ super(inSim);
+ eventTime = SimProcess.STARTING;
+ myProcess = p;
+ myThread = new Thread (this);
+ myThread.setDaemon (true);
+ myThread.start();
+ nextAll = ((ThreadProcessSimulator)sim).threadAllHead();
+ ((ThreadProcessSimulator)sim).setThreadAllHead(this);
+ }
+
+ public static final SimThread getThread (SimProcess p, ThreadProcessSimulator inSim) {
+ if (threadFreeHead == null) return new SimThread (p, inSim);
+ SimThread th = threadFreeHead; threadFreeHead = threadFreeHead.nextFree;
+ th.myProcess = p;
+ th.eventTime = SimProcess.STARTING;
+ th.sim = inSim;
+ th.priority = 1.0;
+ return th;
+ }
+
+ // A SimThread object created and started is never destroyed
+ // till the end of the simulation.
+ // if the associated process is killed, an error exception will be thrown.
+ // this exception will be caught here and the thread will be
+ // passivated (in the next iteration).
+
+ public final void run() {
+ while(true) {
+ try {
+ passivate();
+ myProcess.actions(); // myProcess starts its life.
+ ((ThreadProcessSimulator)sim).dispatch(); // Give control to another process.
+ } catch (SimThreadError e) {} // An SimThreadError exception is thrown because
+ // the process is killed.
+ myProcess.setScheduledEvent(null);
+ myProcess = null;
+ nextFree = threadFreeHead; threadFreeHead = this;
+ }
+ }
+
+ public void actions() {
+ // This method will be executed only once.
+ // It transfers the control from the executive to this thread.
+ // The control will then be passed from process to process,
+ // which will execute the events if any.
+ // Control will be returned to the executive only at the end of simulation.
+ ((ThreadProcessSimulator)sim).setCurrentProcess(myProcess);
+ activate(); // Notify this thread to be ready to take control
+ // as soon as the caller's thread is passivated.
+ simPassivate(((ThreadProcessSimulator)sim)); // Passivate the main thread (executive).
+ }
+
+
+ protected synchronized final void activate() {
+ // Notifies this thread to be ready to take control.
+ // It will take control when the calling thread passivates.
+ n++; notify();
+ }
+
+
+ protected synchronized final void passivate() {
+ try {n--; if (n<0) wait();}
+ catch (InterruptedException e) {
+ n++; throw error;
+ // This thread is waken up and interrupted because
+ // the kill() method was called on its associated process.
+ // Throws a SimThreadError that
+ // will be caught by run() method.
+ }
+ }
+
+
+ protected static final void simActivate(ThreadProcessSimulator sim) {
+ EventList evl = sim.getEventList();
+ synchronized (evl) {evl.notify(); }
+ }
+
+ protected static final void simPassivate(ThreadProcessSimulator sim) {
+ EventList evl = sim.getEventList();
+ synchronized (evl) {
+ try { evl.wait(); } catch (InterruptedException e) {}
+ }
+ }
+
+ // The following methods are called by kill and killAll
+ // of the Process class.
+
+ protected void kill() {
+ if (eventTime >= 0.0)
+ cancel();
+ myThread.interrupt();
+ }
+
+ protected static void killAll(ThreadProcessSimulator sim) {
+ SimThread th = sim.threadAllHead();
+ while (th != null) {
+ if (th.myProcess != null) th.kill();
+ th = th.nextAll;
+
+ }
+ }
+
+ public String toString() {
+ // To get something useful when printing the event list
+ return "Start or resume process " + myProcess.toString();
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/simprocs/ThreadProcessSimulator.tex b/source/umontreal/iro/lecuyer/simprocs/ThreadProcessSimulator.tex
new file mode 100644
index 0000000..b2b3efc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/ThreadProcessSimulator.tex
@@ -0,0 +1,321 @@
+\defmodule{ThreadProcessSimulator}
+
+Represents a process simulator using Java threads for process synchronization.
+The simulation process threads are synchronized so only one process runs
+at a time.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: ThreadProcessSimulator
+ * Description: process simulator using Java threads for process synchronization
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;\begin{hide}
+
+import umontreal.iro.lecuyer.simevents.Event;
+import umontreal.iro.lecuyer.simevents.eventlist.EventList;
+import umontreal.iro.lecuyer.simevents.Simulator;
+import umontreal.iro.lecuyer.simprocs.SimProcess;\end{hide}
+
+public class ThreadProcessSimulator extends ProcessSimulator \begin{hide} {
+
+ private SimThread threadAllHead = null;
+ //Tete de la liste des SimThread associated to this ThreadProcessSimulator
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+ public ThreadProcessSimulator() \begin{hide} {
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new \class{ThreadProcessSimulator} variable.
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public void init() \begin{hide} {
+ super.init();
+ /*if(threadAllHead != null)
+ killAll();
+ threadAllHead = null; //!!!!! utilisation de killAll instable !!!!!*/
+ } \end{hide}
+\end{code}
+\begin{tabb} Initializes the thread process-driven simulation using
+ \externalclass{umontreal.iro.lecuyer.simevents.eventlist}{SplayTree} algorithm
+ as \externalclass{umontreal.iro.lecuyer.simevents.eventlist}{EventList}.
+ This kills all processes already associated with the current variable.
+\end{tabb}
+\begin{code}
+
+ public void init (EventList evlist) \begin{hide} {
+ super.init (evlist);
+ /*if(threadAllHead != null)
+ killAll(); //!!!!! utilisation de killAll instable !!!!!*/
+ } \end{hide}
+\end{code}
+\begin{tabb} Initializes the thread process-driven simulation
+ using \texttt{evlist} variable as \class{EventList}.
+ This kills all processes already associated with the current variable.
+\end{tabb}
+\begin{htmlonly}
+ \param{evlist}{EventList assigned to the current variable eventlist field}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ public SimThread createControlEvent (SimProcess process) {
+ return SimThread.getThread(process, this);
+ }
+
+ public void delay (SimProcess process, double delay) {
+ if (currentProcess != process)
+ throw new IllegalStateException ("Calling delay() for a process not in EXECUTING state");
+ if (delay < 0.0)
+ throw new IllegalArgumentException ("Calling delay() with negative delay");
+ process.scheduledEvent().schedule (delay);
+ dispatch();
+ ((SimThread)process.scheduledEvent()).passivate();
+ }
+
+ public void suspend(SimProcess process) {
+ SimThread ev = (SimThread)process.scheduledEvent();
+
+ if (ev == null) // DEAD state
+ throw new IllegalStateException ("Calling suspend() for a dead process");
+
+ if (currentProcess == process) { // EXECUTING state
+ dispatch();
+ ev.passivate();
+ return;
+ }
+
+ if (ev.time() >= 0.0 ) { // DELAYED state
+ ev.cancel();
+ ev.setTime (SimProcess.WAITING);
+ return;
+ }
+
+ if (ev.time() == SimProcess.STARTING ) // INITIAL state
+ throw new IllegalStateException
+ ("Calling suspend() for a process in INITIAL state");
+
+ // SUSPENDED state
+ throw new IllegalStateException ("Calling suspend() for a suspended process");
+ }
+
+ public void kill(SimProcess process) {
+ if (process.scheduledEvent() == null)
+ throw new IllegalStateException ("cannot kill a DEAD process");
+ ((SimThread)process.scheduledEvent()).kill();
+ }
+\end{hide}
+ public void killAll() \begin{hide} {
+ SimThread.killAll(this);
+ } \end{hide}
+\end{code}
+\begin{tabb} Kills all threads linked to the current variable.
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ // This method looks for the next process to give it the control.
+ // If there are some event in the eventList, they will be executed
+ // This method doesn't reactivate the executive.
+ // Its behavior is the same of the executive (see the Sim.start method)
+ protected void dispatch() {
+ Event ev;
+ while ((ev = removeFirstEvent()) != null) {
+ if (ev instanceof SimThread) {
+ // This is a process, the control will transfered to it.
+ currentProcess = ((SimThread)ev).myProcess;
+ ((SimThread)ev).activate();
+ return;
+ }
+ else ev.actions();
+ // This event is executed by the calling process.
+ }
+ SimThread.simActivate(this); // Simulation is over.
+ }
+
+ protected SimThread threadAllHead() {
+ return threadAllHead;
+ }
+
+ protected void setThreadAllHead(SimThread thread) {
+ threadAllHead = thread;
+ }
+}
+
+
+
+
+
+// %%%%%%%%%%%%%%%%%%%%%%%%%%%% SimThread %%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// thrown by the passivate() method and caught by the run() method.
+// used to kill a process and recycle its associated thread.
+// This empty class allows to control which Errors are thrown.
+// If the JVM happens to throw an Error, it must not be caught
+// by the SimThread class.
+final class SimThreadError extends Error {
+ public SimThreadError() {}
+}
+
+
+final class SimThread extends Event implements Runnable {
+ static SimThreadError error = new SimThreadError();
+ // thrown by the passivate() method and caught by the run() method.
+ // used to kill a process and recycle its associated thread.
+
+ private int n = 0;
+ // Semaphore: if n < 0 this thread must wait.
+
+ SimProcess myProcess;
+ // The Process to which this thread is associated.
+
+ private Thread myThread;
+ // The Thread in which this SimThread object will run.
+
+ // link with the next thread in the list headed by sim.threadAllHead, used by killAll().
+ private SimThread nextAll = null;
+
+ // Head of the free SimThread list
+ private static SimThread threadFreeHead = null;
+ // link with the next thread in the list
+ private SimThread nextFree = null;
+
+ // Constructor.
+ private SimThread (SimProcess p, ThreadProcessSimulator inSim) {
+ super(inSim);
+ eventTime = SimProcess.STARTING;
+ myProcess = p;
+ myThread = new Thread (this);
+ myThread.setDaemon (true);
+ myThread.start();
+ nextAll = ((ThreadProcessSimulator)sim).threadAllHead();
+ ((ThreadProcessSimulator)sim).setThreadAllHead(this);
+ }
+
+ public static final SimThread getThread (SimProcess p, ThreadProcessSimulator inSim) {
+ if (threadFreeHead == null) return new SimThread (p, inSim);
+ SimThread th = threadFreeHead; threadFreeHead = threadFreeHead.nextFree;
+ th.myProcess = p;
+ th.eventTime = SimProcess.STARTING;
+ th.sim = inSim;
+ th.priority = 1.0;
+ return th;
+ }
+
+ // A SimThread object created and started is never destroyed
+ // till the end of the simulation.
+ // if the associated process is killed, an error exception will be thrown.
+ // this exception will be caught here and the thread will be
+ // passivated (in the next iteration).
+
+ public final void run() {
+ while(true) {
+ try {
+ passivate();
+ myProcess.actions(); // myProcess starts its life.
+ ((ThreadProcessSimulator)sim).dispatch(); // Give control to another process.
+ } catch (SimThreadError e) {} // An SimThreadError exception is thrown because
+ // the process is killed.
+ myProcess.setScheduledEvent(null);
+ myProcess = null;
+ nextFree = threadFreeHead; threadFreeHead = this;
+ }
+ }
+
+ public void actions() {
+ // This method will be executed only once.
+ // It transfers the control from the executive to this thread.
+ // The control will then be passed from process to process,
+ // which will execute the events if any.
+ // Control will be returned to the executive only at the end of simulation.
+ ((ThreadProcessSimulator)sim).setCurrentProcess(myProcess);
+ activate(); // Notify this thread to be ready to take control
+ // as soon as the caller's thread is passivated.
+ simPassivate(((ThreadProcessSimulator)sim)); // Passivate the main thread (executive).
+ }
+
+
+ protected synchronized final void activate() {
+ // Notifies this thread to be ready to take control.
+ // It will take control when the calling thread passivates.
+ n++; notify();
+ }
+
+
+ protected synchronized final void passivate() {
+ try {n--; if (n<0) wait();}
+ catch (InterruptedException e) {
+ n++; throw error;
+ // This thread is waken up and interrupted because
+ // the kill() method was called on its associated process.
+ // Throws a SimThreadError that
+ // will be caught by run() method.
+ }
+ }
+
+
+ protected static final void simActivate(ThreadProcessSimulator sim) {
+ EventList evl = sim.getEventList();
+ synchronized (evl) {evl.notify(); }
+ }
+
+ protected static final void simPassivate(ThreadProcessSimulator sim) {
+ EventList evl = sim.getEventList();
+ synchronized (evl) {
+ try { evl.wait(); } catch (InterruptedException e) {}
+ }
+ }
+
+ // The following methods are called by kill and killAll
+ // of the Process class.
+
+ protected void kill() {
+ if (eventTime >= 0.0)
+ cancel();
+ myThread.interrupt();
+ }
+
+ protected static void killAll(ThreadProcessSimulator sim) {
+ SimThread th = sim.threadAllHead();
+ while (th != null) {
+ if (th.myProcess != null) th.kill();
+ th = th.nextAll;
+
+ }
+ }
+
+ public String toString() {
+ // To get something useful when printing the event list
+ return "Start or resume process " + myProcess.toString();
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/simprocs/UserRecord.java b/source/umontreal/iro/lecuyer/simprocs/UserRecord.java
new file mode 100644
index 0000000..8bd47cc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/UserRecord.java
@@ -0,0 +1,100 @@
+
+
+/*
+ * Class: UserRecord
+ * Description: A record object to store information related to the request
+ of a process for a Resource or for Bin tokens, or when a
+ process waits for a Condition.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.simprocs;
+
+import umontreal.iro.lecuyer.simprocs.SimProcess;
+
+
+/**
+ * This class represents a record object to store
+ * information related to the request of a process for a
+ * {@link Resource} or for {@link Bin} tokens,
+ * or when a process waits for a {@link Condition}.
+ * A {@link UserRecord} is created for each process request.
+ * The record contains the number of units requested
+ * or used, the associated process, and the
+ * simulation time when the request was made.
+ * Lists of processes waiting for a {@link Resource},
+ * {@link Bin}, or {@link Condition}, for example, contain
+ * {@link UserRecord} objects.
+ *
+ */
+public class UserRecord {
+ // Nb. of units taken for this record.
+ protected int numUnits;
+
+ // Process associated to the record
+ protected SimProcess process;
+
+ // Priority of this process.
+ // protected double priority;
+
+ // Time of the record creation
+ protected double requestTime;
+
+ // Constructor.
+ // We do not want the user to construct such objects.
+ protected UserRecord (int n, SimProcess p, double requestTime) {
+ numUnits = n;
+ process = p;
+ this.requestTime = requestTime;
+ }
+
+ /**
+ * Returns the number of units requested or used
+ * by the associated process.
+ *
+ * @return the number of requested or used units
+ *
+ */
+ public int getNumUnits() {
+ return numUnits;
+ }
+
+
+ /**
+ * Returns the process object associated with this record.
+ *
+ * @return the process associated to this record
+ *
+ */
+ public SimProcess getProcess() {
+ return process;
+ }
+
+
+ /**
+ * Returns the time of creation of this record.
+ *
+ * @return the simulation time of the record creation
+ */
+ public double getRequestTime() {
+ return requestTime;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/simprocs/UserRecord.tex b/source/umontreal/iro/lecuyer/simprocs/UserRecord.tex
new file mode 100644
index 0000000..48d2a83
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/UserRecord.tex
@@ -0,0 +1,105 @@
+\defmodule{UserRecord}
+
+This class represents a record object to store
+information related to the request of a process for a
+\class{Resource} or for \class{Bin} tokens,
+or when a process waits for a \class{Condition}.
+A \class{UserRecord} is created for each process request.
+The record contains the number of units requested
+or used, the associated process, and the
+simulation time when the request was made.
+Lists of processes waiting for a \class{Resource},
+\class{Bin}, or \class{Condition}, for example, contain
+\class{UserRecord} objects.
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: UserRecord
+ * Description: A record object to store information related to the request
+ of a process for a Resource or for Bin tokens, or when a
+ process waits for a Condition.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.simprocs;\begin{hide}
+
+import umontreal.iro.lecuyer.simprocs.SimProcess;
+\end{hide}
+
+public class UserRecord \begin{hide} {
+ // Nb. of units taken for this record.
+ protected int numUnits;
+
+ // Process associated to the record
+ protected SimProcess process;
+
+ // Priority of this process.
+ // protected double priority;
+
+ // Time of the record creation
+ protected double requestTime;
+
+ // Constructor.
+ // We do not want the user to construct such objects.
+ protected UserRecord (int n, SimProcess p, double requestTime) {
+ numUnits = n;
+ process = p;
+ this.requestTime = requestTime;
+ }\end{hide}
+
+ public int getNumUnits()\begin{hide} {
+ return numUnits;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the number of units requested or used
+ by the associated process.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of requested or used units}
+\end{htmlonly}
+\begin{code}
+
+ public SimProcess getProcess()\begin{hide} {
+ return process;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the process object associated with this record.
+% This is the process that created the record.
+\end{tabb}
+\begin{htmlonly}
+ \return{the process associated to this record}
+\end{htmlonly}
+\begin{code}
+
+ public double getRequestTime()\begin{hide} {
+ return requestTime;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Returns the time of creation of this record.
+\end{tabb}
+\begin{htmlonly}
+ \return{the simulation time of the record creation}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/simprocs/guidesimprocs.bbl b/source/umontreal/iro/lecuyer/simprocs/guidesimprocs.bbl
new file mode 100644
index 0000000..748f2c2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/guidesimprocs.bbl
@@ -0,0 +1,53 @@
+\begin{thebibliography}{1}
+
+\bibitem{sBIR86a}
+G.~M. Birtwistle, G.~Lomow, B.~Unger, and P.~Luker.
+\newblock Process style packages for discrete event modelling.
+\newblock {\em Transactions of the Society for Computer Simulation},
+ 3-4:279--318, 1986.
+
+\bibitem{sFRA77a}
+W.~R. Franta.
+\newblock {\em The Process View of Simulation}.
+\newblock North Holland, New York, NY, 1977.
+
+\bibitem{iJAC05a}
+P.~H.~M. Jacobs.
+\newblock {\em DSOL: an open source, {J}ava based, suite for continuous and
+ discrete event simulation}.
+\newblock Technische Universiteit Delft, Delft, Netherlands, 2005.
+\newblock Available at \url{http://www.simulation.tudelft.nl/dsol/}.
+
+\bibitem{sJAC04a}
+P.~H.~M. Jacobs and A.~Verbraeck.
+\newblock Single-threaded specification of process-interaction formalism in
+ {J}ava.
+\newblock In R.~G. Ingalls, M.~D. Rosetti, J.~S. Smith, and B.~A. Peters,
+ editors, {\em Proceedings of the 2004 Winter Simulation Conference}, pages
+ 1548--1555. {IEEE} Press, 2004.
+
+\bibitem{sKRE86a}
+W.~Kreutzer.
+\newblock {\em System Simulation - Programming Styles and Languages}.
+\newblock Addison Wesley, New York, NY, 1986.
+
+\bibitem{sLAW00a}
+A.~M. Law and W.~D. Kelton.
+\newblock {\em Simulation Modeling and Analysis}.
+\newblock McGraw-Hill, New York, NY, third edition, 2000.
+
+\bibitem{sLEC05a}
+P.~L'Ecuyer and E.~Buist.
+\newblock Simulation in {J}ava with {SSJ}.
+\newblock In M.~E. Kuhl, N.~M. Steiger, F.~B. Armstrong, and J.~A. Joines,
+ editors, {\em Proceedings of the 2005 Winter Simulation Conference}, pages
+ 611--620, Piscataway, NJ, 2005. {IEEE} Press.
+
+\bibitem{sLEC02a}
+P.~L'Ecuyer, L.~Meliani, and J.~Vaucher.
+\newblock {SSJ}: A framework for stochastic simulation in {J}ava.
+\newblock In E.~Y\"ucesan, C.-H. Chen, J.~L. Snowdon, and J.~M. Charnes,
+ editors, {\em Proceedings of the 2002 Winter Simulation Conference}, pages
+ 234--242. {IEEE} Press, 2002.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/simprocs/guidesimprocs.tex b/source/umontreal/iro/lecuyer/simprocs/guidesimprocs.tex
new file mode 100644
index 0000000..6d9ff16
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/guidesimprocs.tex
@@ -0,0 +1,48 @@
+\documentclass [twoside,12pt]{article}
+%\usepackage{myarticle}
+%\usepackage{alltt}
+%\usepackage{html}
+\usepackage{url}
+\usepackage{ssj}
+
+\def\g {{\;\leftarrow\;}}
+\def\tbu {\tilde{\mbox{\boldmath $u$}}}
+\def\bu {\mbox{\boldmath $u$}}
+
+%%%%%%%%%%%%%%
+%begin{latexonly}
+\def\fiverm {}
+\input prepictex.tex \input pictex.tex \input postpictex.tex
+\valuestolabelleading = -0.1\baselineskip
+%end{latexonly}
+
+% \dateheadtrue
+
+% \includeonly {exemples/Atelier}
+% \includeonly {Process}
+
+% \fulltrue %% Writes all the hidden code.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+\include{title}
+\pagenumbering{roman}
+\tableofcontents
+
+\pagenumbering{arabic}
+\addtocontents{toc}{\bigskip}
+\include{overview}
+
+\include{ProcessSimulator}
+\include{ThreadProcessSimulator}
+\include{DSOLProcessSimulator}
+\include{SimProcess}
+\include{Resource}
+\include{Bin}
+\include{Condition}
+\include{UserRecord}
+
+\bibliography{simul,random,ift,stat,prob} %,temp2} % Dans texmac.
+\bibliographystyle{plain}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/simprocs/overview.tex b/source/umontreal/iro/lecuyer/simprocs/overview.tex
new file mode 100644
index 0000000..9131309
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/overview.tex
@@ -0,0 +1,91 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+\latex{\label {sec:overview}}
+
+Process-oriented simulation is managed through this package.
+A \emph{Process} can be seen as an \emph{active object} whose behavior in
+time is described by a method called \texttt{actions()}.
+Each process must extend the
+\externalclass{umontreal.iro.lecuyer.simprocs}{SimProcess} class and
+must implement this \texttt{actions()} method.
+Processes are created and can be scheduled to start at a given
+simulation time just like events.
+In contrast with the corresponding \texttt{actions()} method of events,
+the method of processes is generally not executed instantaneously in the
+simulation time frame.
+At any given simulation time, at most one process can be \emph{active},
+i.e., executing its \texttt{actions()} method.
+The active process may create and schedule new processes,
+kill suspended processes, and suspend itself. A process is suspended
+for a fixed delay or until a resource becomes available, or a condition
+becomes true. When a process is suspended or finishes its execution,
+another process usually starts or resumes.
+
+These processes may represent ``autonomous'' objects
+such as machines and robots in a factory,
+customers in a retail store,
+vehicles in a transportation or delivery system, etc.
+The process-oriented paradigm is a natural way of describing complex
+systems \cite{sFRA77a,sBIR86a,sKRE86a,sLAW00a} and often leads to more
+compact code than the event-oriented view.
+However, it is often preferred to use events only, because this gives a faster
+simulation program, by avoiding the process-synchronization overhead.
+Most complex discrete-event systems are quite conveniently modeled only
+with events.
+In SSJ, events and processes can be mixed freely.
+The processes actually use events for their synchronization.
+
+The classes \externalclass{umontreal.iro.lecuyer.simprocs}{Resource},
+\externalclass{umontreal.iro.lecuyer.simprocs}{Bin}, and
+\externalclass{umontreal.iro.lecuyer.simprocs}{Condition}
+provide additional mechanisms for process synchronization.
+A \externalclass{umontreal.iro.lecuyer.simprocs}{Resource} corresponds
+to a facility with limited capacity and a waiting queue.
+A process can request an arbitrary number of units of a resource,
+may have to wait until enough units are available,
+can use the resource for a certain time, and eventually releases it.
+A \externalclass{umontreal.iro.lecuyer.simprocs}{Bin} supports
+producer/consumer relationships between processes.
+It corresponds essentially to a pile of free tokens and a queue of
+processes waiting for the tokens.
+A \emph{producer} adds tokens to the pile whereas a
+\emph{consumer} (a process) can ask for tokens.
+When not enough tokens are available, the consumer is blocked
+and placed in the queue.
+The class \externalclass{umontreal.iro.lecuyer.simprocs}{Condition}
+supports the concept of processes waiting
+for a certain boolean condition to be true before continuing their execution.
+
+Two different implementations of processes are available in SSJ, each
+one corresponding to a subclass of \texttt{ProcessSimulator}.
+The first one, called \texttt{ThreadProcessSimulator}, uses Java
+threads as described in Section~4 of \cite{sLEC02a}.
+The second one, \texttt{DSOLProcessSimulator}, is taken from DSOL
+\cite{iJAC05a,sJAC04a} and was
+provided to us by Peter Jacobs.
+Unfortunately, none of these two implementations is fully satisfactory.
+
+Java threads are designed for \emph{real parallelism}, not for the kind of
+\emph{simulated} parallelism required in process-oriented simulation.
+In the Java Development Kit (JDK) 1.3.1 and earlier, \emph{green threads}
+supporting simulated parallelism were available and our original
+implementation of processes\latex{ described in \cite{sLEC02a}}
+is based on them.
+But green threads are no longer supported in recent Java runtime
+environments.
+True (native) threads from the operating system are used instead.
+This adds significant overhead and prevents the use of a large number
+of processes in the simulation.
+\emph{This implementation of processes with threads can be used safely only
+with the JDK versions~1.3.1 or earlier}.
+A program using the thread-based process view can easily be 10 to 20
+times slower than a similar program using the event view only
+(see \cite{sLEC05a} for an example).
+
+%%% D-SOL.
+The second implementation, made by P.{} Jacobs, stays away from threads.
+It uses a Java reflection mechanism that interprets the code of processes
+at runtime and transforms everything into events.
+A program using the process view implemented with the DSOL interpreter
+can be 500 to 1000 times slower than the corresponding event-based program
+but the number of processes is limited only by the available memory.
+
diff --git a/source/umontreal/iro/lecuyer/simprocs/title.tex b/source/umontreal/iro/lecuyer/simprocs/title.tex
new file mode 100644
index 0000000..b5ad6a5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/simprocs/title.tex
@@ -0,0 +1,24 @@
+\begin{titlepage}
+
+\title{simprocs}{Process-driven Simulation Management}
+
+\begin {quote}
+This package offers some (not-so-efficient) facilities for process-driven
+simulation. It provides suspend, resume,
+and synchronization tools for simulated processes.
+It has two different implementations, one based on Java threads
+and the other using an interpreter that translates everything to events.
+The first implementation was developed for \emph{green threads},
+which are essentially \emph{simulated} threads and are available
+in the JDK~1.3.1 and earlier environments by running programs with
+\texttt{java -classic} option.
+Unfortunately, more recent Java virtual machines offer only native threads.
+This limits the number of processes, slows down the execution,
+and sometimes prevent the programs from running properly.
+The second (slower) implementation uses an interpreter borrowed from the DSOL
+simulation system to simulate process interactions in a single thread.
+\end {quote}
+
+\vfill\vfill
+
+\end{titlepage}
diff --git a/source/umontreal/iro/lecuyer/stat/ObservationListener.java b/source/umontreal/iro/lecuyer/stat/ObservationListener.java
new file mode 100644
index 0000000..fc32875
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/ObservationListener.java
@@ -0,0 +1,48 @@
+
+
+/*
+ * Class: ObservationListener
+ * Description: Observation listener
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat;
+
+
+/**
+ * Represents an object that can listen to observations broadcast
+ * by statistical probes.
+ *
+ */
+public interface ObservationListener {
+
+ /**
+ * Receives the new observation <TT>x</TT> broadcast by <TT>probe</TT>.
+ *
+ * @param probe the statistical probe broadcasting the observation.
+ *
+ * @param x the observation being broadcast.
+ *
+ *
+ */
+ public void newObservation (StatProbe probe, double x);
+
+}
diff --git a/source/umontreal/iro/lecuyer/stat/ObservationListener.tex b/source/umontreal/iro/lecuyer/stat/ObservationListener.tex
new file mode 100644
index 0000000..8705854
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/ObservationListener.tex
@@ -0,0 +1,49 @@
+\defmodule{ObservationListener}
+
+Represents an object that can listen to observations broadcast
+by statistical probes.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ObservationListener
+ * Description: Observation listener
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat;
+
+
+public interface ObservationListener\begin{hide} {\end{hide}
+
+ public void newObservation (StatProbe probe, double x);
+\end{code}
+\begin{tabb} Receives the new observation \texttt{x} broadcast by \texttt{probe}.
+\end{tabb}
+\begin{htmlonly}
+ \param{probe}{the statistical probe broadcasting the observation.}
+ \param{x}{the observation being broadcast.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stat/StatProbe.java b/source/umontreal/iro/lecuyer/stat/StatProbe.java
new file mode 100644
index 0000000..c4de7ed
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/StatProbe.java
@@ -0,0 +1,432 @@
+
+
+/*
+ * Class: StatProbe
+ * Description: statistical probe
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat;
+import java.util.List;
+import java.util.ArrayList;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+/**
+ * The objects of this class are <SPAN CLASS="textit">statistical probes</SPAN> or
+ * <SPAN CLASS="textit">collectors</SPAN>, which are elementary devices for collecting
+ * statistics. Each probe collects statistics on a given variable.
+ * The subclasses {@link Tally}, {@link TallyStore}, and
+ * {@link umontreal.iro.lecuyer.simevents.Accumulate Accumulate} (from package
+ * {@link umontreal.iro.lecuyer.simevents simevents})
+ * implement two kinds of probes, for the case of successive observations
+ *
+ * <SPAN CLASS="MATH"><I>X</I><SUB>1</SUB>, <I>X</I><SUB>2</SUB>, <I>X</I><SUB>3</SUB>,...</SPAN>, and for the case of a variable whose value
+ * evolves in time, respectively.
+ *
+ * <P>
+ * Each instance of
+ * {@link StatProbe} contains a list of {@link ObservationListener} that can listen
+ * to individual observations.
+ * When a probe is updated, i.e., receives a new statistical observation,
+ * it broadcasts this new data to all registered observers.
+ * The broadcasting of observations to registered observers can be turned
+ * ON or OFF at any time.
+ * It is initially OFF by default and should stay OFF when there are
+ * no registered observers, to avoid unnecessary overhead.
+ *
+ * <P>
+ * The data collection by the statistical probe itself can also be turned
+ * ON or OFF. By default, it is initially ON.
+ * We can turn it OFF, for example, if we want to use the statistical probe
+ * only to pass data to the observers, and do not need it to store
+ * any information.
+ *
+ * <P>
+ * In the simplest programs, collection is ON, broadcast is OFF, and the overall
+ * stats are accessed via the methods
+ * <TT>min</TT>, <TT>max</TT>, <TT>sum</TT>, <TT>average</TT>, ... of the
+ * collector.
+ *
+ */
+public abstract class StatProbe {
+
+ private List<ObservationListener> listeners = new ArrayList<ObservationListener>();
+ protected String name;
+ protected double maxValue;
+ protected double minValue;
+ protected double sumValue;
+ protected boolean collect = true;
+ protected boolean broadcast = false;
+ protected boolean showNobs = true;
+
+
+
+
+ /**
+ * Initializes the statistical collector.
+ *
+ */
+ abstract public void init();
+
+
+ /**
+ * Sets the name of this statistical collector to <TT>name</TT>.
+ *
+ */
+ public void setName (String name) {
+ this.name = name;
+ }
+
+
+ /**
+ * Returns the name associated with this probe,
+ * or <TT>null</TT> if no name was specified upon construction.
+ *
+ * @return the name associated to this collector,
+ * or <TT>null</TT> if not specified
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+
+ /**
+ * Returns the smallest value taken by the variable
+ * since the last initialization
+ * of this probe.
+ * This returns <TT>Double.POSITIVE_INFINITY</TT>
+ * if the probe was not updated since the last initialization.
+ *
+ * @return the smallest value taken by the collector since last initialization
+ *
+ */
+ public double min() {
+ return minValue;
+ }
+
+
+ /**
+ * Returns the largest value taken by the variable
+ * since the last initialization
+ * of this probe.
+ * This returns <TT>Double.NEGATIVE_INFINITY</TT>
+ * if the probe was not updated since the last initialization.
+ *
+ * @return the largest value taken by the collector since last initialization
+ *
+ */
+ public double max() {
+ return maxValue;
+ }
+
+
+ /**
+ * Returns the sum cumulated so far for this probe.
+ * The meaning of this sum depends on the subclass (e.g., {@link Tally} or
+ * {@link umontreal.iro.lecuyer.simevents.Accumulate Accumulate}).
+ * This returns 0
+ * if the probe was not updated since the last initialization.
+ *
+ * @return the sum for this probe
+ *
+ */
+ public double sum() {
+ return sumValue;
+ }
+
+
+ /**
+ * Returns the average for this collector.
+ * This returns <TT>Double.NaN</TT>
+ * if the probe was not updated since the last initialization.
+ *
+ * @return the average value of the collected observations
+ *
+ */
+ abstract public double average();
+
+
+ /**
+ * Returns a string containing a report for this statistical
+ * collector. The contents of this report depends on the statistical probe as
+ * well as on the parameters set by the user through probe-specific methods.
+ *
+ * @return a report for this probe, represented as a string
+ *
+ */
+ abstract public String report();
+
+
+ /**
+ * Formats and returns a short, one-line report
+ * about this statistical probe. This line is composed of
+ * whitespace-separated fields which must
+ * correspond to the column names given by {@link #shortReportHeader shortReportHeader}().
+ * This report should not contain any end-of-line character, and does not
+ * include the name of the probe.
+ * Its contents depends on the statistical probe as well as on the
+ * parameters set by the user through probe-specific methods.
+ *
+ * @return the short report for the probe.
+ *
+ */
+ abstract public String shortReport();
+
+
+ /**
+ * Returns a string containing
+ * the name of the values returned in the report
+ * strings. The returned string must depend on the type of probe and on
+ * the reporting options only. It must not depend on the observations
+ * received by the probe.
+ * This can be used as header when printing
+ * several reports. For example,
+ * <PRE>
+ * System.out.println (probe1.shortReportHeader());
+ * System.out.println (probe1.getName() + " " + probe1.shortReport());
+ * System.out.println (probe2.getName() + " " + probe2.shortReport());
+ * ...
+ * </PRE>
+ * Alternatively, one can use {@link #report report} <TT>(String,StatProbe[])</TT>
+ * to get a report with aligned probe names.
+ *
+ * @return the header string for the short reports.
+ *
+ */
+ abstract public String shortReportHeader();
+
+
+ /**
+ * Formats short reports for each statistical probe in the array
+ * <TT>probes</TT> while aligning the probes' names.
+ * This method first formats the given global name.
+ * It then determines the maximum length <SPAN CLASS="MATH">1#1</SPAN> of the names of probes in the
+ * given array.
+ * The first line of the report is composed of <SPAN CLASS="MATH">[tex2html_wrap_inline221] + 3</SPAN> spaces followed by the
+ * string returned by {@link #shortReportHeader shortReportHeader} called on the first probe in
+ * <TT>probes</TT>. Each remaining line corresponds to a statistical probe; it
+ * contains the probe's name followed by the contents returned by
+ * {@link #shortReport shortReport}. Note that this method assumes that <TT>probes</TT>
+ * contains no <TT>null</TT> element.
+ *
+ * @param globalName the global name given to the formatted report.
+ *
+ * @param probes the probes to include in the report.
+ *
+ * @return the formatted report.
+ *
+ */
+ public static String report (String globalName, StatProbe[] probes) {
+ int maxn = 0;
+ StatProbe firstProbe = null;
+ for (StatProbe probe : probes) {
+ if (firstProbe == null)
+ firstProbe = probe;
+ String s = probe.getName();
+ if (s != null && s.length() > maxn)
+ maxn = s.length();
+ }
+ if (firstProbe == null)
+ return "";
+ StringBuffer sb = new StringBuffer ("Report for ");
+ sb.append (globalName).append (PrintfFormat.NEWLINE);
+ for (int i = 0; i < maxn; i++)
+ sb.append (' ');
+ sb.append (" ");
+ sb.append (firstProbe.shortReportHeader()).append (PrintfFormat.NEWLINE);
+ for (StatProbe probe : probes) {
+ sb.append
+ (PrintfFormat.s (-maxn, probe.getName()));
+ sb.append (" ");
+ sb.append (probe.shortReport()).append (PrintfFormat.NEWLINE);
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Equivalent to {@link #report((String,StatProbe[])) report}, except that
+ * <TT>probes</TT> is an {@link Iterable} object instead of an array.
+ * Of course, the iterator returned by <TT>probes</TT> should enumerate the
+ * statistical probes to include in the report in a consistent and sensible order.
+ *
+ * @param globalName the global name given to the formatted report.
+ *
+ * @param probes the probes to include in the report.
+ *
+ * @return the formatted report.
+ *
+ */
+ public static String report (String globalName,
+ Iterable<? extends StatProbe> probes) {
+ int maxn = 0;
+ StatProbe firstProbe = null;
+ for (StatProbe probe : probes) {
+ if (firstProbe == null)
+ firstProbe = probe;
+ String s = probe.getName();
+ int sl = s == null ? 4 : s.length();
+ if (sl > maxn)
+ maxn = sl;
+ }
+ if (firstProbe == null)
+ return "";
+ StringBuffer sb = new StringBuffer ("Report for ");
+ sb.append (globalName).append (PrintfFormat.NEWLINE);
+ for (int i = 0; i < maxn; i++)
+ sb.append (' ');
+ sb.append (" ");
+ sb.append (firstProbe.shortReportHeader()).append (PrintfFormat.NEWLINE);
+ for (StatProbe probe : probes) {
+ sb.append
+ (PrintfFormat.s (-maxn, probe.getName()));
+ sb.append (" ");
+ sb.append (probe.shortReport()).append (PrintfFormat.NEWLINE);
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Determines if this statistical probe
+ * is broadcasting observations to registered observers.
+ * The default is <TT>false</TT>.
+ *
+ * @return the status of broadcasting.
+ *
+ */
+ public boolean isBroadcasting() {
+ return broadcast;
+ }
+
+
+ /**
+ * Instructs the probe to turn its broadcasting ON or OFF.
+ * The default value is OFF.
+ *
+ * <P>
+ * Warning: To avoid useless overhead and performance degradation, broadcasting
+ * should never be turned ON when there are no registered observers.
+ *
+ * @param b <TT>true</TT> to turn broadcasting ON, <TT>false</TT> to turn it OFF
+ *
+ *
+ */
+ public void setBroadcasting (boolean b) {
+ broadcast = b;
+ }
+
+
+ /**
+ * Determines if this statistical probe
+ * is collecting values. The default is <TT>true</TT>.
+ *
+ * @return the status of statistical collecting.
+ *
+ */
+ public boolean isCollecting() {
+ return collect;
+ }
+
+
+ /**
+ * Turns ON or OFF the collection of statistical
+ * observations. The default value is ON.
+ * When statistical collection is turned OFF,
+ * observations added to the probe are passed to the
+ * registered observers if broadcasting is turned ON, but are not
+ * counted as observations by the probe itself.
+ *
+ * @param b <TT>true</TT> to activate statistical collection,
+ * <TT>false</TT> to deactivate it
+ *
+ *
+ */
+ public void setCollecting (boolean b) {
+ collect = b;
+ }
+
+
+ /**
+ * Adds the observation listener <TT>l</TT> to the list of observers of
+ * this statistical probe.
+ *
+ * @param l the new observation listener.
+ *
+ * @exception NullPointerException if <TT>l</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public void addObservationListener (ObservationListener l) {
+ if (l == null)
+ throw new NullPointerException();
+ if (!listeners.contains (l))
+ listeners.add (l);
+ }
+
+
+ /**
+ * Removes the observation listener <TT>l</TT> from the list of observers of
+ * this statistical probe.
+ *
+ * @param l the observation listener to be deleted.
+ *
+ *
+ */
+ public void removeObservationListener (ObservationListener l) {
+ listeners.remove (l);
+ }
+
+
+ /**
+ * Removes all observation listeners from the list of observers of
+ * this statistical probe.
+ *
+ */
+ public void clearObservationListeners() {
+ listeners.clear();
+ }
+
+
+ /**
+ * Notifies the observation <TT>x</TT> to all registered observers
+ * if broadcasting is ON. Otherwise, does nothing.
+ *
+ */
+ public void notifyListeners (double x) {
+ if (!broadcast)
+ return;
+ // We could also use the enhanced for loop here, but this is less efficient.
+ final int nl = listeners.size();
+ for (int i = 0; i < nl; i++)
+ listeners.get (i).newObservation (this, x);
+ }
+
+
+ public StatProbe clone() throws CloneNotSupportedException {
+ StatProbe s = (StatProbe)super.clone();
+ s.listeners = new ArrayList<ObservationListener>(listeners);
+ return s;
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/stat/StatProbe.tex b/source/umontreal/iro/lecuyer/stat/StatProbe.tex
new file mode 100644
index 0000000..76bd308
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/StatProbe.tex
@@ -0,0 +1,426 @@
+\defmodule {StatProbe}
+
+The objects of this class are \emph{statistical probes} or
+\emph{collectors}, which are elementary devices for collecting
+statistics. Each probe collects statistics on a given variable.
+The subclasses \class{Tally}, \class{TallyStore}, and
+\externalclass{umontreal.iro.lecuyer.simevents}{Accumulate} (from package
+\externalclass{umontreal.iro.lecuyer}{simevents})
+implement two kinds of probes, for the case of successive observations
+$X_1,X_2,X_3,\dots$, and for the case of a variable whose value
+evolves in time, respectively.
+
+Each instance of
+\class{StatProbe} contains a list of \class{ObservationListener} that can listen
+to individual observations.
+When a probe is updated, i.e., receives a new statistical observation,
+it broadcasts this new data to all registered observers.
+The broadcasting of observations to registered observers can be turned
+ON or OFF at any time.
+It is initially OFF by default and should stay OFF when there are
+no registered observers, to avoid unnecessary overhead.
+% and performance degradation.
+% but is turned ON automatically
+% when a \emph{first} \externalclass{java.util}{Observer} object registers.
+% Afterwards, it can be turned ON or OFF at any time.
+
+The data collection by the statistical probe itself can also be turned
+ON or OFF. By default, it is initially ON.
+We can turn it OFF, for example, if we want to use the statistical probe
+only to pass data to the observers, and do not need it to store
+any information.
+
+In the simplest programs, collection is ON, broadcast is OFF, and the overall
+stats are accessed via the methods
+\texttt{min}, \texttt{max}, \texttt{sum}, \texttt{average}, ... of the
+collector.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: StatProbe
+ * Description: statistical probe
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat;\begin{hide}
+import java.util.List;
+import java.util.ArrayList;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+public abstract class StatProbe\begin{hide} {
+
+ private List<ObservationListener> listeners = new ArrayList<ObservationListener>();
+ protected String name;
+ protected double maxValue;
+ protected double minValue;
+ protected double sumValue;
+ protected boolean collect = true;
+ protected boolean broadcast = false;
+ protected boolean showNobs = true;
+
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ abstract public void init();
+\end{code}
+ \begin{tabb} Initializes the statistical collector.
+ \end{tabb}
+\begin{code}
+
+ public void setName (String name) \begin{hide} {
+ this.name = name;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Sets the name of this statistical collector to \texttt{name}.
+ \end{tabb}
+\begin{code}
+
+ public String getName()\begin{hide} {
+ return name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the name associated with this probe,
+ or \texttt{null} if no name was specified upon construction.
+\end{tabb}
+\begin{htmlonly}
+ \return{the name associated to this collector,
+ or \texttt{null} if not specified}
+\end{htmlonly}
+\begin{code}
+
+ public double min() \begin{hide} {
+ return minValue;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the smallest value taken by the variable
+ since the last initialization
+ of this probe.
+ This returns \texttt{Double.POSITIVE\_INFINITY}
+ if the probe was not updated since the last initialization.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the smallest value taken by the collector since last initialization}
+\end{htmlonly}
+\begin{code}
+
+ public double max() \begin{hide} {
+ return maxValue;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the largest value taken by the variable
+ since the last initialization
+ of this probe.
+ This returns \texttt{Double.NEGATIVE\_INFINITY}
+ if the probe was not updated since the last initialization.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the largest value taken by the collector since last initialization}
+\end{htmlonly}
+\begin{code}
+
+ public double sum() \begin{hide} {
+ return sumValue;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the sum cumulated so far for this probe.
+ The meaning of this sum depends on the subclass (e.g., \class{Tally} or
+ \externalclass{umontreal.iro.lecuyer.simevents}{Accumulate}).
+ This returns 0
+ if the probe was not updated since the last initialization.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the sum for this probe}
+\end{htmlonly}
+\begin{code}
+
+ abstract public double average();
+\end{code}
+ \begin{tabb} Returns the average for this collector.
+ This returns \texttt{Double.NaN}
+ if the probe was not updated since the last initialization.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the average value of the collected observations}
+\end{htmlonly}
+\begin{code}
+
+ abstract public String report();
+\end{code}
+ \begin{tabb} Returns a string containing a report for this statistical
+ collector. The contents of this report depends on the statistical probe as
+ well as on the parameters set by the user through probe-specific methods.
+ \end{tabb}
+\begin{htmlonly}
+ \return{a report for this probe, represented as a string}
+\end{htmlonly}
+\begin{code}
+
+ abstract public String shortReport();
+\end{code}
+\begin{tabb} Formats and returns a short, one-line report
+ about this statistical probe. This line is composed of
+ whitespace-separated fields which must
+ correspond to the column names given by \method{shortReportHeader}{}{()}.
+ This report should not contain any end-of-line character, and does not
+ include the name of the probe.
+ Its contents depends on the statistical probe as well as on the
+ parameters set by the user through probe-specific methods.
+\end{tabb}
+\begin{htmlonly}
+ \return{the short report for the probe.}
+\end{htmlonly}
+\begin{code}
+
+ abstract public String shortReportHeader();
+\end{code}
+\begin{tabb} Returns a string containing
+ the name of the values returned in the report
+ strings. The returned string must depend on the type of probe and on
+the reporting options only. It must not depend on the observations
+received by the probe.
+This can be used as header when printing
+ several reports. For example,
+\begin{verbatim}
+ System.out.println (probe1.shortReportHeader());
+ System.out.println (probe1.getName() + " " + probe1.shortReport());
+ System.out.println (probe2.getName() + " " + probe2.shortReport());
+ ...
+\end{verbatim}
+ Alternatively, one can use \method{report}{}~\texttt{(String,StatProbe[])}
+ to get a report with aligned probe names.
+\end{tabb}
+\begin{htmlonly}
+ \return{the header string for the short reports.}
+\end{htmlonly}
+\begin{code}
+
+ public static String report (String globalName, StatProbe[] probes)\begin{hide} {
+ int maxn = 0;
+ StatProbe firstProbe = null;
+ for (StatProbe probe : probes) {
+ if (firstProbe == null)
+ firstProbe = probe;
+ String s = probe.getName();
+ if (s != null && s.length() > maxn)
+ maxn = s.length();
+ }
+ if (firstProbe == null)
+ return "";
+ StringBuffer sb = new StringBuffer ("Report for ");
+ sb.append (globalName).append (PrintfFormat.NEWLINE);
+ for (int i = 0; i < maxn; i++)
+ sb.append (' ');
+ sb.append (" ");
+ sb.append (firstProbe.shortReportHeader()).append (PrintfFormat.NEWLINE);
+ for (StatProbe probe : probes) {
+ sb.append
+ (PrintfFormat.s (-maxn, probe.getName()));
+ sb.append (" ");
+ sb.append (probe.shortReport()).append (PrintfFormat.NEWLINE);
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats short reports for each statistical probe in the array
+ \texttt{probes} while aligning the probes' names.
+ This method first formats the given global name.
+ It then determines the maximum length $\ell$ of the names of probes in the
+ given array.
+ The first line of the report is composed of $\ell+3$ spaces followed by the
+ string returned by \method{shortReportHeader}{} called on the first probe in
+ \texttt{probes}. Each remaining line corresponds to a statistical probe; it
+ contains the probe's name followed by the contents returned by
+ \method{shortReport}{}. Note that this method assumes that \texttt{probes}
+ contains no \texttt{null} element.
+\end{tabb}
+\begin{htmlonly}
+ \param{globalName}{the global name given to the formatted report.}
+ \param{probes}{the probes to include in the report.}
+ \return{the formatted report.}
+\end{htmlonly}
+\begin{code}
+
+ public static String report (String globalName,
+ Iterable<? extends StatProbe> probes)\begin{hide} {
+ int maxn = 0;
+ StatProbe firstProbe = null;
+ for (StatProbe probe : probes) {
+ if (firstProbe == null)
+ firstProbe = probe;
+ String s = probe.getName();
+ int sl = s == null ? 4 : s.length();
+ if (sl > maxn)
+ maxn = sl;
+ }
+ if (firstProbe == null)
+ return "";
+ StringBuffer sb = new StringBuffer ("Report for ");
+ sb.append (globalName).append (PrintfFormat.NEWLINE);
+ for (int i = 0; i < maxn; i++)
+ sb.append (' ');
+ sb.append (" ");
+ sb.append (firstProbe.shortReportHeader()).append (PrintfFormat.NEWLINE);
+ for (StatProbe probe : probes) {
+ sb.append
+ (PrintfFormat.s (-maxn, probe.getName()));
+ sb.append (" ");
+ sb.append (probe.shortReport()).append (PrintfFormat.NEWLINE);
+ }
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to \method{report}{(String,StatProbe[])}, except that
+ \texttt{probes} is an \class{Iterable} object instead of an array.
+ Of course, the iterator returned by \texttt{probes} should enumerate the
+ statistical probes to include in the report in a consistent and sensible order.
+\end{tabb}
+\begin{htmlonly}
+ \param{globalName}{the global name given to the formatted report.}
+ \param{probes}{the probes to include in the report.}
+ \return{the formatted report.}
+\end{htmlonly}
+\begin{code}
+
+ public boolean isBroadcasting()\begin{hide} {
+ return broadcast;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if this statistical probe
+ is broadcasting observations to registered observers.
+ The default is \texttt{false}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the status of broadcasting.}
+\end{htmlonly}
+\begin{code}
+
+ public void setBroadcasting (boolean b)\begin{hide} {
+ broadcast = b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Instructs the probe to turn its broadcasting ON or OFF.
+ The default value is OFF.
+
+ Warning: To avoid useless overhead and performance degradation, broadcasting
+ should never be turned ON when there are no registered observers.
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{\texttt{true} to turn broadcasting ON, \texttt{false} to turn it OFF}
+\end{htmlonly}
+\begin{code}
+
+ public boolean isCollecting()\begin{hide} {
+ return collect;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if this statistical probe
+ is collecting values. The default is \texttt{true}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the status of statistical collecting.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCollecting (boolean b)\begin{hide} {
+ collect = b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Turns ON or OFF the collection of statistical
+ observations. The default value is ON.
+ When statistical collection is turned OFF,
+ observations added to the probe are passed to the
+ registered observers if broadcasting is turned ON, but are not
+ counted as observations by the probe itself.
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{\texttt{true} to activate statistical collection,
+ \texttt{false} to deactivate it}
+\end{htmlonly}
+\begin{code}
+
+ public void addObservationListener (ObservationListener l)\begin{hide} {
+ if (l == null)
+ throw new NullPointerException();
+ if (!listeners.contains (l))
+ listeners.add (l);
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds the observation listener \texttt{l} to the list of observers of
+ this statistical probe.
+\end{tabb}
+\begin{htmlonly}
+ \param{l}{the new observation listener.}
+ \exception{NullPointerException}{if \texttt{l} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public void removeObservationListener (ObservationListener l)\begin{hide} {
+ listeners.remove (l);
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes the observation listener \texttt{l} from the list of observers of
+ this statistical probe.
+\end{tabb}
+\begin{htmlonly}
+ \param{l}{the observation listener to be deleted.}
+\end{htmlonly}
+\begin{code}
+
+ public void clearObservationListeners()\begin{hide} {
+ listeners.clear();
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes all observation listeners from the list of observers of
+ this statistical probe.
+\end{tabb}
+\begin{code}
+
+ public void notifyListeners (double x)\begin{hide} {
+ if (!broadcast)
+ return;
+ // We could also use the enhanced for loop here, but this is less efficient.
+ final int nl = listeners.size();
+ for (int i = 0; i < nl; i++)
+ listeners.get (i).newObservation (this, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Notifies the observation \texttt{x} to all registered observers
+ if broadcasting is ON. Otherwise, does nothing.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public StatProbe clone() throws CloneNotSupportedException {
+ StatProbe s = (StatProbe)super.clone();
+ s.listeners = new ArrayList<ObservationListener>(listeners);
+ return s;
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stat/Tally.java b/source/umontreal/iro/lecuyer/stat/Tally.java
new file mode 100644
index 0000000..be037bb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/Tally.java
@@ -0,0 +1,686 @@
+
+
+/*
+ * Class: Tally
+ * Description: statistical collector
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.probdist.StudentDist;
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.probdist.ChiSquareDist;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * This type of statistical collector takes a sequence of real-valued
+ * observations and can return the average,
+ * the variance, a confidence interval for the theoretical mean, etc.
+ * Each call to {@link #add add} provides a new observation.
+ * When the broadcasting to observers is activated,
+ * the method {@link #add add} will also pass this new information to its
+ * registered observers.
+ * This type of collector does not memorize the individual observations,
+ * but only their number, sum, sum of squares, maximum, and minimum.
+ * The subclass {@link TallyStore} offers a collector that memorizes
+ * the observations.
+ *
+ */
+public class Tally extends StatProbe implements Cloneable {
+ private int numObs;
+ private double sumSquares;
+ private double curAverage; // The average of the first numObs observations
+ private double curSum2; // The sum (xi - average)^2 of the first numObs
+ // observations.
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat");
+
+ private static enum CIType {CI_NONE, CI_NORMAL, CI_STUDENT};
+
+ protected CIType confidenceInterval = CIType.CI_NONE;
+ protected double level = 0.95;
+ protected int digits = 3;
+
+
+
+ /**
+ * Constructs a new unnamed <TT>Tally</TT> statistical probe.
+ *
+ */
+ public Tally() {
+ super();
+ init();
+ }
+
+
+ /**
+ * Constructs a new <TT>Tally</TT> statistical probe with
+ * name <TT>name</TT>.
+ *
+ * @param name name of the tally
+ *
+ */
+ public Tally (String name) {
+ super();
+ this.name = name;
+ init();
+ }
+
+
+ public void init() {
+ maxValue = Double.NEGATIVE_INFINITY;
+ minValue = Double.POSITIVE_INFINITY;
+ sumValue = 0.0;
+ sumSquares = 0.0;
+ curAverage = 0.0;
+ curSum2 = 0.0;
+ numObs = 0;
+ }
+
+ /**
+ * Gives a new observation <TT>x</TT> to the statistical collector.
+ * If broadcasting to observers is activated for this object,
+ * this method also transmits the new information to the
+ * registered observers by invoking the method
+ * {@link #notifyListeners notifyListeners}.
+ *
+ * @param x observation being added to this tally
+ *
+ *
+ */
+ public void add (double x) {
+ if (collect) {
+ if (x < minValue) minValue = x;
+ if (x > maxValue) maxValue = x;
+ numObs++;
+ // Algorithme dans Knuth ed. 3, p. 232; voir Wikipedia
+ // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#cite_note-1
+ double y = x - curAverage;
+ curAverage += y / numObs;
+ curSum2 += y*(x - curAverage);
+ // On pourrait utiliser l'algorithme correcteur de Kahan pour une
+ // meilleure precision.
+ // (voir http://en.wikipedia.org/wiki/Kahan_summation_algorithm)
+ }
+ notifyListeners (x);
+ }
+
+
+ /**
+ * Returns the number of observations given to this probe
+ * since its last initialization.
+ *
+ * @return the number of collected observations
+ *
+ */
+ public int numberObs() {
+ return numObs;
+ }
+
+
+ /**
+ * Returns the average value of the observations since the last
+ * initialization.
+ *
+ */
+ public double average() {
+ if (numObs < 1) {
+ //System.err.println (
+ // "******* Tally " + name + ": calling average() with " + numObs +
+ // " Observation");
+ log.logp (Level.WARNING, "Tally", "average",
+ "Tally " + name + ": calling average() with " + numObs +
+ " observation");
+ return Double.NaN;
+ }
+ return curAverage;
+ }
+
+
+ /**
+ * Returns the sample variance of the observations since the last
+ * initialization.
+ * This returns <TT>Double.NaN</TT>
+ * if the tally contains less than two observations.
+ *
+ * @return the variance of the observations
+ *
+ */
+ public double variance() {
+ // throws NumberObservationException {
+ // if (numObs < 2) throw NumberObservationException;
+ if (numObs < 2) {
+ //System.err.println (
+ // "******* Tally " + name + ": calling variance() with " + numObs +
+ // " Observation");
+ log.logp (Level.WARNING, "Tally", "variance",
+ "Tally " + name + ": calling variance() with " + numObs +
+ " observation");
+ return Double.NaN;
+ }
+
+ return curSum2 / (numObs-1);
+ }
+
+
+ /**
+ * Returns the sample standard deviation of the observations
+ * since the last initialization.
+ * This returns <TT>Double.NaN</TT>
+ * if the tally contains less than two observations.
+ *
+ * @return the standard deviation of the observations
+ *
+ */
+ public double standardDeviation() {
+ return Math.sqrt (variance());
+ }
+
+
+ /**
+ * Computes a confidence interval on the mean.
+ * Returns, in elements 0 and 1 of the array
+ * object <TT>centerAndRadius[]</TT>, the center and half-length (radius)
+ * of a confidence interval on the true mean of the random variable <SPAN CLASS="MATH"><I>X</I></SPAN>,
+ * with confidence level <TT>level</TT>, assuming that the <SPAN CLASS="MATH"><I>n</I></SPAN> observations
+ * given to this collector are independent and identically distributed
+ * (i.i.d.) copies of <SPAN CLASS="MATH"><I>X</I></SPAN>, and that <SPAN CLASS="MATH"><I>n</I></SPAN> is large enough for the central limit
+ * theorem to hold. This confidence interval is computed based on the statistic
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>Z</I> = bar(X)<SUB>n</SUB>-<I>μ</I>/(<I>S</I><SUB>n, x</SUB>/(n)<SUP>1/2</SUP>)
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number of observations given to this collector since its
+ * last initialization,
+ * <SPAN CLASS="MATH">bar(X)<SUB>n</SUB> =</SPAN> <TT>average()</TT> is the average of these
+ * observations, <SPAN CLASS="MATH"><I>S</I><SUB>n, x</SUB> =</SPAN> <TT>standardDeviation()</TT> is the empirical
+ * standard deviation. Under the assumption that the observations
+ * of <SPAN CLASS="MATH"><I>X</I></SPAN> are i.i.d. and <SPAN CLASS="MATH"><I>n</I></SPAN> is large,
+ * <SPAN CLASS="MATH"><I>Z</I></SPAN> has the standard normal distribution.
+ * The confidence interval given by this method is valid <SPAN CLASS="textit">only if</SPAN>
+ * this assumption is approximately verified.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @param centerAndRadius array of size 2 in which are returned the center
+ * and radius of the confidence interval, respectively
+ *
+ *
+ */
+ public void confidenceIntervalNormal (double level,
+ double[] centerAndRadius) {
+ // Must return an array object, cannot return 2 doubles directly
+ double z;
+ if (numObs < 2) throw new RuntimeException (
+ "Tally " + name +
+ ": Calling confidenceIntervalStudent with < 2 Observations");
+ centerAndRadius[0] = average();
+ z = NormalDist.inverseF01 (0.5 * (level + 1.0));
+ centerAndRadius[1] = z * Math.sqrt (variance() / (double)numObs);
+ }
+
+
+ /**
+ * Computes a confidence interval on the mean.
+ * Returns, in elements 0 and 1 of the array
+ * object <TT>centerAndRadius[]</TT>, the center and half-length (radius)
+ * of a confidence interval on the true mean of the random variable <SPAN CLASS="MATH"><I>X</I></SPAN>,
+ * with confidence level <TT>level</TT>, assuming that the observations
+ * given to this collector are independent and identically distributed
+ * (i.i.d.) copies of <SPAN CLASS="MATH"><I>X</I></SPAN>, and that <SPAN CLASS="MATH"><I>X</I></SPAN> has the normal distribution.
+ * This confidence interval is computed based on the statistic
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>T</I> = bar(X)<SUB>n</SUB>-<I>μ</I>/(<I>S</I><SUB>n, x</SUB>/(n)<SUP>1/2</SUP>)
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number of observations given to this collector since its
+ * last initialization,
+ * <SPAN CLASS="MATH">bar(X)<SUB>n</SUB> =</SPAN> <TT>average()</TT> is the average of these
+ * observations, <SPAN CLASS="MATH"><I>S</I><SUB>n, x</SUB> =</SPAN> <TT>standardDeviation()</TT> is the empirical
+ * standard deviation. Under the assumption that the observations
+ * of <SPAN CLASS="MATH"><I>X</I></SPAN> are i.i.d. and normally distributed,
+ * <SPAN CLASS="MATH"><I>T</I></SPAN> has the Student distribution with <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> degrees of freedom.
+ * The confidence interval given by this method is valid <SPAN CLASS="textit">only if</SPAN>
+ * this assumption is approximately verified, or if <SPAN CLASS="MATH"><I>n</I></SPAN> is large enough
+ * so that <SPAN CLASS="MATH">bar(X)<SUB>n</SUB></SPAN> is approximately normally distributed.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @param centerAndRadius array of size 2 in which are returned the center
+ * and radius of the confidence interval, respectively
+ *
+ *
+ */
+ public void confidenceIntervalStudent (double level,
+ double[] centerAndRadius) {
+ // Must return an array object, cannot return 2 doubles directly
+ double t;
+ if (numObs < 2) throw new RuntimeException (
+ "Tally " + name +
+ ": Calling confidenceIntervalStudent with < 2 Observations");
+ centerAndRadius[0] = average();
+ t = StudentDist.inverseF (numObs - 1, 0.5 * (level + 1.0));
+ centerAndRadius[1] = t * Math.sqrt (variance() / (double)numObs);
+ }
+
+
+ /**
+ * Similar to {@link #confidenceIntervalNormal confidenceIntervalNormal}.
+ * Returns the confidence interval in a formatted string of the form
+ * <BR>
+ * <DIV CLASS="centerline" ID="par423" ALIGN="CENTER">
+ * ``<TT>95% confidence interval for mean (normal): (32.431, 32.487)</TT>'',</DIV>
+ * using <SPAN CLASS="MATH"><I>d</I></SPAN> fractional decimal digits.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @param d number of fractional decimal digits
+ *
+ * @return a confidence interval formatted as a string
+ *
+ */
+ public String formatCINormal (double level, int d) {
+ PrintfFormat str = new PrintfFormat();
+ double ci[] = new double[2];
+ confidenceIntervalNormal (level, ci);
+ str.append (" " + (100*level) + "%");
+ str.append (" confidence interval for mean (normal): (");
+ str.append (7 + d, d-1, d, ci[0] - ci[1]).append (',');
+ str.append (7 + d, d-1, d, ci[0] + ci[1]).append (" )" + PrintfFormat.NEWLINE);
+ return str.toString();
+}
+
+
+ /**
+ * Equivalent to <TT>formatCINormal (level, 3)</TT>.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @return a confidence interval formatted as a string
+ *
+ */
+ public String formatCINormal (double level) {
+ return formatCINormal (level, 3);
+ }
+
+
+ /**
+ * Similar to {@link #confidenceIntervalStudent confidenceIntervalStudent}.
+ * Returns the confidence interval in a formatted string of the form
+ * <BR> <DIV CLASS="centerline" ID="par425" ALIGN="CENTER">
+ * ``<TT>95% confidence interval for mean (student): (32.431, 32.487)</TT>'',</DIV>
+ * using <SPAN CLASS="MATH"><I>d</I></SPAN> fractional decimal digits.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @param d number of fractional decimal digits
+ *
+ * @return a confidence interval formatted as a string
+ *
+ */
+ public String formatCIStudent (double level, int d) {
+ PrintfFormat str = new PrintfFormat();
+ double ci[] = new double[2];
+ confidenceIntervalStudent (level, ci);
+ str.append (" " + (100*level) + "%");
+ str.append (" confidence interval for mean (student): (");
+ str.append (7 + d, d, d-1, ci[0] - ci[1]).append (',');
+ str.append (7 + d, d, d-1, ci[0] + ci[1]).append (" )" + PrintfFormat.NEWLINE);
+ return str.toString();
+ }
+
+
+ /**
+ * Equivalent to <TT>formatCIStudent (level, 3)</TT>.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @return a confidence interval formatted as a string
+ *
+ */
+ public String formatCIStudent (double level) {
+ return formatCIStudent (level, 3);
+ }
+
+
+ /**
+ * Computes a confidence interval on the variance.
+ * Returns, in elements 0 and 1 of array <TT>interval</TT>, the
+ * left and right boundaries <SPAN CLASS="MATH">[<I>I</I><SUB>1</SUB>, <I>I</I><SUB>2</SUB>]</SPAN> of a confidence interval on the true
+ * variance <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP></SPAN> of the random variable <SPAN CLASS="MATH"><I>X</I></SPAN>, with confidence level
+ * <TT>level</TT>, assuming that the observations
+ * given to this collector are independent and identically distributed
+ * (i.i.d.) copies of <SPAN CLASS="MATH"><I>X</I></SPAN>, and that <SPAN CLASS="MATH"><I>X</I></SPAN> has the normal distribution.
+ * This confidence interval is computed based on the statistic
+ *
+ * <SPAN CLASS="MATH"><I>χ</I><SUP>2</SUP><SUB>n-1</SUB> = (<I>n</I> - 1)<I>S</I><SUP>2</SUP><SUB>n</SUB>/<I>σ</I><SUP>2</SUP></SPAN>
+ * where <SPAN CLASS="MATH"><I>n</I></SPAN> is the number of observations given to this collector since its
+ * last initialization, and <SPAN CLASS="MATH"><I>S</I><SUP>2</SUP><SUB>n</SUB> =</SPAN> <TT>variance()</TT> is the empirical
+ * variance of these observations. Under the assumption that the observations
+ * of <SPAN CLASS="MATH"><I>X</I></SPAN> are i.i.d. and normally distributed,
+ *
+ * <SPAN CLASS="MATH"><I>χ</I><SUP>2</SUP><SUB>n-1</SUB></SPAN> has the chi-square distribution with <SPAN CLASS="MATH"><I>n</I> - 1</SPAN> degrees of freedom.
+ * Given the <TT>level</TT>
+ * <SPAN CLASS="MATH">= 1 - <I>α</I></SPAN>,
+ * one has
+ * <SPAN CLASS="MATH"><I>P</I>[<I>χ</I><SUP>2</SUP><SUB>n-1</SUB> < <I>x</I><SUB>1</SUB>] = <I>P</I>[<I>χ</I><SUP>2</SUP><SUB>n-1</SUB> > <I>x</I><SUB>2</SUB>] = <I>α</I>/2</SPAN>
+ * and
+ * <SPAN CLASS="MATH">[<I>I</I><SUB>1</SUB>, <I>I</I><SUB>2</SUB>] = [(<I>n</I> - 1)<I>S</I><SUP>2</SUP><SUB>n</SUB>/<I>x</I><SUB>2</SUB>, (<I>n</I> - 1)<I>S</I><SUP>2</SUP><SUB>n</SUB>/<I>x</I><SUB>1</SUB>]</SPAN>.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @param interval array of size 2 in which are returned the left
+ * and right boundaries of the confidence interval, respectively
+ *
+ *
+ */
+ public void confidenceIntervalVarianceChi2 (double level,
+ double[] interval) {
+ // Must return an array object, cannot return 2 doubles directly
+ if (numObs < 2) throw new RuntimeException (
+ "Tally " + name +
+ ": calling confidenceIntervalVarianceChi2 with < 2 observations");
+ double w = (numObs - 1)*variance();
+ double x2 = ChiSquareDist.inverseF (numObs - 1, 0.5 * (1.0 + level));
+ double x1 = ChiSquareDist.inverseF (numObs - 1, 0.5 * (1.0 - level));
+ interval[0] = w / x2;
+ interval[1] = w / x1;
+ }
+
+
+ /**
+ * Similar to {@link #confidenceIntervalVarianceChi2 confidenceIntervalVarianceChi2}.
+ * Returns the confidence interval in a formatted string of the form
+ * <BR>
+ * <DIV CLASS="centerline" ID="par441" ALIGN="CENTER">
+ * ``<TT>95.0% confidence interval for variance (chi2):
+ * ( 510.642, 519.673 )</TT>'',</DIV>
+ * using <SPAN CLASS="MATH"><I>d</I></SPAN> fractional decimal digits.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true variance
+ *
+ * @param d number of fractional decimal digits
+ *
+ * @return a confidence interval formatted as a string
+ *
+ */
+ public String formatCIVarianceChi2 (double level, int d) {
+ PrintfFormat str = new PrintfFormat();
+ double ci[] = new double[2];
+ confidenceIntervalVarianceChi2 (level, ci);
+ str.append (" " + (100*level) + "%");
+ str.append (" confidence interval for variance (chi2): (");
+ str.append (7 + d, d, d-1, ci[0]).append (',');
+ str.append (7 + d, d, d-1, ci[1]).append (" )" + PrintfFormat.NEWLINE);
+ return str.toString();
+}
+
+
+ /**
+ * Returns a formatted string that contains a report on this probe.
+ *
+ * @return a statistical report formatted as a string
+ *
+ */
+ public String report() {
+ return report(level, digits);
+ }
+
+
+ /**
+ * Returns a formatted string that contains a report on this probe
+ * with a confidence interval level <TT>level</TT> using <SPAN CLASS="MATH"><I>d</I></SPAN>
+ * fractional decimal digits.
+ *
+ * @param level desired probability that the confidence
+ * interval covers the true mean
+ *
+ * @param d number of fractional decimal digits
+ *
+ * @return a statistical report formatted as a string
+ *
+ */
+ public String report (double level, int d) {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("REPORT on Tally stat. collector ==> " + name);
+ str.append (PrintfFormat.NEWLINE + " num. obs. min max average standard dev." + PrintfFormat.NEWLINE);
+ str.append (7 + d, (int)numObs); str.append (" ");
+ str.append (9 + d, d, d-1, (double)minValue); str.append (" ");
+ str.append (9 + d, d, d-1, (double)maxValue); str.append (" ");
+ str.append (9 + d, d, d-1, (double)average()); str.append (" ");
+ str.append (9 + d, d, d-1, standardDeviation());
+ str.append (PrintfFormat.NEWLINE);
+
+ switch (confidenceInterval) {
+ case CI_NORMAL:
+ str.append (formatCINormal (level, d));
+ break;
+ case CI_STUDENT:
+ str.append (formatCIStudent (level, d));
+ break;
+ }
+
+ return str.toString();
+ }
+
+
+ public String shortReportHeader() {
+ PrintfFormat pf = new PrintfFormat();
+ if (showNobs)
+ pf.append (-8, "num obs.").append (" ");
+ pf.append (-8, " min").append (" ");
+ pf.append (-8, " max").append (" ");
+ pf.append (-8, " average").append (" ");
+ pf.append (-8, " std. dev.");
+ if (confidenceInterval != CIType.CI_NONE)
+ pf.append (" ").append (-12, "conf. int.");
+
+ return pf.toString();
+ }
+
+ /**
+ * Formats and returns a short
+ * statistical report for this tally.
+ * The returned single-line report contains the minimum value,
+ * the maximum value,
+ * the average, and the standard deviation, in that order, separated
+ * by three spaces. If the number of observations is shown in the short report,
+ * a column containing the
+ * number of observations in this tally is added.
+ *
+ * @return the string containing the report.
+ *
+ */
+ public String shortReport() {
+ PrintfFormat pf = new PrintfFormat();
+ if (showNobs)
+ pf.append (-8, numberObs());
+ pf.append (9, 3, 2, min()).append (" ");
+ pf.append (9, 3, 2, max()).append (" ");
+ pf.append (10, 3, 2, average()).append (" ");
+ if (numberObs() >= 2)
+ pf.append (11, 3, 2, standardDeviation());
+ else
+ pf.append (11, "---");
+
+ if (confidenceInterval != CIType.CI_NONE) {
+ double[] ci = new double[2];
+ switch (confidenceInterval) {
+ case CI_NORMAL:
+ confidenceIntervalNormal (level, ci);
+ break;
+ case CI_STUDENT:
+ confidenceIntervalStudent (level, ci);
+ break;
+ }
+ pf.append (" ").append ((100*level) + "% (");
+ pf.append (9, 3, 2, ci[0] - ci[1]).append (',');
+ pf.append (9, 3, 2, ci[0] + ci[1]).append (")");
+ }
+
+ return pf.toString();
+ }
+
+
+ /**
+ * Returns a formatted string that contains a report on this probe (as in
+ * {@link #report report}), followed by a confidence interval (as in
+ * {@link #formatCIStudent formatCIStudent}), using <SPAN CLASS="MATH"><I>d</I></SPAN> fractional decimal digits.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @param d number of fractional decimal digits
+ *
+ * @return a statistical report with a confidence interval, formatted as a string
+ *
+ */
+ public String reportAndCIStudent (double level, int d) {
+ CIType oldCIType = confidenceInterval;
+
+ try {
+ confidenceInterval = CIType.CI_STUDENT;
+ return report(level, d);
+ } finally {
+ confidenceInterval = oldCIType;
+ }
+ }
+
+
+ /**
+ * Same as {@link #reportAndCIStudent reportAndCIStudent} <TT>(level, 3)</TT>.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ * @return a statistical report with a confidence interval, formatted as a string
+ *
+ */
+ public String reportAndCIStudent (double level) {
+ return reportAndCIStudent (level, 3);
+ }
+
+
+ /**
+ * Returns the level of confidence for the intervals on the mean displayed in
+ * reports. The default confidence level is 0.95.
+ *
+ * @return desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ */
+ public double getConfidenceLevel() {
+ return level;
+ }
+
+
+ /**
+ * Sets the level of confidence for the intervals on the mean displayed in
+ * reports.
+ *
+ * @param level desired probability that the (random) confidence
+ * interval covers the true mean (a constant)
+ *
+ *
+ */
+ public void setConfidenceLevel (double level) {
+ if (level < 0.0)
+ throw new IllegalArgumentException("level < 0");
+ if (level >= 1.0)
+ throw new IllegalArgumentException("level >= 1");
+ this.level = level;
+ }
+
+
+ /**
+ * Indicates that no confidence interval needs to be printed in
+ * reports formatted by {@link #report report}, and {@link #shortReport shortReport}.
+ * This restores the default behavior of the reporting system.
+ *
+ */
+ public void setConfidenceIntervalNone() {
+ confidenceInterval = CIType.CI_NONE;
+ }
+
+
+ /**
+ * Indicates that a confidence interval on the true mean, based on the
+ * central limit theorem, needs to be included in reports formatted by
+ * {@link #report report} and {@link #shortReport shortReport}. The confidence interval is
+ * formatted using {@link #formatCINormal formatCINormal}.
+ *
+ */
+ public void setConfidenceIntervalNormal() {
+ confidenceInterval = CIType.CI_NORMAL;
+ }
+
+
+ /**
+ * Indicates that a confidence interval on the true mean, based on the
+ * normality assumption, needs to be included in
+ * reports formatted by {@link #report report} and {@link #shortReport shortReport}.
+ * The confidence interval is formatted using {@link #formatCIStudent formatCIStudent}.
+ *
+ */
+ public void setConfidenceIntervalStudent() {
+ confidenceInterval = CIType.CI_STUDENT;
+ }
+
+
+ /**
+ * Determines if the number of observations must be displayed in reports.
+ * By default, the number of observations is displayed.
+ *
+ * @param showNumObs the value of the indicator.
+ *
+ *
+ */
+ public void setShowNumberObs (boolean showNumObs) {
+ showNobs = showNumObs;
+ }
+
+
+ /**
+ * Clones this object.
+ *
+ */
+ public Tally clone() {
+ try {
+ return (Tally)super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException ("Tally can't clone");
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stat/Tally.tex b/source/umontreal/iro/lecuyer/stat/Tally.tex
new file mode 100644
index 0000000..312d9ee
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/Tally.tex
@@ -0,0 +1,661 @@
+\defmodule {Tally}
+
+\latex{A subclass of \class{StatProbe}.}
+This type of statistical collector takes a sequence of real-valued
+observations\latex{ $X_1,X_2,X_3,\dots$} and can return the average,
+the variance, a confidence interval for the theoretical mean, etc.
+Each call to \method{add}{} provides a new observation.
+When the broadcasting to observers is activated,
+the method \method{add}{} will also pass this new information to its
+registered observers.
+This type of collector does not memorize the individual observations,
+but only their number, sum, sum of squares, maximum, and minimum.
+The subclass \class{TallyStore} offers a collector that memorizes
+the observations.
+% The collector can be reinitialized by \method{init}{}.
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: Tally
+ * Description: statistical collector
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat;
+\begin{hide}
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import umontreal.iro.lecuyer.probdist.StudentDist;
+import umontreal.iro.lecuyer.probdist.NormalDist;
+import umontreal.iro.lecuyer.probdist.ChiSquareDist;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+\end{hide}
+
+public class Tally extends StatProbe implements Cloneable\begin{hide} {
+ private int numObs;
+ private double sumSquares;
+ private double curAverage; // The average of the first numObs observations
+ private double curSum2; // The sum (xi - average)^2 of the first numObs
+ // observations.
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat");
+
+ private static enum CIType {CI_NONE, CI_NORMAL, CI_STUDENT};
+
+ protected CIType confidenceInterval = CIType.CI_NONE;
+ protected double level = 0.95;
+ protected int digits = 3;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public Tally() \begin{hide} {
+ super();
+ init();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new unnamed \texttt{Tally} statistical probe.
+ \end{tabb}
+\begin{code}
+
+ public Tally (String name) \begin{hide} {
+ super();
+ this.name = name;
+ init();
+ } \end{hide}
+\end{code}
+ \begin{tabb} Constructs a new \texttt{Tally} statistical probe with
+ name \texttt{name}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{name}{name of the tally}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public void init() {
+ maxValue = Double.NEGATIVE_INFINITY;
+ minValue = Double.POSITIVE_INFINITY;
+ sumValue = 0.0;
+ sumSquares = 0.0;
+ curAverage = 0.0;
+ curSum2 = 0.0;
+ numObs = 0;
+ }\end{hide}
+
+ public void add (double x) \begin{hide} {
+ if (collect) {
+ if (x < minValue) minValue = x;
+ if (x > maxValue) maxValue = x;
+ numObs++;
+ // Algorithme dans Knuth ed. 3, p. 232; voir Wikipedia
+ // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#cite_note-1
+ double y = x - curAverage;
+ curAverage += y / numObs;
+ curSum2 += y*(x - curAverage);
+ // On pourrait utiliser l'algorithme correcteur de Kahan pour une
+ // meilleure precision.
+ // (voir http://en.wikipedia.org/wiki/Kahan_summation_algorithm)
+ }
+ notifyListeners (x);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Gives a new observation \texttt{x} to the statistical collector.
+ If broadcasting to observers is activated for this object,
+ this method also transmits the new information to the
+ registered observers by invoking the method
+ \method{notifyListeners}{}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x}{observation being added to this tally}
+\end{htmlonly}
+\begin{code}
+
+ public int numberObs() \begin{hide} {
+ return numObs;
+ } \end{hide}
+\end{code}
+ \begin{tabb} Returns the number of observations given to this probe
+ since its last initialization.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the number of collected observations}
+\end{htmlonly}
+\begin{code}
+
+ public double average() \begin{hide} {
+ if (numObs < 1) {
+ //System.err.println (
+ // "******* Tally " + name + ": calling average() with " + numObs +
+ // " Observation");
+ log.logp (Level.WARNING, "Tally", "average",
+ "Tally " + name + ": calling average() with " + numObs +
+ " observation");
+ return Double.NaN;
+ }
+ return curAverage;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the average value of the observations since the last
+ initialization.
+ \end{tabb}
+\begin{code}
+
+ public double variance() \begin{hide} {
+ // throws NumberObservationException {
+ // if (numObs < 2) throw NumberObservationException;
+ if (numObs < 2) {
+ //System.err.println (
+ // "******* Tally " + name + ": calling variance() with " + numObs +
+ // " Observation");
+ log.logp (Level.WARNING, "Tally", "variance",
+ "Tally " + name + ": calling variance() with " + numObs +
+ " observation");
+ return Double.NaN;
+ }
+
+ return curSum2 / (numObs-1);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the sample variance of the observations since the last
+ initialization.
+ This returns \texttt{Double.NaN}
+ if the tally contains less than two observations.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the variance of the observations}
+\end{htmlonly}
+\begin{code}
+
+ public double standardDeviation() \begin{hide} {
+ return Math.sqrt (variance());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the sample standard deviation of the observations
+ since the last initialization.
+ This returns \texttt{Double.NaN}
+ if the tally contains less than two observations.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the standard deviation of the observations}
+\end{htmlonly}
+\begin{code}
+
+ public void confidenceIntervalNormal (double level,
+ double[] centerAndRadius) \begin{hide} {
+ // Must return an array object, cannot return 2 doubles directly
+ double z;
+ if (numObs < 2) throw new RuntimeException (
+ "Tally " + name +
+ ": Calling confidenceIntervalStudent with < 2 Observations");
+ centerAndRadius[0] = average();
+ z = NormalDist.inverseF01 (0.5 * (level + 1.0));
+ centerAndRadius[1] = z * Math.sqrt (variance() / (double)numObs);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes a confidence interval on the mean.
+ Returns, in elements 0 and 1 of the array
+ object \texttt{centerAndRadius[]}, the center and half-length (radius)
+ of a confidence interval on the true mean of the random variable $X$,
+ with confidence level \texttt{level}, assuming that the $n$ observations
+ given to this collector are independent and identically distributed
+ (i.i.d.) copies of $X$, and that $n$ is large enough for the central limit
+ theorem to hold. This confidence interval is computed based on the statistic
+ $$
+ Z = {\bar X_n - \mu\latex{\over}\html{ / (}{S_{n,x}/\sqrt{n}}\html{)}}
+ $$
+ where $n$ is the number of observations given to this collector since its
+ last initialization, $\bar X_n =$ \texttt{average()} is the average of these
+ observations, $S_{n,x} =$ \texttt{standardDeviation()} is the empirical
+ standard deviation. Under the assumption that the observations
+ of $X$ are i.i.d.{} and $n$ is large,
+ $Z$ has the standard normal distribution.
+ The confidence interval given by this method is valid \emph{only if}
+ this assumption is approximately verified.
+ \end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \param{centerAndRadius}{array of size 2 in which are returned the center
+ and radius of the confidence interval, respectively}
+\end{htmlonly}
+\begin{code}
+
+ public void confidenceIntervalStudent (double level,
+ double[] centerAndRadius) \begin{hide} {
+ // Must return an array object, cannot return 2 doubles directly
+ double t;
+ if (numObs < 2) throw new RuntimeException (
+ "Tally " + name +
+ ": Calling confidenceIntervalStudent with < 2 Observations");
+ centerAndRadius[0] = average();
+ t = StudentDist.inverseF (numObs - 1, 0.5 * (level + 1.0));
+ centerAndRadius[1] = t * Math.sqrt (variance() / (double)numObs);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes a confidence interval on the mean.
+ Returns, in elements 0 and 1 of the array
+ object \texttt{centerAndRadius[]}, the center and half-length (radius)
+ of a confidence interval on the true mean of the random variable $X$,
+ with confidence level \texttt{level}, assuming that the observations
+ given to this collector are independent and identically distributed
+ (i.i.d.) copies of $X$, and that $X$ has the normal distribution.
+ This confidence interval is computed based on the statistic
+ $$
+ T = {\bar X_n - \mu\latex{\over}\html{ / (}{S_{n,x}/\sqrt{n}}\html{)}}
+ $$
+ where $n$ is the number of observations given to this collector since its
+ last initialization, $\bar X_n =$ \texttt{average()} is the average of these
+ observations, $S_{n,x} =$ \texttt{standardDeviation()} is the empirical
+ standard deviation. Under the assumption that the observations
+ of $X$ are i.i.d.{} and normally distributed,
+ $T$ has the Student distribution with $n-1$ degrees of freedom.
+ The confidence interval given by this method is valid \emph{only if}
+ this assumption is approximately verified, or if $n$ is large enough
+ so that $\bar X_n$ is approximately normally distributed.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \param{centerAndRadius}{array of size 2 in which are returned the center
+ and radius of the confidence interval, respectively}
+\end{htmlonly}
+\begin{code}
+
+ public String formatCINormal (double level, int d) \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+ double ci[] = new double[2];
+ confidenceIntervalNormal (level, ci);
+ str.append (" " + (100*level) + "%");
+ str.append (" confidence interval for mean (normal): (");
+ str.append (7 + d, d-1, d, ci[0] - ci[1]).append (',');
+ str.append (7 + d, d-1, d, ci[0] + ci[1]).append (" )" + PrintfFormat.NEWLINE);
+ return str.toString();
+}\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{confidenceIntervalNormal}{}.
+ Returns the confidence interval in a formatted string of the form \\
+\centerline{``\texttt{95\% confidence interval for mean (normal): (32.431,\, 32.487)}'',}
+ using $d$ fractional decimal digits.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \param{d}{number of fractional decimal digits}
+ \return{a confidence interval formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String formatCINormal (double level) \begin{hide} {
+ return formatCINormal (level, 3);
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to \texttt{formatCINormal (level, 3)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \return{a confidence interval formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String formatCIStudent (double level, int d) \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+ double ci[] = new double[2];
+ confidenceIntervalStudent (level, ci);
+ str.append (" " + (100*level) + "%");
+ str.append (" confidence interval for mean (student): (");
+ str.append (7 + d, d, d-1, ci[0] - ci[1]).append (',');
+ str.append (7 + d, d, d-1, ci[0] + ci[1]).append (" )" + PrintfFormat.NEWLINE);
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Similar to \method{confidenceIntervalStudent}{}.
+ Returns the confidence interval in a formatted string of the form\\
+ \centerline{``\texttt{95\% confidence interval for mean (student): (32.431,\, 32.487)}'',}
+ using $d$ fractional decimal digits.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \param{d}{number of fractional decimal digits}
+ \return{a confidence interval formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String formatCIStudent (double level) \begin{hide} {
+ return formatCIStudent (level, 3);
+ }\end{hide}
+\end{code}
+\begin{tabb} Equivalent to \texttt{formatCIStudent (level, 3)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \return{a confidence interval formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public void confidenceIntervalVarianceChi2 (double level,
+ double[] interval) \begin{hide} {
+ // Must return an array object, cannot return 2 doubles directly
+ if (numObs < 2) throw new RuntimeException (
+ "Tally " + name +
+ ": calling confidenceIntervalVarianceChi2 with < 2 observations");
+ double w = (numObs - 1)*variance();
+ double x2 = ChiSquareDist.inverseF (numObs - 1, 0.5 * (1.0 + level));
+ double x1 = ChiSquareDist.inverseF (numObs - 1, 0.5 * (1.0 - level));
+ interval[0] = w / x2;
+ interval[1] = w / x1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes a confidence interval on the variance.
+ Returns, in elements 0 and 1 of array \texttt{interval}, the
+ left and right boundaries $[I_1,I_2]$ of a confidence interval on the true
+ variance $\sigma^2$ of the random variable $X$, with confidence level
+ \texttt{level}, assuming that the observations
+ given to this collector are independent and identically distributed
+ (i.i.d.) copies of $X$, and that $X$ has the normal distribution.
+ This confidence interval is computed based on the statistic
+ $ \chi^2_{n-1} = (n-1)S^2_{n}/\sigma^2 $
+ where $n$ is the number of observations given to this collector since its
+ last initialization, and $S^2_n =$ \texttt{variance()} is the empirical
+ variance of these observations. Under the assumption that the observations
+ of $X$ are i.i.d.{} and normally distributed,
+ $\chi^2_{n-1}$ has the chi-square distribution with $n-1$ degrees of freedom.
+ Given the \texttt{level} $ = 1 - \alpha$,
+ one has $P[\chi^2_{n-1} < x_1] = P[\chi^2_{n-1} > x_2] = \alpha/2$
+ and $[I_1,I_2] = [(n-1)S^2_{n}/x_2,\; (n-1)S^2_{n}/x_1]$.
+% The confidence interval given by this method is valid \emph{only if}
+% this assumption is approximately verified, or if $n$ is large enough
+% so that $\bar X_n$ is approximately normally distributed.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \param{interval}{array of size 2 in which are returned the left
+ and right boundaries of the confidence interval, respectively}
+\end{htmlonly}
+\begin{code}
+
+ public String formatCIVarianceChi2 (double level, int d) \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+ double ci[] = new double[2];
+ confidenceIntervalVarianceChi2 (level, ci);
+ str.append (" " + (100*level) + "%");
+ str.append (" confidence interval for variance (chi2): (");
+ str.append (7 + d, d, d-1, ci[0]).append (',');
+ str.append (7 + d, d, d-1, ci[1]).append (" )" + PrintfFormat.NEWLINE);
+ return str.toString();
+}\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{confidenceIntervalVarianceChi2}{}.
+ Returns the confidence interval in a formatted string of the form \\
+\centerline{``\texttt{95.0\% confidence interval for variance (chi2):
+ ( 510.642, 519.673 )}'',}
+ using $d$ fractional decimal digits.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true variance}
+ \param{d}{number of fractional decimal digits}
+ \return{a confidence interval formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String report() \begin{hide} {
+ return report(level, digits);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a formatted string that contains a report on this probe.
+ \end{tabb}
+\begin{htmlonly}
+ \return{a statistical report formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String report (double level, int d) \begin{hide} {
+ PrintfFormat str = new PrintfFormat();
+ str.append ("REPORT on Tally stat. collector ==> " + name);
+ str.append (PrintfFormat.NEWLINE + " num. obs. min max average standard dev." + PrintfFormat.NEWLINE);
+ str.append (7 + d, (int)numObs); str.append (" ");
+ str.append (9 + d, d, d-1, (double)minValue); str.append (" ");
+ str.append (9 + d, d, d-1, (double)maxValue); str.append (" ");
+ str.append (9 + d, d, d-1, (double)average()); str.append (" ");
+ str.append (9 + d, d, d-1, standardDeviation());
+ str.append (PrintfFormat.NEWLINE);
+
+ switch (confidenceInterval) {
+ case CI_NORMAL:
+ str.append (formatCINormal (level, d));
+ break;
+ case CI_STUDENT:
+ str.append (formatCIStudent (level, d));
+ break;
+ }
+
+ return str.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns a formatted string that contains a report on this probe
+ with a confidence interval level \texttt{level} using $d$
+ fractional decimal digits.
+ \end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the confidence
+ interval covers the true mean}
+ \param{d}{number of fractional decimal digits}
+ \return{a statistical report formatted as a string}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public String shortReportHeader() {
+ PrintfFormat pf = new PrintfFormat();
+ if (showNobs)
+ pf.append (-8, "num obs.").append (" ");
+ pf.append (-8, " min").append (" ");
+ pf.append (-8, " max").append (" ");
+ pf.append (-8, " average").append (" ");
+ pf.append (-8, " std. dev.");
+ if (confidenceInterval != CIType.CI_NONE)
+ pf.append (" ").append (-12, "conf. int.");
+
+ return pf.toString();
+ }\end{hide}
+
+ public String shortReport()\begin{hide} {
+ PrintfFormat pf = new PrintfFormat();
+ if (showNobs)
+ pf.append (-8, numberObs());
+ pf.append (9, 3, 2, min()).append (" ");
+ pf.append (9, 3, 2, max()).append (" ");
+ pf.append (10, 3, 2, average()).append (" ");
+ if (numberObs() >= 2)
+ pf.append (11, 3, 2, standardDeviation());
+ else
+ pf.append (11, "---");
+
+ if (confidenceInterval != CIType.CI_NONE) {
+ double[] ci = new double[2];
+ switch (confidenceInterval) {
+ case CI_NORMAL:
+ confidenceIntervalNormal (level, ci);
+ break;
+ case CI_STUDENT:
+ confidenceIntervalStudent (level, ci);
+ break;
+ }
+ pf.append (" ").append ((100*level) + "% (");
+ pf.append (9, 3, 2, ci[0] - ci[1]).append (',');
+ pf.append (9, 3, 2, ci[0] + ci[1]).append (")");
+ }
+
+ return pf.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats and returns a short
+ statistical report for this tally.
+ The returned single-line report contains the minimum value,
+ the maximum value,
+ the average, and the standard deviation, in that order, separated
+ by three spaces. If the number of observations is shown in the short report,
+ a column containing the
+ number of observations in this tally is added.
+\end{tabb}
+\begin{htmlonly}
+ \return{the string containing the report.}
+\end{htmlonly}
+\begin{code}
+
+ public String reportAndCIStudent (double level, int d) \begin{hide} {
+ CIType oldCIType = confidenceInterval;
+
+ try {
+ confidenceInterval = CIType.CI_STUDENT;
+ return report(level, d);
+ } finally {
+ confidenceInterval = oldCIType;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a formatted string that contains a report on this probe (as in
+ \method{report}{}), followed by a confidence interval (as in
+ \method{formatCIStudent}{}), using $d$ fractional decimal digits.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \param{d}{number of fractional decimal digits}
+ \return{a statistical report with a confidence interval, formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public String reportAndCIStudent (double level) \begin{hide} {
+ return reportAndCIStudent (level, 3);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{reportAndCIStudent}{} \texttt{(level, 3)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+ \return{a statistical report with a confidence interval, formatted as a string}
+\end{htmlonly}
+\begin{code}
+
+ public double getConfidenceLevel()\begin{hide} {
+ return level;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the level of confidence for the intervals on the mean displayed in
+ reports. The default confidence level is 0.95.
+\end{tabb}
+\begin{htmlonly}
+ \return{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+\end{htmlonly}
+\begin{code}
+
+ public void setConfidenceLevel (double level)\begin{hide} {
+ if (level < 0.0)
+ throw new IllegalArgumentException("level < 0");
+ if (level >= 1.0)
+ throw new IllegalArgumentException("level >= 1");
+ this.level = level;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Sets the level of confidence for the intervals on the mean displayed in
+ reports.
+\end{tabb}
+\begin{htmlonly}
+ \param{level}{desired probability that the (random) confidence
+ interval covers the true mean (a constant)}
+\end{htmlonly}
+\begin{code}
+
+ public void setConfidenceIntervalNone()\begin{hide} {
+ confidenceInterval = CIType.CI_NONE;
+ }\end{hide}
+\end{code}
+\begin{tabb} Indicates that no confidence interval needs to be printed in
+ reports formatted by \method{report}{}, and \method{shortReport}{}.
+ This restores the default behavior of the reporting system.
+\end{tabb}
+\begin{code}
+
+ public void setConfidenceIntervalNormal()\begin{hide} {
+ confidenceInterval = CIType.CI_NORMAL;
+ }\end{hide}
+\end{code}
+\begin{tabb} Indicates that a confidence interval on the true mean, based on the
+ central limit theorem, needs to be included in reports formatted by
+ \method{report}{} and \method{shortReport}{}. The confidence interval is
+ formatted using \method{formatCINormal}{}.
+\end{tabb}
+\begin{code}
+
+ public void setConfidenceIntervalStudent()\begin{hide} {
+ confidenceInterval = CIType.CI_STUDENT;
+ }\end{hide}
+\end{code}
+\begin{tabb} Indicates that a confidence interval on the true mean, based on the
+ normality assumption, needs to be included in
+ reports formatted by \method{report}{} and \method{shortReport}{}.
+ The confidence interval is formatted using \method{formatCIStudent}{}.
+\end{tabb}
+\begin{code}
+
+ public void setShowNumberObs (boolean showNumObs)\begin{hide} {
+ showNobs = showNumObs;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Determines if the number of observations must be displayed in reports.
+ By default, the number of observations is displayed.
+\end{tabb}
+\begin{htmlonly}
+ \param{showNumObs}{the value of the indicator.}
+\end{htmlonly}
+\begin{code}
+
+ public Tally clone()\begin{hide} {
+ try {
+ return (Tally)super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException ("Tally can't clone");
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Clones this object.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stat/TallyHistogram.java b/source/umontreal/iro/lecuyer/stat/TallyHistogram.java
new file mode 100644
index 0000000..754c1df
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/TallyHistogram.java
@@ -0,0 +1,255 @@
+
+
+/*
+ * Class: TallyHistogram
+ * Description: Histogram of a tally
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+
+/**
+ * This class is an extension of {@link Tally} which gives a more detailed view
+ * of the observations statistics. The individual observations are assumed to
+ * fall into different bins (boxes) of equal width on an interval.
+ * The total number of observations falling into the bins are kept in an array
+ * of counters. This is useful, for example, if one wish to build a histogram
+ * from the observations. One must access the array of bin counters
+ * to compute quantities not supported by the methods in {@link Tally}.
+ *
+ * <P>
+ * <SPAN CLASS="textit">Never add or remove observations directly</SPAN> on the array of
+ * bin counters because this would put the {@link Tally} counters in an
+ * inconsistent state.
+ *
+ */
+public class TallyHistogram extends Tally {
+ private int[] co; // counter: num of values in bin[i]
+ private int numBins; // number of bins
+ private double m_h; // width of 1 bin
+ private double m_a; // left boundary of first bin
+ private double m_b; // right boundary of last bin
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat");
+
+
+
+ /**
+ * Constructs a <TT>TallyHistogram</TT> statistical probe.
+ * Divide the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> into <SPAN CLASS="MATH"><I>s</I></SPAN> bins of equal width and initializes
+ * a counter to 0 for each bin. Whenever an observation falls into a bin,
+ * the bin counter is increased by 1. There are two extra bins (and counters)
+ * that count the number of observations <SPAN CLASS="MATH"><I>x</I></SPAN> that fall outside the interval
+ * <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>: one for those <SPAN CLASS="MATH"><I>x</I> < <I>a</I></SPAN>, and the other for those <SPAN CLASS="MATH"><I>x</I> > <I>b</I></SPAN>.
+ *
+ * @param a left boundary of interval
+ *
+ * @param b right boundary of interval
+ *
+ * @param s number of bins
+ *
+ *
+ */
+ public TallyHistogram(double a, double b, int s) {
+ super();
+ init (a, b, s);
+ }
+
+
+ /**
+ * Constructs a new <TT>TallyHistogram</TT> statistical probe with
+ * name <TT>name</TT>.
+ *
+ * @param name the name of the tally.
+ *
+ * @param a left boundary of interval
+ *
+ * @param b right boundary of interval
+ *
+ * @param s number of bins
+ *
+ */
+ public TallyHistogram (String name, double a, double b, int s) {
+ super (name);
+ init (a, b, s);
+ }
+
+
+ /**
+ * Initializes this object.
+ * Divide the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> into <SPAN CLASS="MATH"><I>s</I></SPAN> bins of equal width and initializes
+ * all counters to 0.
+ *
+ * @param s number of bins
+ *
+ * @param a left boundary of interval
+ *
+ * @param b right boundary of interval
+ *
+ *
+ */
+ public void init (double a, double b, int s) {
+ /* The counters co[1] to co[s] contains the number of observations
+ falling in the interval [a, b].
+ co[0] is the number of observations < a,
+ and co[s+1] is the number of observations > b.
+ */
+
+ super.init();
+ if (b <= a)
+ throw new IllegalArgumentException (" b <= a");
+ co = new int[s + 2];
+ numBins = s;
+ m_h = (b - a) / s;
+ m_a = a;
+ m_b = b;
+ for (int i = 0; i <= s + 1; i++)
+ co[i] = 0;
+ }
+
+
+ /**
+ * Gives a new observation <SPAN CLASS="MATH"><I>x</I></SPAN> to the statistical collectors.
+ * Increases by 1 the bin counter in which value <SPAN CLASS="MATH"><I>x</I></SPAN> falls.
+ * Values that fall outside the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> are added in extra bin
+ * counter bin[0] if <SPAN CLASS="MATH"><I>x</I> < <I>a</I></SPAN>, and in bin[<SPAN CLASS="MATH"><I>s</I> + 1</SPAN>] if <SPAN CLASS="MATH"><I>x</I> > <I>b</I></SPAN>.
+ *
+ * @param x observation value
+ *
+ *
+ */
+ public void add (double x) {
+ super.add(x);
+ if (x < m_a)
+ ++co[0];
+ else if (x > m_b)
+ ++co[1 + numBins];
+ else {
+ int i = 1 + (int) ((x - m_a) / m_h);
+ ++co[i];
+ }
+ }
+
+
+ /**
+ * Returns the bin counters. Each counter contains the number of
+ * observations that fell in its corresponding bin.
+ * The counters bin[<SPAN CLASS="MATH"><I>i</I></SPAN>],
+ * <SPAN CLASS="MATH"><I>i</I> = 1, 2,…, <I>s</I></SPAN> contain the number of observations
+ * that fell in each subinterval of <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>. Values that fell outside the interval
+ * <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> were added in extra bin counter bin[0] if <SPAN CLASS="MATH"><I>x</I> < <I>a</I></SPAN>, and in bin[<SPAN CLASS="MATH"><I>s</I> + 1</SPAN>]
+ * if <SPAN CLASS="MATH"><I>x</I> > <I>b</I></SPAN>. There are thus <SPAN CLASS="MATH"><I>s</I> + 2</SPAN> counters.
+ *
+ * @return the array of counters
+ *
+ */
+ public int[] getCounters() {
+ return co;
+ }
+
+
+ /**
+ * Returns the number of bins <SPAN CLASS="MATH"><I>s</I></SPAN> dividing the interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ * Does not count the two extra bins for the values of <SPAN CLASS="MATH"><I>x</I> < <I>a</I></SPAN> or <SPAN CLASS="MATH"><I>x</I> > <I>b</I></SPAN>.
+ *
+ * @return the number of bins
+ *
+ */
+ public int getNumBins() {
+ return numBins;
+ }
+
+
+ /**
+ * Returns the left boundary <SPAN CLASS="MATH"><I>a</I></SPAN> of interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ *
+ * @return left boundary of interval
+ *
+ */
+ public double getA() {
+ return m_a;
+ }
+
+
+ /**
+ * Returns the right boundary <SPAN CLASS="MATH"><I>b</I></SPAN> of interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN>.
+ *
+ * @return right boundary of interval
+ *
+ */
+ public double getB() {
+ return m_b;
+ }
+
+
+ /**
+ * Clones this object and the array which stores the counters.
+ *
+ */
+ public TallyHistogram clone() {
+ TallyHistogram image = (TallyHistogram)super.clone();
+ int[] coco = new int[2 + numBins];
+ System.arraycopy (co, 0, coco, 0, 2 + numBins);
+ image.co = coco;
+ image.m_h = m_h;
+ image.m_a = m_a;
+ image.m_b = m_b;
+ image.numBins = numBins;
+ return image;
+ }
+
+
+ /**
+ * Returns the bin counters as a <TT>String</TT>.
+ *
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer ();
+ sb.append ("---------------------------------------" +
+ PrintfFormat.NEWLINE);
+ sb.append (name + PrintfFormat.NEWLINE);
+ sb.append ("Interval = [ " + m_a + ", " + m_b + " ]" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Number of bins = " + numBins + " + 2" + PrintfFormat.NEWLINE);
+ sb.append (PrintfFormat.NEWLINE + "Counters = {" +
+ PrintfFormat.NEWLINE);
+ sb.append (" (-inf, " + PrintfFormat.f(6, 3, m_a)
+ + ") " + co[0] + PrintfFormat.NEWLINE);
+ for (int i = 1; i <= numBins; i++) {
+ double a = m_a + (i-1)*m_h;
+ double b = m_a + i*m_h;
+ sb.append (" (" +
+ PrintfFormat.f(6, 3, a) + ", " +
+ PrintfFormat.f(6, 3, b) + ") " + co[i] +
+ PrintfFormat.NEWLINE);
+ }
+ sb.append (" (" + PrintfFormat.f(6, 3, m_b)
+ + ", inf) " + co[numBins + 1] +
+ PrintfFormat.NEWLINE);
+ sb.append ("}" + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stat/TallyHistogram.tex b/source/umontreal/iro/lecuyer/stat/TallyHistogram.tex
new file mode 100644
index 0000000..f52c21f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/TallyHistogram.tex
@@ -0,0 +1,254 @@
+\defmodule {TallyHistogram}
+
+This class is an extension of \class{Tally} which gives a more detailed view
+of the observations statistics. The individual observations are assumed to
+fall into different bins (boxes) of equal width on an interval.
+The total number of observations falling into the bins are kept in an array
+of counters. This is useful, for example, if one wish to build a histogram
+from the observations. One must access the array of bin counters
+to compute quantities not supported by the methods in \class{Tally}.
+
+\emph{Never add or remove observations directly} on the array of
+bin counters because this would put the \class{Tally} counters in an
+inconsistent state.
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: TallyHistogram
+ * Description: Histogram of a tally
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Richard Simard
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat;\begin{hide}
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+
+public class TallyHistogram extends Tally \begin{hide} {
+ private int[] co; // counter: num of values in bin[i]
+ private int numBins; // number of bins
+ private double m_h; // width of 1 bin
+ private double m_a; // left boundary of first bin
+ private double m_b; // right boundary of last bin
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat");
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public TallyHistogram(double a, double b, int s) \begin{hide} {
+ super();
+ init (a, b, s);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a \texttt{TallyHistogram} statistical probe.
+Divide the interval $[a,b]$ into $s$ bins of equal width and initializes
+a counter to 0 for each bin. Whenever an observation falls into a bin,
+the bin counter is increased by 1. There are two extra bins (and counters)
+that count the number of observations $x$ that fall outside the interval
+$[a,b]$: one for those $x< a$, and the other for those $x > b$.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{left boundary of interval}
+ \param{b}{right boundary of interval}
+ \param{s}{number of bins}
+\end{htmlonly}
+\begin{code}
+
+ public TallyHistogram (String name, double a, double b, int s) \begin{hide} {
+ super (name);
+ init (a, b, s);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{TallyHistogram} statistical probe with
+name \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the name of the tally.}
+ \param{a}{left boundary of interval}
+ \param{b}{right boundary of interval}
+ \param{s}{number of bins}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public void init (double a, double b, int s) \begin{hide} {
+ /* The counters co[1] to co[s] contains the number of observations
+ falling in the interval [a, b].
+ co[0] is the number of observations < a,
+ and co[s+1] is the number of observations > b.
+ */
+
+ super.init();
+ if (b <= a)
+ throw new IllegalArgumentException (" b <= a");
+ co = new int[s + 2];
+ numBins = s;
+ m_h = (b - a) / s;
+ m_a = a;
+ m_b = b;
+ for (int i = 0; i <= s + 1; i++)
+ co[i] = 0;
+ } \end{hide}
+\end{code}
+\begin{tabb} Initializes this object.
+Divide the interval $[a,b]$ into $s$ bins of equal width and initializes
+all counters to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{number of bins}
+ \param{a}{left boundary of interval}
+ \param{b}{right boundary of interval}
+\end{htmlonly}
+\begin{code}
+
+ public void add (double x) \begin{hide} {
+ super.add(x);
+ if (x < m_a)
+ ++co[0];
+ else if (x > m_b)
+ ++co[1 + numBins];
+ else {
+ int i = 1 + (int) ((x - m_a) / m_h);
+ ++co[i];
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Gives a new observation $x$ to the statistical collectors.
+Increases by 1 the bin counter in which value $x$ falls.
+Values that fall outside the interval $[a,b]$ are added in extra bin
+counter bin[0] if $x < a$, and in bin[$s+1$] if $x > b$.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{observation value}
+\end{htmlonly}
+\begin{code}
+
+ public int[] getCounters() \begin{hide} {
+ return co;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the bin counters. Each counter contains the number of
+observations that fell in its corresponding bin.
+The counters bin[$i$], $i=1, 2, \ldots, s$ contain the number of observations
+that fell in each subinterval of $[a,b]$. Values that fell outside the interval
+$[a,b]$ were added in extra bin counter bin[0] if $x < a$, and in bin[$s+1$]
+if $x > b$. There are thus $s+2$ counters.
+\end{tabb}
+\begin{htmlonly}
+ \return{the array of counters}
+\end{htmlonly}
+\begin{code}
+
+ public int getNumBins() \begin{hide} {
+ return numBins;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the number of bins $s$ dividing the interval $[a,b]$.
+Does not count the two extra bins for the values of $x<a$ or $x>b$.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of bins}
+\end{htmlonly}
+\begin{code}
+
+ public double getA() \begin{hide} {
+ return m_a;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the left boundary $a$ of interval $[a,b]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{left boundary of interval}
+\end{htmlonly}
+\begin{code}
+
+ public double getB() \begin{hide} {
+ return m_b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the right boundary $b$ of interval $[a,b]$.
+\end{tabb}
+\begin{htmlonly}
+ \return{right boundary of interval}
+\end{htmlonly}
+\begin{code}
+
+ public TallyHistogram clone()\begin{hide} {
+ TallyHistogram image = (TallyHistogram)super.clone();
+ int[] coco = new int[2 + numBins];
+ System.arraycopy (co, 0, coco, 0, 2 + numBins);
+ image.co = coco;
+ image.m_h = m_h;
+ image.m_a = m_a;
+ image.m_b = m_b;
+ image.numBins = numBins;
+ return image;
+ }\end{hide}
+\end{code}
+\begin{tabb} Clones this object and the array which stores the counters.
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ StringBuffer sb = new StringBuffer ();
+ sb.append ("---------------------------------------" +
+ PrintfFormat.NEWLINE);
+ sb.append (name + PrintfFormat.NEWLINE);
+ sb.append ("Interval = [ " + m_a + ", " + m_b + " ]" +
+ PrintfFormat.NEWLINE);
+ sb.append ("Number of bins = " + numBins + " + 2" + PrintfFormat.NEWLINE);
+ sb.append (PrintfFormat.NEWLINE + "Counters = {" +
+ PrintfFormat.NEWLINE);
+ sb.append (" (-inf, " + PrintfFormat.f(6, 3, m_a)
+ + ") " + co[0] + PrintfFormat.NEWLINE);
+ for (int i = 1; i <= numBins; i++) {
+ double a = m_a + (i-1)*m_h;
+ double b = m_a + i*m_h;
+ sb.append (" (" +
+ PrintfFormat.f(6, 3, a) + ", " +
+ PrintfFormat.f(6, 3, b) + ") " + co[i] +
+ PrintfFormat.NEWLINE);
+ }
+ sb.append (" (" + PrintfFormat.f(6, 3, m_b)
+ + ", inf) " + co[numBins + 1] +
+ PrintfFormat.NEWLINE);
+ sb.append ("}" + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the bin counters as a \texttt{String}.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stat/TallyStore.java b/source/umontreal/iro/lecuyer/stat/TallyStore.java
new file mode 100644
index 0000000..d6f7270
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/TallyStore.java
@@ -0,0 +1,249 @@
+
+
+/*
+ * Class: TallyStore
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat;
+import cern.colt.list.DoubleArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+
+
+
+/**
+ * This class is a variant of {@link Tally} for which the individual
+ * observations are stored in a list implemented as a
+ * {@link cern.colt.list.DoubleArrayList DoubleArrayList}.
+ * The {@link cern.colt.list.DoubleArrayList DoubleArrayList} object used to
+ * store the values can be either passed to the constructor or created by
+ * the constructor, and can be accessed via the
+ * {@link #getDoubleArrayList getDoubleArrayList} method.
+ *
+ * <P>
+ * The same counters as in {@link Tally} are maintained and are used by
+ * the inherited methods. One must access the list of observations
+ * to compute quantities not supported by the methods in
+ * {@link Tally}, and/or to use methods provided by the COLT package.
+ *
+ * <P>
+ * <SPAN CLASS="textit">Never add or remove observations directly</SPAN> on the
+ * {@link cern.colt.list.DoubleArrayList DoubleArrayList} object,
+ * because this would put the counters of the <TT>TallyStore</TT> object in an
+ * inconsistent state.
+ *
+ * <P>
+ * There are two potential reasons for using a {@link TallyStore} object
+ * instead of directly using a {@link cern.colt.list.DoubleArrayList DoubleArrayList} object:
+ * (a) it can broadcast observations and
+ * (b) it maintains a few additional counters that may speed up some operations
+ * such as computing the average.
+ *
+ */
+public class TallyStore extends Tally {
+
+ private DoubleArrayList array = null; // Where the observations are stored.
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat");
+
+
+
+ /**
+ * Constructs a new <TT>TallyStore</TT> statistical probe.
+ *
+ */
+ public TallyStore() {
+ super();
+ array = new DoubleArrayList();
+ }
+
+
+ /**
+ * Constructs a new <TT>TallyStore</TT> statistical probe with name <TT>name</TT>.
+ *
+ * @param name the name of the tally.
+ *
+ *
+ */
+ public TallyStore (String name) {
+ super (name);
+ array = new DoubleArrayList();
+ }
+
+
+ /**
+ * Constructs a new <TT>TallyStore</TT> statistical probe
+ * with given initial capacity <TT>capacity</TT> for its associated array.
+ *
+ * @param capacity initial capacity of the array of observations
+ *
+ *
+ */
+ public TallyStore (int capacity) {
+ super();
+ array = new DoubleArrayList (capacity);
+ }
+
+
+ /**
+ * Constructs a new <TT>TallyStore</TT> statistical probe with
+ * name <TT>name</TT> and given initial capacity <TT>capacity</TT> for its
+ * associated array.
+ *
+ * @param name the name of the tally.
+ *
+ * @param capacity initial capacity of the array of observations
+ *
+ *
+ */
+ public TallyStore (String name, int capacity) {
+ super (name);
+ array = new DoubleArrayList (capacity);
+ }
+
+
+ /**
+ * Constructs a new <TT>TallyStore</TT> statistical probe
+ * with given associated array. This array must be empty.
+ *
+ * @param a array that will contain observations
+ *
+ */
+ public TallyStore (DoubleArrayList a) {
+ super();
+ array = a;
+ array.clear();
+ }
+
+
+ public void init() {
+ super.init();
+ // We must call super before any actions inside constructors.
+ // Unfortunately, the base class calls init, which would
+ // result in a NullPointerException.
+ if (array != null)
+ array.clear();
+ }
+
+ public void add (double x) {
+ if (collect) array.add (x);
+ super.add(x);
+ }
+
+ /**
+ * Returns the observations stored in this probe.
+ *
+ * @return the array of observations associated with this object
+ *
+ */
+ public double[] getArray() {
+ array.trimToSize();
+ return array.elements();
+ }
+
+
+ /**
+ * Returns the {@link cern.colt.list.DoubleArrayList DoubleArrayList}
+ * object that contains the observations for this probe. <SPAN CLASS="textbf">WARNING:</SPAN>
+ * In previous releases, this function was named <TT>getArray</TT>.
+ *
+ * @return the array of observations associated with this object
+ *
+ */
+ public DoubleArrayList getDoubleArrayList() {
+ array.trimToSize();
+ return array;
+ }
+
+
+ /**
+ * Sorts the elements of this probe using the <TT>quicksort</TT>
+ * from Colt.
+ *
+ */
+ public void quickSort() {
+ array.quickSort();
+ }
+
+
+ /**
+ * Returns the sample covariance of the observations contained
+ * in this tally, and the other tally <TT>t2</TT>.
+ * Both tallies must have the same number of observations.
+ * This returns <TT>Double.NaN</TT>
+ * if the tallies do not contain the same number of observations, or
+ * if they contain less than two observations.
+ *
+ * @param t2 the other tally.
+ *
+ * @return the sample covariance.
+ *
+ */
+ public double covariance (TallyStore t2) {
+ if (numberObs() != t2.numberObs()) {
+ // System.err.println ("******* TallyStore.covariance(): " +
+ // "Tally's with different number of observations");
+ log.logp (Level.WARNING, "TallyStore", "covariance",
+ "This tally, with name " + getName() + ", contains " + numberObs() +
+ " observations while " + "the given tally, with name " +
+ t2.getName() + ", contains " + t2.numberObs() + "observations");
+ return Double.NaN;
+ }
+
+ if (numberObs() < 2 || t2.numberObs() < 2) {
+ //System.err.println ("******* TallyStore.covariance() with " +
+ // numberObs() + " Observation");
+ log.logp (Level.WARNING, "TallyStore", "covariance",
+ "This tally, with name " + getName() + ", contains " + numberObs() + " observation");
+ return Double.NaN;
+ }
+
+ return cern.jet.stat.Descriptive.covariance (
+ getDoubleArrayList(), t2.getDoubleArrayList());
+ }
+
+
+ /**
+ * Clones this object and the array which stores the observations.
+ *
+ */
+ public TallyStore clone() {
+ TallyStore t = (TallyStore)super.clone();
+ t.array = (DoubleArrayList)array.clone();
+ return t;
+ }
+
+
+ /**
+ * Returns the observations stored in this object as a <TT>String</TT>.
+ *
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer ();
+ for (int i=0; i<numberObs(); i++)
+ sb.append (i + " " + array.getQuick(i) +
+ PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stat/TallyStore.tex b/source/umontreal/iro/lecuyer/stat/TallyStore.tex
new file mode 100644
index 0000000..6dd033d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/TallyStore.tex
@@ -0,0 +1,263 @@
+\defmodule {TallyStore}
+
+This class is a variant of \class{Tally} for which the individual
+observations are stored in a list implemented as a
+\externalclass{cern.colt.list}{DoubleArrayList}.
+\begin{latexonly}%
+The class \texttt{DoubleArrayList} is imported from the COLT library and
+provides an efficient way of storing and manipulating a list of
+real-valued numbers in a dynamic array.
+\end{latexonly}%
+The \externalclass{cern.colt.list}{DoubleArrayList} object used to
+store the values can be either passed to the constructor or created by
+the constructor, and can be accessed via the
+\method{getDoubleArrayList}{} method.
+
+The same counters as in \class{Tally} are maintained and are used by
+the inherited methods. One must access the list of observations
+to compute quantities not supported by the methods in
+\class{Tally}, and/or to use methods provided by the COLT package.
+
+
+\emph{Never add or remove observations directly} on the
+\externalclass{cern.colt.list}{DoubleArrayList} object,
+because this would put the counters of the \texttt{TallyStore} object in an
+inconsistent state.
+
+There are two potential reasons for using a \class{TallyStore} object
+instead of directly using a \externalclass{cern.colt.list}{DoubleArrayList} object:
+(a) it can broadcast observations and
+(b) it maintains a few additional counters that may speed up some operations
+such as computing the average.
+
+
+
+\bigskip\hrule
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: TallyStore
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat;\begin{hide}
+import cern.colt.list.DoubleArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import umontreal.iro.lecuyer.util.PrintfFormat;
+\end{hide}
+
+
+public class TallyStore extends Tally \begin{hide} {
+
+ private DoubleArrayList array = null; // Where the observations are stored.
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat");
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public TallyStore() \begin{hide} {
+ super();
+ array = new DoubleArrayList();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{TallyStore} statistical probe.
+\end{tabb}
+\begin{code}
+
+ public TallyStore (String name) \begin{hide} {
+ super (name);
+ array = new DoubleArrayList();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{TallyStore} statistical probe with name \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the name of the tally.}
+\end{htmlonly}
+\begin{code}
+
+ public TallyStore (int capacity) \begin{hide} {
+ super();
+ array = new DoubleArrayList (capacity);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{TallyStore} statistical probe
+ with given initial capacity \texttt{capacity} for its associated array.
+\end{tabb}
+\begin{htmlonly}
+ \param{capacity}{initial capacity of the array of observations}
+\end{htmlonly}
+\begin{code}
+
+ public TallyStore (String name, int capacity) \begin{hide} {
+ super (name);
+ array = new DoubleArrayList (capacity);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{TallyStore} statistical probe with
+name \texttt{name} and given initial capacity \texttt{capacity} for its
+ associated array.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the name of the tally.}
+ \param{capacity}{initial capacity of the array of observations}
+\end{htmlonly}
+\begin{code}
+
+ public TallyStore (DoubleArrayList a) \begin{hide} {
+ super();
+ array = a;
+ array.clear();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{TallyStore} statistical probe
+ with given associated array. This array must be empty.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{array that will contain observations}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public void init() {
+ super.init();
+ // We must call super before any actions inside constructors.
+ // Unfortunately, the base class calls init, which would
+ // result in a NullPointerException.
+ if (array != null)
+ array.clear();
+ }
+
+ public void add (double x) {
+ if (collect) array.add (x);
+ super.add(x);
+ }\end{hide}
+
+ public double[] getArray() \begin{hide} {
+ array.trimToSize();
+ return array.elements();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the observations stored in this probe.
+% \textbf{WARNING:}
+% The returned array is the stored internal array itself, including invalid
+% elements between size and capacity. If the returned \texttt{double} array is
+% modified, then this probe itself will be modified. To get the number of
+% valid elements (observations) in the array, use
+% \externalmethod{umontreal.iro.lecuyer.stat}{Tally}{numberObs}{}.
+% \richard{I now use trimToSize to remove the extra elements.}
+\end{tabb}
+\begin{htmlonly}
+ \return{the array of observations associated with this object}
+\end{htmlonly}
+\begin{code}
+
+ public DoubleArrayList getDoubleArrayList() \begin{hide} {
+ array.trimToSize();
+ return array;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the \externalclass{cern.colt.list}{DoubleArrayList}
+ object that contains the observations for this probe. \textbf{WARNING:}
+ In previous releases, this function was named \texttt{getArray}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the array of observations associated with this object}
+\end{htmlonly}
+\begin{code}
+
+ public void quickSort() \begin{hide} {
+ array.quickSort();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sorts the elements of this probe using the \texttt{quicksort}
+ from Colt.
+\end{tabb}
+\begin{code}
+
+ public double covariance (TallyStore t2)\begin{hide} {
+ if (numberObs() != t2.numberObs()) {
+ // System.err.println ("******* TallyStore.covariance(): " +
+ // "Tally's with different number of observations");
+ log.logp (Level.WARNING, "TallyStore", "covariance",
+ "This tally, with name " + getName() + ", contains " + numberObs() +
+ " observations while " + "the given tally, with name " +
+ t2.getName() + ", contains " + t2.numberObs() + "observations");
+ return Double.NaN;
+ }
+
+ if (numberObs() < 2 || t2.numberObs() < 2) {
+ //System.err.println ("******* TallyStore.covariance() with " +
+ // numberObs() + " Observation");
+ log.logp (Level.WARNING, "TallyStore", "covariance",
+ "This tally, with name " + getName() + ", contains " + numberObs() + " observation");
+ return Double.NaN;
+ }
+
+ return cern.jet.stat.Descriptive.covariance (
+ getDoubleArrayList(), t2.getDoubleArrayList());
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the sample covariance of the observations contained
+ in this tally, and the other tally \texttt{t2}.
+ Both tallies must have the same number of observations.
+ This returns \texttt{Double.NaN}
+ if the tallies do not contain the same number of observations, or
+ if they contain less than two observations.
+\end{tabb}
+\begin{htmlonly}
+ \param{t2}{the other tally.}
+ \return{the sample covariance.}
+\end{htmlonly}
+\begin{code}
+
+ public TallyStore clone()\begin{hide} {
+ TallyStore t = (TallyStore)super.clone();
+ t.array = (DoubleArrayList)array.clone();
+ return t;
+ }\end{hide}
+\end{code}
+\begin{tabb} Clones this object and the array which stores the observations.
+\end{tabb}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ StringBuffer sb = new StringBuffer ();
+ for (int i=0; i<numberObs(); i++)
+ sb.append (i + " " + array.getQuick(i) +
+ PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the observations stored in this object as a \texttt{String}.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stat/guidestat.bbl b/source/umontreal/iro/lecuyer/stat/guidestat.bbl
new file mode 100644
index 0000000..fcaf76a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/guidestat.bbl
@@ -0,0 +1,8 @@
+\begin{thebibliography}{1}
+
+\bibitem{iGAM98a}
+E.~Gamma, R.~Helm, R.~Johnson, and J.~Vlissides.
+\newblock {\em Design Patterns: Elements of Reusable Object-Oriented Software}.
+\newblock Addison-Wesley, Reading, MA, second edition, 1998.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/stat/guidestat.tex b/source/umontreal/iro/lecuyer/stat/guidestat.tex
new file mode 100644
index 0000000..17a72d0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/guidestat.tex
@@ -0,0 +1,45 @@
+\documentclass [12pt]{article}
+\usepackage{amssymb}
+\usepackage{url}
+\usepackage{ssj}
+\usepackage{color}
+\usepackage{pictexwd}
+
+
+% \dateheadtrue
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+\begin{titlepage}
+\title{stat}{Tools for Collecting Statistics}
+
+\vfill
+\end{titlepage}
+\pagenumbering{roman}
+\tableofcontents
+
+\pagenumbering{arabic}
+
+
+%\appendix
+\include{overview}
+\include{ObservationListener}
+\include{StatProbe}
+\include{Tally}
+\include{TallyStore}
+\include{TallyHistogram}
+
+\include{list/overview}
+\include{list/ArrayOfObservationListener}
+\include{list/ListOfStatProbes}
+\include{list/ListOfTallies}
+\include{list/ListOfTalliesWithCovariance}
+
+%\setcounter{section}{1}
+%\renewcommand{\thesection}{\Alph{section}.}
+
+
+\bibliography{simul,random,ift,stat,prob,vrt}
+\bibliographystyle{plain}
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/stat/list/ArrayOfObservationListener.java b/source/umontreal/iro/lecuyer/stat/list/ArrayOfObservationListener.java
new file mode 100644
index 0000000..deaaa7a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ArrayOfObservationListener.java
@@ -0,0 +1,51 @@
+
+/*
+ * Class: ArrayOfObservationListener
+ * Description: Array of observation listener
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat.list;
+
+import umontreal.iro.lecuyer.stat.StatProbe;
+
+
+/**
+ * Represents an object that can listen to observations broadcast
+ * by lists of statistical probes.
+ *
+ */
+public interface ArrayOfObservationListener {
+
+ /**
+ * Receives the new array of observations <TT>x</TT> broadcast by the
+ * list of statistical probes <TT>listOfProbes</TT>.
+ *
+ * @param listOfProbes the list of statistical probes broadcasting the observation.
+ *
+ * @param x the array of observations being broadcast.
+ *
+ *
+ */
+ public void newArrayOfObservations (ListOfStatProbes<?> listOfProbes,
+ double[] x);
+
+}
diff --git a/source/umontreal/iro/lecuyer/stat/list/ArrayOfObservationListener.tex b/source/umontreal/iro/lecuyer/stat/list/ArrayOfObservationListener.tex
new file mode 100644
index 0000000..ee219b2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ArrayOfObservationListener.tex
@@ -0,0 +1,52 @@
+\defmodule{ArrayOfObservationListener}
+
+Represents an object that can listen to observations broadcast
+by lists of statistical probes.
+
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: ArrayOfObservationListener
+ * Description: Array of observation listener
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat.list;
+\begin{hide}
+import umontreal.iro.lecuyer.stat.StatProbe;
+\end{hide}
+
+public interface ArrayOfObservationListener\begin{hide} {\end{hide}
+
+ public void newArrayOfObservations (ListOfStatProbes<?> listOfProbes,
+ double[] x);
+\end{code}
+\begin{tabb} Receives the new array of observations \texttt{x} broadcast by the
+ list of statistical probes \texttt{listOfProbes}.
+\end{tabb}
+\begin{htmlonly}
+ \param{listOfProbes}{the list of statistical probes broadcasting the observation.}
+ \param{x}{the array of observations being broadcast.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stat/list/ListOfStatProbes.java b/source/umontreal/iro/lecuyer/stat/list/ListOfStatProbes.java
new file mode 100644
index 0000000..dfa376f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ListOfStatProbes.java
@@ -0,0 +1,469 @@
+
+
+/*
+ * Class: ListOfStatProbes
+ * Description: List of statistical probes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat.list;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.RandomAccess;
+import umontreal.iro.lecuyer.stat.StatProbe;
+
+
+
+/**
+ * Represents a list of statistical probes that
+ * can be managed simultaneously.
+ * Each element of this list is a {@link umontreal.iro.lecuyer.stat.StatProbe StatProbe}
+ * instance which can be obtained and manipulated.
+ *
+ * <P>
+ * When constructing a list of statistical probes, one specifies the
+ * concrete subclass of the {@link StatProbe} objects in it.
+ * One then creates an empty list of probes, and fills
+ * it with statistical probes.
+ * If the list is not intended to be modified, one can then use the
+ * {@link #setUnmodifiable setUnmodifiable} to prevent any change in the contents of
+ * the list.
+ *
+ * <P>
+ * Each list of statistical probes can have a global name describing
+ * the contents of its elements, and local names associated with each
+ * individual probe. For example, a list of statistical probes for
+ * the waiting times can have the global name <TT>Waiting times</TT>
+ * while the individual probes have local names <TT>type 1</TT>,
+ * <TT>type 2</TT>, etc. These names are used for formatting reports.
+ *
+ * <P>
+ * Facilities are provided to fill arrays with sums, averages, etc. obtained
+ * from the individual statistical probes.
+ * Methods are also provided to manipulate the contents of the list.
+ * However, one should always call <TT>init</TT> immediately after adding or
+ * removing statistical probes in the list.
+ *
+ */
+public class ListOfStatProbes<E extends StatProbe>
+ implements Cloneable, List<E>, RandomAccess {
+
+ // probes must implement RandomAccess, otherwise this class must not implement RandomAccess.
+ private List<E> probes;
+ private List<ArrayOfObservationListener> listeners = new ArrayList<ArrayOfObservationListener>();
+ protected boolean collect = true;
+ protected boolean broadcast = false;
+ protected String name;
+
+
+ /**
+ * Constructs an empty list of statistical probes.
+ *
+ */
+ public ListOfStatProbes() {
+ probes = new ArrayList<E>();
+ }
+
+
+ /**
+ * Constructs an empty list of statistical probes with
+ * name <TT>name</TT>.
+ *
+ * @param name the name of the new list.
+ *
+ *
+ */
+ public ListOfStatProbes (String name) {
+ probes = new ArrayList<E>();
+ this.name = name;
+ }
+
+
+ /**
+ * Returns the global name of this list of statistical probes.
+ *
+ * @return the global name of the list.
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+
+ /**
+ * Sets the global name of this list to <TT>name</TT>.
+ *
+ * @param name the new global name of the list.
+ *
+ *
+ */
+ public void setName (String name) {
+ this.name = name;
+ }
+
+
+ /**
+ * Determines if this list of statistical probes is
+ * modifiable, i.e., if probes can be added or removed.
+ * Any list of statistical probes is modifiable by default, until one calls the
+ * {@link #setUnmodifiable setUnmodifiable} method.
+ *
+ */
+ public boolean isModifiable() {
+ return probes instanceof ArrayList;
+ }
+
+
+ /**
+ * Forbids any future modification to this list of
+ * statistical probes.
+ * After this method is called, any attempt to modify the list results
+ * in an exception.
+ * Setting a list unmodifiable can be useful if some data structures
+ * are defined depending on the probes in the list.
+ *
+ */
+ public void setUnmodifiable() {
+ if (isModifiable())
+ probes = Collections.unmodifiableList (probes);
+ }
+
+
+ /**
+ * Initializes this list of statistical probes by calling
+ * {@link umontreal.iro.lecuyer.stat.StatProbe#init(()) init} on each element.
+ *
+ */
+ public void init() {
+ for (StatProbe probe : probes) {
+ if (probe != null)
+ probe.init();
+ }
+ }
+
+
+ /**
+ * For each probe in the list, computes
+ * the sum by calling {@link umontreal.iro.lecuyer.stat.StatProbe#sum(()) sum}, and stores
+ * the results into the array <TT>s</TT>. This method throws an exception if the size of <TT>s</TT>
+ * mismatches with the size of the list.
+ *
+ * @param s the array to be filled with sums.
+ *
+ * @exception NullPointerException if <TT>s</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>s.length</TT>
+ * does not correspond to {@link #size(()) size}.
+ *
+ *
+ */
+ public void sum (double[] s) {
+ if (s.length != size())
+ throw new IllegalArgumentException
+ ("Invalid length of the given array: given length is " + s.length +
+ ", required length is " + size());
+ int i = 0;
+ for (StatProbe probe : probes)
+ s[i++] = probe == null ? Double.NaN : probe.sum();
+ }
+
+
+ /**
+ * For each probe in this list, computes
+ * the average by calling {@link umontreal.iro.lecuyer.stat.StatProbe#average(()) average}, and stores
+ * the results into the array <TT>a</TT>. This method throws an exception if the size of <TT>s</TT>
+ * mismatches with the size of the list.
+ *
+ * @param a the array to be filled with averages.
+ *
+ * @exception NullPointerException if <TT>a</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>a.length</TT>
+ * does not correspond to {@link #size(()) size}.
+ *
+ *
+ */
+ public void average (double[] a) {
+ if (a.length != size())
+ throw new IllegalArgumentException
+ ("Invalid length of the given array: given length is " + a.length +
+ ", required length is " + size());
+ int i = 0;
+ for (StatProbe probe : probes)
+ a[i++] = probe == null ? Double.NaN : probe.average();
+ }
+
+
+ /**
+ * Determines if this list of statistical probes
+ * is collecting values. Each probe of the list could or could not
+ * be collecting values. The default is <TT>true</TT>.
+ *
+ * @return the status of statistical collecting.
+ *
+ */
+ public boolean isCollecting() {
+ return collect;
+ }
+
+
+ /**
+ * Sets the status of the statistical collecting
+ * mechanism to <TT>c</TT>. A <TT>true</TT> value
+ * turns statistical collecting ON, a <TT>false</TT>
+ * value turns it OFF.
+ *
+ * @param c the status of statistical collecting.
+ *
+ *
+ */
+ public void setCollecting (boolean c) {
+ collect = c;
+ }
+
+
+ /**
+ * Determines if this list of statistical probes
+ * is broadcasting observations to registered observers.
+ * The default is <TT>false</TT>.
+ *
+ * @return the status of broadcasting.
+ *
+ */
+ public boolean isBroadcasting() {
+ return broadcast;
+ }
+
+
+ /**
+ * Sets the status of the observation broadcasting
+ * mechanism to <TT>b</TT>. A <TT>true</TT> value
+ * turns broadcasting ON, a <TT>false</TT>
+ * value turns it OFF.
+ *
+ * @param b the status of broadcasting.
+ *
+ *
+ */
+ public void setBroadcasting (boolean b) {
+ broadcast = b;
+ }
+
+
+ /**
+ * Adds the observation listener <TT>l</TT> to the list of observers of
+ * this list of statistical probes.
+ *
+ * @param l the new observation listener.
+ *
+ * @exception NullPointerException if <TT>l</TT> is <TT>null</TT>.
+ *
+ *
+ */
+ public void addArrayOfObservationListener (ArrayOfObservationListener l) {
+ if (l == null)
+ throw new NullPointerException();
+ if (!listeners.contains (l))
+ listeners.add (l);
+ }
+
+
+ /**
+ * Removes the observation listener <TT>l</TT> from the list of observers of
+ * this list of statistical probes.
+ *
+ * @param l the observation listener to be deleted.
+ *
+ *
+ */
+ public void removeArrayOfObservationListener (ArrayOfObservationListener l) {
+ listeners.remove (l);
+ }
+
+
+ /**
+ * Removes all observation listeners from the list of observers of
+ * this list of statistical probes.
+ *
+ */
+ public void clearArrayOfObservationListeners() {
+ listeners.clear();
+ }
+
+
+ /**
+ * Notifies the observation <TT>x</TT> to all registered observers
+ * if broadcasting is ON. Otherwise, does nothing.
+ *
+ */
+ public void notifyListeners (double[] x) {
+ if (!broadcast)
+ return;
+ // We could also use the enhanced for loop here, but this is less efficient.
+ final int nl = listeners.size();
+ for (int i = 0; i < nl; i++)
+ listeners.get (i).newArrayOfObservations (this, x);
+ }
+
+
+ /**
+ * Formats a report for each probe in the list of
+ * statistical probes. The returned string is constructed by
+ * using <TT>StatProbe.report (getName(), this)</TT>.
+ *
+ * @return the report formatted as a string.
+ *
+ */
+ public String report() {
+ return StatProbe.report (name, this);
+ }
+
+
+ /**
+ * Clones this object. This makes a shallow copy
+ * of this list, i.e., this does not clone all the probes in the list.
+ * The created clone is modifiable, even if the original list is unmodifiable.
+ *
+ */
+ public ListOfStatProbes<E> clone() {
+ ListOfStatProbes<E> sa;
+ try {
+ sa = (ListOfStatProbes<E>)super.clone();
+ }
+ catch (CloneNotSupportedException cne) {
+ throw new IllegalStateException ("CloneNotSupportedException for a class implementing Cloneable");
+ }
+ if (probes != null)
+ sa.probes = new ArrayList<E> (probes);
+ return sa;
+ }
+
+
+
+ public boolean add (E o) {
+ return probes.add (o);
+ }
+
+ public void add (int index, E o) {
+ probes.add (index, o);
+ }
+
+ public boolean addAll (Collection<? extends E> c) {
+ return probes.addAll (c);
+ }
+
+ public boolean addAll (int index, Collection<? extends E> c) {
+ return probes.addAll (index, c);
+ }
+
+ public void clear() {
+ probes.clear();
+ }
+
+ public boolean contains (Object o) {
+ return probes.contains (o);
+ }
+
+ public boolean containsAll (Collection<?> c) {
+ return probes.containsAll (c);
+ }
+
+ public boolean equals (Object o) {
+ return probes.equals (o);
+ }
+
+ public E get (int index) {
+ return probes.get (index);
+ }
+
+ public int hashCode() {
+ return probes.hashCode();
+ }
+
+ public int indexOf (Object o) {
+ return probes.indexOf (o);
+ }
+
+ public boolean isEmpty() {
+ return probes.isEmpty();
+ }
+
+ public Iterator<E> iterator() {
+ return probes.iterator();
+ }
+
+ public int lastIndexOf (Object o) {
+ return probes.lastIndexOf (o);
+ }
+
+ public ListIterator<E> listIterator() {
+ return probes.listIterator();
+ }
+
+ public ListIterator<E> listIterator (int index) {
+ return probes.listIterator();
+ }
+
+ public E remove (int index) {
+ return probes.remove (index);
+ }
+
+ public boolean remove (Object o) {
+ return probes.remove (o);
+ }
+
+ public boolean removeAll (Collection<?> c) {
+ return probes.removeAll (c);
+ }
+
+ public boolean retainAll (Collection<?> c) {
+ return probes.retainAll (c);
+ }
+
+ public E set (int index, E element) {
+ return probes.set (index, element);
+ }
+
+ public int size() {
+ return probes.size();
+ }
+
+ public List<E> subList (int fromIndex, int toIndex) {
+ return probes.subList (fromIndex, toIndex);
+ }
+
+ public Object[] toArray() {
+ return probes.toArray();
+ }
+
+ public <T> T[] toArray (T[] a) {
+ return probes.toArray (a);
+ }
+
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/stat/list/ListOfStatProbes.tex b/source/umontreal/iro/lecuyer/stat/list/ListOfStatProbes.tex
new file mode 100644
index 0000000..e3ed689
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ListOfStatProbes.tex
@@ -0,0 +1,454 @@
+\defmodule{ListOfStatProbes}
+
+Represents a list of statistical probes that
+can be managed simultaneously.
+Each element of this list is a \externalclass{umontreal.iro.lecuyer.stat}{StatProbe}
+instance which can be obtained and manipulated.
+
+When constructing a list of statistical probes, one specifies the
+concrete subclass of the \class{StatProbe} objects in it.
+One then creates an empty list of probes, and fills
+it with statistical probes.
+If the list is not intended to be modified, one can then use the
+\method{setUnmodifiable}{} to prevent any change in the contents of
+the list.
+
+Each list of statistical probes can have a global name describing
+the contents of its elements, and local names associated with each
+individual probe. For example, a list of statistical probes for
+the waiting times can have the global name \texttt{Waiting times}
+while the individual probes have local names \texttt{type 1},
+\texttt{type 2}, etc. These names are used for formatting reports.
+
+Facilities are provided to fill arrays with sums, averages, etc.\ obtained
+from the individual statistical probes.
+Methods are also provided to manipulate the contents of the list.
+However, one should always call \texttt{init} immediately after adding or
+removing statistical probes in the list.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ListOfStatProbes
+ * Description: List of statistical probes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat.list;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.RandomAccess;
+import umontreal.iro.lecuyer.stat.StatProbe;
+\end{hide}
+
+
+public class ListOfStatProbes<E extends StatProbe>
+ implements Cloneable, List<E>, RandomAccess\begin{hide} {
+
+ // probes must implement RandomAccess, otherwise this class must not implement RandomAccess.
+ private List<E> probes;
+ private List<ArrayOfObservationListener> listeners = new ArrayList<ArrayOfObservationListener>();
+ protected boolean collect = true;
+ protected boolean broadcast = false;
+ protected String name;\end{hide}
+\end{code}
+\subsubsection*{Constructors}
+\begin{code}
+
+ public ListOfStatProbes()\begin{hide} {
+ probes = new ArrayList<E>();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs an empty list of statistical probes.
+\end{tabb}
+\begin{code}
+
+ public ListOfStatProbes (String name)\begin{hide} {
+ probes = new ArrayList<E>();
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs an empty list of statistical probes with
+ name \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the name of the new list.}
+\end{htmlonly}
+\subsubsection*{Methods}
+\begin{code}
+
+ public String getName()\begin{hide} {
+ return name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the global name of this list of statistical probes.
+\end{tabb}
+\begin{htmlonly}
+ \return{the global name of the list.}
+\end{htmlonly}
+\begin{code}
+
+ public void setName (String name)\begin{hide} {
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the global name of this list to \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the new global name of the list.}
+\end{htmlonly}
+\begin{code}
+
+ public boolean isModifiable()\begin{hide} {
+ return probes instanceof ArrayList;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if this list of statistical probes is
+ modifiable, i.e., if probes can be added or removed.
+ Any list of statistical probes is modifiable by default, until one calls the
+ \method{setUnmodifiable}{} method.
+\end{tabb}
+\begin{code}
+
+ public void setUnmodifiable()\begin{hide} {
+ if (isModifiable())
+ probes = Collections.unmodifiableList (probes);
+ }\end{hide}
+\end{code}
+\begin{tabb} Forbids any future modification to this list of
+ statistical probes.
+ After this method is called, any attempt to modify the list results
+ in an exception.
+ Setting a list unmodifiable can be useful if some data structures
+ are defined depending on the probes in the list.
+\end{tabb}
+\begin{code}
+
+ public void init()\begin{hide} {
+ for (StatProbe probe : probes) {
+ if (probe != null)
+ probe.init();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Initializes this list of statistical probes by calling
+ \externalmethod{umontreal.iro.lecuyer.stat}{StatProbe}{init}{()} on each element.
+\end{tabb}
+\begin{code}
+
+ public void sum (double[] s)\begin{hide} {
+ if (s.length != size())
+ throw new IllegalArgumentException
+ ("Invalid length of the given array: given length is " + s.length +
+ ", required length is " + size());
+ int i = 0;
+ for (StatProbe probe : probes)
+ s[i++] = probe == null ? Double.NaN : probe.sum();
+ }\end{hide}
+\end{code}
+\begin{tabb} For each probe in the list, computes
+ the sum by calling \externalmethod{umontreal.iro.lecuyer.stat}{StatProbe}{sum}{()}, and stores
+ the results into the array \texttt{s}. This method throws an exception if the size of \texttt{s}
+ mismatches with the size of the list.
+\end{tabb}
+\begin{htmlonly}
+ \param{s}{the array to be filled with sums.}
+ \exception{NullPointerException}{if \texttt{s} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{s.length}
+ does not correspond to \method{size}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public void average (double[] a)\begin{hide} {
+ if (a.length != size())
+ throw new IllegalArgumentException
+ ("Invalid length of the given array: given length is " + a.length +
+ ", required length is " + size());
+ int i = 0;
+ for (StatProbe probe : probes)
+ a[i++] = probe == null ? Double.NaN : probe.average();
+ }\end{hide}
+\end{code}
+\begin{tabb} For each probe in this list, computes
+ the average by calling \externalmethod{umontreal.iro.lecuyer.stat}{StatProbe}{average}{()}, and stores
+ the results into the array \texttt{a}. This method throws an exception if the size of \texttt{s}
+ mismatches with the size of the list.
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the array to be filled with averages.}
+ \exception{NullPointerException}{if \texttt{a} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{a.length}
+ does not correspond to \method{size}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public boolean isCollecting()\begin{hide} {
+ return collect;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if this list of statistical probes
+ is collecting values. Each probe of the list could or could not
+ be collecting values. The default is \texttt{true}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the status of statistical collecting.}
+\end{htmlonly}
+\begin{code}
+
+ public void setCollecting (boolean c)\begin{hide} {
+ collect = c;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the status of the statistical collecting
+ mechanism to \texttt{c}. A \texttt{true} value
+ turns statistical collecting ON, a \texttt{false}
+ value turns it OFF.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the status of statistical collecting.}
+\end{htmlonly}
+\begin{code}
+
+ public boolean isBroadcasting()\begin{hide} {
+ return broadcast;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if this list of statistical probes
+ is broadcasting observations to registered observers.
+ The default is \texttt{false}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the status of broadcasting.}
+\end{htmlonly}
+\begin{code}
+
+ public void setBroadcasting (boolean b)\begin{hide} {
+ broadcast = b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the status of the observation broadcasting
+ mechanism to \texttt{b}. A \texttt{true} value
+ turns broadcasting ON, a \texttt{false}
+ value turns it OFF.
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{the status of broadcasting.}
+\end{htmlonly}
+\begin{code}
+
+ public void addArrayOfObservationListener (ArrayOfObservationListener l)\begin{hide} {
+ if (l == null)
+ throw new NullPointerException();
+ if (!listeners.contains (l))
+ listeners.add (l);
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds the observation listener \texttt{l} to the list of observers of
+ this list of statistical probes.
+\end{tabb}
+\begin{htmlonly}
+ \param{l}{the new observation listener.}
+ \exception{NullPointerException}{if \texttt{l} is \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public void removeArrayOfObservationListener (ArrayOfObservationListener l)\begin{hide} {
+ listeners.remove (l);
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes the observation listener \texttt{l} from the list of observers of
+ this list of statistical probes.
+\end{tabb}
+\begin{htmlonly}
+ \param{l}{the observation listener to be deleted.}
+\end{htmlonly}
+\begin{code}
+
+ public void clearArrayOfObservationListeners()\begin{hide} {
+ listeners.clear();
+ }\end{hide}
+\end{code}
+\begin{tabb} Removes all observation listeners from the list of observers of
+ this list of statistical probes.
+\end{tabb}
+\begin{code}
+
+ public void notifyListeners (double[] x)\begin{hide} {
+ if (!broadcast)
+ return;
+ // We could also use the enhanced for loop here, but this is less efficient.
+ final int nl = listeners.size();
+ for (int i = 0; i < nl; i++)
+ listeners.get (i).newArrayOfObservations (this, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Notifies the observation \texttt{x} to all registered observers
+ if broadcasting is ON. Otherwise, does nothing.
+\end{tabb}
+\begin{code}
+
+ public String report()\begin{hide} {
+ return StatProbe.report (name, this);
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats a report for each probe in the list of
+ statistical probes. The returned string is constructed by
+ using \texttt{StatProbe.report (getName(), this)}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the report formatted as a string.}
+\end{htmlonly}
+\begin{code}
+
+ public ListOfStatProbes<E> clone()\begin{hide} {
+ ListOfStatProbes<E> sa;
+ try {
+ sa = (ListOfStatProbes<E>)super.clone();
+ }
+ catch (CloneNotSupportedException cne) {
+ throw new IllegalStateException ("CloneNotSupportedException for a class implementing Cloneable");
+ }
+ if (probes != null)
+ sa.probes = new ArrayList<E> (probes);
+ return sa;
+ }\end{hide}
+\end{code}
+\begin{tabb} Clones this object. This makes a shallow copy
+ of this list, i.e., this does not clone all the probes in the list.
+ The created clone is modifiable, even if the original list is unmodifiable.
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ public boolean add (E o) {
+ return probes.add (o);
+ }
+
+ public void add (int index, E o) {
+ probes.add (index, o);
+ }
+
+ public boolean addAll (Collection<? extends E> c) {
+ return probes.addAll (c);
+ }
+
+ public boolean addAll (int index, Collection<? extends E> c) {
+ return probes.addAll (index, c);
+ }
+
+ public void clear() {
+ probes.clear();
+ }
+
+ public boolean contains (Object o) {
+ return probes.contains (o);
+ }
+
+ public boolean containsAll (Collection<?> c) {
+ return probes.containsAll (c);
+ }
+
+ public boolean equals (Object o) {
+ return probes.equals (o);
+ }
+
+ public E get (int index) {
+ return probes.get (index);
+ }
+
+ public int hashCode() {
+ return probes.hashCode();
+ }
+
+ public int indexOf (Object o) {
+ return probes.indexOf (o);
+ }
+
+ public boolean isEmpty() {
+ return probes.isEmpty();
+ }
+
+ public Iterator<E> iterator() {
+ return probes.iterator();
+ }
+
+ public int lastIndexOf (Object o) {
+ return probes.lastIndexOf (o);
+ }
+
+ public ListIterator<E> listIterator() {
+ return probes.listIterator();
+ }
+
+ public ListIterator<E> listIterator (int index) {
+ return probes.listIterator();
+ }
+
+ public E remove (int index) {
+ return probes.remove (index);
+ }
+
+ public boolean remove (Object o) {
+ return probes.remove (o);
+ }
+
+ public boolean removeAll (Collection<?> c) {
+ return probes.removeAll (c);
+ }
+
+ public boolean retainAll (Collection<?> c) {
+ return probes.retainAll (c);
+ }
+
+ public E set (int index, E element) {
+ return probes.set (index, element);
+ }
+
+ public int size() {
+ return probes.size();
+ }
+
+ public List<E> subList (int fromIndex, int toIndex) {
+ return probes.subList (fromIndex, toIndex);
+ }
+
+ public Object[] toArray() {
+ return probes.toArray();
+ }
+
+ public <T> T[] toArray (T[] a) {
+ return probes.toArray (a);
+ }
+
+\end{hide}
+\end{code}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
+
diff --git a/source/umontreal/iro/lecuyer/stat/list/ListOfTallies.java b/source/umontreal/iro/lecuyer/stat/list/ListOfTallies.java
new file mode 100644
index 0000000..b5b20c7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ListOfTallies.java
@@ -0,0 +1,427 @@
+
+
+/*
+ * Class: ListOfTallies
+ * Description: List of statistical collectors.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat.list;
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import cern.colt.list.DoubleArrayList;
+import cern.colt.matrix.DoubleMatrix1D;
+import cern.colt.matrix.DoubleMatrix2D;
+
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.stat.TallyStore;
+
+
+
+/**
+ * Represents a list of tally statistical collectors.
+ * Each element of the list is an instance of {@link umontreal.iro.lecuyer.stat.Tally Tally},
+ * and a vector of observations can be added with
+ * the {@link #add((double[])) add} method.
+ * This class defines factory methods to fill a newly-constructed list
+ * with <TT>Tally</TT> or <TT>TallyStore</TT> instances.
+ *
+ */
+public class ListOfTallies<E extends Tally> extends ListOfStatProbes<E> {
+
+
+
+ /**
+ * Constructs a new empty list of tallies.
+ *
+ */
+ public ListOfTallies() {
+ super();
+ }
+
+
+ /**
+ * Constructs a new empty list of tallies with name <TT>name</TT>.
+ *
+ * @param name the name of the new list.
+ *
+ *
+ */
+ public ListOfTallies (String name) {
+ super (name);
+ }
+
+
+ /**
+ * This factory method constructs and returns a list of tallies with <TT>size</TT> instances of
+ * {@link Tally}.
+ *
+ * @param size the size of the list.
+ *
+ * @return the created list.
+ *
+ */
+ public static ListOfTallies<Tally> createWithTally (int size) {
+ ListOfTallies<Tally> list = new ListOfTallies<Tally>();
+ for (int i = 0; i < size; i++)
+ list.add (new Tally());
+ return list;
+ }
+
+
+ /**
+ * This factory method constructs and returns a list of tallies with <TT>size</TT> instances of
+ * {@link TallyStore}.
+ *
+ * @param size the size of the list.
+ *
+ * @return the created list.
+ *
+ */
+ public static ListOfTallies<TallyStore> createWithTallyStore (int size) {
+ ListOfTallies<TallyStore> list = new ListOfTallies<TallyStore>();
+ for (int i = 0; i < size; i++)
+ list.add (new TallyStore());
+ return list;
+ }
+
+
+ /**
+ * Adds the observation <TT>x[i]</TT> in
+ * tally <TT>i</TT> of this list, for <TT>i = 0,..., size() - 1</TT>.
+ * No observation is added if the value is <TT>Double.NaN</TT>,
+ * or if collecting is turned OFF.
+ * If broadcasting is ON, the given array is notified
+ * to all registered observers.
+ * The given array <TT>x</TT> not being stored by this object,
+ * it can be freely used and modified after the call to this method.
+ *
+ * @param x the array of observations.
+ *
+ * @exception NullPointerException if <TT>x</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the length of
+ * <TT>x</TT> does not correspond to <TT>size()</TT>.
+ *
+ *
+ */
+ public void add (double[] x) {
+ int l = size();
+ if (x.length != l)
+ throw new IllegalArgumentException
+ ("Incompatible array length: given " +
+ x.length + ", required " + l);
+ if (collect)
+ for (int i = 0; i < l; i++) {
+ double v = x[i];
+ Tally ta = get (i);
+ if (!Double.isNaN (v) && ta != null)
+ ta.add (v);
+ }
+ notifyListeners (x);
+ }
+
+
+ /**
+ * Assuming that each tally in this list contains
+ * the same number of observations, returns
+ * the number of observations in tally 0, or
+ * 0 if this list is empty.
+ *
+ * @return the number of observations.
+ *
+ */
+ public int numberObs() {
+ if (size() == 0)
+ return 0;
+ Tally t0 = get (0);
+ return t0 == null ? 0 : t0.numberObs();
+ }
+
+
+ /**
+ * Tests that every tally in this list contains the
+ * same number of observations.
+ * This returns <TT>true</TT> if and only if all
+ * tallies have the same number of observations, or if this list is empty.
+ * If observations are always added using the
+ * {@link #add((double[])) add} method from this class, and not
+ * {@link umontreal.iro.lecuyer.stat.Tally#add((double)) add} from
+ * {@link Tally}, this method always returns <TT>true</TT>.
+ *
+ * @return the success indicator of the test.
+ *
+ */
+ public boolean areAllNumberObsEqual() {
+ final int l = size();
+ int n = numberObs();
+ for (int i = 1; i < l; i++) {
+ Tally t = get (i);
+ if (t.numberObs() != n)
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Computes the average for each tally
+ * in this list, and stores the averages in the array <TT>r</TT>.
+ * If the tally <TT>i</TT> has no observation,
+ * the <TT>Double.NaN</TT> value is stored
+ * in the array, at index <TT>i</TT>.
+ *
+ */
+ public void average (double[] r) {
+ final int l = size();
+ for (int i = 0; i < l; i++) {
+ // Manual check to avoid repetitive logs when all tallies
+ // have 0 observation.
+ Tally ta = get (i);
+ double v = ta == null || ta.numberObs() == 0 ? Double.NaN : ta.average();
+ r[i] = v;
+ }
+ }
+
+
+ /**
+ * For each tally in this list, computes
+ * the sample variance, and stores the variances into the array <TT>v</TT>.
+ * If, for some tally <TT>i</TT>, there are not enough
+ * observations for estimating the variance,
+ * <TT>Double.NaN</TT> is stored in the array.
+ *
+ * @param v the array to be filled with sample variances.
+ *
+ * @exception NullPointerException if <TT>v</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>v.length</TT>
+ * does not correspond to {@link umontreal.iro.lecuyer.stat.list.ListOfStatProbes#size(()) size}.
+ *
+ *
+ */
+ public void variance (double[] v) {
+ if (size() != v.length)
+ throw new IllegalArgumentException
+ ("Invalid length of given array");
+ for (int i = 0; i < v.length; i++) {
+ Tally tally = get (i);
+ if (tally == null || tally.numberObs() < 2)
+ v[i] = Double.NaN;
+ else
+ v[i] = tally.variance();
+ }
+ }
+
+
+ /**
+ * For each tally in this list, computes
+ * the sample standard deviation, and stores the standard deviations
+ * into the array <TT>std</TT>.
+ * This is equivalent to calling {@link #variance((double[])) variance} and
+ * performing a square root on every element
+ * of the filled array.
+ *
+ * @param std the array to be filled with standard deviations.
+ *
+ * @exception NullPointerException if <TT>std</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>std.length</TT>
+ * does not correspond to <TT>size()</TT>.
+ *
+ *
+ */
+ public void standardDeviation (double[] std) {
+ if (size() != std.length)
+ throw new IllegalArgumentException
+ ("Invalid length of given array");
+ for (int i = 0; i < std.length; i++) {
+ Tally tally = get (i);
+ if (tally == null || tally.numberObs() < 2)
+ std[i] = Double.NaN;
+ else
+ std[i] = tally.standardDeviation();
+ }
+ }
+
+
+ /**
+ * Returns the empirical covariance of the observations in tallies
+ * with indices <TT>i</TT> and <TT>j</TT>. If
+ * <SPAN CLASS="MATH"><I>x</I><SUB>1</SUB>,…, <I>x</I><SUB>n</SUB></SPAN> represent the
+ * observations in tally <TT>i</TT> whereas
+ * <SPAN CLASS="MATH"><I>y</I><SUB>1</SUB>,…, <I>y</I><SUB>n</SUB></SPAN> represent the
+ * observations in tally <TT>j</TT>, then the covariance is given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>S</I><SUB>X, Y</SUB> = 1#1∑<SUB>k=1</SUB><SUP>n</SUP>(<I>x</I><SUB>k</SUB> - bar(X)<SUB>n</SUB>)(<I>y</I><SUB>k</SUB> - bar(Y)<SUB>n</SUB>) = [tex2html_wrap_indisplay273](∑<SUB>k=1</SUB><SUP>n</SUP><I>x</I><SUB>k</SUB><I>y</I><SUB>k</SUB> - 2#2∑<SUB>k=1</SUB><SUP>n</SUP><I>x</I><SUB>k</SUB>∑<SUB>r=1</SUB><SUP>n</SUP><I>y</I><SUB>r</SUB>).
+ * </DIV><P></P>
+ * This returns <TT>Double.NaN</TT>
+ * if the tallies do not contain the same number of observations, or
+ * if they contain less than two observations.
+ * This method throws an exception if the
+ * underlying tallies are not capable of storing
+ * observations, i.e. if the tallies are not TallyStores.
+ * The {@link ListOfTalliesWithCovariance}
+ * subclass provides an alternative implementation
+ * of this method which does not require the
+ * observations to be stored.
+ *
+ * @param i the index of the first tally.
+ *
+ * @param j the index of the second tally.
+ *
+ * @return the value of the covariance.
+ * @exception ArrayIndexOutOfBoundsException if one or both
+ * indices are out of bounds.
+ *
+ *
+ */
+ public double covariance (int i, int j) {
+ if (i == j)
+ return get (i).variance();
+
+ TallyStore tallyi = (TallyStore)get (i);
+ TallyStore tallyj = (TallyStore)get (j);
+ return tallyi.covariance (tallyj);
+ }
+
+
+ /**
+ * Returns the empirical correlation between
+ * the observations in tallies with indices <TT>i</TT> and <TT>j</TT>.
+ * If the tally <TT>i</TT> contains a sample of the random
+ * variate <SPAN CLASS="MATH"><I>X</I></SPAN> and the tally <TT>j</TT> contains a sample of <SPAN CLASS="MATH"><I>Y</I></SPAN>,
+ * this corresponds to
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * Cor(<I>X</I>, <I>Y</I>) = Cov(<I>X</I>, <I>Y</I>)/((X)(Y))<SUP>1/2</SUP>.
+ * </DIV><P></P>
+ *
+ * <P>
+ * This method uses {@link #covariance((int, int)) covariance}
+ * to obtain an estimate of the covariance, and
+ * {@link umontreal.iro.lecuyer.stat.Tally#variance(()) variance} in
+ * class {@link Tally} to obtain the sample variances.
+ *
+ * @param i the index of the first tally.
+ *
+ * @param j the index of the second tally.
+ *
+ * @return the value of the correlation.
+ * @exception ArrayIndexOutOfBoundsException if one or both
+ * indices are out of bounds.
+ *
+ *
+ */
+ public double correlation (int i, int j) {
+ if (i == j)
+ return 1.0;
+ double cov = covariance (i, j);
+ Tally tallyi = get (i);
+ Tally tallyj = get (j);
+ if (tallyi == null || tallyj == null)
+ return Double.NaN;
+ return cov/Math.sqrt (tallyi.variance()*tallyj.variance());
+ }
+
+
+ /**
+ * Constructs and returns the sample covariance matrix
+ * for the tallies in this list. The given <SPAN CLASS="MATH"><I>d</I>×<I>d</I></SPAN> matrix <TT>c</TT>,
+ * where <SPAN CLASS="MATH"><I>d</I> =</SPAN> <TT>size()</TT>,
+ * is filled with the computed sample covariances.
+ * Element <TT>c.get (i, j)</TT> corresponds to
+ * the result of
+ * <TT>covariance (i, j)</TT>.
+ *
+ * @param c the matrix to be filled with the sample covariances.
+ *
+ * @exception NullPointerException if <TT>c</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the number of rows or columns
+ * in <TT>c</TT> does not correspond to <TT>size()</TT>.
+ *
+ *
+ */
+ public void covariance (DoubleMatrix2D c) {
+ int l = size();
+ if (c.rows() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of rows in covariance matrix");
+ if (c.columns() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of columns in covariance matrix");
+ for (int i1 = 0; i1 < l; i1++)
+ c.setQuick (i1, i1, get (i1).variance());
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++) {
+ double cov = covariance (i1, i2);
+ c.setQuick (i1, i2, cov);
+ c.setQuick (i2, i1, cov);
+ }
+ }
+
+
+ /**
+ * Similar to {@link #covariance((DoubleMatrix2D)) covariance} for computing
+ * the sample correlation matrix.
+ *
+ * @param c the matrix to be filled with the correlations.
+ *
+ * @exception NullPointerException if <TT>c</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if the number of rows or columns in <TT>c</TT>
+ * does not correspond to {@link umontreal.iro.lecuyer.stat.list.ListOfStatProbes#size(()) size}.
+ *
+ *
+ */
+ public void correlation (DoubleMatrix2D c) {
+ int l = size();
+ if (c.rows() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of rows in correlation matrix");
+ if (c.columns() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of columns in correlation matrix");
+ for (int i1 = 0; i1 < l; i1++)
+ c.setQuick (i1, i1, 1.0);
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++) {
+ double cor = correlation (i1, i2);
+ c.setQuick (i1, i2, cor);
+ c.setQuick (i2, i1, cor);
+ }
+ }
+
+
+ /**
+ * Clones this object. This makes a shallow copy
+ * of this list, i.e., this does not clone all the tallies in the list.
+ * The created clone is modifiable, even if the original list is unmodifiable.
+ *
+ */
+ public ListOfTallies<E> clone() {
+ return (ListOfTallies<E>)super.clone();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/stat/list/ListOfTallies.tex b/source/umontreal/iro/lecuyer/stat/list/ListOfTallies.tex
new file mode 100644
index 0000000..ce72058
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ListOfTallies.tex
@@ -0,0 +1,398 @@
+\defmodule{ListOfTallies}
+
+Represents a list of tally statistical collectors.
+Each element of the list is an instance of \externalclass{umontreal.iro.lecuyer.stat}{Tally},
+and a vector of observations can be added with
+the \method{add}{(double[])} method.
+This class defines factory methods to fill a newly-constructed list
+with \texttt{Tally} or \texttt{TallyStore} instances.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ListOfTallies
+ * Description: List of statistical collectors.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat.list;\begin{hide}
+
+import umontreal.iro.lecuyer.util.PrintfFormat;
+import cern.colt.list.DoubleArrayList;
+import cern.colt.matrix.DoubleMatrix1D;
+import cern.colt.matrix.DoubleMatrix2D;
+
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.stat.TallyStore;
+\end{hide}
+
+
+public class ListOfTallies<E extends Tally> extends ListOfStatProbes<E>\begin{hide} {
+\end{hide}
+\end{code}
+\subsubsection*{Constructors}
+\begin{code}
+
+ public ListOfTallies()\begin{hide} {
+ super();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new empty list of tallies.
+\end{tabb}
+\begin{code}
+
+ public ListOfTallies (String name)\begin{hide} {
+ super (name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new empty list of tallies with name \texttt{name}.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the name of the new list.}
+\end{htmlonly}
+\subsubsection*{Methods}
+\begin{code}
+
+ public static ListOfTallies<Tally> createWithTally (int size)\begin{hide} {
+ ListOfTallies<Tally> list = new ListOfTallies<Tally>();
+ for (int i = 0; i < size; i++)
+ list.add (new Tally());
+ return list;
+ }\end{hide}
+\end{code}
+\begin{tabb} This factory method constructs and returns a list of tallies with \texttt{size} instances of
+ \class{Tally}.
+\end{tabb}
+\begin{htmlonly}
+ \param{size}{the size of the list.}
+ \return{the created list.}
+\end{htmlonly}
+\begin{code}
+
+ public static ListOfTallies<TallyStore> createWithTallyStore (int size)\begin{hide} {
+ ListOfTallies<TallyStore> list = new ListOfTallies<TallyStore>();
+ for (int i = 0; i < size; i++)
+ list.add (new TallyStore());
+ return list;
+ }\end{hide}
+\end{code}
+\begin{tabb} This factory method constructs and returns a list of tallies with \texttt{size} instances of
+ \class{TallyStore}.
+\end{tabb}
+\begin{htmlonly}
+ \param{size}{the size of the list.}
+ \return{the created list.}
+\end{htmlonly}
+\begin{code}
+
+ public void add (double[] x)\begin{hide} {
+ int l = size();
+ if (x.length != l)
+ throw new IllegalArgumentException
+ ("Incompatible array length: given " +
+ x.length + ", required " + l);
+ if (collect)
+ for (int i = 0; i < l; i++) {
+ double v = x[i];
+ Tally ta = get (i);
+ if (!Double.isNaN (v) && ta != null)
+ ta.add (v);
+ }
+ notifyListeners (x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds the observation \texttt{x[i]} in
+ tally \texttt{i} of this list, for \texttt{i = 0,\ldots, size() - 1}.
+ No observation is added if the value is \texttt{Double.NaN},
+ or if collecting is turned OFF.
+ If broadcasting is ON, the given array is notified
+ to all registered observers.
+ The given array \texttt{x} not being stored by this object,
+ it can be freely used and modified after the call to this method.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the array of observations.}
+ \exception{NullPointerException}{if \texttt{x} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the length of
+ \texttt{x} does not correspond to \texttt{size()}.}
+\end{htmlonly}
+\begin{code}
+
+ public int numberObs()\begin{hide} {
+ if (size() == 0)
+ return 0;
+ Tally t0 = get (0);
+ return t0 == null ? 0 : t0.numberObs();
+ }\end{hide}
+\end{code}
+\begin{tabb} Assuming that each tally in this list contains
+ the same number of observations, returns
+ the number of observations in tally~0, or
+ 0 if this list is empty.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of observations.}
+\end{htmlonly}
+\begin{code}
+
+ public boolean areAllNumberObsEqual()\begin{hide} {
+ final int l = size();
+ int n = numberObs();
+ for (int i = 1; i < l; i++) {
+ Tally t = get (i);
+ if (t.numberObs() != n)
+ return false;
+ }
+ return true;
+ }\end{hide}
+\end{code}
+\begin{tabb} Tests that every tally in this list contains the
+ same number of observations.
+ This returns \texttt{true} if and only if all
+ tallies have the same number of observations, or if this list is empty.
+ If observations are always added using the
+ \method{add}{(double[])} method from this class, and not
+ \externalmethod{umontreal.iro.lecuyer.stat}{Tally}{add}{(double)} from
+ \class{Tally}, this method always returns \texttt{true}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the success indicator of the test.}
+\end{htmlonly}
+\begin{code}
+
+ public void average (double[] r)\begin{hide} {
+ final int l = size();
+ for (int i = 0; i < l; i++) {
+ // Manual check to avoid repetitive logs when all tallies
+ // have 0 observation.
+ Tally ta = get (i);
+ double v = ta == null || ta.numberObs() == 0 ? Double.NaN : ta.average();
+ r[i] = v;
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the average for each tally
+ in this list, and stores the averages in the array \texttt{r}.
+ If the tally \texttt{i} has no observation,
+ the \texttt{Double.NaN} value is stored
+ in the array, at index~\texttt{i}.
+\end{tabb}
+\begin{code}
+
+ public void variance (double[] v)\begin{hide} {
+ if (size() != v.length)
+ throw new IllegalArgumentException
+ ("Invalid length of given array");
+ for (int i = 0; i < v.length; i++) {
+ Tally tally = get (i);
+ if (tally == null || tally.numberObs() < 2)
+ v[i] = Double.NaN;
+ else
+ v[i] = tally.variance();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} For each tally in this list, computes
+ the sample variance, and stores the variances into the array \texttt{v}.
+ If, for some tally~\texttt{i}, there are not enough
+ observations for estimating the variance,
+ \texttt{Double.NaN} is stored in the array.
+\end{tabb}
+\begin{htmlonly}
+ \param{v}{the array to be filled with sample variances.}
+ \exception{NullPointerException}{if \texttt{v} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{v.length}
+ does not correspond to \externalmethod{umontreal.iro.lecuyer.stat.list}{ListOfStatProbes}{size}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public void standardDeviation (double[] std)\begin{hide} {
+ if (size() != std.length)
+ throw new IllegalArgumentException
+ ("Invalid length of given array");
+ for (int i = 0; i < std.length; i++) {
+ Tally tally = get (i);
+ if (tally == null || tally.numberObs() < 2)
+ std[i] = Double.NaN;
+ else
+ std[i] = tally.standardDeviation();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} For each tally in this list, computes
+ the sample standard deviation, and stores the standard deviations
+ into the array \texttt{std}.
+ This is equivalent to calling \method{variance}{(double[])} and
+ performing a square root on every element
+ of the filled array.
+\end{tabb}
+\begin{htmlonly}
+ \param{std}{the array to be filled with standard deviations.}
+ \exception{NullPointerException}{if \texttt{std} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{std.length}
+ does not correspond to \texttt{size()}.}
+\end{htmlonly}
+\begin{code}
+
+ public double covariance (int i, int j)\begin{hide} {
+ if (i == j)
+ return get (i).variance();
+
+ TallyStore tallyi = (TallyStore)get (i);
+ TallyStore tallyj = (TallyStore)get (j);
+ return tallyi.covariance (tallyj);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the empirical covariance of the observations in tallies
+ with indices \texttt{i} and \texttt{j}. If $x_1,\ldots,x_n$ represent the
+ observations in tally \texttt{i} whereas $y_1,\ldots,y_n$ represent the
+ observations in tally \texttt{j}, then the covariance is given by
+ \[ S_{X, Y} =
+ \frac{1}{n-1}\sum_{k=1}^n (x_k - \bar{X}_n)(y_k - \bar{Y}_n)
+ = \frac{1}{n-1}\left(\sum_{k=1}^n x_ky_k - \frac{1}{n}
+ \sum_{k=1}^n x_k\sum_{r=1}^n y_r\right).\]
+ This returns \texttt{Double.NaN}
+ if the tallies do not contain the same number of observations, or
+ if they contain less than two observations.
+ This method throws an exception if the
+ underlying tallies are not capable of storing
+ observations, i.e. if the tallies are not TallyStores.
+ The \class{ListOfTalliesWithCovariance}
+ subclass provides an alternative implementation
+ of this method which does not require the
+ observations to be stored.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the index of the first tally.}
+ \param{j}{the index of the second tally.}
+ \return{the value of the covariance.}
+ \exception{ArrayIndexOutOfBoundsException}{if one or both
+ indices are out of bounds.}
+\end{htmlonly}
+\begin{code}
+
+ public double correlation (int i, int j)\begin{hide} {
+ if (i == j)
+ return 1.0;
+ double cov = covariance (i, j);
+ Tally tallyi = get (i);
+ Tally tallyj = get (j);
+ if (tallyi == null || tallyj == null)
+ return Double.NaN;
+ return cov/Math.sqrt (tallyi.variance()*tallyj.variance());
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the empirical correlation between
+ the observations in tallies with indices \texttt{i} and \texttt{j}.
+ If the tally \texttt{i} contains a sample of the random
+ variate $X$ and the tally \texttt{j} contains a sample of $Y$,
+ this corresponds to
+ \[\mathrm{Cor}(X, Y)=\Cov(X, Y)
+ /\sqrt{\Var(X)\Var(Y)}.\]
+
+ This method uses \method{covariance}{(int, int)}
+ to obtain an estimate of the covariance, and
+ \externalmethod{umontreal.iro.lecuyer.stat}{Tally}{variance}{()} in
+class \class{Tally} to obtain the sample variances.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the index of the first tally.}
+ \param{j}{the index of the second tally.}
+ \return{the value of the correlation.}
+ \exception{ArrayIndexOutOfBoundsException}{if one or both
+ indices are out of bounds.}
+\end{htmlonly}
+\begin{code}
+
+ public void covariance (DoubleMatrix2D c)\begin{hide} {
+ int l = size();
+ if (c.rows() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of rows in covariance matrix");
+ if (c.columns() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of columns in covariance matrix");
+ for (int i1 = 0; i1 < l; i1++)
+ c.setQuick (i1, i1, get (i1).variance());
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++) {
+ double cov = covariance (i1, i2);
+ c.setQuick (i1, i2, cov);
+ c.setQuick (i2, i1, cov);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs and returns the sample covariance matrix
+ for the tallies in this list. The given $d\times d$ matrix \texttt{c},
+ where $d=$~\texttt{size()},
+ is filled with the computed sample covariances.
+ Element \texttt{c.get (i, j)} corresponds to
+ the result of
+ \texttt{covariance (i, j)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the matrix to be filled with the sample covariances.}
+ \exception{NullPointerException}{if \texttt{c} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the number of rows or columns
+ in \texttt{c} does not correspond to \texttt{size()}.}
+\end{htmlonly}
+\begin{code}
+
+ public void correlation (DoubleMatrix2D c)\begin{hide} {
+ int l = size();
+ if (c.rows() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of rows in correlation matrix");
+ if (c.columns() != l)
+ throw new IllegalArgumentException
+ ("Invalid number of columns in correlation matrix");
+ for (int i1 = 0; i1 < l; i1++)
+ c.setQuick (i1, i1, 1.0);
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++) {
+ double cor = correlation (i1, i2);
+ c.setQuick (i1, i2, cor);
+ c.setQuick (i2, i1, cor);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{covariance}{(DoubleMatrix2D)} for computing
+ the sample correlation matrix.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the matrix to be filled with the correlations.}
+ \exception{NullPointerException}{if \texttt{c} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if the number of rows or columns in \texttt{c}
+ does not correspond to \externalmethod{umontreal.iro.lecuyer.stat.list}{ListOfStatProbes}{size}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public ListOfTallies<E> clone()\begin{hide} {
+ return (ListOfTallies<E>)super.clone();
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Clones this object. This makes a shallow copy
+ of this list, i.e., this does not clone all the tallies in the list.
+ The created clone is modifiable, even if the original list is unmodifiable.
+\end{tabb}
diff --git a/source/umontreal/iro/lecuyer/stat/list/ListOfTalliesWithCovariance.java b/source/umontreal/iro/lecuyer/stat/list/ListOfTalliesWithCovariance.java
new file mode 100644
index 0000000..d243134
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ListOfTalliesWithCovariance.java
@@ -0,0 +1,323 @@
+
+
+/*
+ * Class: ListOfTalliesWithCovariance
+ * Description: List of tallies with covariance
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stat.list;
+
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.stat.TallyStore;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import cern.colt.matrix.DoubleMatrix1D;
+
+
+
+/**
+ * Extends {@link ListOfTallies} to add support for the computation
+ * of the sample covariance between each pair of elements
+ * in a list, without storing all observations.
+ * This list of tallies contains internal structures to keep track of
+ *
+ * <SPAN CLASS="MATH">bar(X)<SUB>n, i</SUB></SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>d</I> - 1</SPAN>, and
+ *
+ * <SPAN CLASS="MATH">∑<SUB>k=0</SUB><SUP>n-1</SUP>(<I>X</I><SUB>i, k</SUB> - bar(X)<SUB>k, i</SUB>)(<I>X</I><SUB>j, k</SUB> - bar(X)<SUB>k, j</SUB>)/<I>n</I></SPAN>, for
+ *
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>d</I> - 2</SPAN> and
+ * <SPAN CLASS="MATH"><I>j</I> = 1,…, <I>d</I> - 1</SPAN>, with <SPAN CLASS="MATH"><I>j</I> > <I>i</I></SPAN>.
+ * Here,
+ * <SPAN CLASS="MATH">bar(X)<SUB>n, i</SUB></SPAN> is the <SPAN CLASS="MATH"><I>i</I></SPAN>th component of
+ * <SPAN CLASS="MATH">bar(<I><B>X</B></I>)<SUB>n</SUB></SPAN>, the average vector,
+ * and
+ * <SPAN CLASS="MATH">bar(X)<SUB>0, i</SUB> = 0</SPAN> for
+ * <SPAN CLASS="MATH"><I>i</I> = 0,…, <I>d</I> - 1</SPAN>.
+ * The value <SPAN CLASS="MATH"><I>X</I><SUB>i, k</SUB></SPAN> corresponds to the <SPAN CLASS="MATH"><I>i</I></SPAN>th component of the <SPAN CLASS="MATH"><I>k</I></SPAN>th observation
+ * <SPAN CLASS="MATH"><I><B>X</B></I><SUB>k</SUB></SPAN>.
+ * These sums are updated every time a vector is added to this list, and
+ * are used to estimate the covariances.
+ *
+ * <P>
+ * Note: the size of the list of tallies must remain fixed because of the
+ * data structures used for computing sample covariances.
+ * As a result, the first call to <TT>init</TT> makes this list
+ * unmodifiable.
+ *
+ * <P>
+ * Note: for the sample covariance to be computed between a pair
+ * of tallies, the number of observations in each tally
+ * should be the same. It is therefore recommended to always
+ * add complete vectors of observations to this list.
+ * Moreover, one must use
+ * the {@link #add add} method in this class to add vectors of observations for
+ * the sums used for covariance estimation to be updated correctly.
+ * Failure to use this method, e.g., adding observations
+ * to each individual tally in the list, will result in an incorrect
+ * estimate of the covariances, unless the tallies
+ * in the list can store observations.
+ * For example, the following code, which adds the vector <TT>v</TT> in
+ * the list of tallies <TT>list</TT>, works correctly only if the list
+ * contains instances of {@link TallyStore}:
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ * for (int i = 0; i < v.length; i++)
+ * <BR> list.get (i).add (v[i]);
+ * <BR></TT>
+ * </DIV>
+ * But the following code is always correct:
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ * list.add (v);
+ * <BR></TT>
+ * </DIV>
+ *
+ */
+public class ListOfTalliesWithCovariance<E extends Tally>
+ extends ListOfTallies<E> {
+ private double[] tempArray;
+ private double[][] sxy;
+
+ // Determines if we use a numerically stable covariance
+ // formula.
+ private boolean isStable = true;
+
+ // The average of the first observations, for each tally
+ private double[] curAverages;
+
+ // The sum (xi - average)(yi - average) of the first observations
+ private double[][] curSum2;
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat.list");
+
+
+
+ /**
+ * Creates an empty list of tallies with covariance support.
+ * One must fill the list with tallies, and call {@link #init init} before
+ * adding any observation.
+ *
+ */
+ public ListOfTalliesWithCovariance() {
+ super();
+ }
+
+
+ /**
+ * Creates an empty list of tallies with covariance
+ * support and name <TT>name</TT>.
+ * One must fill the list with tallies, and call {@link #init init} before
+ * adding any observation.
+ *
+ * @param name the name of the new list.
+ *
+ *
+ */
+ public ListOfTalliesWithCovariance (String name) {
+ super (name);
+ }
+
+
+ /**
+ * This factory method constructs and returns a list of tallies with <TT>size</TT> instances of
+ * {@link Tally}.
+ *
+ * @param size the size of the list.
+ *
+ * @return the created list.
+ *
+ */
+ public static ListOfTalliesWithCovariance<Tally> createWithTally (int size) {
+ ListOfTalliesWithCovariance<Tally> list = new ListOfTalliesWithCovariance<Tally>();
+ for (int i = 0; i < size; i++)
+ list.add (new Tally());
+ list.init();
+ return list;
+ }
+
+
+ /**
+ * This factory method constructs and returns a list of tallies with <TT>size</TT> instances of
+ * {@link TallyStore}.
+ *
+ * @param size the size of the list.
+ *
+ * @return the created list.
+ *
+ */
+ public static ListOfTalliesWithCovariance<TallyStore> createWithTallyStore
+ (int size) {
+ ListOfTalliesWithCovariance<TallyStore> list = new ListOfTalliesWithCovariance<TallyStore>();
+ for (int i = 0; i < size; i++)
+ list.add (new TallyStore());
+ list.init();
+ return list;
+ }
+
+
+ private void createSxy() {
+ int l = size();
+ if (isStable) {
+ curAverages = new double[l];
+ curSum2 = new double[l-1][];
+ for (int i = 0; i < l - 1; i++)
+ curSum2[i] = new double[l - 1 - i];
+ }
+ else {
+ sxy = new double[l - 1][];
+ for (int i = 0; i < l - 1; i++)
+ sxy[i] = new double[l - 1 - i];
+ }
+ tempArray = new double[l];
+ }
+
+ public void init() {
+ super.init();
+
+ if (isModifiable()) {
+ setUnmodifiable();
+ createSxy();
+ }
+ if (isStable) {
+ for (int i = 0; i < curAverages.length; i++)
+ curAverages[i] = 0;
+ for (int i = 0; i < curSum2.length; i++)
+ for (int j = 0; j < curSum2[i].length; j++)
+ curSum2[i][j] = 0;
+ }
+ else
+ for (int i = 0; i < sxy.length; i++)
+ for (int j = 0; j < sxy[i].length; j++)
+ sxy[i][j] = 0;
+ }
+
+ /**
+ * Adds a new vector of observations <TT>x</TT> to this
+ * list of tallies, and updates the internal data structures computing
+ * averages, and sums of products.
+ * One must use this method instead of adding observations to
+ * individual tallies to get a covariance estimate.
+ *
+ * @param x the new vector of observations.
+ *
+ *
+ */
+ public void add (double[] x) {
+ int l = size();
+
+ int structSize = 0;
+ structSize = (isStable) ? curSum2.length : sxy.length;
+ if (structSize != l - 1)
+ throw new IllegalArgumentException ("The structure's size mismatches the list's size");
+
+ super.add (x);
+ if (isStable) {
+ int numObs = get (0).numberObs();
+ // get (i1).average() would return the average over the n
+ // observations, but we need the average over the last n-1 observations.
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++)
+ curSum2[i1][i2 - i1 - 1] += (numObs - 1)*
+ (x[i1] - curAverages[i1])*(x[i2] - curAverages[i2])/numObs;
+ for (int i = 0; i < l; i++)
+ curAverages[i] += (x[i] - curAverages[i])/numObs;
+ // Now, curAverages[i] == get (i).average()
+ }
+ else
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++)
+ sxy[i1][i2 - i1 - 1] += x[i1]*x[i2];
+ }
+
+
+ public void add (DoubleMatrix1D x) {
+ x.toArray (tempArray);
+ add (tempArray);
+ }
+
+ public double covariance (int i, int j) {
+ if (i == j)
+ return get (i).variance();
+ if (i > j) {
+ // Make sure that i1 < i2, to have a single case
+ int tmp = i;
+ i = j;
+ j = tmp;
+ }
+
+ Tally tallyi = get (i);
+ Tally tallyj = get (j);
+ if (tallyi == null || tallyj == null)
+ return Double.NaN;
+ int n = tallyi.numberObs();
+ if (n != tallyj.numberObs()) {
+ log.logp (Level.WARNING, "ListOfTalliesWithCovariance", "covariance",
+ "Tally " + i + ", with name " + tallyi.getName() + ", contains "
+ + n + " observations while " +
+ "tally " + j + ", with name " + tallyj.getName() + ", contains " + tallyj.numberObs() + "observations");
+ return Double.NaN;
+ }
+
+ if (n < 2) {
+ log.logp (Level.WARNING, "ListOfTalliesWithCovariance", "covariance",
+ "Tally " + i + ", with name " + tallyi.getName() + ", contains " + n + " observation");
+ return Double.NaN;
+ }
+ if (tallyi instanceof TallyStore && tallyj instanceof TallyStore)
+ return ((TallyStore) tallyi).covariance ((TallyStore) tallyj);
+ else if (isStable)
+ return curSum2[i][j - i - 1]/(n-1);
+ else {
+ double sum1 = tallyi.sum();
+ double sum2 = tallyj.sum();
+ double sum12 = sxy[i][j - i - 1];
+ return (sum12 - sum1*sum2/n)/(n-1);
+ }
+ }
+
+ /**
+ * Clones this object.
+ * This clones the list of tallies and the data structures holding the sums of products but
+ * not the tallies comprising the list.
+ * The created clone is modifiable, even though the original list is unmodifiable.
+ *
+ */
+ public ListOfTalliesWithCovariance<E> clone() {
+ ListOfTalliesWithCovariance<E> ta = (ListOfTalliesWithCovariance<E>)super.clone();
+ ta.tempArray = new double[size()];
+ if (curAverages != null)
+ ta.curAverages = curAverages.clone();
+ if (sxy != null) {
+ ta.sxy = new double[sxy.length][];
+ for (int i = 0; i < sxy.length; i++)
+ ta.sxy[i] = sxy[i].clone();
+ }
+ if (curSum2 != null) {
+ ta.curSum2 = new double[curSum2.length][];
+ for (int i = 0; i < curSum2.length; i++)
+ ta.curSum2[i] = curSum2[i].clone();
+ }
+ return ta;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/stat/list/ListOfTalliesWithCovariance.tex b/source/umontreal/iro/lecuyer/stat/list/ListOfTalliesWithCovariance.tex
new file mode 100644
index 0000000..fe3de0a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/ListOfTalliesWithCovariance.tex
@@ -0,0 +1,305 @@
+\defmodule{ListOfTalliesWithCovariance}
+
+Extends \class{ListOfTallies} to add support for the computation
+of the sample covariance between each pair of elements
+in a list, without storing all observations.
+This list of tallies contains internal structures to keep track of
+$\bar X_{n, i}$ for $i=0, \ldots, d-1$, and
+$\sum_{k=0}^{n-1} (X_{i, k} - \bar X_{k, i})(X_{j, k} - \bar X_{k, j})/n$, for
+$i=0,\ldots, d-2$ and $j=1,\ldots,d-1$, with $j>i$.
+Here, $\bar X_{n, i}$ is the $i$th component of $\barboldX_n$, the average vector,
+and $\bar X_{0, i}=0$ for $i=0,\ldots,d-1$.
+The value $X_{i,k}$ corresponds to the $i$th component of the $k$th observation
+$\boldX_k$.
+These sums are updated every time a vector is added to this list, and
+are used to estimate the covariances.
+
+Note: the size of the list of tallies must remain fixed because of the
+data structures used for computing sample covariances.
+As a result, the first call to \texttt{init} makes this list
+unmodifiable.
+
+Note: for the sample covariance to be computed between a pair
+of tallies, the number of observations in each tally
+should be the same. It is therefore recommended to always
+add complete vectors of observations to this list.
+Moreover, one must use
+the \method{add}{} method in this class to add vectors of observations for
+the sums used for covariance estimation to be updated correctly.
+Failure to use this method, e.g., adding observations
+to each individual tally in the list, will result in an incorrect
+estimate of the covariances, unless the tallies
+in the list can store observations.
+For example, the following code, which adds the vector \texttt{v} in
+the list of tallies \texttt{list}, works correctly only if the list
+contains instances of \class{TallyStore}:
+\begin{vcode}
+ for (int i = 0; i < v.length; i++)
+ list.get (i).add (v[i]);
+\end{vcode}
+But the following code is always correct:
+\begin{vcode}
+ list.add (v);
+\end{vcode}
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ListOfTalliesWithCovariance
+ * Description: List of tallies with covariance
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since 2007
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stat.list;\begin{hide}
+
+import umontreal.iro.lecuyer.stat.Tally;
+import umontreal.iro.lecuyer.stat.TallyStore;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import cern.colt.matrix.DoubleMatrix1D;
+\end{hide}
+
+
+public class ListOfTalliesWithCovariance<E extends Tally>
+ extends ListOfTallies<E>\begin{hide} {
+ private double[] tempArray;
+ private double[][] sxy;
+
+ // Determines if we use a numerically stable covariance
+ // formula.
+ private boolean isStable = true;
+
+ // The average of the first observations, for each tally
+ private double[] curAverages;
+
+ // The sum (xi - average)(yi - average) of the first observations
+ private double[][] curSum2;
+ private Logger log = Logger.getLogger ("umontreal.iro.lecuyer.stat.list");
+\end{hide}
+\end{code}
+\subsubsection*{Constructors}
+\begin{code}
+
+ public ListOfTalliesWithCovariance()\begin{hide} {
+ super();
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an empty list of tallies with covariance support.
+ One must fill the list with tallies, and call \method{init}{} before
+ adding any observation.
+\end{tabb}
+\begin{code}
+
+ public ListOfTalliesWithCovariance (String name)\begin{hide} {
+ super (name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates an empty list of tallies with covariance
+ support and name \texttt{name}.
+ One must fill the list with tallies, and call \method{init}{} before
+ adding any observation.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the name of the new list.}
+\end{htmlonly}
+\subsubsection*{Methods}
+\begin{code}
+
+ public static ListOfTalliesWithCovariance<Tally> createWithTally (int size)\begin{hide} {
+ ListOfTalliesWithCovariance<Tally> list = new ListOfTalliesWithCovariance<Tally>();
+ for (int i = 0; i < size; i++)
+ list.add (new Tally());
+ list.init();
+ return list;
+ }\end{hide}
+\end{code}
+\begin{tabb} This factory method constructs and returns a list of tallies with \texttt{size} instances of
+ \class{Tally}.
+\end{tabb}
+\begin{htmlonly}
+ \param{size}{the size of the list.}
+ \return{the created list.}
+\end{htmlonly}
+\begin{code}
+
+ public static ListOfTalliesWithCovariance<TallyStore> createWithTallyStore
+ (int size)\begin{hide} {
+ ListOfTalliesWithCovariance<TallyStore> list = new ListOfTalliesWithCovariance<TallyStore>();
+ for (int i = 0; i < size; i++)
+ list.add (new TallyStore());
+ list.init();
+ return list;
+ }\end{hide}
+\end{code}
+\begin{tabb} This factory method constructs and returns a list of tallies with \texttt{size} instances of
+ \class{TallyStore}.
+\end{tabb}
+\begin{htmlonly}
+ \param{size}{the size of the list.}
+ \return{the created list.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private void createSxy() {
+ int l = size();
+ if (isStable) {
+ curAverages = new double[l];
+ curSum2 = new double[l-1][];
+ for (int i = 0; i < l - 1; i++)
+ curSum2[i] = new double[l - 1 - i];
+ }
+ else {
+ sxy = new double[l - 1][];
+ for (int i = 0; i < l - 1; i++)
+ sxy[i] = new double[l - 1 - i];
+ }
+ tempArray = new double[l];
+ }
+
+ public void init() {
+ super.init();
+
+ if (isModifiable()) {
+ setUnmodifiable();
+ createSxy();
+ }
+ if (isStable) {
+ for (int i = 0; i < curAverages.length; i++)
+ curAverages[i] = 0;
+ for (int i = 0; i < curSum2.length; i++)
+ for (int j = 0; j < curSum2[i].length; j++)
+ curSum2[i][j] = 0;
+ }
+ else
+ for (int i = 0; i < sxy.length; i++)
+ for (int j = 0; j < sxy[i].length; j++)
+ sxy[i][j] = 0;
+ }\end{hide}
+
+ public void add (double[] x)\begin{hide} {
+ int l = size();
+
+ int structSize = 0;
+ structSize = (isStable) ? curSum2.length : sxy.length;
+ if (structSize != l - 1)
+ throw new IllegalArgumentException ("The structure's size mismatches the list's size");
+
+ super.add (x);
+ if (isStable) {
+ int numObs = get (0).numberObs();
+ // get (i1).average() would return the average over the n
+ // observations, but we need the average over the last n-1 observations.
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++)
+ curSum2[i1][i2 - i1 - 1] += (numObs - 1)*
+ (x[i1] - curAverages[i1])*(x[i2] - curAverages[i2])/numObs;
+ for (int i = 0; i < l; i++)
+ curAverages[i] += (x[i] - curAverages[i])/numObs;
+ // Now, curAverages[i] == get (i).average()
+ }
+ else
+ for (int i1 = 0; i1 < l - 1; i1++)
+ for (int i2 = i1 + 1; i2 < l; i2++)
+ sxy[i1][i2 - i1 - 1] += x[i1]*x[i2];
+ }\end{hide}
+\end{code}
+\begin{tabb} Adds a new vector of observations \texttt{x} to this
+ list of tallies, and updates the internal data structures computing
+ averages, and sums of products.
+ One must use this method instead of adding observations to
+ individual tallies to get a covariance estimate.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the new vector of observations.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void add (DoubleMatrix1D x) {
+ x.toArray (tempArray);
+ add (tempArray);
+ }
+
+ public double covariance (int i, int j) {
+ if (i == j)
+ return get (i).variance();
+ if (i > j) {
+ // Make sure that i1 < i2, to have a single case
+ int tmp = i;
+ i = j;
+ j = tmp;
+ }
+
+ Tally tallyi = get (i);
+ Tally tallyj = get (j);
+ if (tallyi == null || tallyj == null)
+ return Double.NaN;
+ int n = tallyi.numberObs();
+ if (n != tallyj.numberObs()) {
+ log.logp (Level.WARNING, "ListOfTalliesWithCovariance", "covariance",
+ "Tally " + i + ", with name " + tallyi.getName() + ", contains "
+ + n + " observations while " +
+ "tally " + j + ", with name " + tallyj.getName() + ", contains " + tallyj.numberObs() + "observations");
+ return Double.NaN;
+ }
+
+ if (n < 2) {
+ log.logp (Level.WARNING, "ListOfTalliesWithCovariance", "covariance",
+ "Tally " + i + ", with name " + tallyi.getName() + ", contains " + n + " observation");
+ return Double.NaN;
+ }
+ if (tallyi instanceof TallyStore && tallyj instanceof TallyStore)
+ return ((TallyStore) tallyi).covariance ((TallyStore) tallyj);
+ else if (isStable)
+ return curSum2[i][j - i - 1]/(n-1);
+ else {
+ double sum1 = tallyi.sum();
+ double sum2 = tallyj.sum();
+ double sum12 = sxy[i][j - i - 1];
+ return (sum12 - sum1*sum2/n)/(n-1);
+ }
+ }\end{hide}
+
+ public ListOfTalliesWithCovariance<E> clone()\begin{hide} {
+ ListOfTalliesWithCovariance<E> ta = (ListOfTalliesWithCovariance<E>)super.clone();
+ ta.tempArray = new double[size()];
+ if (curAverages != null)
+ ta.curAverages = curAverages.clone();
+ if (sxy != null) {
+ ta.sxy = new double[sxy.length][];
+ for (int i = 0; i < sxy.length; i++)
+ ta.sxy[i] = sxy[i].clone();
+ }
+ if (curSum2 != null) {
+ ta.curSum2 = new double[curSum2.length][];
+ for (int i = 0; i < curSum2.length; i++)
+ ta.curSum2[i] = curSum2[i].clone();
+ }
+ return ta;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Clones this object.
+ This clones the list of tallies and the data structures holding the sums of products but
+ not the tallies comprising the list.
+ The created clone is modifiable, even though the original list is unmodifiable.
+\end{tabb}
diff --git a/source/umontreal/iro/lecuyer/stat/list/overview.tex b/source/umontreal/iro/lecuyer/stat/list/overview.tex
new file mode 100644
index 0000000..38cbd32
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/list/overview.tex
@@ -0,0 +1,51 @@
+\latex{\section*{Overview of package \texttt{stat.list}}\addcontentsline{toc}{section}{Overview of package \texttt{stat.list}}}
+
+\label{over-li-tal}
+Provides support for lists of statistical probes.
+Sometimes, a simulator computes several related performance measures
+such as the quality of service for different call types in a phone
+call center, the waiting times of different types of customers, the
+average number of pieces of different types a machine processes, etc.
+A list of statistical probes, in contrast with an ordinary array, can
+be resized. Since a list of statistical probes implements
+the Java \texttt{List} interface, one can iterate over each probe,
+e.g., to set reporting options.
+In addition to an ordinary list, a list of probes
+provides facilities to get a vector of averages, a vector of sums,
+and to create reports.
+
+In the Java programming language,
+a list is usually constructed empty, and filled with items. Lists of
+statistical probes can be constructed this generic way, or created
+using factory methods that automatically construct the probes.
+
+\externalclass{umontreal.iro.lecuyer.stat.list}{ListOfStatProbes} is
+the base class for
+lists of statistical probes. It can hold a list of any
+\externalclass{umontreal.iro.lecuyer.stat}{StatProbe} subclass, and
+provides the basic
+facilities to obtain an array of sums, an array of averages, etc.
+Subclasses provide probe-specific functionalities for adding
+vectors of observations, computing sample covariances, etc.
+\externalclass{umontreal.iro.lecuyer.stat.list}{ListOfTallies} is
+used to contain \externalclass{umontreal.iro.lecuyer.stat}{Tally} instances.
+A subclass,
+\externalclass{umontreal.iro.lecuyer.stat.list}{ListOfTalliesWithCovariance}, is
+provided to add support for covariance computation without storing observations.
+\iffalse
+The \externalclass{umontreal.iro.lecuyer.stat.list}{ListOfFunctionOfMultipleMeansTallies}
+is available to regroup
+\externalclass{umontreal.iro.lecuyer.stat}{FunctionOfMultipleMeansTally}
+objects.
+\fi
+
+All classes in this package representing lists of probes support the
+observer design pattern
+similarly to the classes in package \texttt{stat}.
+A list of statistical probes maintains a list of registered
+\externalclass{umontreal.iro.lecuyer.stat.list}{ArrayOfObservationListener}
+objects, and broadcasts information to all its registered
+observers when it receives a new vector of observations.
+Any object that implements the interface
+\externalclass{umontreal.iro.lecuyer.stat.list}{ArrayOfObservationListener}
+can register as an observer.
diff --git a/source/umontreal/iro/lecuyer/stat/overview.tex b/source/umontreal/iro/lecuyer/stat/overview.tex
new file mode 100644
index 0000000..aef15aa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stat/overview.tex
@@ -0,0 +1,100 @@
+\latex{\section*{Overview of package \texttt{stat}}\addcontentsline{toc}{section}{Overview of package \texttt{stat}}}
+
+This package provides elementary tools for collecting statistics and computing
+confidence intervals. The base class
+\externalclass{umontreal.iro.lecuyer.stat}{StatProbe}
+implements common methods needed by all probes.
+Its subclass \externalclass{umontreal.iro.lecuyer.stat}{Tally}
+collects data as a sequence of observations $X_1,X_2,\dots$, and computes
+sample averages, sample standard deviations, and confidence intervals
+based on the normality assumption. \richard{Toujours bas\'e sur la normale?}
+The class \externalclass{umontreal.iro.lecuyer.stat}{TallyStore} is
+similar, but it also stores the individual observations in a list
+implemented as a \externalclass{cern.colt.list}{DoubleArrayList}%
+\begin{latexonly}, a class imported from the COLT library\end{latexonly}.
+This permits one to compute more quantities and to use the methods
+provided by COLT for computing descriptive statistics.
+
+The class \externalclass{umontreal.iro.lecuyer.simevents}{Accumulate}%
+\begin{latexonly}, in package \texttt{simevents},\end{latexonly}
+computes integrals and averages with respect to time.
+This class is in package \texttt{simevents} because its operation depends
+on the simulation clock.
+
+\iffalse %%%%%%%%
+The class
+\externalclass{umontreal.iro.lecuyer.stat}{FunctionOfMultipleMeansTally}
+collects data as a sequence of $d$-dimensional
+vectors $\boldX_1, \boldX_2, \ldots$,
+and computes a function of sample averages with a sample variance, and
+a confidence interval on the corresponding function of means using the
+delta theorem.
+The function of averages to consider, as well as its gradient, need to
+be computed by an implementation of
+\externalclass{umontreal.iro.lecuyer.util}{MultivariateFunction},
+for example,
+\externalclass{umontreal.iro.lecuyer.util}{RatioFunction} for
+a ratio of two quantities.
+\fi %%%%%%%%%
+
+All classes that represent statistical probes
+support the \textit{observer} design pattern, well-known
+in software engineering\begin{latexonly} \cite{iGAM98a}\end{latexonly}.
+%% Gamma et al., Design Patterns---Elements of Reusable
+%% Object-Oriented Software, Addison-Wesley, second ed., 1998.
+This pattern facilitates the separation of data generation (by the
+simulation program) from data processing (for statistical reports and
+displays). This can be very helpful in particular in large simulation
+programs or libraries, where different objects may need to process
+the same data in different ways.
+A statistical probe maintains a list of registered
+\externalclass{umontreal.iro.lecuyer.stat}{ObservationListener}
+objects, and broadcasts information to all its registered
+observers whenever appropriate. %For the
+%\externalclass{umontreal.iro.lecuyer.stat}{StatProbe} class,
+%this happens whenever a new observation is given to the probe.
+Any object that implements the interface
+\externalclass{umontreal.iro.lecuyer.stat}{ObservationListener}
+can register as an observer. For an example, see the program
+\texttt{QueueObs} in the directory \texttt{examples}.
+
+Subpackages of package \texttt{stat} provide matrices of \texttt{Tally}'s
+%(see the overview on page \pageref{over-mat-tal} of this guide),
+and lists of \texttt{Tally}'s. %(see page \pageref{over-li-tal} of this guide).
+% and lists of \texttt{Tally}'s with control variables
+% (see page \pageref{over-licv-tal} of this guide).
+
+
+
+%%%%%%%%% Enlever ce qui suit: trop technique pour l'overview.
+\begin{comment}
+
+When writing complex simulation programs, in data generation portions,
+one uses the statistical
+probes as usual but with observation notification turned on. To turn
+on observation notification, one simply calls
+\externalmethod{umontreal.iro.lecuyer.stat}{StatProbe}{notifyObs}{}
+on the probes. In this mode, a probe becomes a \emph{distribution
+ agency} which broadcasts received observations to
+all \emph{observers} that registered to it. It will also fulfill its
+collector functionality unless it is disabled explicitly by calling
+the
+\externalmethod{umontreal.iro.lecuyer.stat}{StatProbe}{stopCollectStat}{}
+method.
+When the statistical collection is disabled, the probes are only
+distribution agencies.
+
+Data processing parts of the program are implemented through
+observers. An observer is a class
+implementing the \externalclass{java.util}{Observer} interface. It
+can be registered to any class extending
+\externalclass{java.util}{Observable}, including
+\externalclass{umontreal.iro.lecuyer.stat}{StatProbe}.
+The observed value is passed
+to the observers through the second argument of the
+\externalmethod{java.util}{Observer}{update}{} method which is an
+\texttt{Object}. The argument must then be
+type-casted to a \texttt{Double} wrapper object
+before the observation value can be extracted.
+
+\end{comment}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotion.java b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotion.java
new file mode 100644
index 0000000..f538d74
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotion.java
@@ -0,0 +1,263 @@
+
+
+/*
+ * Class: BrownianMotion
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * This class represents a <SPAN CLASS="textit">Brownian motion</SPAN> process
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I>) : <I>t</I> >= 0}</SPAN>,
+ * sampled at times
+ * <SPAN CLASS="MATH">0 = <I>t</I><SUB>0</SUB> < <I>t</I><SUB>1</SUB> < <SUP> ... </SUP> < <I>t</I><SUB>d</SUB></SPAN>.
+ * This process obeys the stochastic differential equation
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:Brownian-motion"></A>
+ * <I>dX</I>(<I>t</I>) = <I>μdt</I> + <I>σdB</I>(<I>t</I>),
+ * </DIV><P></P>
+ * with initial condition <SPAN CLASS="MATH"><I>X</I>(0) = <I>x</I><SUB>0</SUB></SPAN>,
+ * where <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are the drift and volatility parameters,
+ * and
+ * <SPAN CLASS="MATH">{<I>B</I>(<I>t</I>), <I>t</I> >= 0}</SPAN> is a standard Brownian motion
+ * (with drift 0 and volatility 1).
+ * This process has stationary and independent increments over disjoint
+ * time intervals (it is a Lévy process) and the increment over an interval
+ * of length <SPAN CLASS="MATH"><I>t</I></SPAN> is normally distributed with mean <SPAN CLASS="MATH"><I>μt</I></SPAN> and variance
+ * <SPAN CLASS="MATH"><I>σ</I><SUP>2</SUP><I>t</I></SPAN>.
+ *
+ * <P>
+ * In this class, this process is generated using the sequential (or random walk)
+ * technique: <SPAN CLASS="MATH"><I>X</I>(0) = <I>x</I><SUB>0</SUB></SPAN> and
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:Brownian-motion-sequential"></A>
+ * <I>X</I>(<I>t</I><SUB>j</SUB>) - <I>X</I>(<I>t</I><SUB>j-1</SUB>) = <I>μ</I>(<I>t</I><SUB>j</SUB> - <I>t</I><SUB>j-1</SUB>) + <I>σ</I>(t_j - t_j-1)<SUP>1/2</SUP><I>Z</I><SUB>j</SUB>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB>∼<I>N</I>(0, 1)</SPAN>.
+ *
+ */
+public class BrownianMotion extends StochasticProcess {
+ protected NormalGen gen;
+ protected double mu,
+ sigma;
+ // Precomputed values for standard BM
+ protected double[] mudt,
+ sigmasqrdt;
+
+
+
+ /**
+ * Constructs a new <TT>BrownianMotion</TT> with
+ * parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN> <TT>mu</TT>, <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>x0</TT>.
+ * The normal variates <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB></SPAN> in will be
+ * generated by inversion using <TT>stream</TT>.
+ *
+ */
+ public BrownianMotion (double x0, double mu, double sigma,
+ RandomStream stream) {
+ this (x0, mu, sigma, new NormalGen (stream));
+ }
+
+
+ /**
+ * Constructs a new <TT>BrownianMotion</TT> with parameters <SPAN CLASS="MATH"><I>μ</I> =</SPAN>
+ * <TT>mu</TT>, <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT> and initial value
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>x0</TT>.
+ * Here, the normal variate generator
+ * {@link umontreal.iro.lecuyer.randvar.NormalGen NormalGen} is specified
+ * directly instead of specifying the stream and using inversion.
+ * The normal generator <TT>gen</TT> can use another method than inversion.
+ *
+ */
+ public BrownianMotion (double x0, double mu, double sigma, NormalGen gen) {
+ this.mu = mu;
+ this.sigma = sigma;
+ this.x0 = x0;
+ this.gen = gen;
+ }
+
+
+ public double nextObservation() {
+ double x = path[observationIndex];
+ x += mudt[observationIndex]
+ + sigmasqrdt[observationIndex] * gen.nextDouble();
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }
+
+
+ /**
+ * Generates and returns the next observation at time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB> =</SPAN>
+ * <TT>nextTime</TT>. It uses the previous observation time <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> defined earlier
+ * (either by this method or by <TT>setObservationTimes</TT>),
+ * as well as the value of the previous observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will reset the observations time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN>
+ * for this process to <TT>nextTime</TT>. The user must make sure that
+ * the <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN> supplied is
+ * <SPAN CLASS="MATH"> >= <I>t</I><SUB>j</SUB></SPAN>.
+ *
+ */
+ public double nextObservation (double nextTime) {
+ // This method is useful for generating variance gamma processes
+ double x = path[observationIndex];
+ double previousTime = t[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ x += mu * dt + sigma * Math.sqrt (dt) * gen.nextDouble();
+ path[observationIndex] = x;
+ return x;
+ }
+
+
+ /**
+ * Generates an observation of the process in <TT>dt</TT> time units,
+ * assuming that the process has value <SPAN CLASS="MATH"><I>x</I></SPAN> at the current time.
+ * Uses the process parameters specified in the constructor.
+ * Note that this method does not affect the sample path of the process
+ * stored internally (if any).
+ *
+ */
+ public double nextObservation (double x, double dt) {
+ x += mu * dt + sigma * Math.sqrt (dt) * gen.nextDouble();
+ return x;
+ }
+
+
+ public double[] generatePath() {
+ double x = x0;
+ for (int j = 0; j < d; j++) {
+ x += mudt[j] + sigmasqrdt[j] * gen.nextDouble();
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ /**
+ * Same as generatePath(), but a vector of uniform random numbers
+ * must be provided to the method. These uniform random numbers are used
+ * to generate the path.
+ *
+ */
+ public double[] generatePath (double[] uniform01) {
+ double x = x0;
+ for (int j = 0; j < d; j++) {
+ x += mudt[j] + sigmasqrdt[j] * NormalDist.inverseF01(uniform01[j]);
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }
+
+ /**
+ * Resets the parameters
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) = <texttt>x0</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called too frequently.
+ *
+ */
+ public void setParams (double x0, double mu, double sigma) {
+ this.x0 = x0;
+ this.mu = mu;
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.sigma = sigma;
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }
+
+
+ /**
+ * Resets the random stream of the normal generator to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) { gen.setStream (stream); }
+
+
+ /**
+ * Returns the random stream of the normal generator.
+ *
+ */
+ public RandomStream getStream() { return gen.getStream (); }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>μ</I></SPAN>.
+ *
+ */
+ public double getMu() { return mu; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() { return sigma; }
+
+
+ /**
+ * Returns the normal random variate generator used.
+ * The {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * used by that generator can be changed via
+ * <TT>getGen().setStream(stream)</TT>, for example.
+ *
+ */
+ public NormalGen getGen() { return gen; }
+
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ mudt = new double[d];
+ sigmasqrdt = new double[d];
+ for (int j = 0; j < d; j++) {
+ double dt = t[j+1] - t[j];
+ mudt[j] = mu * dt;
+ sigmasqrdt[j] = sigma * Math.sqrt (dt);
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotion.tex b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotion.tex
new file mode 100644
index 0000000..ad6d74f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotion.tex
@@ -0,0 +1,255 @@
+\defmodule {BrownianMotion}
+
+This class represents a \emph{Brownian motion} process $\{X(t) : t \geq 0 \}$,
+sampled at times $0 = t_0 < t_1 < \cdots < t_d$.
+This process obeys the stochastic differential equation
+\begin{equation}
+ dX(t) = \mu dt + \sigma dB(t),
+ \label{eq:Brownian-motion}
+\end{equation}
+with initial condition $X(0)= x_0$,
+where $\mu$ and $\sigma$ are the drift and volatility parameters,
+and $\{B(t),\, t\ge 0\}$ is a standard Brownian motion
+(with drift 0 and volatility 1).
+This process has stationary and independent increments over disjoint
+time intervals (it is a L\'evy process) and the increment over an interval
+of length $t$ is normally distributed with mean $\mu t$ and variance $\sigma^2 t$.
+
+In this class, this process is generated using the sequential (or random walk)
+technique: $X(0)=x_0$ and
+\begin{equation}
+ X(t_j) - X(t_{j-1}) = \mu(t_j - t_{j-1}) + \sigma \sqrt{t_j - t_{j-1}} Z_j
+ \label{eq:Brownian-motion-sequential}
+\end{equation}
+where $Z_j \sim N(0,1)$.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BrownianMotion
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class BrownianMotion extends StochasticProcess \begin{hide} {
+ protected NormalGen gen;
+ protected double mu,
+ sigma;
+ // Precomputed values for standard BM
+ protected double[] mudt,
+ sigmasqrdt;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BrownianMotion (double x0, double mu, double sigma,
+ RandomStream stream)\begin{hide} {
+ this (x0, mu, sigma, new NormalGen (stream));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotion} with
+parameters $\mu =$ \texttt{mu}, $\sigma =$ \texttt{sigma} and initial value
+$X(t_{0}) =$ \texttt{x0}.
+The normal variates $Z_j$ in (\ref{eq:Brownian-motion-sequential}) will be
+generated by inversion using \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotion (double x0, double mu, double sigma, NormalGen gen)\begin{hide} {
+ this.mu = mu;
+ this.sigma = sigma;
+ this.x0 = x0;
+ this.gen = gen;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotion} with parameters $\mu =$
+\texttt{mu}, $\sigma =$ \texttt{sigma} and initial value $X(t_{0}) =$ \texttt{x0}.
+Here, the normal variate generator
+\externalclass{umontreal.iro.lecuyer.randvar}{NormalGen} is specified
+directly instead of specifying the stream and using inversion.
+The normal generator \texttt{gen} can use another method than inversion.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code} \begin{hide}
+
+ public double nextObservation() {
+ double x = path[observationIndex];
+ x += mudt[observationIndex]
+ + sigmasqrdt[observationIndex] * gen.nextDouble();
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }
+
+\end{hide}
+ public double nextObservation (double nextTime) \begin{hide} {
+ // This method is useful for generating variance gamma processes
+ double x = path[observationIndex];
+ double previousTime = t[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ x += mu * dt + sigma * Math.sqrt (dt) * gen.nextDouble();
+ path[observationIndex] = x;
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates and returns the next observation at time $t_{j+1} =$
+ \texttt{nextTime}. It uses the previous observation time $t_{j}$ defined earlier
+(either by this method or by \texttt{setObservationTimes}),
+as well as the value of the previous observation $X(t_j)$.
+\emph{Warning}: This method will reset the observations time $t_{j+1}$
+for this process to \texttt{nextTime}. The user must make sure that
+the $t_{j+1}$ supplied is $\geq t_{j}$.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation (double x, double dt) \begin{hide} {
+ x += mu * dt + sigma * Math.sqrt (dt) * gen.nextDouble();
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates an observation of the process in \texttt{dt} time units,
+assuming that the process has value $x$ at the current time.
+Uses the process parameters specified in the constructor.
+Note that this method does not affect the sample path of the process
+stored internally (if any).
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double[] generatePath() {
+ double x = x0;
+ for (int j = 0; j < d; j++) {
+ x += mudt[j] + sigmasqrdt[j] * gen.nextDouble();
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+
+ public double[] generatePath (double[] uniform01) \begin{hide} {
+ double x = x0;
+ for (int j = 0; j < d; j++) {
+ x += mudt[j] + sigmasqrdt[j] * NormalDist.inverseF01(uniform01[j]);
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as generatePath(), but a vector of uniform random numbers
+must be provided to the method. These uniform random numbers are used
+to generate the path.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }\end{hide}
+
+ public void setParams (double x0, double mu, double sigma) \begin{hide} {
+ this.x0 = x0;
+ this.mu = mu;
+ if (sigma <= 0)
+ throw new IllegalArgumentException ("sigma <= 0");
+ this.sigma = sigma;
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the parameters $X(t_{0}) = \texttt{x0}$, $\mu = \texttt{mu}$ and
+$\sigma = \texttt{sigma}$ of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called too frequently.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} { gen.setStream (stream); }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the random stream of the normal generator to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} { return gen.getStream (); }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the random stream of the normal generator.
+\end{tabb}
+\begin{code}
+
+ public double getMu() \begin{hide} { return mu; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\mu$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma() \begin{hide} { return sigma; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public NormalGen getGen() \begin{hide} { return gen; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the normal random variate generator used.
+The \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+used by that generator can be changed via
+\texttt{getGen().setStream(stream)}, for example.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ mudt = new double[d];
+ sigmasqrdt = new double[d];
+ for (int j = 0; j < d; j++) {
+ double dt = t[j+1] - t[j];
+ mudt[j] = mu * dt;
+ sigmasqrdt[j] = sigma * Math.sqrt (dt);
+ }
+ }
+
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionBridge.java b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionBridge.java
new file mode 100644
index 0000000..1419786
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionBridge.java
@@ -0,0 +1,330 @@
+
+
+/*
+ * Class: BrownianMotionBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Represents a Brownian motion process
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I>) : <I>t</I> >= 0}</SPAN>
+ * sampled using the <SPAN CLASS="textit">bridge sampling</SPAN> technique
+ * (see for example).
+ * This technique generates first the value <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>d</SUB>)</SPAN> at the last observation time,
+ * then the value at time <SPAN CLASS="MATH"><I>t</I><SUB>d/2</SUB></SPAN> (or the nearest integer),
+ * then the values at time <SPAN CLASS="MATH"><I>t</I><SUB>d/4</SUB></SPAN> and at time <SPAN CLASS="MATH"><I>t</I><SUB>3d/4</SUB></SPAN>
+ * (or the nearest integers), and so on.
+ * If the process has already been sampled at times <SPAN CLASS="MATH"><I>t</I><SUB>i</SUB> < <I>t</I><SUB>k</SUB></SPAN> but not
+ * in between, the next sampling point in that interval will be
+ * <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> where
+ * <SPAN CLASS="MATH"><I>j</I> = floor((<I>i</I> + <I>k</I>)/2)</SPAN>.
+ * For example, if the sampling times used are
+ * {
+ * <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB>, <I>t</I><SUB>1</SUB>, <I>t</I><SUB>2</SUB>, <I>t</I><SUB>3</SUB>, <I>t</I><SUB>4</SUB>, <I>t</I><SUB>5</SUB></SPAN>},
+ * then the observations are generated in the following order:
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>5</SUB>)</SPAN>, <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>2</SUB>)</SPAN>, <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>1</SUB>)</SPAN>, <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>3</SUB>)</SPAN>, <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>4</SUB>)</SPAN>.
+ *
+ * <P>
+ * <SPAN CLASS="textit">Warning</SPAN>:
+ * Both the <TT>generatePath</TT> and the <TT>nextObservation</TT> methods from
+ * {@link umontreal.iro.lecuyer.stochprocess.BrownianMotion BrownianMotion} are
+ * modified to use the bridge method.
+ *
+ * In the case of <TT>nextObservation</TT>, the user should understand
+ * that the observations returned are <SPAN CLASS="textit">not</SPAN> ordered chronologically.
+ * However they will be once an entire path is generated and the observations
+ * are read from the internal array (referenced by the <TT>getPath</TT> method)
+ * that contains them.
+ *
+ * <P>
+ * The method <TT>nextObservation(double nextTime)</TT> differs from that of
+ * the class
+ * {@link umontreal.iro.lecuyer.stochprocess.BrownianMotion BrownianMotion}
+ * in that <TT>nextTime</TT> represents
+ * the next observation time <SPAN CLASS="textit">of the Brownian bridge</SPAN>.
+ * However, the <SPAN CLASS="MATH"><I>t</I><SUB>i</SUB></SPAN> supplied must still be non-decreasing with <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ *
+ * <P>
+ * Note also that, if the path is not entirely generated before being read
+ * from this array, there will be ``pollution'' from the previous path generated,
+ * and the observations will not represent a sample path of this process.
+ *
+ */
+public class BrownianMotionBridge extends BrownianMotion {
+ protected int bridgeCounter = -1; // Before 1st observ
+
+ // For precomputations for B Bridge
+ protected double[] wMuDt,
+ wSqrtDt;
+ protected int[] wIndexList,
+ ptIndex;
+
+
+
+ /**
+ * Constructs a new <TT>BrownianMotionBridge</TT> with
+ * parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) = <texttt>x0</texttt></SPAN>.
+ * The normal variates will be
+ * generated by inversion using the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} <TT>stream</TT>.
+ *
+ */
+ public BrownianMotionBridge (double x0, double mu, double sigma,
+ RandomStream stream) {
+ super (x0, mu, sigma, stream);
+ }
+
+
+ /**
+ * Constructs a new <TT>BrownianMotionBridge</TT> with
+ * parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) = <texttt>x0</texttt></SPAN>.
+ * The normal variates will be
+ * generated by the
+ * {@link umontreal.iro.lecuyer.randvar.NormalGen NormalGen} <TT>gen</TT>.
+ *
+ *
+ */
+ public BrownianMotionBridge (double x0, double mu, double sigma,
+ NormalGen gen) {
+ super (x0, mu, sigma, gen);
+ }
+
+
+ public double nextObservation() {
+ double x;
+ if (bridgeCounter == -1) {
+ x = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * gen.nextDouble ();
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter*3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ x = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * gen.nextDouble ();
+
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = x;
+ return x;
+ }
+
+ public double nextObservation (double nextTime) {
+ double x;
+ if (bridgeCounter == -1) {
+ t[d] = nextTime;
+
+ wMuDt[0] = 0.0; // The end point of the Wiener process
+ // w/ Brownian bridge has expectation = 0
+ wSqrtDt[0] = sigma * Math.sqrt(t[d] - t[0]);
+ // = sigma*sqrt(Dt) of end point
+
+ x = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * gen.nextDouble ();
+
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter*3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ t[newIndex] = nextTime;
+
+ double dtRL = t[oldIndexR] - t[oldIndexL];
+ if (dtRL != 0.0) {
+ wMuDt[newIndex] = (t[newIndex]-t[oldIndexL]) / dtRL;
+ } else {
+ wMuDt[newIndex] = 0.0;
+ }
+ wSqrtDt[newIndex] = sigma * Math.sqrt (
+ wMuDt[newIndex] * (t[oldIndexR] - t[newIndex]));
+
+ x = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * gen.nextDouble ();
+
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = x;
+ return x;
+ }
+
+ public double[] generatePath() {
+ // Generation of Brownian bridge process
+ int oldIndexL, oldIndexR, newIndex;
+ path[d] = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * gen.nextDouble ();
+
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * gen.nextDouble ();
+ }
+
+ // resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (double[] uniform01){
+ int oldIndexL, oldIndexR, newIndex;
+ path[d] = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * NormalDist.inverseF01(uniform01[0]);
+
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * NormalDist.inverseF01(uniform01[1 + j/3]);
+ }
+
+ // resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ bridgeCounter = -1;
+ }
+
+ protected void init() {
+ super.init();
+
+ /* For Brownian Bridge */
+
+ // Quantities for Brownian Bridge process
+ wMuDt = new double[d + 1];
+ wSqrtDt = new double[d + 1];
+ wIndexList = new int[3 * (d)];
+ ptIndex = new int[d + 1];
+ double tem = 0;
+
+ int indexCounter = 0;
+ int newIndex, oldLeft, oldRight;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ wMuDt[0] = 0.0; // The end point of the Wiener process
+ // w/ Brownian bridge has expectation = 0
+ if (t[d] < t[0])
+ throw new IllegalStateException(" t[d] < t[0]");
+ wSqrtDt[0] = sigma * Math.sqrt(t[d] - t[0]);
+ // = sigma*sqrt(Dt) of end point
+
+ for (int powOfTwo = 1; powOfTwo <= d / 2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) {
+ ptIndex[2*j] = ptIndex[j];
+ }
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldLeft = 2 * j - 2;
+ oldRight = 2 * j;
+ newIndex = (int) (0.5 * (ptIndex[oldLeft] + ptIndex[oldRight]));
+
+ wMuDt[newIndex] = (t[newIndex] - t[ptIndex[oldLeft]]) /
+ (t[ptIndex[oldRight]] - t[ptIndex[oldLeft]]);
+ tem = (t[newIndex] - t[ptIndex[oldLeft]]) *
+ (t[ptIndex[oldRight]] - t[newIndex])
+ / (t[ptIndex[oldRight]] - t[ptIndex[oldLeft]]);
+
+ // Test for NaN (z != z); 0/0 gives a NaN
+ if (tem < 0 || tem != tem) {
+ System.out.printf ("t[newIndex] - t[ptIndex[oldLeft]] = %g%n", t[newIndex] - t[ptIndex[oldLeft]]);
+ System.out.printf ("t[ptIndex[oldRight]] - t[newIndex] = %g%n", t[ptIndex[oldRight]] - t[newIndex]);
+ System.out.printf ("t[ptIndex[oldRight]] - t[ptIndex[oldLeft]] = %g%n", t[ptIndex[oldRight]] - t[ptIndex[oldLeft]]);
+ System.out.printf ("t[ptIndex[oldRight]] = %g%n", t[ptIndex[oldRight]]);
+ System.out.printf ("t[ptIndex[oldLeft]] = %g%n", t[ptIndex[oldLeft]]);
+ throw new IllegalStateException(" tem < 0 or NaN");
+ }
+ wSqrtDt[newIndex] = sigma * Math.sqrt (tem);
+
+ ptIndex[oldLeft + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldLeft];
+ wIndexList[indexCounter + 1] = newIndex;
+ wIndexList[indexCounter + 2] = ptIndex[oldRight];
+
+ indexCounter += 3;
+ }
+ }
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k - 1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+ wMuDt[ptIndex[k - 1] + 1] = (t[ptIndex[k - 1] + 1] - t[ptIndex[k - 1]]) /
+ (t[ptIndex[k]] - t[ptIndex[k - 1]]);
+ tem = (t[ptIndex[k - 1] + 1] - t[ptIndex[k - 1]]) *
+ (t[ptIndex[k]] - t[ptIndex[k - 1] + 1])
+ / (t[ptIndex[k]] - t[ptIndex[k - 1]]);
+
+ // Test for NaN (z != z); 0/0 gives a NaN
+ if (tem < 0 || tem != tem) {
+ System.out.printf ("t[ptIndex[k-1]+1] - t[ptIndex[k-1]] = %g%n", t[ptIndex[k - 1] + 1] - t[ptIndex[k - 1]]);
+ System.out.printf ("t[ptIndex[k]] - t[ptIndex[k-1]+1] = %g%n", t[ptIndex[k]] - t[ptIndex[k - 1] + 1]);
+ System.out.printf ("t[ptIndex[k]] - t[ptIndex[k-1]] = %g%n", t[ptIndex[k]] - t[ptIndex[k - 1]]);
+ System.out.printf ("t[ptIndex[k]] = %20.16g%n", t[ptIndex[k]]);
+ System.out.printf ("t[ptIndex[k-1]] = %20.16g%n", t[ptIndex[k - 1]]);
+ throw new IllegalStateException(" tem < 0 or NaN");
+ }
+ wSqrtDt[ptIndex[k - 1] + 1] = sigma * Math.sqrt (tem);
+ wIndexList[indexCounter] = ptIndex[k] - 2;
+ wIndexList[indexCounter + 1] = ptIndex[k] - 1;
+ wIndexList[indexCounter + 2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionBridge.tex b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionBridge.tex
new file mode 100644
index 0000000..123827c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionBridge.tex
@@ -0,0 +1,326 @@
+\defmodule {BrownianMotionBridge}
+
+Represents a Brownian motion process $\{X(t) : t \geq 0 \}$
+sampled using the \emph{bridge sampling} technique
+(see for example \cite{fGLA04a}).
+This technique generates first the value $X(t_d)$ at the last observation time,
+then the value at time $t_{d/2}$ (or the nearest integer),
+then the values at time $t_{d/4}$ and at time $t_{3d/4}$
+(or the nearest integers), and so on.
+If the process has already been sampled at times $t_i < t_k$ but not
+in between, the next sampling point in that interval will be
+$t_j$ where $j = \lfloor (i + k)/2 \rfloor$.
+For example, if the sampling times used are
+\{$t_{0}, t_{1}, t_{2}, t_{3}, t_{4}, t_{5}$\},
+then the observations are generated in the following order:
+$X(t_{5})$, $X(t_{2})$, $X(t_{1})$, $X(t_{3})$, $X(t_{4})$.
+
+
+\emph{Warning}:
+Both the \texttt{generatePath} and the \texttt{nextObservation} methods from
+\externalclass{umontreal.iro.lecuyer.stochprocess}{Brown\-ian\-Motion} are
+modified to use the bridge method.
+\pierre{We should probably remove the \texttt{nextObservation} methods from here.}
+In the case of \texttt{nextObservation}, the user should understand
+that the observations returned are \emph{not} ordered chronologically.
+However they will be once an entire path is generated and the observations
+are read from the internal array (referenced by the \texttt{getPath} method)
+that contains them.
+
+The method \texttt{nextObservation(double nextTime)} differs from that of
+the class
+\externalclass{umontreal.iro.lecuyer.stochprocess}{Brown\-ian\-Motion}
+ in that \texttt{nextTime} represents
+the next observation time \emph{of the Brownian bridge}.
+However, the $t_{i}$ supplied must still be non-decreasing with $i$.
+
+Note also that, if the path is not entirely generated before being read
+from this array, there will be ``pollution'' from the previous path generated,
+and the observations will not represent a sample path of this process.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BrownianMotionBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class BrownianMotionBridge extends BrownianMotion \begin{hide} {
+ protected int bridgeCounter = -1; // Before 1st observ
+
+ // For precomputations for B Bridge
+ protected double[] wMuDt,
+ wSqrtDt;
+ protected int[] wIndexList,
+ ptIndex;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BrownianMotionBridge (double x0, double mu, double sigma,
+ RandomStream stream) \begin{hide} {
+ super (x0, mu, sigma, stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotionBridge} with
+parameters $\mu = \texttt{mu}$, $\sigma = \texttt{sigma}$ and initial value
+$X(t_{0}) = \texttt{x0}$.
+The normal variates will be
+generated by inversion using the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotionBridge (double x0, double mu, double sigma,
+ NormalGen gen) \begin{hide} {
+ super (x0, mu, sigma, gen);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotionBridge} with
+parameters $\mu = \texttt{mu}$, $\sigma = \texttt{sigma}$ and initial value
+$X(t_{0}) = \texttt{x0}$.
+The normal variates will be
+generated by the
+\externalclass{umontreal.iro.lecuyer.randvar}{NormalGen} \texttt{gen}.
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ double x;
+ if (bridgeCounter == -1) {
+ x = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * gen.nextDouble ();
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter*3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ x = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * gen.nextDouble ();
+
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = x;
+ return x;
+ }
+
+ public double nextObservation (double nextTime) {
+ double x;
+ if (bridgeCounter == -1) {
+ t[d] = nextTime;
+
+ wMuDt[0] = 0.0; // The end point of the Wiener process
+ // w/ Brownian bridge has expectation = 0
+ wSqrtDt[0] = sigma * Math.sqrt(t[d] - t[0]);
+ // = sigma*sqrt(Dt) of end point
+
+ x = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * gen.nextDouble ();
+
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter*3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ t[newIndex] = nextTime;
+
+ double dtRL = t[oldIndexR] - t[oldIndexL];
+ if (dtRL != 0.0) {
+ wMuDt[newIndex] = (t[newIndex]-t[oldIndexL]) / dtRL;
+ } else {
+ wMuDt[newIndex] = 0.0;
+ }
+ wSqrtDt[newIndex] = sigma * Math.sqrt (
+ wMuDt[newIndex] * (t[oldIndexR] - t[newIndex]));
+
+ x = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * gen.nextDouble ();
+
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = x;
+ return x;
+ }
+
+ public double[] generatePath() {
+ // Generation of Brownian bridge process
+ int oldIndexL, oldIndexR, newIndex;
+ path[d] = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * gen.nextDouble ();
+
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * gen.nextDouble ();
+ }
+
+ // resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (double[] uniform01){
+ int oldIndexL, oldIndexR, newIndex;
+ path[d] = x0 + mu*(t[d]-t[0]) + wSqrtDt[0] * NormalDist.inverseF01(uniform01[0]);
+
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL])
+ * wMuDt[newIndex] + wSqrtDt[newIndex] * NormalDist.inverseF01(uniform01[1 + j/3]);
+ }
+
+ // resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ bridgeCounter = -1;
+ }
+
+ protected void init() {
+ super.init();
+
+ /* For Brownian Bridge */
+
+ // Quantities for Brownian Bridge process
+ wMuDt = new double[d + 1];
+ wSqrtDt = new double[d + 1];
+ wIndexList = new int[3 * (d)];
+ ptIndex = new int[d + 1];
+ double tem = 0;
+
+ int indexCounter = 0;
+ int newIndex, oldLeft, oldRight;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ wMuDt[0] = 0.0; // The end point of the Wiener process
+ // w/ Brownian bridge has expectation = 0
+ if (t[d] < t[0])
+ throw new IllegalStateException(" t[d] < t[0]");
+ wSqrtDt[0] = sigma * Math.sqrt(t[d] - t[0]);
+ // = sigma*sqrt(Dt) of end point
+
+ for (int powOfTwo = 1; powOfTwo <= d / 2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) {
+ ptIndex[2*j] = ptIndex[j];
+ }
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldLeft = 2 * j - 2;
+ oldRight = 2 * j;
+ newIndex = (int) (0.5 * (ptIndex[oldLeft] + ptIndex[oldRight]));
+
+ wMuDt[newIndex] = (t[newIndex] - t[ptIndex[oldLeft]]) /
+ (t[ptIndex[oldRight]] - t[ptIndex[oldLeft]]);
+ tem = (t[newIndex] - t[ptIndex[oldLeft]]) *
+ (t[ptIndex[oldRight]] - t[newIndex])
+ / (t[ptIndex[oldRight]] - t[ptIndex[oldLeft]]);
+
+ // Test for NaN (z != z); 0/0 gives a NaN
+ if (tem < 0 || tem != tem) {
+ System.out.printf ("t[newIndex] - t[ptIndex[oldLeft]] = %g%n", t[newIndex] - t[ptIndex[oldLeft]]);
+ System.out.printf ("t[ptIndex[oldRight]] - t[newIndex] = %g%n", t[ptIndex[oldRight]] - t[newIndex]);
+ System.out.printf ("t[ptIndex[oldRight]] - t[ptIndex[oldLeft]] = %g%n", t[ptIndex[oldRight]] - t[ptIndex[oldLeft]]);
+ System.out.printf ("t[ptIndex[oldRight]] = %g%n", t[ptIndex[oldRight]]);
+ System.out.printf ("t[ptIndex[oldLeft]] = %g%n", t[ptIndex[oldLeft]]);
+ throw new IllegalStateException(" tem < 0 or NaN");
+ }
+ wSqrtDt[newIndex] = sigma * Math.sqrt (tem);
+
+ ptIndex[oldLeft + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldLeft];
+ wIndexList[indexCounter + 1] = newIndex;
+ wIndexList[indexCounter + 2] = ptIndex[oldRight];
+
+ indexCounter += 3;
+ }
+ }
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k - 1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+ wMuDt[ptIndex[k - 1] + 1] = (t[ptIndex[k - 1] + 1] - t[ptIndex[k - 1]]) /
+ (t[ptIndex[k]] - t[ptIndex[k - 1]]);
+ tem = (t[ptIndex[k - 1] + 1] - t[ptIndex[k - 1]]) *
+ (t[ptIndex[k]] - t[ptIndex[k - 1] + 1])
+ / (t[ptIndex[k]] - t[ptIndex[k - 1]]);
+
+ // Test for NaN (z != z); 0/0 gives a NaN
+ if (tem < 0 || tem != tem) {
+ System.out.printf ("t[ptIndex[k-1]+1] - t[ptIndex[k-1]] = %g%n", t[ptIndex[k - 1] + 1] - t[ptIndex[k - 1]]);
+ System.out.printf ("t[ptIndex[k]] - t[ptIndex[k-1]+1] = %g%n", t[ptIndex[k]] - t[ptIndex[k - 1] + 1]);
+ System.out.printf ("t[ptIndex[k]] - t[ptIndex[k-1]] = %g%n", t[ptIndex[k]] - t[ptIndex[k - 1]]);
+ System.out.printf ("t[ptIndex[k]] = %20.16g%n", t[ptIndex[k]]);
+ System.out.printf ("t[ptIndex[k-1]] = %20.16g%n", t[ptIndex[k - 1]]);
+ throw new IllegalStateException(" tem < 0 or NaN");
+ }
+ wSqrtDt[ptIndex[k - 1] + 1] = sigma * Math.sqrt (tem);
+ wIndexList[indexCounter] = ptIndex[k] - 2;
+ wIndexList[indexCounter + 1] = ptIndex[k] - 1;
+ wIndexList[indexCounter + 2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCA.java b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCA.java
new file mode 100644
index 0000000..9b2564f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCA.java
@@ -0,0 +1,171 @@
+
+
+/*
+ * Class: BrownianMotionPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.*;
+
+
+
+/**
+ * A Brownian motion process
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I>) : <I>t</I> >= 0}</SPAN> sampled using the
+ * <SPAN CLASS="textit">principal component</SPAN> decomposition (PCA).
+ *
+ */
+public class BrownianMotionPCA extends BrownianMotion {
+
+ protected double[][] sigmaCov; // Matrice de covariance du vecteur des observ.
+ // sigmaCov [i][j] = Cov[X(t_{i+1}),X(t_{j+1})].
+ protected double[][] A; // sigmaCov = AA' (PCA decomposition).
+ protected double[] z; // vector of standard normals.
+ protected double[] sortedEigenvalues;
+ protected boolean isDecompPCA;
+
+
+
+
+ /**
+ * Constructs a new <TT>BrownianMotionBridge</TT> with
+ * parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) = <texttt>x0</texttt></SPAN>.
+ * The normal variates will be generated by inversion using <TT>stream</TT>.
+ *
+ */
+ public BrownianMotionPCA (double x0, double mu, double sigma,
+ RandomStream stream) {
+ super (x0, mu, sigma, stream);
+ isDecompPCA = false;
+ }
+
+
+ /**
+ * Constructs a new <TT>BrownianMotionBridge</TT> with
+ * parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) = <texttt>x0</texttt></SPAN>.
+ * The normal variates will be generated by <TT>gen</TT>.
+ *
+ */
+ public BrownianMotionPCA (double x0, double mu, double sigma,
+ NormalGen gen) {
+ super (x0, mu, sigma, gen);
+ isDecompPCA = false;
+ }
+
+
+ public double nextObservation() {
+ throw new UnsupportedOperationException ("nextObservation() is not defined in BrownianMotionPCA");
+ }
+
+ public void setParams (double x0, double mu, double sigma) {
+ super.setParams(x0, mu, sigma);
+ isDecompPCA = true; // the setParams method defined in BrownianMotion calls init() which does
+ // the PCA decompostion
+ }
+
+ public double[] generatePath() {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = gen.nextDouble ();
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (double[] uniform01) {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = NormalDist.inverseF01(uniform01[j]);
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[][] decompPCA (double[][] sigma){
+ // L'objet SingularValueDecomposition permet de recuperer la matrice
+ // des valeurs propres en ordre decroissant et celle des vecteurs propres de
+ // sigma (pour une matrice symetrique et definie-positive seulement).
+ SingularValueDecomposition sv =
+ new SingularValueDecomposition (new DenseDoubleMatrix2D (sigma));
+ DoubleMatrix2D D = sv.getS (); // diagonal
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); i++){
+ sortedEigenvalues[i] = D.getQuick (i, i);
+ D.setQuick (i, i, Math.sqrt (D.getQuick (i, i)));
+ }
+ DoubleMatrix2D P = sv.getV(); // right factor matrix
+ return P.zMult (D, null).toArray ();
+ }
+
+ /**
+ * Returns the sorted eigenvalues obtained in the PCA decomposition.
+ *
+ */
+ public double[] getSortedEigenvalues() {
+ return sortedEigenvalues;
+ }
+
+
+
+ protected void init() {
+ super.init();
+ z = new double[d];
+ sortedEigenvalues = new double[d];
+
+ // Initialize sigmaCov, based on the observation times.
+ sigmaCov = new double[d][d];
+ for (int i = 0; i < d; i++) {
+ for (int j = i; j < d; j++) {
+ sigmaCov[i][j] = sigma * sigma * t[i+1];
+ sigmaCov[j][i] = sigmaCov[i][j];
+ }
+ }
+ A = decompPCA (sigmaCov);
+ isDecompPCA = true;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCA.tex b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCA.tex
new file mode 100644
index 0000000..25c961b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCA.tex
@@ -0,0 +1,174 @@
+\defmodule {BrownianMotionPCA}
+
+A Brownian motion process $\{X(t) : t \geq 0 \}$ sampled using the
+\emph{principal component} decomposition (PCA) \cite{fGLA04a,fIMA06a,fLEC04a}.
+
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BrownianMotionPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import cern.colt.matrix.DoubleMatrix2D;
+import cern.colt.matrix.impl.DenseDoubleMatrix2D;
+import cern.colt.matrix.linalg.*;
+
+\end{hide}
+
+public class BrownianMotionPCA extends BrownianMotion \begin{hide} {
+
+ protected double[][] sigmaCov; // Matrice de covariance du vecteur des observ.
+ // sigmaCov [i][j] = Cov[X(t_{i+1}),X(t_{j+1})].
+ protected double[][] A; // sigmaCov = AA' (PCA decomposition).
+ protected double[] z; // vector of standard normals.
+ protected double[] sortedEigenvalues;
+ protected boolean isDecompPCA;
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BrownianMotionPCA (double x0, double mu, double sigma,
+ RandomStream stream) \begin{hide} {
+ super (x0, mu, sigma, stream);
+ isDecompPCA = false;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotionBridge} with
+parameters $\mu = \texttt{mu}$, $\sigma = \texttt{sigma}$ and initial value
+$X(t_{0}) = \texttt{x0}$.
+The normal variates will be generated by inversion using \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotionPCA (double x0, double mu, double sigma,
+ NormalGen gen) \begin{hide} {
+ super (x0, mu, sigma, gen);
+ isDecompPCA = false;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotionBridge} with
+parameters $\mu = \texttt{mu}$, $\sigma = \texttt{sigma}$ and initial value
+$X(t_{0}) = \texttt{x0}$.
+The normal variates will be generated by \texttt{gen}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ throw new UnsupportedOperationException ("nextObservation() is not defined in BrownianMotionPCA");
+ }
+
+ public void setParams (double x0, double mu, double sigma) {
+ super.setParams(x0, mu, sigma);
+ isDecompPCA = true; // the setParams method defined in BrownianMotion calls init() which does
+ // the PCA decompostion
+ }
+
+ public double[] generatePath() {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = gen.nextDouble ();
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (double[] uniform01) {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = NormalDist.inverseF01(uniform01[j]);
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[][] decompPCA (double[][] sigma){
+ // L'objet SingularValueDecomposition permet de recuperer la matrice
+ // des valeurs propres en ordre decroissant et celle des vecteurs propres de
+ // sigma (pour une matrice symetrique et definie-positive seulement).
+ SingularValueDecomposition sv =
+ new SingularValueDecomposition (new DenseDoubleMatrix2D (sigma));
+ DoubleMatrix2D D = sv.getS (); // diagonal
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); i++){
+ sortedEigenvalues[i] = D.getQuick (i, i);
+ D.setQuick (i, i, Math.sqrt (D.getQuick (i, i)));
+ }
+ DoubleMatrix2D P = sv.getV(); // right factor matrix
+ return P.zMult (D, null).toArray ();
+ }\end{hide}
+
+ public double[] getSortedEigenvalues() \begin{hide} {
+ return sortedEigenvalues;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the sorted eigenvalues obtained in the PCA decomposition.
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ protected void init() {
+ super.init();
+ z = new double[d];
+ sortedEigenvalues = new double[d];
+
+ // Initialize sigmaCov, based on the observation times.
+ sigmaCov = new double[d][d];
+ for (int i = 0; i < d; i++) {
+ for (int j = i; j < d; j++) {
+ sigmaCov[i][j] = sigma * sigma * t[i+1];
+ sigmaCov[j][i] = sigmaCov[i][j];
+ }
+ }
+ A = decompPCA (sigmaCov);
+ isDecompPCA = true;
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCAEqualSteps.java b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCAEqualSteps.java
new file mode 100644
index 0000000..05b129b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCAEqualSteps.java
@@ -0,0 +1,162 @@
+
+
+/*
+ * Class: BrownianMotionPCAEqualSteps
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Same as BrownianMotionPCA, but uses a trick to
+ * speed up the calculation when the time steps
+ * are equidistant.
+ *
+ */
+public class BrownianMotionPCAEqualSteps extends BrownianMotion {
+
+ double dt;
+ protected double[][] A; // sigmaCov = AA' (PCA decomposition).
+ protected double[] z; // vector of standard normals.
+ protected boolean isDecompPCA;
+ protected double[] sortedEigenvalues;
+
+
+
+
+
+
+ /**
+ * Constructs a new <TT>BrownianMotionPCAEqualSteps</TT>.
+ *
+ */
+ public BrownianMotionPCAEqualSteps (double x0, double mu, double sigma,
+ RandomStream stream) {
+ super (x0, mu, sigma, stream);
+ isDecompPCA = false;
+ }
+
+
+ /**
+ * Constructs a new <TT>BrownianMotionPCAEqualSteps</TT>.
+ *
+ */
+ public BrownianMotionPCAEqualSteps (double x0, double mu, double sigma,
+ NormalGen gen) {
+ super (x0, mu, sigma, gen);
+ isDecompPCA = false;
+ }
+
+
+
+ public double nextObservation() {
+ throw new UnsupportedOperationException
+ ("nextObservation() not defined for PCA.");
+ }
+
+
+
+ public double[] generatePath() {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = gen.nextDouble ();
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath(double[] QMCpointsBM) {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = NormalDist.inverseF01(QMCpointsBM[j]);
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public void setObservationTimes(double[] t, int d){
+ super.setObservationTimes(t,d);
+ this.dt = t[1] - t[0];
+ for(int i = 1; i < d; i++)
+ if( Math.abs((t[i+1] - t[i])/dt - 1.0) > 1e-7 )
+ throw new IllegalArgumentException("Not equidistant times");
+ }
+
+
+ public void setObservationTimes(double dt, int d){
+ this.dt = dt;
+ super.setObservationTimes(dt,d);
+ }
+
+
+ protected void init() {
+ super.init();
+ if(observationTimesSet){
+ final double twoOverSqrt2dP1 = 2.0/Math.sqrt(2.0*d+1.0);
+ final double piOver2dP1 = Math.PI/(2.0*d+1.0);
+
+ z = new double[d];
+ // A contains the eigenvectors (as columns), times sqrt(eigenvalues).
+ A = new double[d][d];
+ sortedEigenvalues = new double[d];
+ for(int ic = 1; ic <= d; ic++){
+ final double tempSin = Math.sin( (2*ic-1)*piOver2dP1/2.0 );
+ sortedEigenvalues[ic-1] = dt/4.0/tempSin/tempSin * sigma*sigma;
+ for(int ir = 1; ir <= d; ir++){
+ A[ir-1][ic-1] = twoOverSqrt2dP1 * Math.sin( (2*ic-1)*piOver2dP1*ir );
+ A[ir-1][ic-1] *= Math.sqrt( sortedEigenvalues[ic-1] );
+ }
+ }
+ double[][] AA = new double[d][d];
+ for(int ic = 0; ic < d; ic++){
+ for(int ir = 0; ir < d; ir++){
+ double sum = 0.0;
+ for(int k = 0; k < d; k++) sum += A[ir][k]*A[ic][k];
+ AA[ir][ic] = sum;
+ }
+ }
+ isDecompPCA = true;
+ }
+ }
+ public double[] getSortedEigenvalues(){
+ return sortedEigenvalues;
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCAEqualSteps.tex b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCAEqualSteps.tex
new file mode 100644
index 0000000..2e679d6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/BrownianMotionPCAEqualSteps.tex
@@ -0,0 +1,172 @@
+\defmodule {BrownianMotionPCAEqualSteps}
+
+Same as BrownianMotionPCA, but uses a trick to
+speed up the calculation when the time steps
+are equidistant.
+
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: BrownianMotionPCAEqualSteps
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class BrownianMotionPCAEqualSteps extends BrownianMotion \begin{hide} {
+
+ double dt;
+ protected double[][] A; // sigmaCov = AA' (PCA decomposition).
+ protected double[] z; // vector of standard normals.
+ protected boolean isDecompPCA;
+ protected double[] sortedEigenvalues;
+
+
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public BrownianMotionPCAEqualSteps (double x0, double mu, double sigma,
+ RandomStream stream) \begin{hide} {
+ super (x0, mu, sigma, stream);
+ isDecompPCA = false;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotionPCAEqualSteps}.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotionPCAEqualSteps (double x0, double mu, double sigma,
+ NormalGen gen) \begin{hide} {
+ super (x0, mu, sigma, gen);
+ isDecompPCA = false;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{BrownianMotionPCAEqualSteps}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+
+ public double nextObservation() {
+ throw new UnsupportedOperationException
+ ("nextObservation() not defined for PCA.");
+ }
+
+
+
+ public double[] generatePath() {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = gen.nextDouble ();
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath(double[] QMCpointsBM) {
+ if(!isDecompPCA) {init();} // if the decomposition is not done, do it...
+ for (int j = 0; j < d; j++)
+ z[j] = NormalDist.inverseF01(QMCpointsBM[j]);
+ for (int j = 0; j < d; j++) {
+ double sum = 0.0;
+ for (int k = 0; k < d; k++)
+ sum += A[j][k] * z[k];
+ path[j+1] = x0 + mu * t[j+1] + sum;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public void setObservationTimes(double[] t, int d){
+ super.setObservationTimes(t,d);
+ this.dt = t[1] - t[0];
+ for(int i = 1; i < d; i++)
+ if( Math.abs((t[i+1] - t[i])/dt - 1.0) > 1e-7 )
+ throw new IllegalArgumentException("Not equidistant times");
+ }
+
+
+ public void setObservationTimes(double dt, int d){
+ this.dt = dt;
+ super.setObservationTimes(dt,d);
+ }
+
+
+ protected void init() {
+ super.init();
+ if(observationTimesSet){
+ final double twoOverSqrt2dP1 = 2.0/Math.sqrt(2.0*d+1.0);
+ final double piOver2dP1 = Math.PI/(2.0*d+1.0);
+
+ z = new double[d];
+ // A contains the eigenvectors (as columns), times sqrt(eigenvalues).
+ A = new double[d][d];
+ sortedEigenvalues = new double[d];
+ for(int ic = 1; ic <= d; ic++){
+ final double tempSin = Math.sin( (2*ic-1)*piOver2dP1/2.0 );
+ sortedEigenvalues[ic-1] = dt/4.0/tempSin/tempSin * sigma*sigma;
+ for(int ir = 1; ir <= d; ir++){
+ A[ir-1][ic-1] = twoOverSqrt2dP1 * Math.sin( (2*ic-1)*piOver2dP1*ir );
+ A[ir-1][ic-1] *= Math.sqrt( sortedEigenvalues[ic-1] );
+ }
+ }
+ double[][] AA = new double[d][d];
+ for(int ic = 0; ic < d; ic++){
+ for(int ir = 0; ir < d; ir++){
+ double sum = 0.0;
+ for(int k = 0; k < d; k++) sum += A[ir][k]*A[ic][k];
+ AA[ir][ic] = sum;
+ }
+ }
+ isDecompPCA = true;
+ }
+ }
+ public double[] getSortedEigenvalues(){
+ return sortedEigenvalues;
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/CIRProcess.java b/source/umontreal/iro/lecuyer/stochprocess/CIRProcess.java
new file mode 100644
index 0000000..ca4fd17
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/CIRProcess.java
@@ -0,0 +1,303 @@
+
+
+/*
+ * Class: CIRProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * This class represents a <SPAN CLASS="textit">CIR</SPAN> (Cox, Ingersoll, Ross) process
+ *
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I>) : <I>t</I> >= 0}</SPAN>, sampled at times
+ * <SPAN CLASS="MATH">0 = <I>t</I><SUB>0</SUB> < <I>t</I><SUB>1</SUB> < <SUP> ... </SUP> < <I>t</I><SUB>d</SUB></SPAN>.
+ * This process obeys the stochastic differential equation
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:cir"></A>
+ * <I>dX</I>(<I>t</I>) = <I>α</I>(<I>b</I> - <I>X</I>(<I>t</I>))<I>dt</I> + <I>σ</I>(X(t))<SUP>1/2</SUP> <I>dB</I>(<I>t</I>)
+ * </DIV><P></P>
+ * with initial condition <SPAN CLASS="MATH"><I>X</I>(0) = <I>x</I><SUB>0</SUB></SPAN>,
+ * where <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are positive constants,
+ * and
+ * <SPAN CLASS="MATH">{<I>B</I>(<I>t</I>), <I>t</I> >= 0}</SPAN> is a standard Brownian motion
+ * (with drift 0 and volatility 1).
+ * This process is <SPAN CLASS="textit">mean-reverting</SPAN> in the sense that it always tends to
+ * drift toward its general mean <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * The process is generated using the sequential
+ * technique
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:cir-seq"></A>
+ * <I>X</I>(<I>t</I><SUB>j</SUB>) = {<I>σ</I><SUP>2</SUP>(1-<I>e</I><SUP>-<I>α</I>(t<SUB>j</SUB>-t<SUB>j-1</SUB>)</SUP>)/4<I>α</I>}<I>χ</I><SUP>′2</SUP><SUB><I>ν</I></SUB>({4<I>αe</I><SUP>-<I>α</I>(t<SUB>j</SUB>-t<SUB>j-1</SUB>)</SUP><I>X</I>(<I>t</I><SUB>j-1</SUB>)}/{<I>σ</I><SUP>2</SUP>(1-<I>e</I><SUP>-<I>α</I>(t<SUB>j</SUB>-t<SUB>j-1</SUB>)</SUP>)}),
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>ν</I> = 4<I>bα</I>/<I>σ</I><SUP>2</SUP></SPAN>, and
+ * <SPAN CLASS="MATH"><I>χ</I><SUP>′ 2</SUP><SUB><I>ν</I></SUB>(<I>λ</I>)</SPAN> is a noncentral
+ * chi-square random variable with <SPAN CLASS="MATH"><I>ν</I></SPAN> degrees of freedom and noncentrality
+ * parameter <SPAN CLASS="MATH"><I>λ</I></SPAN>.
+ *
+ */
+public class CIRProcess extends StochasticProcess {
+ private RandomStream stream;
+ protected ChiSquareNoncentralGen gen;
+ protected double alpha,
+ beta,
+ sigma,
+ nu; // number of degrees of freedom
+ // Precomputed values
+ protected double[] parc,
+ parlam;
+
+
+
+ /**
+ * Constructs a new <TT>CIRProcess</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>α</I> = <texttt>alpha</texttt></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) = <texttt>x0</texttt></SPAN>. The noncentral chi-square variates
+ *
+ * <SPAN CLASS="MATH"><I>χ</I><SUP>′2</SUP><SUB><I>ν</I></SUB>(<I>λ</I>)</SPAN> will be
+ * generated by inversion using the stream <TT>stream</TT>.
+ *
+ */
+ public CIRProcess (double x0, double alpha, double b, double sigma,
+ RandomStream stream) {
+ this (x0, alpha, b, sigma, new ChiSquareNoncentralGen (stream, null));
+ }
+
+
+ /**
+ * The noncentral chi-square variate generator <TT>gen</TT>
+ * is specified directly instead of specifying the stream.
+ * <TT>gen</TT> can use a method other than inversion.
+ *
+ */
+ public CIRProcess (double x0, double alpha, double b, double sigma,
+ ChiSquareNoncentralGen gen) {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ nu = 4.0*b*alpha/(sigma*sigma);
+ this.gen = gen;
+ stream = gen.getStream();
+ }
+
+
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double lambda = xOld * parlam[observationIndex];
+ double x;
+ if (gen.getClass() == ChiSquareNoncentralPoisGen.class)
+ x = parc[observationIndex] *
+ ChiSquareNoncentralPoisGen.nextDouble(stream, nu, lambda);
+ else if (gen.getClass() == ChiSquareNoncentralGamGen.class)
+ x = parc[observationIndex] *
+ ChiSquareNoncentralGamGen.nextDouble(stream, nu, lambda);
+ else
+ x = parc[observationIndex] *
+ ChiSquareNoncentralGen.nextDouble(stream, nu, lambda);
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }
+
+
+ /**
+ * Generates and returns the next observation at time
+ * <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB> = <texttt>nextTime</texttt></SPAN>, using the previous observation time <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> defined earlier
+ * (either by this method or by <TT>setObservationTimes</TT>),
+ * as well as the value of the previous observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will reset the observations time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN>
+ * for this process to <TT>nextTime</TT>. The user must make sure that
+ * the <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN> supplied is
+ * <SPAN CLASS="MATH"> >= <I>t</I><SUB>j</SUB></SPAN>.
+ *
+ */
+ public double nextObservation (double nextTime) {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double x = nextObservation (xOld, dt);
+ path[observationIndex] = x;
+ return x;
+ }
+
+
+ /**
+ * Generates an observation of the process in <TT>dt</TT> time units,
+ * assuming that the process has value <SPAN CLASS="MATH"><I>x</I></SPAN> at the current time.
+ * Uses the process parameters specified in the constructor.
+ * Note that this method does not affect the sample path of the process
+ * stored internally (if any).
+ *
+ *
+ */
+ public double nextObservation (double x, double dt) {
+ double c = -Math.expm1(-alpha * dt) * sigma * sigma / (4.0*alpha);
+ double lambda = x * Math.exp(-alpha * dt) / c;
+ if (gen.getClass() == ChiSquareNoncentralPoisGen.class)
+ x = c * ChiSquareNoncentralPoisGen.nextDouble(stream, nu, lambda);
+ else if (gen.getClass() == ChiSquareNoncentralGamGen.class)
+ x = c * ChiSquareNoncentralGamGen.nextDouble(stream, nu, lambda);
+ else
+ x = c * ChiSquareNoncentralGen.nextDouble(stream, nu, lambda);
+ return x;
+ }
+
+
+ public double[] generatePath() {
+ double xOld = x0;
+ double x, lambda;
+ int j;
+
+ if (gen.getClass() == ChiSquareNoncentralPoisGen.class) {
+ for (j = 0; j < d; j++) {
+ lambda = xOld * parlam[j];
+ x = parc[j] * ChiSquareNoncentralPoisGen.nextDouble(stream, nu, lambda);
+ path[j + 1] = x;
+ xOld = x;
+ }
+
+ } else if (gen.getClass() == ChiSquareNoncentralGamGen.class) {
+ for (j = 0; j < d; j++) {
+ lambda = xOld * parlam[j];
+ x = parc[j] * ChiSquareNoncentralGamGen.nextDouble(stream, nu, lambda);
+ path[j + 1] = x;
+ xOld = x;
+ }
+
+ } else {
+ for (j = 0; j < d; j++) {
+ lambda = xOld * parlam[j];
+ x = parc[j] * ChiSquareNoncentralGen.nextDouble(stream, nu, lambda);
+ path[j + 1] = x;
+ xOld = x;
+ }
+ }
+
+ observationIndex = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }
+
+
+ /**
+ * Resets the parameters
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) = <texttt>x0</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>α</I> = <texttt>alpha</texttt></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>b</I> = <texttt>b</texttt></SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called too frequently.
+ *
+ */
+ public void setParams (double x0, double alpha, double b, double sigma) {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ nu = 4.0*b*alpha/(sigma*sigma);
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }
+
+
+ /**
+ * Resets the random stream of the noncentral chi-square generator to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) { gen.setStream (stream); }
+
+
+ /**
+ * Returns the random stream of the noncentral chi-square generator.
+ *
+ */
+ public RandomStream getStream() { return gen.getStream (); }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ */
+ public double getAlpha() { return alpha; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB() { return beta; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() { return sigma; }
+
+
+ /**
+ * Returns the noncentral chi-square random variate generator used.
+ * The <TT>RandomStream</TT> used for that generator can be changed via
+ * <TT>getGen().setStream(stream)</TT>, for example.
+ *
+ */
+ public ChiSquareNoncentralGen getGen() { return gen; }
+
+
+ protected void initArrays(int d) {
+ double dt, c;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ c = -Math.expm1(-alpha * dt)*sigma*sigma/(4.0*alpha);
+ parc[j] = c;
+ parlam[j] = Math.exp(-alpha * dt) / c;
+ }
+ }
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ parc = new double[d];
+ parlam = new double[d];
+ initArrays(d);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/CIRProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/CIRProcess.tex
new file mode 100644
index 0000000..494becd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/CIRProcess.tex
@@ -0,0 +1,313 @@
+\defmodule {CIRProcess}
+
+This class represents a \emph{CIR} (Cox, Ingersoll, Ross) process \cite{fCOX85a}
+ $\{X(t) : t \geq 0 \}$, sampled at times $0 = t_0 < t_1 < \cdots < t_d$.
+This process obeys the stochastic differential equation
+\begin{equation}
+ dX(t) = \alpha(b - X(t)) dt + \sigma\sqrt{X(t)}\, dB(t)
+ \label{eq:cir}
+\end{equation}
+with initial condition $X(0)= x_0$,
+where $\alpha$, $b$ and $\sigma$ are positive constants,
+and $\{B(t),\, t\ge 0\}$ is a standard Brownian motion
+(with drift 0 and volatility 1).
+This process is \emph{mean-reverting} in the sense that it always tends to
+drift toward its general mean $b$.
+The process is generated using the sequential
+technique \cite[p. 122]{fGLA04a}
+\begin{latexonly}
+\begin{equation}
+ X(t_j) = \frac{\sigma^2\left(1 - e^{-\alpha(t_j - t_{j-1})}\right)}{4\alpha}
+ \chi^{\prime\,2}_\nu\left(\frac{4\alpha e^{-\alpha(t_j - t_{j-1}) } X(t_{j-1})}
+ {\sigma^2\left(1 - e^{-\alpha(t_j - t_{j-1})}\right)}\right),
+ \label{eq:cir-seq}
+\end{equation}
+\end{latexonly}
+\begin{htmlonly}
+\begin{equation}
+ X(t_j) = \left\{}{\sigma^2\left(1 - e^{-\alpha(t_j - t_{j-1})}\right)/ {4\alpha}\right\}
+ \chi^{\prime2}_\nu\left(\{4\alpha e^{-\alpha(t_j - t_{j-1}) } X(t_{j-1})\}/
+ \{}{\sigma^2\left(1 - e^{-\alpha(t_j - t_{j-1})}\right)}\}\right),
+ \label{eq:cir-seq}
+\end{equation}
+\end{htmlonly}
+where $\nu = 4b\alpha/\sigma^2$, and $\chi^{\prime\,2}_\nu(\lambda)$ is a noncentral
+chi-square random variable with $\nu$ degrees of freedom and noncentrality
+parameter $\lambda$.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: CIRProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class CIRProcess extends StochasticProcess \begin{hide} {
+ private RandomStream stream;
+ protected ChiSquareNoncentralGen gen;
+ protected double alpha,
+ beta,
+ sigma,
+ nu; // number of degrees of freedom
+ // Precomputed values
+ protected double[] parc,
+ parlam;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public CIRProcess (double x0, double alpha, double b, double sigma,
+ RandomStream stream) \begin{hide} {
+ this (x0, alpha, b, sigma, new ChiSquareNoncentralGen (stream, null));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{CIRProcess} with parameters
+$\alpha = \texttt{alpha}$, $b$, $\sigma = \texttt{sigma}$ and initial value
+$X(t_{0}) = \texttt{x0}$. The noncentral chi-square variates
+ $\chi^{\prime2}_\nu(\lambda)$ will be
+generated by inversion using the stream \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public CIRProcess (double x0, double alpha, double b, double sigma,
+ ChiSquareNoncentralGen gen) \begin{hide} {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ nu = 4.0*b*alpha/(sigma*sigma);
+ this.gen = gen;
+ stream = gen.getStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} The noncentral chi-square variate generator \texttt{gen}
+ is specified directly instead of specifying the stream.
+ \texttt{gen} can use a method other than inversion.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double lambda = xOld * parlam[observationIndex];
+ double x;
+ if (gen.getClass() == ChiSquareNoncentralPoisGen.class)
+ x = parc[observationIndex] *
+ ChiSquareNoncentralPoisGen.nextDouble(stream, nu, lambda);
+ else if (gen.getClass() == ChiSquareNoncentralGamGen.class)
+ x = parc[observationIndex] *
+ ChiSquareNoncentralGamGen.nextDouble(stream, nu, lambda);
+ else
+ x = parc[observationIndex] *
+ ChiSquareNoncentralGen.nextDouble(stream, nu, lambda);
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }
+
+\end{hide}
+ public double nextObservation (double nextTime) \begin{hide} {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double x = nextObservation (xOld, dt);
+ path[observationIndex] = x;
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates and returns the next observation at time $t_{j+1} =
+ \texttt{nextTime}$, using the previous observation time $t_{j}$ defined earlier
+(either by this method or by \texttt{setObservation\-Times}),
+as well as the value of the previous observation $X(t_j)$.
+\emph{Warning}: This method will reset the observations time $t_{j+1}$
+for this process to \texttt{nextTime}. The user must make sure that
+the $t_{j+1}$ supplied is $\geq t_{j}$.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation (double x, double dt) \begin{hide} {
+ double c = -Math.expm1(-alpha * dt) * sigma * sigma / (4.0*alpha);
+ double lambda = x * Math.exp(-alpha * dt) / c;
+ if (gen.getClass() == ChiSquareNoncentralPoisGen.class)
+ x = c * ChiSquareNoncentralPoisGen.nextDouble(stream, nu, lambda);
+ else if (gen.getClass() == ChiSquareNoncentralGamGen.class)
+ x = c * ChiSquareNoncentralGamGen.nextDouble(stream, nu, lambda);
+ else
+ x = c * ChiSquareNoncentralGen.nextDouble(stream, nu, lambda);
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates an observation of the process in \texttt{dt} time units,
+assuming that the process has value $x$ at the current time.
+Uses the process parameters specified in the constructor.
+Note that this method does not affect the sample path of the process
+stored internally (if any).
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public double[] generatePath() {
+ double xOld = x0;
+ double x, lambda;
+ int j;
+
+ if (gen.getClass() == ChiSquareNoncentralPoisGen.class) {
+ for (j = 0; j < d; j++) {
+ lambda = xOld * parlam[j];
+ x = parc[j] * ChiSquareNoncentralPoisGen.nextDouble(stream, nu, lambda);
+ path[j + 1] = x;
+ xOld = x;
+ }
+
+ } else if (gen.getClass() == ChiSquareNoncentralGamGen.class) {
+ for (j = 0; j < d; j++) {
+ lambda = xOld * parlam[j];
+ x = parc[j] * ChiSquareNoncentralGamGen.nextDouble(stream, nu, lambda);
+ path[j + 1] = x;
+ xOld = x;
+ }
+
+ } else {
+ for (j = 0; j < d; j++) {
+ lambda = xOld * parlam[j];
+ x = parc[j] * ChiSquareNoncentralGen.nextDouble(stream, nu, lambda);
+ path[j + 1] = x;
+ xOld = x;
+ }
+ }
+
+ observationIndex = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }
+\end{code}
+\begin{tabb} Generates a sample path of the process at all observation times,
+ which are provided in array \texttt{t}.
+ Note that \texttt{t[0]} should be the observation time of \texttt{x0},
+ the initial value of the process, and \texttt{t[]} should have at least $d+1$
+ elements (see the \texttt{setObservationTimes} method).
+\end{tabb}\end{hide}
+\begin{code}
+
+ public void setParams (double x0, double alpha, double b, double sigma) \begin{hide} {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ nu = 4.0*b*alpha/(sigma*sigma);
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the parameters $X(t_{0}) = \texttt{x0}$, $\alpha = \texttt{alpha}$,
+ $b = \texttt{b}$ and $\sigma = \texttt{sigma}$ of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called too frequently.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} { gen.setStream (stream); }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the random stream of the noncentral chi-square generator to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} { return gen.getStream (); }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the random stream of the noncentral chi-square generator.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha() \begin{hide} { return alpha; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\alpha$.
+\end{tabb}
+\begin{code}
+
+ public double getB() \begin{hide} { return beta; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $b$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma() \begin{hide} { return sigma; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public ChiSquareNoncentralGen getGen() \begin{hide} { return gen; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the noncentral chi-square random variate generator used.
+The \texttt{RandomStream} used for that generator can be changed via
+\texttt{getGen().setStream(stream)}, for example.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ protected void initArrays(int d) {
+ double dt, c;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ c = -Math.expm1(-alpha * dt)*sigma*sigma/(4.0*alpha);
+ parc[j] = c;
+ parlam[j] = Math.exp(-alpha * dt) / c;
+ }
+ }
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ parc = new double[d];
+ parlam = new double[d];
+ initArrays(d);
+ }\end{hide}
+\end{code}
+\begin{code}\begin{hide}
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/CIRProcessEuler.java b/source/umontreal/iro/lecuyer/stochprocess/CIRProcessEuler.java
new file mode 100644
index 0000000..10ac16f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/CIRProcessEuler.java
@@ -0,0 +1,251 @@
+
+
+/*
+ * Class: CIRProcessEuler
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * .
+ *
+ * This class represents a <SPAN CLASS="textit">CIR</SPAN> process
+ * as in {@link CIRProcess}, but
+ * the process is generated using the simple Euler scheme
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:cir-seq-euler"></A>
+ * <I>X</I>(<I>t</I><SUB>j</SUB>) - <I>X</I>(<I>t</I><SUB>j-1</SUB>) = <I>α</I>(<I>b</I> - <I>X</I>(<I>t</I><SUB>j-1</SUB>))(<I>t</I><SUB>j</SUB> - <I>t</I><SUB>j-1</SUB>) + <I>σ</I>((t_j - t_j-1)X(t_j-1))<SUP>1/2</SUP> <I>Z</I><SUB>j</SUB>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB>∼<I>N</I>(0, 1)</SPAN>. This is a good approximation only for small
+ * time intervals
+ * <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB> - <I>t</I><SUB>j-1</SUB></SPAN>.
+ *
+ */
+public class CIRProcessEuler extends StochasticProcess {
+ protected NormalGen gen;
+ protected double alpha,
+ beta,
+ sigma;
+ // Precomputed values
+ protected double[] alphadt,
+ sigmasqrdt;
+
+
+
+ /**
+ * Constructs a new <TT>CIRProcessEuler</TT> with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>x0</TT>. The normal variates <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB></SPAN> will be
+ * generated by inversion using the stream <TT>stream</TT>.
+ *
+ */
+ public CIRProcessEuler (double x0, double alpha, double b, double sigma,
+ RandomStream stream) {
+ this (x0, alpha, b, sigma, new NormalGen (stream, new NormalDist()));
+ }
+
+
+ /**
+ * The normal variate generator <TT>gen</TT> is specified directly
+ * instead of specifying the stream.
+ * <TT>gen</TT> can use another method than inversion.
+ *
+ */
+ public CIRProcessEuler (double x0, double alpha, double b, double sigma,
+ NormalGen gen) {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ this.gen = gen;
+ }
+
+
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double x;
+ x = xOld + (beta - xOld) * alphadt[observationIndex]
+ + sigmasqrdt[observationIndex] * Math.sqrt(xOld) * gen.nextDouble();
+ observationIndex++;
+ if (x >= 0.0)
+ path[observationIndex] = x;
+ else
+ path[observationIndex] = 0.;
+ return x;
+ }
+
+
+ /**
+ * Generates and returns the next observation at time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB> =</SPAN>
+ * <TT>nextTime</TT>, using the previous observation time <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> defined earlier
+ * (either by this method or by <TT>setObservationTimes</TT>),
+ * as well as the value of the previous observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will reset the observations time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN>
+ * for this process to <TT>nextTime</TT>. The user must make sure that
+ * the <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN> supplied is
+ * <SPAN CLASS="MATH"> >= <I>t</I><SUB>j</SUB></SPAN>.
+ *
+ */
+ public double nextObservation (double nextTime) {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double x = xOld + alpha * (beta - xOld) * dt
+ + sigma * Math.sqrt (dt*xOld) * gen.nextDouble();
+ if (x >= 0.0)
+ path[observationIndex] = x;
+ else
+ path[observationIndex] = 0.;
+ return x;
+ }
+
+
+ /**
+ * Generates an observation of the process in <TT>dt</TT> time units,
+ * assuming that the process has value <SPAN CLASS="MATH"><I>x</I></SPAN> at the current time.
+ * Uses the process parameters specified in the constructor.
+ * Note that this method does not affect the sample path of the process
+ * stored internally (if any).
+ *
+ *
+ */
+ public double nextObservation (double x, double dt) {
+ x = x + alpha * (beta - x) * dt +
+ sigma * Math.sqrt (dt*x) * gen.nextDouble();
+ if (x >= 0.0)
+ return x;
+ return 0.0;
+ }
+
+
+ public double[] generatePath() {
+ double x;
+ double xOld = x0;
+ for (int j = 0; j < d; j++) {
+ x = xOld + (beta - xOld) * alphadt[j]
+ + sigmasqrdt[j] * Math.sqrt(xOld) * gen.nextDouble();
+ if (x < 0.0)
+ x = 0.0;
+ path[j + 1] = x;
+ xOld = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }
+
+
+ /**
+ * Resets the parameters
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>x0</TT>, <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>,
+ * <SPAN CLASS="MATH"><I>b</I> =</SPAN> <TT>b</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called too frequently.
+ *
+ */
+ public void setParams (double x0, double alpha, double b, double sigma) {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }
+
+
+ /**
+ * Resets the random stream of the normal generator to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ gen.setStream (stream);
+ }
+
+
+ /**
+ * Returns the random stream of the normal generator.
+ *
+ */
+ public RandomStream getStream() {
+ return gen.getStream ();
+ }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ */
+ public double getAlpha() { return alpha; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB() { return beta; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() { return sigma; }
+
+
+ /**
+ * Returns the normal random variate generator used.
+ * The <TT>RandomStream</TT> used for that generator can be changed via
+ * <TT>getGen().setStream(stream)</TT>, for example.
+ *
+ */
+ public NormalGen getGen() { return gen; }
+
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ alphadt = new double[d];
+ sigmasqrdt = new double[d];
+ double dt;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ alphadt[j] = alpha * (dt);
+ sigmasqrdt[j] = sigma * Math.sqrt (dt);
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/CIRProcessEuler.tex b/source/umontreal/iro/lecuyer/stochprocess/CIRProcessEuler.tex
new file mode 100644
index 0000000..861c932
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/CIRProcessEuler.tex
@@ -0,0 +1,258 @@
+\defmodule {CIRProcessEuler}
+
+This class represents a \emph{CIR} process
+as in \class{CIRProcess}, but
+the process is generated using the simple Euler scheme
+\begin{equation}
+ X(t_j) - X(t_{j-1}) = \alpha(b - X(t_{j-1}))(t_j - t_{j-1}) +
+ \sigma \sqrt{(t_j - t_{j-1})X(t_{j-1})}\, Z_j
+ \label{eq:cir-seq-euler}
+\end{equation}
+where $Z_j \sim N(0,1)$. This is a good approximation only for small
+time intervals $t_j - t_{j-1}$.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: CIRProcessEuler
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class CIRProcessEuler extends StochasticProcess \begin{hide} {
+ protected NormalGen gen;
+ protected double alpha,
+ beta,
+ sigma;
+ // Precomputed values
+ protected double[] alphadt,
+ sigmasqrdt;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public CIRProcessEuler (double x0, double alpha, double b, double sigma,
+ RandomStream stream) \begin{hide} {
+ this (x0, alpha, b, sigma, new NormalGen (stream, new NormalDist()));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{CIRProcessEuler} with parameters
+$\alpha =$ \texttt{alpha}, $b$, $\sigma =$ \texttt{sigma} and initial value
+$X(t_{0}) =$ \texttt{x0}. The normal variates $Z_j$ will be
+generated by inversion using the stream \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public CIRProcessEuler (double x0, double alpha, double b, double sigma,
+ NormalGen gen) \begin{hide} {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ this.gen = gen;
+ }\end{hide}
+\end{code}
+\begin{tabb} The normal variate generator \texttt{gen} is specified directly
+instead of specifying the stream.
+ \texttt{gen} can use another method than inversion.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double x;
+ x = xOld + (beta - xOld) * alphadt[observationIndex]
+ + sigmasqrdt[observationIndex] * Math.sqrt(xOld) * gen.nextDouble();
+ observationIndex++;
+ if (x >= 0.0)
+ path[observationIndex] = x;
+ else
+ path[observationIndex] = 0.;
+ return x;
+ }
+
+\end{hide}
+ public double nextObservation (double nextTime) \begin{hide} {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double x = xOld + alpha * (beta - xOld) * dt
+ + sigma * Math.sqrt (dt*xOld) * gen.nextDouble();
+ if (x >= 0.0)
+ path[observationIndex] = x;
+ else
+ path[observationIndex] = 0.;
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates and returns the next observation at time $t_{j+1} =$
+ \texttt{nextTime}, using the previous observation time $t_{j}$ defined earlier
+(either by this method or by \texttt{setObservation\-Times}),
+as well as the value of the previous observation $X(t_j)$.
+\emph{Warning}: This method will reset the observations time $t_{j+1}$
+for this process to \texttt{nextTime}. The user must make sure that
+the $t_{j+1}$ supplied is $\geq t_{j}$.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation (double x, double dt) \begin{hide} {
+ x = x + alpha * (beta - x) * dt +
+ sigma * Math.sqrt (dt*x) * gen.nextDouble();
+ if (x >= 0.0)
+ return x;
+ return 0.0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates an observation of the process in \texttt{dt} time units,
+assuming that the process has value $x$ at the current time.
+Uses the process parameters specified in the constructor.
+Note that this method does not affect the sample path of the process
+stored internally (if any).
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public double[] generatePath() {
+ double x;
+ double xOld = x0;
+ for (int j = 0; j < d; j++) {
+ x = xOld + (beta - xOld) * alphadt[j]
+ + sigmasqrdt[j] * Math.sqrt(xOld) * gen.nextDouble();
+ if (x < 0.0)
+ x = 0.0;
+ path[j + 1] = x;
+ xOld = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }
+\end{code}
+\begin{tabb} Generates a sample path of the process at all observation times,
+ which are provided in array \texttt{t}.
+ Note that \texttt{t[0]} should be the observation time of \texttt{x0},
+ the initial value of the process, and \texttt{t[]} should have at least $d+1$
+ elements (see the \texttt{setObservationTimes} method).
+\end{tabb}\end{hide}
+\begin{code}
+
+ public void setParams (double x0, double alpha, double b, double sigma) \begin{hide} {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the parameters $X(t_{0}) =$ \texttt{x0}, $\alpha =$ \texttt{alpha},
+ $b =$ \texttt{b} and $\sigma =$ \texttt{sigma} of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called too frequently.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ gen.setStream (stream);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the random stream of the normal generator to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ return gen.getStream ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the random stream of the normal generator.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha() \begin{hide} { return alpha; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\alpha$.
+\end{tabb}
+\begin{code}
+
+ public double getB() \begin{hide} { return beta; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $b$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma() \begin{hide} { return sigma; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public NormalGen getGen() \begin{hide} { return gen; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the normal random variate generator used.
+The \texttt{RandomStream} used for that generator can be changed via
+\texttt{getGen().setStream(stream)}, for example.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ alphadt = new double[d];
+ sigmasqrdt = new double[d];
+ double dt;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ alphadt[j] = alpha * (dt);
+ sigmasqrdt[j] = sigma * Math.sqrt (dt);
+ }
+ }\end{hide}
+\end{code}
+\begin{code}\begin{hide}
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcess.java b/source/umontreal/iro/lecuyer/stochprocess/GammaProcess.java
new file mode 100644
index 0000000..7d61c00
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcess.java
@@ -0,0 +1,304 @@
+
+
+/*
+ * Class: GammaProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre Tremblay, Jean-Sebastien Parent & Maxime Dion
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.probdist.GammaDist;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * This class represents a <SPAN CLASS="textit">gamma</SPAN> process
+ *
+ * <SPAN CLASS="MATH">{<I>S</I>(<I>t</I>) = <I>G</I>(<I>t</I>;<I>μ</I>, <I>ν</I>) : <I>t</I> >= 0}</SPAN> with mean parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> and
+ * variance parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ * It is a continuous-time process with stationary,
+ * independent gamma increments such that for any
+ * <SPAN CLASS="MATH"><I>Δt</I> > 0</SPAN>,
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="GammaEqn"></A>
+ * <I>S</I>(<I>t</I> + <I>Δt</I>) = <I>S</I>(<I>t</I>) + <I>X</I>,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>X</I></SPAN> is a random variate from the gamma distribution
+ * <TT>Gamma</TT>
+ * <SPAN CLASS="MATH">(<I>μ</I><SUP>2</SUP><I>Δt</I>/<I>ν</I>, <I>μ</I>/<I>ν</I>)</SPAN>.
+ *
+ * <P>
+ * In this class, the gamma process is sampled sequentially using
+ * equation.
+ *
+ */
+public class GammaProcess extends StochasticProcess {
+ // Make sure that process is strictly increasing: when two successive steps
+ // are equal, reset the new one to old*(1 + EPS)
+ protected static final double EPS = 1.0e-15;
+ protected boolean usesAnti = false;
+
+ protected RandomStream stream; // used in the generatePath(), so must be kept
+ protected GammaGen Ggen;
+ protected double mu,
+ nu;
+ protected double muOverNu,
+ mu2OverNu;
+
+ // For precomputations for standard GP
+ protected double[] mu2dtOverNu;
+
+ protected void setLarger (double[] path, int left, int mid, int right) {
+ /* make sure that path[left] < path[mid] < path[right] by adding a
+ small epsilon to the last two */
+ double myEps;
+ if (path[left] >= 0)
+ myEps = EPS;
+ else
+ myEps = -EPS;
+ path[mid] = path[left] * (1.0 + myEps);
+ if (path[mid] >= path[right])
+ path[right] = path[mid] * (1.0 + myEps);
+ }
+
+
+ protected double setLarger (double[] path, int left, int right) {
+ /* make sure that path[left] < s < path[right] by adding a
+ small epsilon to the last two; return s. */
+ double myEps;
+ if (path[left] >= 0)
+ myEps = EPS;
+ else
+ myEps = -EPS;
+ double s = path[left] * (1.0 + myEps);
+ if (s >= path[right])
+ path[right] = s * (1.0 + myEps);
+ return s;
+ }
+
+
+ protected double setLarger (double v) {
+ /* return a number that is slightly larger than v */
+ if (v >= 0)
+ return v*(1.0 + EPS) + Num.DBL_EPSILON;
+ else
+ return v*(1.0 - EPS);
+ }
+
+
+ /**
+ * Constructs a new <TT>GammaProcess</TT> with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * The gamma variates <SPAN CLASS="MATH"><I>X</I></SPAN> in are generated by inversion
+ * using <TT>stream</TT>.
+ *
+ */
+ public GammaProcess (double s0, double mu, double nu,
+ RandomStream stream) {
+ this (s0, mu, nu, new GammaGen (stream, 1.0));
+// Note that the parameters (the 1.0) supplied to
+// the GammaGen constructors are meaningless.
+// The 'real' parameters are supplied at time of generation
+ }
+
+
+ /**
+ * Constructs a new <TT>GammaProcess</TT> with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * The gamma variates <SPAN CLASS="MATH"><I>X</I></SPAN> in are supplied by the gamma random
+ * variate generator <TT>Ggen</TT>. Note that the parameters of the
+ * {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} object
+ * <TT>Ggen</TT> are not important since the implementation forces the generator
+ * to use the correct parameters (as defined above).
+ *
+ */
+ public GammaProcess (double s0, double mu, double nu, GammaGen Ggen) {
+ this.mu = mu;
+ this.nu = nu;
+ this.x0 = s0;
+ this.Ggen = Ggen;
+ stream = Ggen.getStream();
+ }
+
+ public double nextObservation() {
+ double s = path[observationIndex];
+ double v = s;
+ s += Ggen.nextDouble (stream, mu2dtOverNu[observationIndex], muOverNu);
+ if (s <= v)
+ s = setLarger (v);
+ observationIndex++;
+ observationCounter = observationIndex;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ /**
+ * Generates and returns the next observation at time
+ * <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB> = <texttt>nextTime</texttt></SPAN>,
+ * using the previous observation time <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> defined earlier
+ * (either by this method or by <TT>setObservationTimes</TT>),
+ * as well as the value of the previous observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will reset the observations time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN>
+ * for this process to <TT>nextT</TT>. The user must make sure that
+ * the <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN> supplied is
+ * <SPAN CLASS="MATH"> >= <I>t</I><SUB>j</SUB></SPAN>.
+ *
+ */
+ public double nextObservation (double nextT) {
+ // This method is useful for generating variance gamma processes
+ double s = path[observationIndex];
+ double v = s;
+ double previousT = t[observationIndex];
+ observationIndex++;
+ observationCounter = observationIndex;
+ t[observationIndex] = nextT;
+ double dt = nextT - previousT;
+ s += Ggen.nextDouble (stream, mu2OverNu*dt, muOverNu);
+ if (s <= v)
+ s = setLarger (v);
+ path[observationIndex] = s;
+ return s;
+ }
+
+
+ /**
+ * Generates, returns and saves the path
+ *
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I><SUB>0</SUB>), <I>X</I>(<I>t</I><SUB>1</SUB>),…, <I>X</I>(<I>t</I><SUB>d</SUB>)}</SPAN>.
+ * The gamma variates <SPAN CLASS="MATH"><I>X</I></SPAN> in are generated using the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} <TT>stream</TT>
+ * or the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} included
+ * in the {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} <TT>Ggen</TT>.
+ *
+ */
+ public double[] generatePath() {
+ double s = x0;
+ double v;
+ for (int i = 0; i < d; i++) {
+ v = s;
+ s += Ggen.nextDouble(stream, mu2dtOverNu[i], muOverNu);
+ if (v <= s)
+ s = setLarger (v);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * Generates, returns and saves the path
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I><SUB>0</SUB>), <I>X</I>(<I>t</I><SUB>1</SUB>),…, <I>X</I>(<I>t</I><SUB>d</SUB>)}</SPAN>. This method does not use the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} <TT>stream</TT> nor the
+ * {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} <TT>Ggen</TT>. It
+ * uses the vector of uniform random numbers <SPAN CLASS="MATH"><I>U</I>(0, 1)</SPAN> provided by the user
+ * and generates the path by inversion. The vector <TT>uniform01</TT> must be of
+ * dimension <SPAN CLASS="MATH"><I>d</I></SPAN>.
+ *
+ */
+ public double[] generatePath (double[] uniform01) {
+ double s = x0;
+ double v;
+ for (int i = 0; i < d; i++) {
+ v = s;
+ s += GammaDist.inverseF(mu2dtOverNu[i], muOverNu, 10, uniform01[i]);
+ if (v <= s)
+ s = setLarger (v);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * Sets the parameters
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN> and
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called repeatedly.
+ *
+ */
+ public void setParams (double s0, double mu, double nu) {
+ this.x0 = s0;
+ this.mu = mu;
+ this.nu = nu;
+ if (observationTimesSet) init(); // Otherwise no need to.
+}
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>.
+ *
+ */
+ public double getMu() { return mu; }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ *
+ */
+ public double getNu() { return nu; }
+
+
+ /**
+ * Resets the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * of the {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen}
+ * to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ this.stream = stream;
+ Ggen.setStream (stream);
+ }
+
+
+ /**
+ * Returns the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} <TT>stream</TT>.
+ *
+ */
+ public RandomStream getStream() { return stream; }
+
+
+ protected void init() {
+ // init of StochasticProcess only sets path[0] = x0 if observation times are set.
+ super.init();
+ muOverNu = mu / nu;
+ mu2OverNu = mu * mu / nu;
+ mu2dtOverNu = new double[d];
+ if (observationTimesSet) {
+ for (int i = 0; i < d; i++)
+ mu2dtOverNu[i] = mu2OverNu * (t[i+1] - t[i]);
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/GammaProcess.tex
new file mode 100644
index 0000000..0d329ea
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcess.tex
@@ -0,0 +1,296 @@
+\defmodule {GammaProcess}
+
+This class represents a \emph{gamma} process \cite[page 82]{fMAD98a}
+$\{ S(t) = G(t; \mu, \nu) : t \geq 0 \}$ with mean parameter $\mu$ and
+variance parameter $\nu$.
+It is a continuous-time process with stationary,
+independent gamma increments such that for any $\Delta t > 0$,
+\begin{equation}
+ S(t + \Delta t) = S(t) + X,\label{GammaEqn}
+\end{equation}
+where $X$ is a random variate from the gamma distribution
+\texttt{Gamma}$(\mu^{2}\Delta t / \nu , \mu / \nu)$.
+
+In this class, the gamma process is sampled sequentially using
+equation (\ref{GammaEqn}).
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre Tremblay, Jean-Sebastien Parent & Maxime Dion
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.probdist.GammaDist;
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class GammaProcess extends StochasticProcess \begin{hide} {
+ // Make sure that process is strictly increasing: when two successive steps
+ // are equal, reset the new one to old*(1 + EPS)
+ protected static final double EPS = 1.0e-15;
+ protected boolean usesAnti = false;
+
+ protected RandomStream stream; // used in the generatePath(), so must be kept
+ protected GammaGen Ggen;
+ protected double mu,
+ nu;
+ protected double muOverNu,
+ mu2OverNu;
+
+ // For precomputations for standard GP
+ protected double[] mu2dtOverNu;
+
+ protected void setLarger (double[] path, int left, int mid, int right) {
+ /* make sure that path[left] < path[mid] < path[right] by adding a
+ small epsilon to the last two */
+ double myEps;
+ if (path[left] >= 0)
+ myEps = EPS;
+ else
+ myEps = -EPS;
+ path[mid] = path[left] * (1.0 + myEps);
+ if (path[mid] >= path[right])
+ path[right] = path[mid] * (1.0 + myEps);
+ }
+
+
+ protected double setLarger (double[] path, int left, int right) {
+ /* make sure that path[left] < s < path[right] by adding a
+ small epsilon to the last two; return s. */
+ double myEps;
+ if (path[left] >= 0)
+ myEps = EPS;
+ else
+ myEps = -EPS;
+ double s = path[left] * (1.0 + myEps);
+ if (s >= path[right])
+ path[right] = s * (1.0 + myEps);
+ return s;
+ }
+
+
+ protected double setLarger (double v) {
+ /* return a number that is slightly larger than v */
+ if (v >= 0)
+ return v*(1.0 + EPS) + Num.DBL_EPSILON;
+ else
+ return v*(1.0 - EPS);
+ }\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaProcess (double s0, double mu, double nu,
+ RandomStream stream)\begin{hide} {
+ this (s0, mu, nu, new GammaGen (stream, 1.0));
+// Note that the parameters (the 1.0) supplied to
+// the GammaGen constructors are meaningless.
+// The 'real' parameters are supplied at time of generation
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcess} with parameters $\mu =
+\texttt{mu}$, $\nu = \texttt{nu}$ and initial value $S(t_{0}) = \texttt{s0}$.
+The gamma variates $X$ in (\ref{GammaEqn}) are generated by inversion
+using \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public GammaProcess (double s0, double mu, double nu, GammaGen Ggen) \begin{hide} {
+ this.mu = mu;
+ this.nu = nu;
+ this.x0 = s0;
+ this.Ggen = Ggen;
+ stream = Ggen.getStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcess} with parameters $\mu =
+\texttt{mu}$, $\nu = \texttt{nu}$ and initial value $S(t_{0}) = \texttt{s0}$.
+The gamma variates $X$ in (\ref{GammaEqn}) are supplied by the gamma random
+variate generator \texttt{Ggen}. Note that the parameters of the
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} object
+\texttt{Ggen} are not important since the implementation forces the generator
+to use the correct parameters (as defined above).
+\end{tabb}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+ public double nextObservation() {
+ double s = path[observationIndex];
+ double v = s;
+ s += Ggen.nextDouble (stream, mu2dtOverNu[observationIndex], muOverNu);
+ if (s <= v)
+ s = setLarger (v);
+ observationIndex++;
+ observationCounter = observationIndex;
+ path[observationIndex] = s;
+ return s;
+ } \end{hide}
+
+ public double nextObservation (double nextT) \begin{hide} {
+ // This method is useful for generating variance gamma processes
+ double s = path[observationIndex];
+ double v = s;
+ double previousT = t[observationIndex];
+ observationIndex++;
+ observationCounter = observationIndex;
+ t[observationIndex] = nextT;
+ double dt = nextT - previousT;
+ s += Ggen.nextDouble (stream, mu2OverNu*dt, muOverNu);
+ if (s <= v)
+ s = setLarger (v);
+ path[observationIndex] = s;
+ return s;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates and returns the next observation at time $t_{j+1} =
+\texttt{nextTime}$,
+using the previous observation time $t_{j}$ defined earlier
+(either by this method or by \texttt{setObservationTimes}),
+as well as the value of the previous observation $X(t_j)$.
+\emph{Warning}: This method will reset the observations time $t_{j+1}$
+for this process to \texttt{nextT}. The user must make sure that
+the $t_{j+1}$ supplied is $\geq t_{j}$.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath() \begin{hide} {
+ double s = x0;
+ double v;
+ for (int i = 0; i < d; i++) {
+ v = s;
+ s += Ggen.nextDouble(stream, mu2dtOverNu[i], muOverNu);
+ if (v <= s)
+ s = setLarger (v);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Generates, returns and saves the path
+$\{X(t_{0}), X(t_{1}), \ldots, X(t_{d})\}$.
+The gamma variates $X$ in (\ref{GammaEqn}) are generated using the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} \texttt{stream}
+or the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} included
+in the \externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} \texttt{Ggen}.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (double[] uniform01) \begin{hide} {
+ double s = x0;
+ double v;
+ for (int i = 0; i < d; i++) {
+ v = s;
+ s += GammaDist.inverseF(mu2dtOverNu[i], muOverNu, 10, uniform01[i]);
+ if (v <= s)
+ s = setLarger (v);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ } \end{hide}
+\end{code}
+\begin{tabb} Generates, returns and saves the path $
+\{X(t_{0}), X(t_{1}), \ldots, X(t_{d})\}$. This method does not use the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} \texttt{stream} nor the
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} \texttt{Ggen}. It
+uses the vector of uniform random numbers $U(0, 1)$ provided by the user
+and generates the path by inversion. The vector \texttt{uniform01} must be of
+dimension $d$.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double s0, double mu, double nu) \begin{hide} {
+ this.x0 = s0;
+ this.mu = mu;
+ this.nu = nu;
+ if (observationTimesSet) init(); // Otherwise no need to.
+}\end{hide}
+\end{code}
+\begin{tabb} Sets the parameters $S(t_{0}) = \texttt{s0}$,
+$\mu = \texttt{mu}$ and $\nu = \texttt{nu}$ of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called repeatedly.
+\end{tabb}
+\begin{code}
+
+ public double getMu() \begin{hide} { return mu; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the parameter $\mu$.
+\end{tabb}
+\begin{code}
+
+ public double getNu() \begin{hide} { return nu; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the parameter $\nu$.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ this.stream = stream;
+ Ggen.setStream (stream);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+of the \externalclass{umontreal.iro.lecuyer.randvar}{GammaGen}
+to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} { return stream; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} \texttt{stream}.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ protected void init() {
+ // init of StochasticProcess only sets path[0] = x0 if observation times are set.
+ super.init();
+ muOverNu = mu / nu;
+ mu2OverNu = mu * mu / nu;
+ mu2dtOverNu = new double[d];
+ if (observationTimesSet) {
+ for (int i = 0; i < d; i++)
+ mu2dtOverNu[i] = mu2OverNu * (t[i+1] - t[i]);
+ }
+ }
+
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessBridge.java b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessBridge.java
new file mode 100644
index 0000000..4ce0c50
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessBridge.java
@@ -0,0 +1,299 @@
+
+
+/*
+ * Class: GammaProcessBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre Tremblay and Jean-Sebastien Parent
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.util.Num;
+
+
+/**
+ * This class represents a gamma process
+ *
+ * <SPAN CLASS="MATH">{<I>S</I>(<I>t</I>) = <I>G</I>(<I>t</I>;<I>μ</I>, <I>ν</I>) : <I>t</I> >= 0}</SPAN> with mean parameter <SPAN CLASS="MATH"><I>μ</I></SPAN> and
+ * variance parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>, sampled using the <SPAN CLASS="textit">gamma bridge</SPAN> method
+ * (see for example).
+ * This is analogous to the bridge sampling used in
+ * {@link BrownianMotionBridge}.
+ *
+ * <P>
+ * Note that gamma bridge sampling requires not only gamma variates, but also
+ * <SPAN CLASS="textit">beta</SPAN> variates. The latter generally take a longer time to generate
+ * than the former. The class <TT>GammaSymmetricalBridgeProcess</TT> provides
+ * a faster implementation when the number of observation times
+ * is a power of two.
+ *
+ * <P>
+ * The warning from class {@link BrownianMotionBridge} applies verbatim
+ * to this class.
+ *
+ */
+public class GammaProcessBridge extends GammaProcess {
+ protected BetaGen Bgen;
+ protected double mu2OverNu,
+ mu2dTOverNu;
+ protected double[] bMu2dtOverNuL, // For precomputations for G Bridge
+ bMu2dtOverNuR;
+ protected int[] wIndexList;
+ protected int bridgeCounter = -1; // Before 1st observ
+
+
+
+ /**
+ * Constructs a new <TT>GammaProcessBridge</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * Uses <TT>stream</TT> to generate the gamma and beta variates by inversion.
+ *
+ */
+ public GammaProcessBridge (double s0, double mu, double nu,
+ RandomStream stream) {
+ this (s0, mu, nu, new GammaGen (stream, new GammaDist (1.0)),
+ new BetaGen (stream, new BetaDist (1.0, 1.0)));
+ }
+
+
+ /**
+ * Constructs a new <TT>GammaProcessBridge</TT>. Uses the
+ * random variate generators <TT>Ggen</TT> and <TT>Bgen</TT> to generate the gamma
+ * and beta variates, respectively. Note that both generator uses the
+ * same {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}. Furthermore, the
+ * parameters of the
+ * {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} and
+ * {@link umontreal.iro.lecuyer.randvar.BetaGen BetaGen} objects are not
+ * important since the implementation forces the generators to use
+ * the correct parameters.
+ * (as defined in).
+ *
+ */
+ public GammaProcessBridge (double s0, double mu, double nu,
+ GammaGen Ggen, BetaGen Bgen) {
+ super (s0, mu, nu, Ggen);
+ this.Bgen = Bgen;
+ this.Bgen.setStream(Ggen.getStream()); // to avoid confusion in streams
+ this.stream = Ggen.getStream();
+ }
+
+
+ public double nextObservation() {
+ double s;
+ if (bridgeCounter == -1) {
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ double y = Bgen.nextDouble(stream, bMu2dtOverNuL[newIndex],
+ bMu2dtOverNuR[newIndex], 0.0, 1.0);
+ s = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y ;
+ // make sure the process is strictly increasing
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double nextObservation (double nextT) {
+ double s;
+ if (bridgeCounter == -1) {
+ t[d] = nextT;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ t[newIndex] = nextT;
+ bMu2dtOverNuL[newIndex] = mu2OverNu
+ * (t[newIndex] - t[oldIndexL]);
+ bMu2dtOverNuR[newIndex] = mu2OverNu
+ * (t[oldIndexR] - t[newIndex]);
+
+ double y = Bgen.nextDouble(stream, bMu2dtOverNuL[newIndex],
+ bMu2dtOverNuR[newIndex], 0.0, 1.0);
+
+ s = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y ;
+ // make sure the process is strictly increasing
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double[] generatePath (double[] uniform01) {
+ int oldIndexL, oldIndexR, newIndex;
+ double y;
+
+ path[d] = x0 + GammaDist.inverseF (mu2dTOverNu, muOverNu, 10, uniform01[0]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = BetaDist.inverseF(bMu2dtOverNuL[newIndex], bMu2dtOverNuR[newIndex], 8, uniform01[1 + j/3]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ // make sure the process is strictly increasing
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ //resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath() {
+ int oldIndexL, oldIndexR, newIndex;
+ double y;
+
+ path[d] = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = Bgen.nextDouble(stream, bMu2dtOverNuL[newIndex], bMu2dtOverNuR[newIndex], 0.0, 1.0);
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ // make sure the process is strictly increasing
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ //resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ bridgeCounter = -1;
+ }
+
+ protected void init() {
+ super.init();
+ if (observationTimesSet) {
+
+ // Quantities for gamma bridge process
+ bMu2dtOverNuL = new double[d+1];
+ bMu2dtOverNuR = new double[d+1];
+ wIndexList = new int[3*d];
+
+ int[] ptIndex = new int[d+1];
+ int indexCounter = 0;
+ int newIndex, oldLeft, oldRight;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ mu2OverNu = mu * mu / nu;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+
+ for (int powOfTwo = 1; powOfTwo <= d/2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) { ptIndex[2*j] = ptIndex[j]; }
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldLeft = 2*j - 2;
+ oldRight = 2*j;
+ newIndex = (int) (0.5*(ptIndex[oldLeft] + ptIndex[oldRight]));
+
+ bMu2dtOverNuL[newIndex] = mu * mu
+ * (t[newIndex] - t[ptIndex[oldLeft]]) / nu;
+ bMu2dtOverNuR[newIndex] = mu * mu
+ * (t[ptIndex[oldRight]] - t[newIndex]) / nu;
+
+ ptIndex[oldLeft + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldLeft];
+ wIndexList[indexCounter+1] = newIndex;
+ wIndexList[indexCounter+2] = ptIndex[oldRight];
+
+ indexCounter += 3;
+ }
+ }
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k-1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+
+ bMu2dtOverNuL[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k-1]+1] - t[ptIndex[k-1]]) / nu;
+ bMu2dtOverNuR[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k]] - t[ptIndex[k-1]+1]) / nu;
+
+ wIndexList[indexCounter] = ptIndex[k]-2;
+ wIndexList[indexCounter+1] = ptIndex[k]-1;
+ wIndexList[indexCounter+2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+ }
+
+ /**
+ * Resets the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * of the {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} and
+ * the {@link umontreal.iro.lecuyer.randvar.BetaGen BetaGen} to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ super.setStream(stream);
+ this.Bgen.setStream(stream);
+ this.stream = stream;
+}
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessBridge.tex b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessBridge.tex
new file mode 100644
index 0000000..f951706
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessBridge.tex
@@ -0,0 +1,300 @@
+\defmodule {GammaProcessBridge}
+
+This class represents a gamma process
+$\{ S(t) = G(t; \mu, \nu) : t \geq 0 \}$ with mean parameter $\mu$ and
+variance parameter $\nu$, sampled using the \emph{gamma bridge} method
+(see for example \cite{fRIB02a,fAVR03a}).
+This is analogous to the bridge sampling used in
+\class{BrownianMotionBridge}.
+
+Note that gamma bridge sampling requires not only gamma variates, but also
+\emph{beta} variates. The latter generally take a longer time to generate
+than the former. The class \texttt{GammaSymmetricalBridgeProcess} provides
+a faster implementation when the number of observation times
+is a power of two.
+
+The warning from class \class{BrownianMotionBridge} applies verbatim
+to this class.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaProcessBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre Tremblay and Jean-Sebastien Parent
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.util.Num;
+\end{hide}
+
+public class GammaProcessBridge extends GammaProcess \begin{hide} {
+ protected BetaGen Bgen;
+ protected double mu2OverNu,
+ mu2dTOverNu;
+ protected double[] bMu2dtOverNuL, // For precomputations for G Bridge
+ bMu2dtOverNuR;
+ protected int[] wIndexList;
+ protected int bridgeCounter = -1; // Before 1st observ
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaProcessBridge (double s0, double mu, double nu,
+ RandomStream stream) \begin{hide} {
+ this (s0, mu, nu, new GammaGen (stream, new GammaDist (1.0)),
+ new BetaGen (stream, new BetaDist (1.0, 1.0)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcessBridge} with parameters
+$\mu = \texttt{mu}$, $\nu = \texttt{nu}$ and initial value $S(t_{0}) = \texttt{s0}$.
+Uses \texttt{stream} to generate the gamma and beta variates by inversion.
+\end{tabb}
+\begin{code}
+
+ public GammaProcessBridge (double s0, double mu, double nu,
+ GammaGen Ggen, BetaGen Bgen) \begin{hide} {
+ super (s0, mu, nu, Ggen);
+ this.Bgen = Bgen;
+ this.Bgen.setStream(Ggen.getStream()); // to avoid confusion in streams
+ this.stream = Ggen.getStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcessBridge}. Uses the
+random variate generators \texttt{Ggen} and \texttt{Bgen} to generate the gamma
+and beta variates, respectively. Note that both generator uses the
+same \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}. Furthermore, the
+parameters of the
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} and
+\externalclass{umontreal.iro.lecuyer.randvar}{BetaGen} objects are not
+important since the implementation forces the generators to use
+the correct parameters.
+(as defined in \cite[page 7]{fRIB02a}).
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ double s;
+ if (bridgeCounter == -1) {
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ double y = Bgen.nextDouble(stream, bMu2dtOverNuL[newIndex],
+ bMu2dtOverNuR[newIndex], 0.0, 1.0);
+ s = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y ;
+ // make sure the process is strictly increasing
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double nextObservation (double nextT) {
+ double s;
+ if (bridgeCounter == -1) {
+ t[d] = nextT;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ t[newIndex] = nextT;
+ bMu2dtOverNuL[newIndex] = mu2OverNu
+ * (t[newIndex] - t[oldIndexL]);
+ bMu2dtOverNuR[newIndex] = mu2OverNu
+ * (t[oldIndexR] - t[newIndex]);
+
+ double y = Bgen.nextDouble(stream, bMu2dtOverNuL[newIndex],
+ bMu2dtOverNuR[newIndex], 0.0, 1.0);
+
+ s = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y ;
+ // make sure the process is strictly increasing
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double[] generatePath (double[] uniform01) {
+ int oldIndexL, oldIndexR, newIndex;
+ double y;
+
+ path[d] = x0 + GammaDist.inverseF (mu2dTOverNu, muOverNu, 10, uniform01[0]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = BetaDist.inverseF(bMu2dtOverNuL[newIndex], bMu2dtOverNuR[newIndex], 8, uniform01[1 + j/3]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ // make sure the process is strictly increasing
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ //resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath() {
+ int oldIndexL, oldIndexR, newIndex;
+ double y;
+
+ path[d] = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = Bgen.nextDouble(stream, bMu2dtOverNuL[newIndex], bMu2dtOverNuR[newIndex], 0.0, 1.0);
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ // make sure the process is strictly increasing
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ //resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ bridgeCounter = -1;
+ }
+
+ protected void init() {
+ super.init();
+ if (observationTimesSet) {
+
+ // Quantities for gamma bridge process
+ bMu2dtOverNuL = new double[d+1];
+ bMu2dtOverNuR = new double[d+1];
+ wIndexList = new int[3*d];
+
+ int[] ptIndex = new int[d+1];
+ int indexCounter = 0;
+ int newIndex, oldLeft, oldRight;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ mu2OverNu = mu * mu / nu;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+
+ for (int powOfTwo = 1; powOfTwo <= d/2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) { ptIndex[2*j] = ptIndex[j]; }
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldLeft = 2*j - 2;
+ oldRight = 2*j;
+ newIndex = (int) (0.5*(ptIndex[oldLeft] + ptIndex[oldRight]));
+
+ bMu2dtOverNuL[newIndex] = mu * mu
+ * (t[newIndex] - t[ptIndex[oldLeft]]) / nu;
+ bMu2dtOverNuR[newIndex] = mu * mu
+ * (t[ptIndex[oldRight]] - t[newIndex]) / nu;
+
+ ptIndex[oldLeft + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldLeft];
+ wIndexList[indexCounter+1] = newIndex;
+ wIndexList[indexCounter+2] = ptIndex[oldRight];
+
+ indexCounter += 3;
+ }
+ }
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k-1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+
+ bMu2dtOverNuL[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k-1]+1] - t[ptIndex[k-1]]) / nu;
+ bMu2dtOverNuR[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k]] - t[ptIndex[k-1]+1]) / nu;
+
+ wIndexList[indexCounter] = ptIndex[k]-2;
+ wIndexList[indexCounter+1] = ptIndex[k]-1;
+ wIndexList[indexCounter+2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+ }\end{hide}
+
+ public void setStream (RandomStream stream)\begin{hide} {
+ super.setStream(stream);
+ this.Bgen.setStream(stream);
+ this.stream = stream;
+}\end{hide}
+\end{code}
+\begin{tabb}
+Resets the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+of the \externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} and
+the \externalclass{umontreal.iro.lecuyer.randvar}{BetaGen} to \texttt{stream}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCA.java b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCA.java
new file mode 100644
index 0000000..42485ef
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCA.java
@@ -0,0 +1,216 @@
+
+
+/*
+ * Class: GammaProcessPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent and Maxime Dion
+ * @since july 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+import umontreal.iro.lecuyer.stat.list.*;
+
+
+/**
+ * Represents a <SPAN CLASS="textit">gamma</SPAN> process sampled using the principal
+ * component analysis (PCA). To simulate the gamma process at times
+ *
+ * <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB> < <I>t</I><SUB>1</SUB> < <SUP> ... </SUP> < <I>t</I><SUB>d</SUB></SPAN> by PCA sampling, a Brownian
+ * motion
+ * <SPAN CLASS="MATH">{<I>W</I>(<I>t</I>), <I>t</I> >= 0}</SPAN> with mean <SPAN CLASS="MATH">0</SPAN> and variance parameter <SPAN CLASS="MATH"><I>ν</I></SPAN> is
+ * first generated at times
+ * <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB> < <I>t</I><SUB>1</SUB> < <SUP> ... </SUP> < <I>t</I><SUB>d</SUB></SPAN>
+ * by PCA sampling (see class {@link BrownianMotionPCA}).
+ * The independent increments
+ * <SPAN CLASS="MATH"><I>W</I>(<I>t</I><SUB>j</SUB>) - <I>W</I>(<I>t</I><SUB>j-1</SUB>)</SPAN> of this process
+ * are then transformed into independent <SPAN CLASS="MATH"><I>U</I>(0, 1)</SPAN> random variates <SPAN CLASS="MATH"><I>V</I><SUB>j</SUB></SPAN> via
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>V</I><SUB>j</SUB> = <I>Φ</I>((τ_j-τ_j-1)<SUP>1/2</SUP>[<I>W</I>(<I>τ</I><SUB>j</SUB>) - <I>W</I>(<I>τ</I><SUB>j-1</SUB>)]), <I>j</I> = 1,..., <I>s</I>
+ * </DIV><P></P>
+ * Finally, the increments of the Gamma process are computed as
+ *
+ * <SPAN CLASS="MATH"><I>Y</I>(<I>t</I><SUB>j</SUB>) - <I>Y</I>(<I>t</I><SUB>j-1</SUB>) = <I>G</I><SUP>-1</SUP>(<I>V</I><SUB>j</SUB>)</SPAN>, where <SPAN CLASS="MATH"><I>G</I></SPAN> is the gamma distribution
+ * function.
+ *
+ */
+public class GammaProcessPCA extends GammaProcess {
+ double[] arrayTime;
+ BrownianMotionPCA BMPCA;
+
+
+
+
+ /**
+ * Constructs a new <TT>GammaProcessPCA</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * The random variables are created using <TT>stream</TT>.
+ * Note that the same {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * is used for the <TT>GammaProcessPCA</TT> and for the
+ * {@link BrownianMotionPCA} included in this class. Both the
+ * {@link GammaProcessPCA} and the {@link BrownianMotionPCA} are generated by
+ * inversion.
+ *
+ */
+ public GammaProcessPCA (double s0, double mu, double nu,
+ RandomStream stream) {
+ super (s0, mu, nu, new GammaGen (stream, new GammaDist (1.0)));
+ this.BMPCA = new BrownianMotionPCA(0.0, 0.0, Math.sqrt(nu), stream);
+ }
+
+
+ /**
+ * Constructs a new <TT>GammaProcessPCA</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * All the random variables, i.e. the gamma ones and the normal ones,
+ * are created using the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * included in the {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen}
+ * <TT>Ggen</TT>. Note that the parameters of the
+ * {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} object
+ * are not important since the implementation forces the generator
+ * to use the correct parameters (as defined above).
+ *
+ */
+ public GammaProcessPCA (double s0, double mu, double nu, GammaGen Ggen) {
+ super(s0, mu, nu, Ggen);
+ this.BMPCA = new BrownianMotionPCA(0.0, 0.0, Math.sqrt(nu), Ggen.getStream());
+ }
+
+
+ public double[] generatePath() {
+ double[] uniformsV = new double[d];
+ arrayTime = BMPCA.getObservationTimes();
+ int i;
+ double[] BMpath = BMPCA.generatePath();
+ double sigma;
+ for(i = 0; i < d; i++){
+ sigma = BMPCA.getSigma() * Math.sqrt(arrayTime[i + 1] - arrayTime[i]);
+ uniformsV[i] = NormalDist.cdf01( ( BMpath[i+1] - BMpath[i] )/sigma);
+ }
+ path[0] = x0;
+ for(i = 0; i < d; i++){
+ path[i+1] = path[i] +
+ GammaDist.inverseF(mu2dtOverNu[i], muOverNu, 10, uniformsV[i]);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public double[] generatePath (double[] uniform01) {
+ double[] uniformsV = new double[d];
+ arrayTime = BMPCA.getObservationTimes();
+ int i;
+ double[] BMpath = BMPCA.generatePath(uniform01);
+ double sigma;
+ for(i = 0; i < d; i++){
+ sigma = BMPCA.getSigma() * Math.sqrt(arrayTime[i + 1] - arrayTime[i]);
+ uniformsV[i] = NormalDist.cdf01( ( BMpath[i+1] - BMpath[i] )/sigma);
+ }
+ path[0] = x0;
+ for(i = 0; i < d; i++){
+ path[i+1] = path[i] +
+ GammaDist.inverseF(mu2dtOverNu[i], muOverNu, 10, uniformsV[i]);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ /**
+ * This method is not implemented in this class since the path
+ * cannot be generated sequentially.
+ *
+ */
+ public double nextObservation() {
+ throw new UnsupportedOperationException ("nextObservation is not implemented in GammaProcessPCA");
+ }
+
+
+ /**
+ * This method is not implemented in this class since the path
+ * cannot be generated sequentially.
+ *
+ */
+ public double nextObservation (double nextT) {
+ throw new UnsupportedOperationException ("nextObservation is not implemented in GammaProcessPCA");
+ }
+
+
+ /**
+ * Returns the {@link BrownianMotionPCA} that is included in the
+ * {@link GammaProcessPCA} object.
+ *
+ */
+ public BrownianMotionPCA getBMPCA() {
+ return BMPCA;
+ }
+
+
+
+ /**
+ * Sets the observation times of the {@link GammaProcessPCA} and the
+ * {@link BrownianMotionPCA}.
+ *
+ */
+ public void setObservationTimes (double[] t, int d) {
+ super.setObservationTimes(t, d);
+ BMPCA.setObservationTimes(t, d);
+ }
+
+
+ /**
+ * Sets the parameters <TT>s0</TT>, <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>ν</I></SPAN> to new values, and sets
+ * the variance parameters of the {@link BrownianMotionPCA} to <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ *
+ */
+ public void setParams (double s0, double mu, double nu) {
+ super.setParams(s0, mu, nu);
+ BMPCA.setParams(0.0, 0.0, Math.sqrt(nu));
+ }
+
+
+ /**
+ * Resets the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * of the gamma generator and the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} of
+ * the inner {@link BrownianMotionPCA} to
+ * <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ super.setStream(stream);
+ this.BMPCA.setStream(stream);
+}
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCA.tex b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCA.tex
new file mode 100644
index 0000000..6690fd0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCA.tex
@@ -0,0 +1,204 @@
+\defmodule {GammaProcessPCA}
+
+Represents a \emph{gamma} process sampled using the principal
+component analysis (PCA). To simulate the gamma process at times
+$t_0 < t_1 < \cdots < t_d$ by PCA sampling, a Brownian
+motion $\{ W(t), t \geq 0 \}$ with mean $0$ and variance parameter $\nu$ is
+first generated at times $t_0 < t_1 < \cdots < t_d$
+by PCA sampling (see class \class{BrownianMotionPCA}).
+The independent increments $W(t_{j}) - W(t_{j-1})$ of this process
+are then transformed into independent $U(0, 1)$ random variates $V_{j}$ via
+$$ V_j = \Phi\left(\sqrt{\tau_j-\tau_{j-1}} [W(\tau_j)-W(\tau_{j-1})]\right),
+\quad j=1,\dots,s $$
+Finally, the increments of the Gamma process are computed as
+$ Y(t_{j}) - Y(t_{j-1}) = G^{-1}(V_j)$, where $G$ is the gamma distribution
+ function.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaProcessPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent and Maxime Dion
+ * @since july 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.stat.*;
+import umontreal.iro.lecuyer.stat.list.*;
+\end{hide}
+
+public class GammaProcessPCA extends GammaProcess \begin{hide} {
+ double[] arrayTime;
+ BrownianMotionPCA BMPCA;
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaProcessPCA (double s0, double mu, double nu,
+ RandomStream stream)\begin{hide} {
+ super (s0, mu, nu, new GammaGen (stream, new GammaDist (1.0)));
+ this.BMPCA = new BrownianMotionPCA(0.0, 0.0, Math.sqrt(nu), stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcessPCA} with parameters
+$\mu = \texttt{mu}$, $\nu = \texttt{nu}$ and initial value $S(t_{0}) = \texttt{s0}$.
+The random variables are created using \texttt{stream}.
+Note that the same \externalclass{umontreal.iro.lecuyer.rng}{Random\-Stream}
+ is used for the \texttt{GammaProcessPCA} and for the
+\class{BrownianMotionPCA} included in this class. Both the
+\class{GammaProcessPCA} and the \class{BrownianMotionPCA} are generated by
+inversion.
+\end{tabb}
+\begin{code}
+
+ public GammaProcessPCA (double s0, double mu, double nu, GammaGen Ggen)\begin{hide} {
+ super(s0, mu, nu, Ggen);
+ this.BMPCA = new BrownianMotionPCA(0.0, 0.0, Math.sqrt(nu), Ggen.getStream());
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcessPCA} with parameters
+$\mu = \texttt{mu}$, $\nu = \texttt{nu}$ and initial value $S(t_{0}) = \texttt{s0}$.
+All the random variables, i.e. the gamma ones and the normal ones,
+are created using the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+included in the \externalclass{umontreal.iro.lecuyer.randvar}{GammaGen}
+\texttt{Ggen}. Note that the parameters of the
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} object
+are not important since the implementation forces the generator
+to use the correct parameters (as defined above).
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double[] generatePath() {
+ double[] uniformsV = new double[d];
+ arrayTime = BMPCA.getObservationTimes();
+ int i;
+ double[] BMpath = BMPCA.generatePath();
+ double sigma;
+ for(i = 0; i < d; i++){
+ sigma = BMPCA.getSigma() * Math.sqrt(arrayTime[i + 1] - arrayTime[i]);
+ uniformsV[i] = NormalDist.cdf01( ( BMpath[i+1] - BMpath[i] )/sigma);
+ }
+ path[0] = x0;
+ for(i = 0; i < d; i++){
+ path[i+1] = path[i] +
+ GammaDist.inverseF(mu2dtOverNu[i], muOverNu, 10, uniformsV[i]);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public double[] generatePath (double[] uniform01) {
+ double[] uniformsV = new double[d];
+ arrayTime = BMPCA.getObservationTimes();
+ int i;
+ double[] BMpath = BMPCA.generatePath(uniform01);
+ double sigma;
+ for(i = 0; i < d; i++){
+ sigma = BMPCA.getSigma() * Math.sqrt(arrayTime[i + 1] - arrayTime[i]);
+ uniformsV[i] = NormalDist.cdf01( ( BMpath[i+1] - BMpath[i] )/sigma);
+ }
+ path[0] = x0;
+ for(i = 0; i < d; i++){
+ path[i+1] = path[i] +
+ GammaDist.inverseF(mu2dtOverNu[i], muOverNu, 10, uniformsV[i]);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+
+ public double nextObservation()\begin{hide} {
+ throw new UnsupportedOperationException ("nextObservation is not implemented in GammaProcessPCA");
+ }\end{hide}
+\end{code}
+\begin{tabb} This method is not implemented in this class since the path
+cannot be generated sequentially.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation (double nextT) \begin{hide} {
+ throw new UnsupportedOperationException ("nextObservation is not implemented in GammaProcessPCA");
+ }\end{hide}
+\end{code}
+\begin{tabb} This method is not implemented in this class since the path
+cannot be generated sequentially.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotionPCA getBMPCA() \begin{hide} {
+ return BMPCA;
+ }
+\end{hide}
+\end{code}
+\begin{tabb} Returns the \class{BrownianMotionPCA} that is included in the
+\class{GammaProcessPCA} object.
+\end{tabb}
+\begin{code}
+
+ public void setObservationTimes (double[] t, int d)\begin{hide} {
+ super.setObservationTimes(t, d);
+ BMPCA.setObservationTimes(t, d);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the observation times of the \class{GammaProcessPCA} and the
+\class{BrownianMotionPCA}.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double s0, double mu, double nu)\begin{hide} {
+ super.setParams(s0, mu, nu);
+ BMPCA.setParams(0.0, 0.0, Math.sqrt(nu));
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameters \texttt{s0}, $\mu$ and $\nu$ to new values, and sets
+the variance parameters of the \class{BrownianMotionPCA} to $\nu$.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ super.setStream(stream);
+ this.BMPCA.setStream(stream);
+}\end{hide}
+\end{code}
+\begin{tabb}
+Resets the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+of the gamma generator and the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} of
+the inner \class{BrownianMotionPCA} to
+\texttt{stream}.
+\end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCABridge.java b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCABridge.java
new file mode 100644
index 0000000..dfc986c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCABridge.java
@@ -0,0 +1,205 @@
+
+
+/*
+ * Class: GammaProcessPCABridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Jean-Sebastien Parent and Maxime Dion
+ * @since july 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Same as {@link GammaProcessPCA}, but the generated uniforms
+ * correspond to a bridge transformation of the {@link BrownianMotionPCA}
+ * instead of a sequential transformation.
+ *
+ */
+public class GammaProcessPCABridge extends GammaProcessPCA {
+ protected BrownianMotionBridge BMBridge;
+ protected double mu2OverNu,
+ mu2dTOverNu;
+ protected double[] bMu2dtOverNuL, // For precomputations for G Bridge
+ bMu2dtOverNuR;
+ protected int[] wIndexList;
+
+
+
+ /**
+ * Constructs a new <TT>GammaProcessPCABridge</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * The same stream is used to generate the gamma and beta random numbers. All
+ * these numbers are generated by inversion in the following order:
+ * the first uniform random number generated
+ * is used for the gamma and the other <SPAN CLASS="MATH"><I>d</I> - 1</SPAN> for the beta's.
+ *
+ */
+ public GammaProcessPCABridge (double s0, double mu, double nu,
+ RandomStream stream) {
+ super (s0, mu, nu, stream);
+ this.BMBridge = new BrownianMotionBridge(0.0, 0.0, Math.sqrt(nu), stream);
+ }
+
+
+ public double[] generatePath (double[] uniform01) {
+ // uniformsV[] of size d+1, but element 0 never used.
+ double[] uniformsV = new double[d+1];
+
+ // generate BrownianMotion PCA path
+ double[] BMPCApath = BMPCA.generatePath(uniform01);
+ int oldIndexL;
+ int newIndex;
+ int oldIndexR;
+ double temp, y;
+ // Transform BMPCA points to uniforms using an inverse bridge.
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = BMBridge.wIndexList[j];
+ newIndex = BMBridge.wIndexList[j + 1];
+ oldIndexR = BMBridge.wIndexList[j + 2];
+
+ temp = BMPCApath[newIndex] - BMPCApath[oldIndexL];
+ temp -= (BMPCApath[oldIndexR] - BMPCApath[oldIndexL]) * BMBridge.wMuDt[newIndex];
+ temp /= BMBridge.wSqrtDt[newIndex];
+ uniformsV[newIndex] = NormalDist.cdf01( temp );
+ }
+ double dT = BMPCA.t[d] - BMPCA.t[0];
+ uniformsV[d] = NormalDist.cdf01( ( BMPCApath[d] - BMPCApath[0] - BMPCA.mu*dT )/
+ ( BMPCA.sigma * Math.sqrt(dT) ) );
+
+
+ // Reconstruct the bridge for the GammaProcess from the Brownian uniforms.
+ path[0] = x0;
+ path[d] = x0 + GammaDist.inverseF(mu2dTOverNu, muOverNu, 10, uniformsV[d]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = BetaDist.inverseF(bMu2dtOverNuL[newIndex], bMu2dtOverNuR[newIndex], 8, uniformsV[newIndex]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public double[] generatePath() {
+ double[] u = new double[d];
+ for(int i =0; i < d; i++)
+ u[i] = stream.nextDouble();
+ return generatePath(u);
+ }
+
+
+ public void setParams (double s0, double mu, double nu) {
+ super.setParams(s0, mu, nu);
+ BMBridge.setParams(0.0, 0.0, Math.sqrt(nu));
+ BMPCA.setParams(0.0, 0.0, Math.sqrt(nu));
+ }
+
+
+ public void setObservationTimes (double[] t, int d) {
+ super.setObservationTimes(t, d);
+ BMBridge.setObservationTimes(t, d);
+ }
+
+ /**
+ * Returns the inner {@link BrownianMotionPCA}.
+ *
+ */
+ public BrownianMotionPCA getBMPCA() {
+ return BMPCA;
+ }
+
+
+ protected void init() {
+ super.init();
+ if (observationTimesSet) {
+
+ // Quantities for gamma bridge process
+ bMu2dtOverNuL = new double[d+1];
+ bMu2dtOverNuR = new double[d+1];
+ wIndexList = new int[3*d];
+
+ int[] ptIndex = new int[d+1];
+ int indexCounter = 0;
+ int newIndex, oldLeft, oldRight;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ mu2OverNu = mu * mu / nu;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+
+ for (int powOfTwo = 1; powOfTwo <= d/2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) { ptIndex[2*j] = ptIndex[j]; }
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldLeft = 2*j - 2;
+ oldRight = 2*j;
+ newIndex = (int) (0.5*(ptIndex[oldLeft] + ptIndex[oldRight]));
+
+ bMu2dtOverNuL[newIndex] = mu * mu
+ * (t[newIndex] - t[ptIndex[oldLeft]]) / nu;
+ bMu2dtOverNuR[newIndex] = mu * mu
+ * (t[ptIndex[oldRight]] - t[newIndex]) / nu;
+
+ ptIndex[oldLeft + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldLeft];
+ wIndexList[indexCounter+1] = newIndex;
+ wIndexList[indexCounter+2] = ptIndex[oldRight];
+
+ indexCounter += 3;
+ }
+ }
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k-1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+
+ bMu2dtOverNuL[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k-1]+1] - t[ptIndex[k-1]]) / nu;
+ bMu2dtOverNuR[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k]] - t[ptIndex[k-1]+1]) / nu;
+
+ wIndexList[indexCounter] = ptIndex[k]-2;
+ wIndexList[indexCounter+1] = ptIndex[k]-1;
+ wIndexList[indexCounter+2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCABridge.tex b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCABridge.tex
new file mode 100644
index 0000000..5883765
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCABridge.tex
@@ -0,0 +1,213 @@
+\defmodule {GammaProcessPCABridge}
+
+Same as \class{GammaProcessPCA}, but the generated uniforms
+correspond to a bridge transformation of the \class{BrownianMotionPCA}
+instead of a sequential transformation.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaProcessPCABridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Jean-Sebastien Parent and Maxime Dion
+ * @since july 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class GammaProcessPCABridge extends GammaProcessPCA \begin{hide} {
+ protected BrownianMotionBridge BMBridge;
+ protected double mu2OverNu,
+ mu2dTOverNu;
+ protected double[] bMu2dtOverNuL, // For precomputations for G Bridge
+ bMu2dtOverNuR;
+ protected int[] wIndexList;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaProcessPCABridge (double s0, double mu, double nu,
+ RandomStream stream)\begin{hide} {
+ super (s0, mu, nu, stream);
+ this.BMBridge = new BrownianMotionBridge(0.0, 0.0, Math.sqrt(nu), stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcessPCABridge} with parameters
+$\mu = \texttt{mu}$, $\nu = \texttt{nu}$ and initial value $S(t_{0}) = \texttt{s0}$.
+The same stream is used to generate the gamma and beta random numbers. All
+these numbers are generated by inversion in the following order:
+the first uniform random number generated
+is used for the gamma and the other $d-1$ for the beta's.
+
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double[] generatePath (double[] uniform01) {
+ // uniformsV[] of size d+1, but element 0 never used.
+ double[] uniformsV = new double[d+1];
+
+ // generate BrownianMotion PCA path
+ double[] BMPCApath = BMPCA.generatePath(uniform01);
+ int oldIndexL;
+ int newIndex;
+ int oldIndexR;
+ double temp, y;
+ // Transform BMPCA points to uniforms using an inverse bridge.
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = BMBridge.wIndexList[j];
+ newIndex = BMBridge.wIndexList[j + 1];
+ oldIndexR = BMBridge.wIndexList[j + 2];
+
+ temp = BMPCApath[newIndex] - BMPCApath[oldIndexL];
+ temp -= (BMPCApath[oldIndexR] - BMPCApath[oldIndexL]) * BMBridge.wMuDt[newIndex];
+ temp /= BMBridge.wSqrtDt[newIndex];
+ uniformsV[newIndex] = NormalDist.cdf01( temp );
+ }
+ double dT = BMPCA.t[d] - BMPCA.t[0];
+ uniformsV[d] = NormalDist.cdf01( ( BMPCApath[d] - BMPCApath[0] - BMPCA.mu*dT )/
+ ( BMPCA.sigma * Math.sqrt(dT) ) );
+
+
+ // Reconstruct the bridge for the GammaProcess from the Brownian uniforms.
+ path[0] = x0;
+ path[d] = x0 + GammaDist.inverseF(mu2dTOverNu, muOverNu, 10, uniformsV[d]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = BetaDist.inverseF(bMu2dtOverNuL[newIndex], bMu2dtOverNuR[newIndex], 8, uniformsV[newIndex]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public double[] generatePath() {
+ double[] u = new double[d];
+ for(int i =0; i < d; i++)
+ u[i] = stream.nextDouble();
+ return generatePath(u);
+ }
+
+
+ public void setParams (double s0, double mu, double nu) {
+ super.setParams(s0, mu, nu);
+ BMBridge.setParams(0.0, 0.0, Math.sqrt(nu));
+ BMPCA.setParams(0.0, 0.0, Math.sqrt(nu));
+ }
+
+
+ public void setObservationTimes (double[] t, int d) {
+ super.setObservationTimes(t, d);
+ BMBridge.setObservationTimes(t, d);
+ }\end{hide}
+
+ public BrownianMotionPCA getBMPCA() \begin{hide} {
+ return BMPCA;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the inner \class{BrownianMotionPCA}.
+\end{tabb}
+
+
+\begin{code}
+\begin{hide}
+ protected void init() {
+ super.init();
+ if (observationTimesSet) {
+
+ // Quantities for gamma bridge process
+ bMu2dtOverNuL = new double[d+1];
+ bMu2dtOverNuR = new double[d+1];
+ wIndexList = new int[3*d];
+
+ int[] ptIndex = new int[d+1];
+ int indexCounter = 0;
+ int newIndex, oldLeft, oldRight;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ mu2OverNu = mu * mu / nu;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+
+ for (int powOfTwo = 1; powOfTwo <= d/2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) { ptIndex[2*j] = ptIndex[j]; }
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldLeft = 2*j - 2;
+ oldRight = 2*j;
+ newIndex = (int) (0.5*(ptIndex[oldLeft] + ptIndex[oldRight]));
+
+ bMu2dtOverNuL[newIndex] = mu * mu
+ * (t[newIndex] - t[ptIndex[oldLeft]]) / nu;
+ bMu2dtOverNuR[newIndex] = mu * mu
+ * (t[ptIndex[oldRight]] - t[newIndex]) / nu;
+
+ ptIndex[oldLeft + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldLeft];
+ wIndexList[indexCounter+1] = newIndex;
+ wIndexList[indexCounter+2] = ptIndex[oldRight];
+
+ indexCounter += 3;
+ }
+ }
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k-1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+
+ bMu2dtOverNuL[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k-1]+1] - t[ptIndex[k-1]]) / nu;
+ bMu2dtOverNuR[ptIndex[k-1]+1] = mu * mu
+ * (t[ptIndex[k]] - t[ptIndex[k-1]+1]) / nu;
+
+ wIndexList[indexCounter] = ptIndex[k]-2;
+ wIndexList[indexCounter+1] = ptIndex[k]-1;
+ wIndexList[indexCounter+2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+ }
+
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCASymmetricalBridge.java b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCASymmetricalBridge.java
new file mode 100644
index 0000000..0637f45
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCASymmetricalBridge.java
@@ -0,0 +1,144 @@
+
+
+/*
+ * Class: GammaProcessPCASymmetricalBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent and Maxime Dion
+ * @since july 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Same as {@link GammaProcessPCABridge}, but uses the fast inversion method
+ * for the symmetrical beta distribution, proposed by L'Ecuyer and Simard, to accelerate the generation of the beta random variables.
+ * This class works only in the case where the number of intervals is a power of
+ * 2 and all these intervals are of equal size.
+ *
+ */
+public class GammaProcessPCASymmetricalBridge extends GammaProcessPCABridge {
+
+
+
+ /**
+ * Constructs a new <TT>GammaProcessPCASymmetricalBridge</TT>
+ * with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial
+ * value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * The {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} <TT>stream</TT>
+ * is used in the
+ * {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen}
+ * and in the {@link umontreal.iro.lecuyer.randvar.BetaSymmetricalGen BetaSymmetricalGen}.
+ * These two generators use inversion to generate random numbers. The first
+ * uniform random number generated by <TT>stream</TT> is used for the gamma, and the
+ * other <SPAN CLASS="MATH"><I>d</I> - 1</SPAN> for the beta's.
+ *
+ */
+ public GammaProcessPCASymmetricalBridge (double s0, double mu, double nu,
+ RandomStream stream) {
+ super (s0, mu, nu, stream);
+ }
+
+
+ public double[] generatePath (double[] uniform01) {
+ // uniformsV[] of size d+1, but element 0 never used.
+ double[] uniformsV = new double[d+1];
+
+ // generate BrownianMotion PCA path
+ double[] BMPCApath = BMPCA.generatePath(uniform01);
+ int oldIndexL;
+ int newIndex;
+ int oldIndexR;
+ double temp, y;
+ // Transform BMPCA points to uniforms using an inverse bridge.
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = BMBridge.wIndexList[j];
+ newIndex = BMBridge.wIndexList[j + 1];
+ oldIndexR = BMBridge.wIndexList[j + 2];
+
+ temp = BMPCApath[newIndex] - BMPCApath[oldIndexL];
+ temp -= (BMPCApath[oldIndexR] - BMPCApath[oldIndexL]) *
+ BMBridge.wMuDt[newIndex];
+ temp /= BMBridge.wSqrtDt[newIndex];
+ uniformsV[newIndex] = NormalDist.cdf01(temp);
+ }
+ double dT = BMPCA.t[d] - BMPCA.t[0];
+ uniformsV[d] = NormalDist.cdf01( ( BMPCApath[d] - BMPCApath[0] - BMPCA.mu*dT )/
+ ( BMPCA.sigma * Math.sqrt(dT) ) );
+
+
+ // Reconstruct the bridge for the GammaProcess from the Brownian uniforms.
+ // Here we have to hope that the bridge is implemented in the
+ // same order for the Brownian and Gamma processes.
+
+ path[0] = x0;
+ path[d] = x0 + GammaDist.inverseF(mu2dTOverNu, muOverNu, 10, uniformsV[d]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = BetaSymmetricalDist.inverseF(bMu2dtOverNuL[newIndex], uniformsV[newIndex]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ }
+ //resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public double[] generatePath() {
+ double[] u = new double[d];
+ for(int i =0; i < d; i++)
+ u[i] = stream.nextDouble();
+ return generatePath(u);
+ }
+
+
+ // code taken from GammaSymmetricalBridge to check time is power of 2,
+ // as it is required for the symmetrical bridge.
+ protected void init () {
+ super.init ();
+ if (observationTimesSet) {
+
+ /* Testing to make sure number of observations n = 2^k */
+ int x = d;
+ int y = 1;
+ while (x>1) {
+ x = x / 2;
+ y = y * 2;
+ }
+ if (y != d) throw new IllegalArgumentException
+ ( "GammaSymmetricalBridgeProcess:"
+ +"Number 'n' of observation times is not a power of 2" );
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCASymmetricalBridge.tex b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCASymmetricalBridge.tex
new file mode 100644
index 0000000..b71fa44
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessPCASymmetricalBridge.tex
@@ -0,0 +1,152 @@
+\defmodule {GammaProcessPCASymmetricalBridge}
+
+Same as \class{GammaProcessPCABridge}, but uses the fast inversion method
+for the symmetrical beta distribution, proposed by L'Ecuyer and Simard
+\cite {rLEC06a}, to accelerate the generation of the beta random variables.
+This class works only in the case where the number of intervals is a power of
+2 and all these intervals are of equal size.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaProcessPCASymmetricalBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent and Maxime Dion
+ * @since july 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class GammaProcessPCASymmetricalBridge extends GammaProcessPCABridge \begin{hide} {
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaProcessPCASymmetricalBridge (double s0, double mu, double nu,
+ RandomStream stream) \begin{hide} {
+ super (s0, mu, nu, stream);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{GammaProcessPCASymmetricalBridge}
+with parameters $\mu = \texttt{mu}$, $\nu = \texttt{nu}$ and initial
+value $S(t_{0}) = \texttt{s0}$.
+The \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} \texttt{stream}
+is used in the
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaGen}
+and in the \externalclass{umontreal.iro.lecuyer.randvar}{BetaSymmetricalGen}.
+These two generators use inversion to generate random numbers. The first
+uniform random number generated by \texttt{stream} is used for the gamma, and the
+other $d-1$ for the beta's.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double[] generatePath (double[] uniform01) {
+ // uniformsV[] of size d+1, but element 0 never used.
+ double[] uniformsV = new double[d+1];
+
+ // generate BrownianMotion PCA path
+ double[] BMPCApath = BMPCA.generatePath(uniform01);
+ int oldIndexL;
+ int newIndex;
+ int oldIndexR;
+ double temp, y;
+ // Transform BMPCA points to uniforms using an inverse bridge.
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = BMBridge.wIndexList[j];
+ newIndex = BMBridge.wIndexList[j + 1];
+ oldIndexR = BMBridge.wIndexList[j + 2];
+
+ temp = BMPCApath[newIndex] - BMPCApath[oldIndexL];
+ temp -= (BMPCApath[oldIndexR] - BMPCApath[oldIndexL]) *
+ BMBridge.wMuDt[newIndex];
+ temp /= BMBridge.wSqrtDt[newIndex];
+ uniformsV[newIndex] = NormalDist.cdf01(temp);
+ }
+ double dT = BMPCA.t[d] - BMPCA.t[0];
+ uniformsV[d] = NormalDist.cdf01( ( BMPCApath[d] - BMPCApath[0] - BMPCA.mu*dT )/
+ ( BMPCA.sigma * Math.sqrt(dT) ) );
+
+
+ // Reconstruct the bridge for the GammaProcess from the Brownian uniforms.
+ // Here we have to hope that the bridge is implemented in the
+ // same order for the Brownian and Gamma processes.
+
+ path[0] = x0;
+ path[d] = x0 + GammaDist.inverseF(mu2dTOverNu, muOverNu, 10, uniformsV[d]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ y = BetaSymmetricalDist.inverseF(bMu2dtOverNuL[newIndex], uniformsV[newIndex]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ }
+ //resetStartProcess();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ public double[] generatePath() {
+ double[] u = new double[d];
+ for(int i =0; i < d; i++)
+ u[i] = stream.nextDouble();
+ return generatePath(u);
+ }
+
+
+ // code taken from GammaSymmetricalBridge to check time is power of 2,
+ // as it is required for the symmetrical bridge.
+ protected void init () {
+ super.init ();
+ if (observationTimesSet) {
+
+ /* Testing to make sure number of observations n = 2^k */
+ int x = d;
+ int y = 1;
+ while (x>1) {
+ x = x / 2;
+ y = y * 2;
+ }
+ if (y != d) throw new IllegalArgumentException
+ ( "GammaSymmetricalBridgeProcess:"
+ +"Number 'n' of observation times is not a power of 2" );
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessSymmetricalBridge.java b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessSymmetricalBridge.java
new file mode 100644
index 0000000..a907f80
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessSymmetricalBridge.java
@@ -0,0 +1,242 @@
+
+
+/*
+ * Class: GammaProcessSymmetricalBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Pierre Tremblay and Jean-Sebastien Parent
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.util.*;
+
+
+
+/**
+ * This class differs from <TT>GammaProcessBridge</TT> only in that it requires
+ * the number of interval of the path to be
+ * a power of 2 and of equal size.
+ * It is then possible to generate the bridge process using
+ * a special implementation of the beta random variate generator
+ * (using the <SPAN CLASS="textit">symmetrical</SPAN> beta distribution)
+ * that is much faster (HOW MUCH? QUANTIFY!) than the general case.
+ * Note that when the method <TT>setObservationTimes</TT> is called,
+ * the equality of the size of the time steps is verified.
+ * To allow for differences due to floating point errors, time steps
+ * are considered to be equal if their relative difference is less
+ * than <SPAN CLASS="MATH">10<SUP>-15</SUP></SPAN>.
+ *
+ */
+public class GammaProcessSymmetricalBridge extends GammaProcessBridge {
+ protected BetaSymmetricalGen BSgen;
+
+
+
+ /**
+ * Constructs a new <TT>GammaProcessSymmetricalBridge</TT>
+ * with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial
+ * value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * The random variables are created using the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} <TT>stream</TT>.
+ * Note that the same {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * <TT>stream</TT> is used for the
+ * {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} and for the
+ * {@link umontreal.iro.lecuyer.randvar.BetaSymmetricalGen BetaSymmetricalGen}
+ * inluded in this class.
+ *
+ */
+ public GammaProcessSymmetricalBridge (double s0, double mu, double nu,
+ RandomStream stream) {
+ this (s0, mu, nu, new GammaGen (stream, new GammaDist (1.0)),
+ new BetaSymmetricalGen (stream, new BetaSymmetricalDist (1.0)));
+ }
+
+
+ /**
+ * Constructs a new <TT>GammaProcessSymmetricalBridge</TT>
+ * with parameters
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and initial
+ * value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>. Note that the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} included in
+ * the {@link umontreal.iro.lecuyer.randvar.BetaSymmetricalGen BetaSymmetricalGen}
+ * is sets to the one included in the
+ * {@link umontreal.iro.lecuyer.randvar.GammaGen GammaGen} to avoid confusion.
+ * This {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} is then used to
+ * generate all the random variables.
+ *
+ *
+ */
+ public GammaProcessSymmetricalBridge (double s0, double mu, double nu,
+ GammaGen Ggen,
+ BetaSymmetricalGen BSgen) {
+ super (s0, mu, nu, Ggen, BSgen);
+ this.BSgen = BSgen;
+ BSgen.setStream(Ggen.getStream());
+ }
+
+
+ public double nextObservation() {
+ double s;
+ if (bridgeCounter == -1) {
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ double y = BSgen.nextDouble(stream, bMu2dtOverNuL[newIndex]);
+
+ s = path[oldIndexL] + (path[oldIndexR] - path[oldIndexL]) * y;
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double nextObservation (double nextT) {
+ double s;
+ if (bridgeCounter == -1) {
+ t[d] = nextT;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter*3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ t[newIndex] = nextT;
+ bMu2dtOverNuL[newIndex] = mu2OverNu
+ * (t[newIndex] - t[oldIndexL]);
+
+ double y = BSgen.nextDouble(stream, bMu2dtOverNuL[newIndex]);
+
+ s = path[oldIndexL] + (path[oldIndexR] - path[oldIndexL]) * y;
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double[] generatePath() {
+ int oldIndexL, oldIndexR, newIndex;
+
+ path[d] = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ double y = BSgen.nextDouble(stream, bMu2dtOverNuL[newIndex]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (double[] uniform01) {
+ int oldIndexL, oldIndexR, newIndex;
+
+ path[d] = x0 + GammaDist.inverseF(mu2dTOverNu, muOverNu, 10, uniform01[0]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ double y = BetaSymmetricalDist.inverseF(bMu2dtOverNuL[newIndex], uniform01[1 + j/3]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ protected void init () {
+ super.init ();
+ if (observationTimesSet) {
+
+ /* Testing to make sure number of observations n = 2^k */
+ int k = 0;
+ int x = d;
+ int y = 1;
+ while (x>1) {
+ x = x / 2;
+ y = y * 2;
+ k++;
+ }
+ if (y != d) throw new IllegalArgumentException
+ ( "GammaProcessSymmetricalBridge:"
+ +"Number 'n' of observation times is not a power of 2" );
+
+ /* Testing that time intervals are equidistant */
+ boolean equidistant = true;
+ double macheps = 1.0e-13; // Num.DBL_EPSILON;
+ double dt = t[1] - t[0];
+ for (int i=1; i<d; i++) {
+ if ((t[i+1] - t[i]) != dt) { // not equidistant
+ equidistant = false;
+ /* This compensates the fact that the dt's
+ may be different due to numerical idiosyncracies */
+ if (dt != 0.0)
+ if (Math.abs ((t[i+1] - t[i]) - dt) / dt <= macheps)
+ equidistant = true;
+ }
+ }
+ if (!equidistant) throw new IllegalArgumentException
+ ( "GammaProcessSymmetricalBridge:"
+ +"Observation times of sample paths are not equidistant" );
+ }
+ }
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GammaProcessSymmetricalBridge.tex b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessSymmetricalBridge.tex
new file mode 100644
index 0000000..80f2e08
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GammaProcessSymmetricalBridge.tex
@@ -0,0 +1,241 @@
+\defmodule {GammaProcessSymmetricalBridge}
+
+This class differs from \texttt{GammaProcessBridge} only in that it requires
+the number of interval of the path to be
+a power of 2 and of equal size.
+It is then possible to generate the bridge process using
+a special implementation of the beta random variate generator
+(using the \emph{symmetrical} beta distribution)
+that is much faster (HOW MUCH? QUANTIFY!) than the general case.
+Note that when the method \texttt{setObservationTimes} is called,
+the equality of the size of the time steps is verified.
+To allow for differences due to floating point errors, time steps
+are considered to be equal if their relative difference is less
+than $10^{-15}$.
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GammaProcessSymmetricalBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Pierre Tremblay and Jean-Sebastien Parent
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.util.*;
+
+\end{hide}
+
+public class GammaProcessSymmetricalBridge extends GammaProcessBridge \begin{hide} {
+ protected BetaSymmetricalGen BSgen;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GammaProcessSymmetricalBridge (double s0, double mu, double nu,
+ RandomStream stream) \begin{hide} {
+ this (s0, mu, nu, new GammaGen (stream, new GammaDist (1.0)),
+ new BetaSymmetricalGen (stream, new BetaSymmetricalDist (1.0)));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcessSymmetricalBridge}
+with parameters $\mu = \texttt{mu}$, $\nu = \texttt{nu}$ and initial
+value $S(t_{0}) = \texttt{s0}$.
+The random variables are created using the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} \texttt{stream}.
+Note that the same \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+\texttt{stream} is used for the
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} and for the
+\externalclass{umontreal.iro.lecuyer.randvar}{BetaSymmetricalGen}
+inluded in this class.
+\end{tabb}
+\begin{code}
+
+ public GammaProcessSymmetricalBridge (double s0, double mu, double nu,
+ GammaGen Ggen,
+ BetaSymmetricalGen BSgen) \begin{hide} {
+ super (s0, mu, nu, Ggen, BSgen);
+ this.BSgen = BSgen;
+ BSgen.setStream(Ggen.getStream());
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GammaProcessSymmetricalBridge}
+with parameters $\mu = \texttt{mu}$, $\nu = \texttt{nu}$ and initial
+value $S(t_{0}) = \texttt{s0}$. Note that the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} included in
+the \externalclass{umontreal.iro.lecuyer.randvar}{BetaSymmetricalGen}
+is sets to the one included in the
+\externalclass{umontreal.iro.lecuyer.randvar}{GammaGen} to avoid confusion.
+This \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} is then used to
+generate all the random variables.
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%\subsubsection* {Methods}
+\begin{code} \begin{hide}
+
+ public double nextObservation() {
+ double s;
+ if (bridgeCounter == -1) {
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ double y = BSgen.nextDouble(stream, bMu2dtOverNuL[newIndex]);
+
+ s = path[oldIndexL] + (path[oldIndexR] - path[oldIndexL]) * y;
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double nextObservation (double nextT) {
+ double s;
+ if (bridgeCounter == -1) {
+ t[d] = nextT;
+ mu2dTOverNu = mu2OverNu * (t[d] - t[0]);
+ s = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ if (s <= x0)
+ s = setLarger (x0);
+ bridgeCounter = 0;
+ observationIndex = d;
+ } else {
+ int j = bridgeCounter*3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ t[newIndex] = nextT;
+ bMu2dtOverNuL[newIndex] = mu2OverNu
+ * (t[newIndex] - t[oldIndexL]);
+
+ double y = BSgen.nextDouble(stream, bMu2dtOverNuL[newIndex]);
+
+ s = path[oldIndexL] + (path[oldIndexR] - path[oldIndexL]) * y;
+ if (s <= path[oldIndexL])
+ s = setLarger (path, oldIndexL, oldIndexR);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double[] generatePath() {
+ int oldIndexL, oldIndexR, newIndex;
+
+ path[d] = x0 + Ggen.nextDouble(stream, mu2dTOverNu, muOverNu);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ double y = BSgen.nextDouble(stream, bMu2dtOverNuL[newIndex]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (double[] uniform01) {
+ int oldIndexL, oldIndexR, newIndex;
+
+ path[d] = x0 + GammaDist.inverseF(mu2dTOverNu, muOverNu, 10, uniform01[0]);
+ for (int j = 0; j < 3*(d-1); j+=3) {
+ oldIndexL = wIndexList[j];
+ newIndex = wIndexList[j + 1];
+ oldIndexR = wIndexList[j + 2];
+
+ double y = BetaSymmetricalDist.inverseF(bMu2dtOverNuL[newIndex], uniform01[1 + j/3]);
+
+ path[newIndex] = path[oldIndexL] +
+ (path[oldIndexR] - path[oldIndexL]) * y;
+ if (path[newIndex] <= path[oldIndexL])
+ setLarger (path, oldIndexL, newIndex, oldIndexR);
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ protected void init () {
+ super.init ();
+ if (observationTimesSet) {
+
+ /* Testing to make sure number of observations n = 2^k */
+ int k = 0;
+ int x = d;
+ int y = 1;
+ while (x>1) {
+ x = x / 2;
+ y = y * 2;
+ k++;
+ }
+ if (y != d) throw new IllegalArgumentException
+ ( "GammaProcessSymmetricalBridge:"
+ +"Number 'n' of observation times is not a power of 2" );
+
+ /* Testing that time intervals are equidistant */
+ boolean equidistant = true;
+ double macheps = 1.0e-13; // Num.DBL_EPSILON;
+ double dt = t[1] - t[0];
+ for (int i=1; i<d; i++) {
+ if ((t[i+1] - t[i]) != dt) { // not equidistant
+ equidistant = false;
+ /* This compensates the fact that the dt's
+ may be different due to numerical idiosyncracies */
+ if (dt != 0.0)
+ if (Math.abs ((t[i+1] - t[i]) - dt) / dt <= macheps)
+ equidistant = true;
+ }
+ }
+ if (!equidistant) throw new IllegalArgumentException
+ ( "GammaProcessSymmetricalBridge:"
+ +"Observation times of sample paths are not equidistant" );
+ }
+ }
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricBrownianMotion.java b/source/umontreal/iro/lecuyer/stochprocess/GeometricBrownianMotion.java
new file mode 100644
index 0000000..537c55e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricBrownianMotion.java
@@ -0,0 +1,218 @@
+
+
+/*
+ * Class: GeometricBrownianMotion
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * .
+ *
+ * Represents a <SPAN CLASS="textit">geometric Brownian motion</SPAN> (GBM) process
+ * <SPAN CLASS="MATH">{<I>S</I>(<I>t</I>), <I>t</I> >= 0}</SPAN>,
+ * which evolves according to the stochastic differential equation
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:GBM"></A>
+ * <I>dS</I>(<I>t</I>) = <I>μS</I>(<I>t</I>)<I>dt</I> + <I>σS</I>(<I>t</I>)<I>dB</I>(<I>t</I>),
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are the drift and volatility parameters,
+ * and
+ * <SPAN CLASS="MATH">{<I>B</I>(<I>t</I>), <I>t</I> >= 0}</SPAN> is a standard Brownian motion
+ * (for which
+ * <SPAN CLASS="MATH"><I>B</I>(<I>t</I>)∼<I>N</I>(0, <I>t</I>)</SPAN>).
+ * This process can also be written as the exponential of a Brownian motion:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:GBM2"></A>
+ * <I>S</I>(<I>t</I>) = <I>S</I>(0)exp[(<I>μ</I> - <I>σ</I><SUP>2</SUP>/2)<I>t</I> + <I>σtB</I>(<I>t</I>)] = <I>S</I>(0)exp[<I>X</I>(<I>t</I>)],
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I>) = (<I>μ</I> - <I>σ</I><SUP>2</SUP>/2)<I>t</I> + <I>σtB</I>(<I>t</I>)</SPAN>.
+ * The GBM process is simulated by simulating the BM process <SPAN CLASS="MATH"><I>X</I></SPAN> and taking the exponential.
+ * This BM process is stored internally.
+ *
+ */
+public class GeometricBrownianMotion extends StochasticProcess {
+
+ protected NormalGen gen;
+ protected BrownianMotion bm; // The underlying BM process X.
+ protected double mu,
+ sigma;
+ protected double[] mudt;
+
+
+
+ /**
+ * Same as <TT>GeometricBrownianMotion (s0, mu, sigma,
+ * new BrownianMotion (0.0, 0.0, 1.0, stream))</TT>.
+ *
+ */
+ public GeometricBrownianMotion (double s0, double mu, double sigma,
+ RandomStream stream) {
+ this (s0, mu, sigma, new BrownianMotion (0.0, 0.0, 1.0, stream));
+ }
+
+
+ /**
+ * Constructs a new <TT>GeometricBrownianMotion</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>, and
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>,
+ * using <TT>bm</TT> as the underlying {@link BrownianMotion}.
+ * The parameters of <TT>bm</TT> are automatically reset to
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> - <I>σ</I><SUP>2</SUP>/2</SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN>, regardless of the original parameters
+ * of <TT>bm</TT>.
+ * The observation times are the same as those of <TT>bm</TT>. The generation
+ * method depends on that of <TT>bm</TT> (sequential, bridge sampling, PCA, etc.).
+ *
+ */
+ public GeometricBrownianMotion (double s0, double mu, double sigma,
+ BrownianMotion bm) {
+ this.bm = bm;
+ setParams (s0, mu, sigma);
+ }
+
+ public void setObservationTimes (double[] t, int d) {
+ this.d = d;
+ super.setObservationTimes (t, d);
+ bm.setObservationTimes (t, d);
+ }
+
+ public double nextObservation() {
+ // Note : this implementation is general, to deal with
+ // the possibility of generating bm with bridge sampling, for example. ???
+
+ double s = x0 * Math.exp (bm.nextObservation());
+ observationIndex = bm.getCurrentObservationIndex();
+ path[observationIndex] = s;
+ // Could be different than simply 'observationCounter++' because of the
+ // possibility of Brownian bridge
+
+ return s;
+ }
+
+ public double[] generatePath() {
+ path[0] = x0;
+ bm.generatePath ();
+ for (int i = 1; i <= d; ++i)
+ path[i] = x0 * Math.exp (bm.getObservation(i));
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ setStream (stream);
+ return generatePath();
+ }
+
+ /**
+ * Same as in <TT>StochasticProcess</TT>, but also invokes
+ * <TT>resetStartProcess</TT> for the underlying <TT>BrownianMotion</TT> object.
+ *
+ */
+ public void resetStartProcess() {
+ observationCounter = 0;
+ bm.resetStartProcess();
+ }
+
+
+ /**
+ * Sets the parameters
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN> and
+ *
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called repeatedly.
+ *
+ */
+ public void setParams (double s0, double mu, double sigma) {
+ this.x0 = s0;
+ this.mu = mu;
+ this.sigma = sigma;
+ bm.setParams (0.0, mu - 0.5 * sigma * sigma, sigma);
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }
+
+
+ /**
+ * Resets the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * for the underlying Brownian motion to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) { (bm.gen).setStream (stream); }
+
+
+ /**
+ * Returns the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * for the underlying Brownian motion.
+ *
+ */
+ public RandomStream getStream() { return (bm.gen).getStream (); }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>μ</I></SPAN>.
+ *
+ */
+ public double getMu() { return mu; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() { return sigma; }
+
+
+ /**
+ * Returns the {@link umontreal.iro.lecuyer.randvar.NormalGen NormalGen} used.
+ *
+ */
+ public NormalGen getGen() { return gen; }
+
+
+ /**
+ * Returns a reference to the {@link BrownianMotion} object
+ * used to generate the process.
+ *
+ */
+ public BrownianMotion getBrownianMotion() {
+ return bm;
+ }
+
+
+ protected void init() {
+ super.init(); // Maybe useless...
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricBrownianMotion.tex b/source/umontreal/iro/lecuyer/stochprocess/GeometricBrownianMotion.tex
new file mode 100644
index 0000000..ddcbad6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricBrownianMotion.tex
@@ -0,0 +1,212 @@
+\defmodule {GeometricBrownianMotion}
+
+Represents a \emph{geometric Brownian motion} (GBM) process $\{S(t),\, t\ge 0\}$,
+which evolves according to the stochastic differential equation
+\begin{equation}
+ dS(t) = \mu S(t) dt + \sigma S(t) dB(t),
+ \label{eq:GBM}
+\end{equation}
+where $\mu$ and $\sigma$ are the drift and volatility parameters,
+and $\{B(t),\, t\ge 0\}$ is a standard Brownian motion
+(for which $B(t)\sim N(0,t)$).
+This process can also be written as the exponential of a Brownian motion:
+\begin{equation}
+ S(t) = S(0) \exp \left[ (\mu - \sigma^{2}/2) t + \sigma t B(t) \right]
+ = S(0) \exp \left[ X(t) \right],
+ \label{eq:GBM2}
+\end{equation}
+where $X(t) = (\mu - \sigma^{2}/2) t + \sigma t B(t)$.
+The GBM process is simulated by simulating the BM process $X$ and taking the exponential.
+This BM process is stored internally.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GeometricBrownianMotion
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class GeometricBrownianMotion extends StochasticProcess \begin{hide} {
+
+ protected NormalGen gen;
+ protected BrownianMotion bm; // The underlying BM process X.
+ protected double mu,
+ sigma;
+ protected double[] mudt;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GeometricBrownianMotion (double s0, double mu, double sigma,
+ RandomStream stream) \begin{hide} {
+ this (s0, mu, sigma, new BrownianMotion (0.0, 0.0, 1.0, stream));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Same as \texttt{GeometricBrownianMotion (s0, mu, sigma,
+new BrownianMotion (0.0, 0.0, 1.0, stream))}.
+\end{tabb}
+\begin{code}
+
+ public GeometricBrownianMotion (double s0, double mu, double sigma,
+ BrownianMotion bm) \begin{hide} {
+ this.bm = bm;
+ setParams (s0, mu, sigma);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{GeometricBrownianMotion} with parameters
+ $\mu = \texttt{mu}$, $\sigma = \texttt{sigma}$, and $S(t_0) = \texttt{s0}$,
+using \texttt{bm} as the underlying \class{BrownianMotion}.
+The parameters of \texttt{bm} are automatically reset to
+$\mu-\sigma^2/2$ and $\sigma$, regardless of the original parameters
+ of \texttt{bm}.
+The observation times are the same as those of \texttt{bm}. The generation
+method depends on that of \texttt{bm} (sequential, bridge sampling, PCA, etc.).
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}\begin{hide}
+ public void setObservationTimes (double[] t, int d) {
+ this.d = d;
+ super.setObservationTimes (t, d);
+ bm.setObservationTimes (t, d);
+ }
+
+ public double nextObservation() {
+ // Note : this implementation is general, to deal with
+ // the possibility of generating bm with bridge sampling, for example. ???
+
+ double s = x0 * Math.exp (bm.nextObservation());
+ observationIndex = bm.getCurrentObservationIndex();
+ path[observationIndex] = s;
+ // Could be different than simply 'observationCounter++' because of the
+ // possibility of Brownian bridge
+
+ return s;
+ }
+
+ public double[] generatePath() {
+ path[0] = x0;
+ bm.generatePath ();
+ for (int i = 1; i <= d; ++i)
+ path[i] = x0 * Math.exp (bm.getObservation(i));
+ observationCounter = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ setStream (stream);
+ return generatePath();
+ }\end{hide}
+
+ public void resetStartProcess() \begin{hide} {
+ observationCounter = 0;
+ bm.resetStartProcess();
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as in \texttt{StochasticProcess}, but also invokes
+\texttt{resetStartProcess} for the underlying \texttt{BrownianMotion} object.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double s0, double mu, double sigma) \begin{hide} {
+ this.x0 = s0;
+ this.mu = mu;
+ this.sigma = sigma;
+ bm.setParams (0.0, mu - 0.5 * sigma * sigma, sigma);
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Sets the parameters $S(t_{0}) = \texttt{s0}$, $\mu = \texttt{mu}$ and
+$\sigma = \texttt{sigma}$ of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called repeatedly.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} { (bm.gen).setStream (stream); }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+for the underlying Brownian motion to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} { return (bm.gen).getStream (); }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+for the underlying Brownian motion.
+\end{tabb}
+\begin{code}
+
+ public double getMu() \begin{hide} { return mu; }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $\mu$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma() \begin{hide} { return sigma; }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public NormalGen getGen() \begin{hide} { return gen; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the \externalclass{umontreal.iro.lecuyer.randvar}{NormalGen} used.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotion getBrownianMotion() \begin{hide} {
+ return bm;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to the \class{BrownianMotion} object
+used to generate the process.
+\end{tabb}
+
+\begin{code} \begin{hide}
+
+ protected void init() {
+ super.init(); // Maybe useless...
+ }
+
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricLevyProcess.java b/source/umontreal/iro/lecuyer/stochprocess/GeometricLevyProcess.java
new file mode 100644
index 0000000..7705f2e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricLevyProcess.java
@@ -0,0 +1,227 @@
+
+
+/*
+ * Class: GeometricLevyProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * .
+ *
+ * Abstract class used as a parent class for the exponentiation
+ * of a Lévy process <SPAN CLASS="MATH"><I>X</I>(<I>t</I>)</SPAN>:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>S</I>(<I>t</I>) = <I>S</I>(0)exp(<I>X</I>(<I>t</I>) + (<I>r</I> - <I>ω</I><SUB>RN</SUB>)<I>t</I>).
+ * </DIV><P></P>
+ * The interest is here denoted <SPAN CLASS="MATH"><I>r</I></SPAN> and is refered to
+ * as <TT>muGeom</TT> in the class below.
+ * The risk neutral correction is given by
+ * <SPAN CLASS="MATH"><I>ω</I><SUB>RN</SUB></SPAN>
+ * and takes into account risk aversion in the pricing of
+ * assets; its value depends on the specific Lévy process
+ * that is used.
+ *
+ * <P>
+ * {@link GeometricNormalInverseGaussianProcess} is
+ * implemented as a child of this class
+ * and so could {@link GeometricVarianceGammaProcess}
+ * and {@link GeometricBrownianMotion}.
+ *
+ */
+public abstract class GeometricLevyProcess extends StochasticProcess {
+
+ protected StochasticProcess levyProcess;
+ protected double omegaRiskNeutralCorrection;
+ protected double muGeom; // usually the interest rate
+ protected double[] muGeomRNdt; // risk neutral corrected
+ protected double[] muGeomRNdT; // risk neutral corrected, from time t0.
+
+ protected void init()
+ {
+ super.init();
+ if (observationTimesSet)
+ {
+ // Telling the variance gamma proc. about the observ. times
+ levyProcess.setObservationTimes (t, d);
+
+ // We need to know in which order the observations are generated
+ this.observationIndexFromCounter
+ = levyProcess.getArrayMappingCounterToIndex();
+
+ muGeomRNdt = new double[d];
+ for (int i = 0; i < d; i++){
+ muGeomRNdt[i] = (muGeom-omegaRiskNeutralCorrection) *
+ (t[i+1] - t[i]);
+ }
+ muGeomRNdT = new double[d+1];
+ for (int i = 0; i <= d; i++){
+ muGeomRNdT[i] = (muGeom-omegaRiskNeutralCorrection) *
+ (t[i] - t[0]);
+ }
+ }
+ }
+
+
+
+ /**
+ * Generates a path.
+ *
+ */
+ public double[] generatePath() {
+ double s = x0;
+ resetStartProcess();
+ double[] arithmPath = levyProcess.generatePath();
+ for (int i = 0; i < d; i++)
+ {
+ s *= Math.exp (muGeomRNdt[i] + arithmPath[i+1] - arithmPath[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+
+ /**
+ * Returns the next observation.
+ * It will also work on a Lévy process which is sampled using
+ * the bridge order, but it will return the observations in
+ * the bridge order.
+ * If the underlying Lévy process is of the PCA type, this
+ * method is not usable.
+ *
+ */
+ public double nextObservation() {
+ double levy = levyProcess.nextObservation();
+ observationIndex = levyProcess.getCurrentObservationIndex();
+ path[observationIndex] = x0 *
+ Math.exp( muGeomRNdT[observationIndex] + levy );
+ return path[observationIndex];
+ }
+
+
+ /**
+ * Resets the step counter of the geometric process and
+ * the underlying Lévy process to the start value.
+ *
+ */
+ public void resetStartProcess() {
+ super.init();
+ levyProcess.resetStartProcess();
+ }
+
+
+ /**
+ * Sets the observation times on the geometric process
+ * and the underlying Lévy process.
+ *
+ */
+ public void setObservationTimes(double[] time, int d) {
+ super.setObservationTimes(time, d);
+ levyProcess.setObservationTimes(time, d);
+ }
+
+
+ /**
+ * Returns the risk neutral correction.
+ *
+ */
+ public double getOmega() {
+ return omegaRiskNeutralCorrection;
+ }
+
+
+ /**
+ * Returns the geometric drift parameter,
+ * which is usually the interest rate, <SPAN CLASS="MATH"><I>r</I></SPAN>.
+ *
+ */
+ public double getMuGeom() {
+ return muGeom;
+ }
+
+
+ /**
+ * Sets the drift parameter (interest rate) of the geometric term.
+ *
+ */
+ public void setMuGeom (double muGeom) {
+ this.muGeom = muGeom;
+ }
+
+
+ /**
+ * Returns the Lévy process.
+ *
+ */
+ public StochasticProcess getLevyProcess() {
+ return levyProcess;
+ }
+
+
+ /**
+ * Changes the value of
+ * <SPAN CLASS="MATH"><I>ω</I><SUB>RN</SUB></SPAN>.
+ * There should usually be no need to redefine the risk neutral
+ * correction from the value set by the constructor. However it
+ * is sometimes not unique, e.g. in {@link GeometricNormalInverseGaussianProcess}.
+ *
+ */
+ public void resetRiskNeutralCorrection (double omegaRN) {
+ omegaRiskNeutralCorrection = omegaRN;
+ init();
+ }
+
+
+ /**
+ * Returns the stream from the underlying Lévy process.
+ * If the underlying Lévy process has multiple streams, it returns
+ * what the <TT>getStream()</TT> method of that process was made to return.
+ *
+ */
+ public RandomStream getStream() {
+ return levyProcess.getStream();
+ }
+
+
+ /**
+ * Resets the stream in the underlying Lévy process.
+ * If the underlying Lévy process has multiple streams, it sets
+ * the streams on this process in the same way as <TT>setStream()</TT>
+ * for that process.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ levyProcess.setStream(stream);
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricLevyProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/GeometricLevyProcess.tex
new file mode 100644
index 0000000..1f5abeb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricLevyProcess.tex
@@ -0,0 +1,220 @@
+\defmodule {GeometricLevyProcess}
+
+Abstract class used as a parent class for the exponentiation
+of a L\'evy process $X(t)$:
+\begin{equation}
+S(t) = S(0) \exp\left(X(t) + (r - \omega_{RN}) t\right).
+\end{equation}
+The interest is here denoted $r$ and is refered to
+as \texttt{muGeom} in the class below.
+The risk neutral correction is given by $\omega_{RN}$
+and takes into account risk aversion in the pricing of
+assets; its value depends on the specific L\'evy process
+that is used.
+
+\class{GeometricNormalInverseGaussianProcess} is
+implemented as a child of this class
+and so could \class{GeometricVarianceGammaProcess}
+and \class{GeometricBrownianMotion}.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GeometricLevyProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public abstract class GeometricLevyProcess extends StochasticProcess \begin{hide} {
+
+ protected StochasticProcess levyProcess;
+ protected double omegaRiskNeutralCorrection;
+ protected double muGeom; // usually the interest rate
+ protected double[] muGeomRNdt; // risk neutral corrected
+ protected double[] muGeomRNdT; // risk neutral corrected, from time t0.
+
+ protected void init()
+ {
+ super.init();
+ if (observationTimesSet)
+ {
+ // Telling the variance gamma proc. about the observ. times
+ levyProcess.setObservationTimes (t, d);
+
+ // We need to know in which order the observations are generated
+ this.observationIndexFromCounter
+ = levyProcess.getArrayMappingCounterToIndex();
+
+ muGeomRNdt = new double[d];
+ for (int i = 0; i < d; i++){
+ muGeomRNdt[i] = (muGeom-omegaRiskNeutralCorrection) *
+ (t[i+1] - t[i]);
+ }
+ muGeomRNdT = new double[d+1];
+ for (int i = 0; i <= d; i++){
+ muGeomRNdT[i] = (muGeom-omegaRiskNeutralCorrection) *
+ (t[i] - t[0]);
+ }
+ }
+ }
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double[] generatePath() \begin{hide} {
+ double s = x0;
+ resetStartProcess();
+ double[] arithmPath = levyProcess.generatePath();
+ for (int i = 0; i < d; i++)
+ {
+ s *= Math.exp (muGeomRNdt[i] + arithmPath[i+1] - arithmPath[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates a path.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation() \begin{hide} {
+ double levy = levyProcess.nextObservation();
+ observationIndex = levyProcess.getCurrentObservationIndex();
+ path[observationIndex] = x0 *
+ Math.exp( muGeomRNdT[observationIndex] + levy );
+ return path[observationIndex];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the next observation.
+It will also work on a L\'evy process which is sampled using
+the bridge order, but it will return the observations in
+the bridge order.
+If the underlying L\'evy process is of the PCA type, this
+method is not usable.
+\end{tabb}
+\begin{code}
+
+ public void resetStartProcess() \begin{hide} {
+ super.init();
+ levyProcess.resetStartProcess();
+ }\end{hide}
+\end{code}
+\begin{tabb} Resets the step counter of the geometric process and
+the underlying L\'evy process to the start value.
+\end{tabb}
+\begin{code}
+
+ public void setObservationTimes(double[] time, int d) \begin{hide} {
+ super.setObservationTimes(time, d);
+ levyProcess.setObservationTimes(time, d);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the observation times on the geometric process
+and the underlying L\'evy process.
+\end{tabb}
+\begin{code}
+
+ public double getOmega() \begin{hide} {
+ return omegaRiskNeutralCorrection;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the risk neutral correction.
+\end{tabb}
+\begin{code}
+
+ public double getMuGeom() \begin{hide} {
+ return muGeom;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the geometric drift parameter,
+which is usually the interest rate, $r$.
+\end{tabb}
+\begin{code}
+
+ public void setMuGeom (double muGeom) \begin{hide} {
+ this.muGeom = muGeom;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the drift parameter (interest rate) of the geometric term.
+\end{tabb}
+\begin{code}
+
+ public StochasticProcess getLevyProcess() \begin{hide} {
+ return levyProcess;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the L\'evy process.
+\end{tabb}
+\begin{code}
+
+ public void resetRiskNeutralCorrection (double omegaRN) \begin{hide} {
+ omegaRiskNeutralCorrection = omegaRN;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Changes the value of $\omega_{RN}$.
+There should usually be no need to redefine the risk neutral
+correction from the value set by the constructor. However it
+is sometimes not unique, e.g. in \class{GeometricNormalInverseGaussianProcess}
+\cite{fALB04a}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ return levyProcess.getStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the stream from the underlying L\'evy process.
+If the underlying L\'evy process has multiple streams, it returns
+what the \texttt{getStream()} method of that process was made to return.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ levyProcess.setStream(stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Resets the stream in the underlying L\'evy process.
+If the underlying L\'evy process has multiple streams, it sets
+the streams on this process in the same way as \texttt{setStream()}
+for that process.
+\end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricNormalInverseGaussianProcess.java b/source/umontreal/iro/lecuyer/stochprocess/GeometricNormalInverseGaussianProcess.java
new file mode 100644
index 0000000..07790e5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricNormalInverseGaussianProcess.java
@@ -0,0 +1,153 @@
+
+
+/*
+ * Class: GeometricNormalInverseGaussianProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * .
+ *
+ * The geometric normal inverse gaussian (GNIG) process
+ * is the exponentiation of a {@link NormalInverseGaussianProcess}:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>S</I>(<I>t</I>) = <I>S</I><SUB>0</SUB>exp[(<I>r</I> - <I>ω</I><SUB>RN</SUB>)<I>t</I> + NIG(<I>t</I>;<I>α</I>, <I>β</I>, <I>μ</I>, <I>δ</I>)],
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>r</I></SPAN> is the interest rate.
+ * It is a strictly positive process, which is useful in finance.
+ * There is also a neutral
+ * correction in the exponential,
+ * <SPAN CLASS="MATH"><I>ω</I><SUB>RN</SUB> = <I>μ</I> + <I>δγ</I> - <I>δ</I>(α^2-(1+β)^2)<SUP>1/2</SUP></SPAN>,
+ * which takes into account the market price of risk.
+ * The underlying NIG process must start at zero, NIG<SPAN CLASS="MATH">(<I>t</I><SUB>0</SUB>) = 0</SPAN>
+ * and the initial time should also be set to zero, <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB> = 0</SPAN>,
+ * both for the NIG and GNIG.
+ *
+ */
+public class GeometricNormalInverseGaussianProcess extends
+ GeometricLevyProcess {
+
+
+
+ /**
+ * Constructs a new <TT>GeometricNormalInverseGaussianProcess</TT>.
+ * The parameters of the NIG process will be overwritten by the parameters
+ * given to the GNIG, with the initial value of the NIG set to 0.
+ * The observation times of the NIG will also be changed to
+ * those of the GNIG.
+ *
+ */
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamBrownian,
+ NormalInverseGaussianProcess nigP) {
+ levyProcess = nigP;
+ ((NormalInverseGaussianProcess)levyProcess).setParams(0.0, alpha, beta, mu, delta);
+ this.x0 = s0;
+ this.muGeom = muGeom;
+ omegaRiskNeutralCorrection =
+ mu + delta*((NormalInverseGaussianProcess)levyProcess).getGamma() -
+ delta*Math.sqrt(alpha*alpha - (1.+beta)*(1.+beta));
+ }
+
+
+ /**
+ * Constructs a new <TT>GeometricNormalInverseGaussianProcess</TT>.
+ * The process <TT>igP</TT>
+ * will be used internally by the underlying {@link NormalInverseGaussianProcess}.
+ *
+ */
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamBrownian,
+ InverseGaussianProcess igP) {
+
+ levyProcess = new NormalInverseGaussianProcess (0.0, alpha, beta, mu,
+ delta, streamBrownian, igP);
+ this.x0 = s0;
+ this.muGeom = muGeom;
+ omegaRiskNeutralCorrection =
+ mu + delta*((NormalInverseGaussianProcess)levyProcess).getGamma() -
+ delta*Math.sqrt(alpha*alpha - (1.+beta)*(1.+beta));
+ }
+
+
+ /**
+ * Constructs a new <TT>GeometricNormalInverseGaussianProcess</TT>.
+ * The drift of the geometric term, <TT>muGeom</TT>, is usually the interest rate <SPAN CLASS="MATH"><I>r</I></SPAN>.
+ * <TT>s0</TT> is the initial value of the process and the other four parameters
+ * are the parameters of the underlying {@link NormalInverseGaussianProcess} process.
+ *
+ */
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamBrownian,
+ RandomStream streamNIG1,
+ RandomStream streamNIG2,
+ String igType) {
+ levyProcess = new NormalInverseGaussianProcess (0.0, alpha, beta, mu, delta,
+ streamBrownian, streamNIG1,
+ streamNIG2, igType);
+ this.x0 = s0;
+ this.muGeom = muGeom;
+ omegaRiskNeutralCorrection =
+ mu + delta*((NormalInverseGaussianProcess)levyProcess).getGamma() -
+ delta*Math.sqrt(alpha*alpha - (1.+beta)*(1.+beta));
+ }
+
+
+ /**
+ * Constructs a new <TT>GeometricNormalInverseGaussianProcess</TT>.
+ * The String <TT>igType</TT> corresponds to the type of {@link InverseGaussianProcess}
+ * that will be used by the underlying {@link NormalInverseGaussianProcess}.
+ * All {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s used to generate the underlying
+ * {@link NormalInverseGaussianProcess} and its underlying {@link InverseGaussianProcess}
+ * are set to the same given <TT>streamAll</TT>.
+ *
+ */
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamAll,
+ String igType) {
+ this(s0,muGeom,alpha,beta,mu,delta,streamAll,streamAll,streamAll,igType);
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricNormalInverseGaussianProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/GeometricNormalInverseGaussianProcess.tex
new file mode 100644
index 0000000..dec7a84
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricNormalInverseGaussianProcess.tex
@@ -0,0 +1,153 @@
+\defmodule {GeometricNormalInverseGaussianProcess}
+
+
+The geometric normal inverse gaussian (GNIG) process
+is the exponentiation of a \class{Normal\-Inverse\-Gaussian\-Process}:
+\begin{equation}
+S(t) = S_0 \exp\left[ (r-\omega_{RN})t + \mbox{NIG}(t;\alpha,\beta,\mu,\delta) \right],
+\end{equation}
+where $r$ is the interest rate.
+It is a strictly positive process, which is useful in finance.
+There is also a neutral
+correction in the exponential, $\omega_{RN}= \mu + \delta\gamma-\delta
+\sqrt{\alpha^2-(1+\beta)^2}$,
+which takes into account the market price of risk.
+The underlying NIG process must start at zero, NIG$(t_0) = 0 $
+and the initial time should also be set to zero, $t_0 = 0$,
+both for the NIG and GNIG.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GeometricNormalInverseGaussianProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class GeometricNormalInverseGaussianProcess extends
+ GeometricLevyProcess \begin{hide} {
+\end{hide}
+\end{code}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamBrownian,
+ NormalInverseGaussianProcess nigP)\begin{hide} {
+ levyProcess = nigP;
+ ((NormalInverseGaussianProcess)levyProcess).setParams(0.0, alpha, beta, mu, delta);
+ this.x0 = s0;
+ this.muGeom = muGeom;
+ omegaRiskNeutralCorrection =
+ mu + delta*((NormalInverseGaussianProcess)levyProcess).getGamma() -
+ delta*Math.sqrt(alpha*alpha - (1.+beta)*(1.+beta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{Geometric\-Normal\-Inverse\-Gaussian\-Process}.
+The parameters of the NIG process will be overwritten by the parameters
+given to the GNIG, with the initial value of the NIG set to 0.
+The observation times of the NIG will also be changed to
+those of the GNIG.
+\end{tabb}
+\begin{code}
+
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamBrownian,
+ InverseGaussianProcess igP) \begin{hide} {
+
+ levyProcess = new NormalInverseGaussianProcess (0.0, alpha, beta, mu,
+ delta, streamBrownian, igP);
+ this.x0 = s0;
+ this.muGeom = muGeom;
+ omegaRiskNeutralCorrection =
+ mu + delta*((NormalInverseGaussianProcess)levyProcess).getGamma() -
+ delta*Math.sqrt(alpha*alpha - (1.+beta)*(1.+beta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GeometricNormalInverseGaussianProcess}.
+The process \texttt{igP}
+will be used internally by the underlying \class{NormalInverseGaussianProcess}.
+\end{tabb}
+\begin{code}
+
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamBrownian,
+ RandomStream streamNIG1,
+ RandomStream streamNIG2,
+ String igType) \begin{hide} {
+ levyProcess = new NormalInverseGaussianProcess (0.0, alpha, beta, mu, delta,
+ streamBrownian, streamNIG1,
+ streamNIG2, igType);
+ this.x0 = s0;
+ this.muGeom = muGeom;
+ omegaRiskNeutralCorrection =
+ mu + delta*((NormalInverseGaussianProcess)levyProcess).getGamma() -
+ delta*Math.sqrt(alpha*alpha - (1.+beta)*(1.+beta));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GeometricNormalInverseGaussianProcess}.
+The drift of the geometric term, \texttt{muGeom}, is usually the interest rate $r$.
+\texttt{s0} is the initial value of the process and the other four parameters
+are the parameters of the underlying \class{NormalInverseGaussianProcess} process.
+\end{tabb}
+
+\begin{code}
+
+ public GeometricNormalInverseGaussianProcess (
+ double s0, double muGeom,
+ double alpha, double beta,
+ double mu, double delta,
+ RandomStream streamAll,
+ String igType) \begin{hide} {
+ this(s0,muGeom,alpha,beta,mu,delta,streamAll,streamAll,streamAll,igType);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{GeometricNormalInverseGaussianProcess}.
+The String \texttt{igType} corresponds to the type of \class{InverseGaussianProcess}
+that will be used by the underlying \class{NormalInverseGaussianProcess}.
+All \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s used to generate the underlying
+\class{NormalInverseGaussianProcess} and its underlying \class{InverseGaussianProcess}
+are set to the same given \texttt{streamAll}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricVarianceGammaProcess.java b/source/umontreal/iro/lecuyer/stochprocess/GeometricVarianceGammaProcess.java
new file mode 100644
index 0000000..6b87be6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricVarianceGammaProcess.java
@@ -0,0 +1,284 @@
+
+
+/*
+ * Class: GeometricVarianceGammaProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre Tremblay
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * This class represents a <SPAN CLASS="textit">geometric variance gamma</SPAN> process <SPAN CLASS="MATH"><I>S</I>(<I>t</I>)</SPAN>
+ * (see). This stochastic process is defined by the
+ * equation
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="GeoVGeqn"></A>
+ * <I>S</I>(<I>t</I>) = <I>S</I>(0) exp(<I>μt</I> + <I>X</I>(<I>t</I>;<I>σ</I>, <I>ν</I>, <I>θ</I>) + <I>ωt</I>),
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>X</I></SPAN> is a variance gamma process and
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="omegaEqn"></A>
+ * <I>ω</I> = (1/<I>ν</I>) ln(1 - <I>θν</I> - <I>σ</I><SUP>2</SUP><I>ν</I>/2).
+ * </DIV><P></P>
+ *
+ */
+public class GeometricVarianceGammaProcess extends StochasticProcess {
+ protected VarianceGammaProcess vargamma;
+ protected double theta,
+ nu,
+ mu,
+ sigma,
+ omega,
+ muPlusOmega;
+ protected double[] mudt;
+
+
+
+ /**
+ * Constructs a new <TT>GeometricVarianceGammaProcess</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN> and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * The <TT>stream</TT> is used to generate the {@link VarianceGammaProcess} object used to implement
+ * <SPAN CLASS="MATH"><I>X</I></SPAN> in.
+ *
+ */
+ public GeometricVarianceGammaProcess (double s0, double theta,
+ double sigma, double nu,
+ double mu, RandomStream stream) {
+ vargamma = new VarianceGammaProcess (0.0, theta, sigma, nu, stream);
+ setParams (s0, theta, sigma, nu, mu);
+ }
+
+
+ /**
+ * Constructs a new <TT>GeometricVarianceGammaProcess</TT>.
+ * The parameters
+ * <SPAN CLASS="MATH"><I>θ</I>, <I>σ</I>, <I>ν</I></SPAN> are set to the parameters of the
+ * {@link VarianceGammaProcess} <TT>vargamma</TT>. The parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>
+ * is set to <TT>mu</TT> and the initial values
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ *
+ */
+ public GeometricVarianceGammaProcess (double s0, double mu,
+ VarianceGammaProcess vargamma)
+ {
+ this.vargamma = vargamma;
+ setParams (s0, vargamma.getTheta (), vargamma.getSigma (),
+ vargamma.getNu (), mu);
+ }
+
+
+ public double nextObservation() {
+ double nextX = vargamma.nextObservation();
+ observationIndex = vargamma.getCurrentObservationIndex();
+ // Could be different than simply 'observationIndex++' because of the
+ // possibility of Gamma/Brownian bridge
+ observationCounter++;
+
+ double s = x0 * Math.exp (muPlusOmega * (t[observationIndex] - t[0])
+ + nextX);
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double[] generatePath() {
+ double s = x0;
+ resetStartProcess();
+ double[] vgpath = vargamma.generatePath();
+ for (int i = 0; i < d; i++) {
+ s *= Math.exp (mudt[i] + vgpath[i+1] - vgpath[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter++;
+ return path;
+ }
+ // allows the user to create a path by specifiying the uniform random numbers to be used
+ public double[] generatePath (double[] uniform01) {
+ double s = x0;
+ resetStartProcess();
+
+ double[] vgpath = vargamma.generatePath(uniform01);
+ for (int i = 0; i < d; i++) {
+ s *= Math.exp (mudt[i] + vgpath[i+1] - vgpath[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter++;
+ return path;
+ }
+
+
+ // method not verified by JS... old stuff
+ public double getCurrentUpperBound() {
+ // Find index for last observation generated (chronologically)
+ int j = 0; // By default, t0 !
+ int i = observationCounter - 1;
+ double tForIthObserv;
+ while (i > 0) {
+ tForIthObserv = t[observationIndexFromCounter[i]];
+ if (tForIthObserv <= t[observationCounter] && tForIthObserv > t[j])
+ j = i;
+ i--;
+ }
+
+ // Calculate bound following recipe
+ double u = 0.0;
+ GammaProcess gpos = ((VarianceGammaProcessDiff) vargamma).getGpos();
+ double[] gposPath = gpos.getPath();
+ double deltaGpos = gposPath[observationIndex] - gposPath[j];
+ double s = path[observationIndex];
+ if (muPlusOmega < 0)
+ u = s * Math.exp (deltaGpos);
+ else u = s * Math.exp (muPlusOmega * (t[observationIndex] - t[j])
+ + deltaGpos);
+ return u;
+ }
+
+
+ /**
+ * Resets the <TT>GeometricaVarianceGammaProcess</TT>,
+ * but also applies the <TT>resetStartProcess</TT> method to the
+ * {@link VarianceGammaProcess} object used to generate this process.
+ *
+ */
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ vargamma.resetStartProcess();
+ }
+
+
+ /**
+ * Sets the parameters
+ *
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ *
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN> and
+ * <SPAN CLASS="MATH"><I>μ</I> = <texttt>mu</texttt></SPAN> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called repeatedly.
+ *
+ */
+ public void setParams (double s0, double theta, double sigma, double nu,
+ double mu) {
+ this.x0 = s0;
+ this.theta = theta;
+ this.sigma = sigma;
+ this.nu = nu;
+ this.mu = mu;
+ if (observationTimesSet) init(); // Otherwise no need to.
+ }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>θ</I></SPAN>.
+ *
+ */
+ public double getTheta() { return theta; }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>μ</I></SPAN>.
+ *
+ */
+ public double getMu() { return mu; }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ *
+ */
+ public double getNu() { return nu; }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() { return sigma; }
+
+
+ /**
+ * Returns the value of the quantity <SPAN CLASS="MATH"><I>ω</I></SPAN> defined in.
+ *
+ */
+ public double getOmega() { return omega; }
+
+
+ /**
+ * Returns a reference to the variance gamma process <SPAN CLASS="MATH"><I>X</I></SPAN> defined
+ * in the constructor.
+ *
+ */
+ public VarianceGammaProcess getVarianceGammaProcess() {
+ return vargamma;
+}
+
+
+ protected void init() {
+ super.init();
+ if (1 <= theta*nu + sigma*sigma*nu / 2.0)
+ throw new IllegalArgumentException ("theta*nu + sigma*sigma*nu / 2 >= 1");
+ omega = Math.log (1 - theta*nu - sigma*sigma*nu / 2.0) / nu;
+ muPlusOmega = mu + omega;
+
+ if (observationTimesSet) {
+ // Telling the variance gamma proc. about the observ. times
+ vargamma.setObservationTimes (t, d);
+
+ // We need to know in which order the observations are generated
+ this.observationIndexFromCounter
+ = vargamma.getArrayMappingCounterToIndex();
+
+ mudt = new double[d];
+ for (int i = 0; i < d; i++)
+ mudt[i] = muPlusOmega * (t[i+1] - t[i]);
+ }
+ }
+
+
+ public void setStream (RandomStream stream) {
+ vargamma.setStream(stream);
+ }
+
+
+ public RandomStream getStream () {
+ return vargamma.getStream();
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/GeometricVarianceGammaProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/GeometricVarianceGammaProcess.tex
new file mode 100644
index 0000000..c637552
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/GeometricVarianceGammaProcess.tex
@@ -0,0 +1,290 @@
+%
+% Pierre Tremblay, July 2003.
+%
+
+\defmodule {GeometricVarianceGammaProcess}
+
+This class represents a \emph{geometric variance gamma} process $S(t)$
+(see \cite[page 86]{fMAD98a}). This stochastic process is defined by the
+equation
+\eq
+S(t) = S(0) \mbox{ exp}(\mu t + X(t; \sigma, \nu, \theta) + \omega t),
+\label{GeoVGeqn}
+\endeq
+where $X$ is a variance gamma process and
+\eq
+\omega = (1/\nu) \mbox{ ln}( 1 - \theta \nu - \sigma^{2} \nu /2).
+\label{omegaEqn}
+\endeq
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: GeometricVarianceGammaProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Pierre Tremblay
+ * @since July 2003
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class GeometricVarianceGammaProcess extends StochasticProcess \begin{hide} {
+ protected VarianceGammaProcess vargamma;
+ protected double theta,
+ nu,
+ mu,
+ sigma,
+ omega,
+ muPlusOmega;
+ protected double[] mudt;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public GeometricVarianceGammaProcess (double s0, double theta,
+ double sigma, double nu,
+ double mu, RandomStream stream) \begin{hide} {
+ vargamma = new VarianceGammaProcess (0.0, theta, sigma, nu, stream);
+ setParams (s0, theta, sigma, nu, mu);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{GeometricVarianceGammaProcess} with parameters
+$\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$, $\nu = \texttt{nu}$,
+$\mu = \texttt{mu}$ and initial value $S(t_{0}) = \texttt{s0}$.
+The \texttt{stream} is used to generate the \class{VarianceGammaProcess} object used to implement
+$X$ in (\ref{GeoVGeqn}).
+\end{tabb}
+\begin{code}
+
+ public GeometricVarianceGammaProcess (double s0, double mu,
+ VarianceGammaProcess vargamma)
+ \begin{hide} {
+ this.vargamma = vargamma;
+ setParams (s0, vargamma.getTheta (), vargamma.getSigma (),
+ vargamma.getNu (), mu);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{GeometricVarianceGammaProcess}.
+The parameters $\theta, \sigma, \nu$ are set to the parameters of the
+ \class{VarianceGammaProcess} \texttt{vargamma}. The parameter $\mu$
+is set to \texttt{mu} and the initial values $S(t_{0}) = \texttt{s0}$.
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{hide}\begin{code}
+
+ public double nextObservation() {
+ double nextX = vargamma.nextObservation();
+ observationIndex = vargamma.getCurrentObservationIndex();
+ // Could be different than simply 'observationIndex++' because of the
+ // possibility of Gamma/Brownian bridge
+ observationCounter++;
+
+ double s = x0 * Math.exp (muPlusOmega * (t[observationIndex] - t[0])
+ + nextX);
+ path[observationIndex] = s;
+ return s;
+ }
+
+ public double[] generatePath() {
+ double s = x0;
+ resetStartProcess();
+ double[] vgpath = vargamma.generatePath();
+ for (int i = 0; i < d; i++) {
+ s *= Math.exp (mudt[i] + vgpath[i+1] - vgpath[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter++;
+ return path;
+ }
+ // allows the user to create a path by specifiying the uniform random numbers to be used
+ public double[] generatePath (double[] uniform01) {
+ double s = x0;
+ resetStartProcess();
+
+ double[] vgpath = vargamma.generatePath(uniform01);
+ for (int i = 0; i < d; i++) {
+ s *= Math.exp (mudt[i] + vgpath[i+1] - vgpath[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter++;
+ return path;
+ }
+
+
+ // method not verified by JS... old stuff
+ public double getCurrentUpperBound() {
+ // Find index for last observation generated (chronologically)
+ int j = 0; // By default, t0 !
+ int i = observationCounter - 1;
+ double tForIthObserv;
+ while (i > 0) {
+ tForIthObserv = t[observationIndexFromCounter[i]];
+ if (tForIthObserv <= t[observationCounter] && tForIthObserv > t[j])
+ j = i;
+ i--;
+ }
+
+ // Calculate bound following recipe
+ double u = 0.0;
+ GammaProcess gpos = ((VarianceGammaProcessDiff) vargamma).getGpos();
+ double[] gposPath = gpos.getPath();
+ double deltaGpos = gposPath[observationIndex] - gposPath[j];
+ double s = path[observationIndex];
+ if (muPlusOmega < 0)
+ u = s * Math.exp (deltaGpos);
+ else u = s * Math.exp (muPlusOmega * (t[observationIndex] - t[j])
+ + deltaGpos);
+ return u;
+ }
+\end{code}
+% \begin{tabb}
+% Returns the upper bound defined as $U_{i}$ in eqns (11) and (12) of
+% (CITE:AVRAM03), for $i$ the current observation index.
+% It has the property that $S(t) \leq U_{i}$
+% for $t_{j} \leq t \leq t_{i}$, where $j$ is the index of .
+% \emph{Warning:} The variance gamma process $X$ must be an instance
+% of \texttt{VarianceGammaProcessDiff} in order for this upper bound to be well defined.
+% \end{tabb}
+\end{hide}\begin{code}
+
+ public void resetStartProcess() \begin{hide} {
+ observationIndex = 0;
+ observationCounter = 0;
+ vargamma.resetStartProcess();
+ }\end{hide}
+\end{code}
+\begin{tabb} Resets the \texttt{GeometricaVarianceGammaProcess},
+but also applies the \texttt{resetStartProcess} method to the
+\class{VarianceGammaProcess} object used to generate this process.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double s0, double theta, double sigma, double nu,
+ double mu) \begin{hide} {
+ this.x0 = s0;
+ this.theta = theta;
+ this.sigma = sigma;
+ this.nu = nu;
+ this.mu = mu;
+ if (observationTimesSet) init(); // Otherwise no need to.
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Sets the parameters
+$S(t_{0}) = \texttt{s0}$, $\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$,
+$\nu = \texttt{nu}$ and $\mu = \texttt{mu}$ of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called repeatedly.
+\end{tabb}
+\begin{code}
+
+ public double getTheta() \begin{hide} { return theta; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the parameter $\theta$.
+\end{tabb}
+\begin{code}
+
+ public double getMu() \begin{hide} { return mu; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the parameter $\mu$.
+\end{tabb}
+\begin{code}
+
+ public double getNu() \begin{hide} { return nu; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the parameter $\nu$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma() \begin{hide} { return sigma; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the parameter $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public double getOmega() \begin{hide} { return omega; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the quantity $\omega$ defined in (\ref{omegaEqn}).
+\end{tabb}
+\begin{code}
+
+ public VarianceGammaProcess getVarianceGammaProcess() \begin{hide} {
+ return vargamma;
+}\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to the variance gamma process $X$ defined
+in the constructor.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void init() {
+ super.init();
+ if (1 <= theta*nu + sigma*sigma*nu / 2.0)
+ throw new IllegalArgumentException ("theta*nu + sigma*sigma*nu / 2 >= 1");
+ omega = Math.log (1 - theta*nu - sigma*sigma*nu / 2.0) / nu;
+ muPlusOmega = mu + omega;
+
+ if (observationTimesSet) {
+ // Telling the variance gamma proc. about the observ. times
+ vargamma.setObservationTimes (t, d);
+
+ // We need to know in which order the observations are generated
+ this.observationIndexFromCounter
+ = vargamma.getArrayMappingCounterToIndex();
+
+ mudt = new double[d];
+ for (int i = 0; i < d; i++)
+ mudt[i] = muPlusOmega * (t[i+1] - t[i]);
+ }
+ }
+
+
+ public void setStream (RandomStream stream) {
+ vargamma.setStream(stream);
+ }
+
+
+ public RandomStream getStream () {
+ return vargamma.getStream();
+ }
+
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcess.java b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcess.java
new file mode 100644
index 0000000..eb95bc4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcess.java
@@ -0,0 +1,248 @@
+
+
+/*
+ * Class: InverseGaussianProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * The inverse Gaussian process is a non-decreasing process
+ * where the increments are additive and are given by the
+ * inverse gaussian distribution,
+ * {@link umontreal.iro.lecuyer.probdist.InverseGaussianDist InverseGaussianDist}.
+ * With parameters <SPAN CLASS="MATH"><I>δ</I></SPAN> and <SPAN CLASS="MATH"><I>γ</I></SPAN>, the
+ * time increments are given by
+ * {@link umontreal.iro.lecuyer.probdist.InverseGaussianDist InverseGaussianDist}
+ * <SPAN CLASS="MATH">(<I>δdt</I>/<I>γ</I>, <I>δ</I><SUP>2</SUP><I>dt</I><SUP>2</SUP>)</SPAN>.
+ *
+ * <P>
+ * [We here use the inverse gaussian distribution
+ * parametrized with IGDist
+ * <SPAN CLASS="MATH">(<I>μ</I>, <I>λ</I>)</SPAN>, where
+ * <SPAN CLASS="MATH"><I>μ</I> = <I>δ</I>/<I>γ</I></SPAN>
+ * and
+ * <SPAN CLASS="MATH"><I>λ</I> = <I>δ</I><SUP>2</SUP></SPAN>. If we instead used the parametrization
+ *
+ * <SPAN CLASS="MATH">IGDist<SUP>1#1</SUP>(<I>δ</I>, <I>γ</I>)</SPAN>,
+ * then the increment distribution of our process would have been written
+ * more simply as
+ * <SPAN CLASS="MATH">IGDist<SUP>[tex2html_wrap_inline160]</SUP>(<I>δdt</I>, <I>γ</I>)</SPAN>.]
+ *
+ * <P>
+ * The increments are generated by using
+ * the inversion of the cumulative distribution function.
+ * It therefore uses only one {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}.
+ * Subclasses of this class use different generating methods and some need
+ * two {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s.
+ *
+ * <P>
+ * The initial value of this process is the initial observation time.
+ *
+ */
+public class InverseGaussianProcess extends StochasticProcess {
+
+ protected RandomStream stream;
+
+ protected double delta;
+ protected double gamma;
+
+ protected double deltaOverGamma;
+ protected double deltaSquare;
+ // mu and lambda are the common names of the params for InverseGaussianGen.
+ protected double[] imu;
+ protected double[] ilam;
+
+ // Number of random streams needed by the current class
+ // to generate an IG process. For this class, = 1, for subclasses
+ // will sometimes be, = 2.
+ int numberOfRandomStreams;
+
+
+
+ // needed by InverseGaussianProcessMSH
+ protected InverseGaussianProcess() { }
+
+
+ /**
+ * Constructs a new <TT>InverseGaussianProcess</TT>.
+ * The initial value <SPAN CLASS="MATH"><I>s</I>0</SPAN> will be overridden by <SPAN CLASS="MATH"><I>t</I>[0]</SPAN> when
+ * the observation times are set.
+ *
+ */
+ public InverseGaussianProcess (double s0, double delta, double gamma,
+ RandomStream stream) {
+ this.x0 = s0;
+ setParams(delta, gamma);
+ this.stream = stream;
+ numberOfRandomStreams = 1;
+ }
+
+
+ public double[] generatePath() {
+ double s = x0;
+ for (int i = 0; i < d; i++) {
+ s += InverseGaussianGen.nextDouble(stream, imu[i], ilam[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ /**
+ * Instead of using the internal stream to generate the path,
+ * uses an array of uniforms <SPAN CLASS="MATH"><I>U</I>[0, 1)</SPAN>. The array should be
+ * of the length of the number of periods in the observation
+ * times. This method is useful for {@link NormalInverseGaussianProcess}.
+ *
+ */
+ public double[] generatePath (double[] uniforms01) {
+ double s = x0;
+ for (int i = 0; i < d; i++) {
+ s += InverseGaussianDist.inverseF(imu[i], ilam[i], uniforms01[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * This method does not work for this class, but will
+ * be useful for the subclasses that require two streams.
+ *
+ */
+ public double[] generatePath (double[] uniforms01, double[] uniforms01b) {
+ throw new UnsupportedOperationException("Use generatePath with 1 stream.");
+ }
+
+
+ public double nextObservation () {
+ double s = path[observationIndex];
+ s += InverseGaussianGen.nextDouble(stream,
+ imu[observationIndex], ilam[observationIndex]);
+ observationIndex++;
+ observationCounter = observationIndex;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ /**
+ * Sets the parameters.
+ *
+ */
+ public void setParams (double delta, double gamma) {
+ this.delta = delta;
+ this.gamma = gamma;
+ deltaOverGamma = delta/gamma;
+ deltaSquare = delta*delta;
+ init();
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>γ</I></SPAN>.
+ *
+ */
+ public double getGamma() {
+ return gamma;
+ }
+
+
+ /**
+ * Returns the analytic average which is
+ * <SPAN CLASS="MATH"><I>δt</I>/<I>γ</I></SPAN>,
+ * with <SPAN CLASS="MATH"><I>t</I> =</SPAN> <TT>time</TT>.
+ *
+ */
+ public double getAnalyticAverage (double time) {
+ return delta*time/gamma;
+ }
+
+
+ /**
+ * Returns the analytic variance which is
+ * <SPAN CLASS="MATH">(<I>δt</I>)<SUP>2</SUP></SPAN>,
+ * with <SPAN CLASS="MATH"><I>t</I> =</SPAN> <TT>time</TT>.
+ *
+ */
+ public double getAnalyticVariance (double time) {
+ return delta*delta*time*time;
+ }
+
+
+ protected void init () {
+ super.init(); // set path[0] to s0
+ if(observationTimesSet){
+ x0 = t[0];
+ path[0] = x0;
+ imu = new double[d];
+ ilam = new double[d];
+ for (int j = 0; j < d; j++)
+ {
+ double temp = delta * (t[j+1] - t[j]);
+ imu[j] = temp / gamma;
+ ilam[j] = temp * temp;
+ }
+ }
+ }
+
+ public RandomStream getStream () {
+ // It is assumed that stream is always the same
+ // as the stream in the InverseGaussianGen.
+ return stream;
+ }
+
+ public void setStream (RandomStream stream) {
+ this.stream = stream;
+ }
+
+ /**
+ * Returns the number of random streams of this process.
+ * It is useful because some subclasses use different number
+ * of streams. It returns 1 for {@link InverseGaussianProcess}.
+ *
+ */
+ public int getNumberOfRandomStreams() {
+ return numberOfRandomStreams;
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcess.tex
new file mode 100644
index 0000000..903dbd1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcess.tex
@@ -0,0 +1,244 @@
+\defmodule {InverseGaussianProcess}
+
+The inverse Gaussian process is a non-decreasing process
+where the increments are additive and are given by the
+inverse gaussian distribution,
+\externalclass{umontreal.iro.lecuyer.probdist}{InverseGaussianDist}.
+With parameters $\delta$ and $\gamma$, the
+time increments are given by
+\externalclass{umontreal.iro.lecuyer.probdist}{InverseGaussianDist}$(\delta dt/\gamma,
+\delta^2 dt^2)$.
+
+
+[We here use the inverse gaussian distribution
+parametrized with {IGDist}$(\mu,\lambda)$, where $\mu=\delta/\gamma$
+and $\lambda=\delta^2$. If we instead used the parametrization
+$\textrm{IGDist}^\star(\delta, \gamma)$,
+then the increment distribution of our process would have been written
+more simply as $\textrm{IGDist}^\star(\delta dt, \gamma)$.]
+
+The increments are generated by using
+the inversion of the cumulative distribution function.
+It therefore uses only one \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+Subclasses of this class use different generating methods and some need
+two \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s.
+
+The initial value of this process is the initial observation time.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGaussianProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class InverseGaussianProcess extends StochasticProcess \begin{hide} {
+
+ protected RandomStream stream;
+
+ protected double delta;
+ protected double gamma;
+
+ protected double deltaOverGamma;
+ protected double deltaSquare;
+ // mu and lambda are the common names of the params for InverseGaussianGen.
+ protected double[] imu;
+ protected double[] ilam;
+
+ // Number of random streams needed by the current class
+ // to generate an IG process. For this class, = 1, for subclasses
+ // will sometimes be, = 2.
+ int numberOfRandomStreams;
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}\begin{hide}
+ // needed by InverseGaussianProcessMSH
+ protected InverseGaussianProcess() { }
+\end{hide}
+
+ public InverseGaussianProcess (double s0, double delta, double gamma,
+ RandomStream stream) \begin{hide} {
+ this.x0 = s0;
+ setParams(delta, gamma);
+ this.stream = stream;
+ numberOfRandomStreams = 1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{InverseGaussianProcess}.
+The initial value $s0$ will be overridden by $t[0]$ when
+the observation times are set.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double[] generatePath() {
+ double s = x0;
+ for (int i = 0; i < d; i++) {
+ s += InverseGaussianGen.nextDouble(stream, imu[i], ilam[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+
+ public double[] generatePath (double[] uniforms01) \begin{hide} {
+ double s = x0;
+ for (int i = 0; i < d; i++) {
+ s += InverseGaussianDist.inverseF(imu[i], ilam[i], uniforms01[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Instead of using the internal stream to generate the path,
+uses an array of uniforms $U[0,1)$. The array should be
+of the length of the number of periods in the observation
+times. This method is useful for \class{NormalInverseGaussianProcess}.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (double[] uniforms01, double[] uniforms01b) \begin{hide} {
+ throw new UnsupportedOperationException("Use generatePath with 1 stream.");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+This method does not work for this class, but will
+be useful for the subclasses that require two streams.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextObservation () {
+ double s = path[observationIndex];
+ s += InverseGaussianGen.nextDouble(stream,
+ imu[observationIndex], ilam[observationIndex]);
+ observationIndex++;
+ observationCounter = observationIndex;
+ path[observationIndex] = s;
+ return s;
+ }\end{hide}
+
+ public void setParams (double delta, double gamma) \begin{hide} {
+ this.delta = delta;
+ this.gamma = gamma;
+ deltaOverGamma = delta/gamma;
+ deltaSquare = delta*delta;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameters.
+\end{tabb}
+\begin{code}
+
+ public double getDelta() \begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\delta$.
+\end{tabb}
+\begin{code}
+
+ public double getGamma() \begin{hide} {
+ return gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $\gamma$.
+\end{tabb}
+\begin{code}
+
+ public double getAnalyticAverage (double time) \begin{hide} {
+ return delta*time/gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the analytic average which is $\delta t/ \gamma$,
+ with $t=$ \texttt{time}.
+\end{tabb}
+\begin{code}
+
+ public double getAnalyticVariance (double time) \begin{hide} {
+ return delta*delta*time*time;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the analytic variance which is $(\delta t)^2$,
+ with $t=$ \texttt{time}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void init () {
+ super.init(); // set path[0] to s0
+ if(observationTimesSet){
+ x0 = t[0];
+ path[0] = x0;
+ imu = new double[d];
+ ilam = new double[d];
+ for (int j = 0; j < d; j++)
+ {
+ double temp = delta * (t[j+1] - t[j]);
+ imu[j] = temp / gamma;
+ ilam[j] = temp * temp;
+ }
+ }
+ }
+
+ public RandomStream getStream () {
+ // It is assumed that stream is always the same
+ // as the stream in the InverseGaussianGen.
+ return stream;
+ }
+
+ public void setStream (RandomStream stream) {
+ this.stream = stream;
+ }\end{hide}
+
+ public int getNumberOfRandomStreams() \begin{hide} {
+ return numberOfRandomStreams;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the number of random streams of this process.
+It is useful because some subclasses use different number
+of streams. It returns 1 for \class{InverseGaussianProcess}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessBridge.java b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessBridge.java
new file mode 100644
index 0000000..795f701
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessBridge.java
@@ -0,0 +1,285 @@
+
+/*
+ * The conditional bridge distribution is known \cite{fWEB03a},
+ * but is not integrable, therefore there can be no bridge method
+ * using simply inversion, with a single
+ * \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+ */
+
+/*
+ * Class: InverseGaussianProcessBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Samples the path by bridge sampling:
+ * first finding the process value at
+ * the final time and then the middle time, etc.
+ * The method <TT>nextObservation()</TT> returns the path value
+ * in that non-sequential order.
+ * This class uses two
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s to generate
+ * a path.
+ *
+ */
+public class InverseGaussianProcessBridge extends InverseGaussianProcessMSH {
+
+ // Careful: mu and lambda are completely different from parent class.
+ protected double[] imu2;
+ protected double[] imuLambdaZ;
+ protected double[] imuOver2LambdaZ;
+
+ protected int[] wIndexList;
+ protected int bridgeCounter = -1; // Before 1st observ
+
+
+
+
+ /**
+ * Constructs a new <TT>InverseGaussianProcessBridge</TT>.
+ * The initial value <TT>s0</TT> will be overridden by <SPAN CLASS="MATH"><I>t</I>[0]</SPAN> when
+ * the observation times are set.
+ *
+ */
+ public InverseGaussianProcessBridge (double s0, double delta,
+ double gamma, RandomStream stream,
+ RandomStream otherStream) {
+ super(s0, delta, gamma, stream, otherStream);
+ numberOfRandomStreams = 2;
+ }
+
+
+ /**
+ * Generates the path. The two inner
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s
+ * are sampled alternatively.
+ *
+ */
+ public double[] generatePath() {
+ bridgeCounter = -1;
+ observationIndex = 0;
+ path[0] = x0;
+ for (int j = 0; j < d; j++) nextObservation();
+ return path;
+ }
+
+
+ /**
+ * Instead of using the internal streams to generate the path,
+ * it uses two arrays of uniforms <SPAN CLASS="MATH"><I>U</I>[0, 1)</SPAN>. The length of the arrays <TT>unifNorm</TT>
+ * and <TT>unifOther</TT> should be equal to the number of time steps, excluding <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB></SPAN>.
+ *
+ */
+ public double[] generatePath (double[] unifNorm, double[] unifOther) {
+ double s = x0;
+ RandomStream cacheStreamNormal = stream;
+ RandomStream cacheStreamOther = otherStream;
+ stream = new NonRandomStream(unifNorm);
+ normalGen.setStream(stream);
+ otherStream = new NonRandomStream(unifOther);
+
+ bridgeCounter = -1;
+ observationIndex = 0;
+ path[0] = x0;
+ for (int j = 0; j < d; j++) nextObservation();
+
+ stream = cacheStreamNormal;
+ normalGen.setStream(stream);
+ otherStream = cacheStreamOther;
+ return path;
+ }
+
+
+ /**
+ * Returns the next observation in the bridge order,
+ * not the sequential order.
+ *
+ */
+ public double nextObservation() {
+ double s;
+ if (bridgeCounter == -1)
+ {
+ double temp = delta * (t[d] - t[0]);
+ s = x0 + InverseGaussianMSHGen.nextDouble(otherStream,
+ normalGen, temp/gamma, temp*temp);
+ bridgeCounter = 0;
+ observationIndex = d;
+ }
+ else
+ {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ // Use the fact that \chi^2_1 is equivalent to a normal squared.
+ double q = normalGen.nextDouble();
+ q *= q;
+
+ double z = path[oldIndexR];
+ double mu = imu[newIndex];
+ double muLambda = imuLambdaZ[newIndex]/z;
+ double mu2 = imu2[newIndex];
+ double muOver2Lambda = imuOver2LambdaZ[newIndex]*z;
+
+ double root = mu + muOver2Lambda*mu*q -
+ muOver2Lambda*Math.sqrt(4.*muLambda*q + mu2*q*q);
+ double probabilityRoot1 = mu*(1.+root)/(1.+mu)/(root+mu);
+ // Check if reject first root for 2nd one: root2=mu^2/root1.
+ if (otherStream.nextDouble() > probabilityRoot1)
+ root = mu2/root;
+ s = path[oldIndexL] + (path[oldIndexR] - path[oldIndexL]) /(1.0 + root);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }
+
+
+ public void resetStartProcess () {
+ observationIndex = 0;
+ observationCounter = 0;
+ bridgeCounter = -1;
+ }
+
+
+ protected void init () {
+ // imu[] etc. in super.init() will be overriden here.
+ // Necessary nonetheless.
+ super.init();
+
+ double tauX;
+ double tauY;
+ double mu;
+ double lambdaZ;
+ if (observationTimesSet) {
+ wIndexList = new int[3*d];
+
+ int[] ptIndex = new int[d+1];
+ int indexCounter = 0;
+ int newIndex, oldIndexL, oldIndexR;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ // Careful: mu and lambda are completely different from parent class.
+ imu = new double[d+1];
+ ilam = new double[d+1];
+ imu2 = new double[d+1];
+ imuLambdaZ = new double[d+1];
+ imuOver2LambdaZ= new double[d+1];
+
+ for (int powOfTwo = 1; powOfTwo <= d/2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) ptIndex[2*j] = ptIndex[j];
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldIndexL = 2*j - 2;
+ oldIndexR = 2*j;
+ newIndex = (int) (0.5*(ptIndex[oldIndexL] + ptIndex[oldIndexR]));
+
+ tauX = t[newIndex] - t[ptIndex[oldIndexL]];
+ tauY = t[ptIndex[oldIndexR]] - t[newIndex];
+ mu = tauY/tauX;
+ lambdaZ = delta*delta*tauY*tauY;
+ imu[newIndex] = mu;
+ ilam[newIndex] = lambdaZ;
+ imu2[newIndex] = mu*mu;
+ imuLambdaZ[newIndex] = mu*lambdaZ;
+ imuOver2LambdaZ[newIndex] = mu/2./lambdaZ;
+
+ ptIndex[oldIndexL + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldIndexL];
+ wIndexList[indexCounter+1] = newIndex;
+ wIndexList[indexCounter+2] = ptIndex[oldIndexR];
+
+ indexCounter += 3;
+ }
+ }
+
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k-1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+
+ tauX = t[ptIndex[k-1]+1] - t[ptIndex[k-1]];
+ tauY = t[ptIndex[k]] - t[ptIndex[k-1]+1];
+ mu = tauY/tauX;
+ lambdaZ = delta*delta*tauY*tauY;
+ imu[ptIndex[k-1]+1] = mu;
+ ilam[ptIndex[k-1]+1] = lambdaZ;
+ imu2[ptIndex[k-1]+1] = mu*mu;
+ imuLambdaZ[ptIndex[k-1]+1] = mu*lambdaZ;
+ imuOver2LambdaZ[ptIndex[k-1]+1] = mu/2./lambdaZ;
+
+ wIndexList[indexCounter] = ptIndex[k]-2;
+ wIndexList[indexCounter+1] = ptIndex[k]-1;
+ wIndexList[indexCounter+2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+ }
+
+ /**
+ * Only returns a stream if both inner streams are the same.
+ *
+ */
+ public RandomStream getStream() {
+ if( stream != otherStream)
+ throw new IllegalStateException("Two different streams or more are present");
+ return stream;
+ }
+
+
+ /**
+ * Sets the streams.
+ *
+ */
+ public void setStream (RandomStream stream, RandomStream otherStream) {
+ this.stream = stream;
+ this.otherStream = otherStream;
+ normalGen.setStream(stream);
+ }
+
+
+ /**
+ * Sets both inner streams to the same <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ setStream(stream, stream);
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessBridge.tex b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessBridge.tex
new file mode 100644
index 0000000..9fb8519
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessBridge.tex
@@ -0,0 +1,293 @@
+\defmodule {InverseGaussianProcessBridge}
+
+Samples the path by bridge sampling:
+first finding the process value at
+the final time and then the middle time, etc.
+The method \texttt{nextObservation()} returns the path value
+in that non-sequential order.
+This class uses two
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s to generate
+a path \cite{fWEB03a}.
+
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}\begin{hide}
+/*
+ * The conditional bridge distribution is known \cite{fWEB03a},
+ * but is not integrable, therefore there can be no bridge method
+ * using simply inversion, with a single
+ * \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+ */
+
+/*
+ * Class: InverseGaussianProcessBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class InverseGaussianProcessBridge extends InverseGaussianProcessMSH \begin{hide} {
+
+ // Careful: mu and lambda are completely different from parent class.
+ protected double[] imu2;
+ protected double[] imuLambdaZ;
+ protected double[] imuOver2LambdaZ;
+
+ protected int[] wIndexList;
+ protected int bridgeCounter = -1; // Before 1st observ
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseGaussianProcessBridge (double s0, double delta,
+ double gamma, RandomStream stream,
+ RandomStream otherStream) \begin{hide} {
+ super(s0, delta, gamma, stream, otherStream);
+ numberOfRandomStreams = 2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{InverseGaussianProcessBridge}.
+The initial value \texttt{s0} will be overridden by $t[0]$ when
+the observation times are set.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double[] generatePath() \begin{hide} {
+ bridgeCounter = -1;
+ observationIndex = 0;
+ path[0] = x0;
+ for (int j = 0; j < d; j++) nextObservation();
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates the path. The two inner
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s
+are sampled alternatively.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (double[] unifNorm, double[] unifOther) \begin{hide} {
+ double s = x0;
+ RandomStream cacheStreamNormal = stream;
+ RandomStream cacheStreamOther = otherStream;
+ stream = new NonRandomStream(unifNorm);
+ normalGen.setStream(stream);
+ otherStream = new NonRandomStream(unifOther);
+
+ bridgeCounter = -1;
+ observationIndex = 0;
+ path[0] = x0;
+ for (int j = 0; j < d; j++) nextObservation();
+
+ stream = cacheStreamNormal;
+ normalGen.setStream(stream);
+ otherStream = cacheStreamOther;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Instead of using the internal streams to generate the path,
+it uses two arrays of uniforms $U[0,1)$. The length of the arrays \texttt{unifNorm}
+ and \texttt{unifOther} should be equal to the number of time steps, excluding $t_0$.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation() \begin{hide} {
+ double s;
+ if (bridgeCounter == -1)
+ {
+ double temp = delta * (t[d] - t[0]);
+ s = x0 + InverseGaussianMSHGen.nextDouble(otherStream,
+ normalGen, temp/gamma, temp*temp);
+ bridgeCounter = 0;
+ observationIndex = d;
+ }
+ else
+ {
+ int j = bridgeCounter * 3;
+ int oldIndexL = wIndexList[j];
+ int newIndex = wIndexList[j + 1];
+ int oldIndexR = wIndexList[j + 2];
+
+ // Use the fact that \chi^2_1 is equivalent to a normal squared.
+ double q = normalGen.nextDouble();
+ q *= q;
+
+ double z = path[oldIndexR];
+ double mu = imu[newIndex];
+ double muLambda = imuLambdaZ[newIndex]/z;
+ double mu2 = imu2[newIndex];
+ double muOver2Lambda = imuOver2LambdaZ[newIndex]*z;
+
+ double root = mu + muOver2Lambda*mu*q -
+ muOver2Lambda*Math.sqrt(4.*muLambda*q + mu2*q*q);
+ double probabilityRoot1 = mu*(1.+root)/(1.+mu)/(root+mu);
+ // Check if reject first root for 2nd one: root2=mu^2/root1.
+ if (otherStream.nextDouble() > probabilityRoot1)
+ root = mu2/root;
+ s = path[oldIndexL] + (path[oldIndexR] - path[oldIndexL]) /(1.0 + root);
+ bridgeCounter++;
+ observationIndex = newIndex;
+ }
+ observationCounter = bridgeCounter + 1;
+ path[observationIndex] = s;
+ return s;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the next observation in the bridge order,
+not the sequential order.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public void resetStartProcess () {
+ observationIndex = 0;
+ observationCounter = 0;
+ bridgeCounter = -1;
+ }
+
+
+ protected void init () {
+ // imu[] etc. in super.init() will be overriden here.
+ // Necessary nonetheless.
+ super.init();
+
+ double tauX;
+ double tauY;
+ double mu;
+ double lambdaZ;
+ if (observationTimesSet) {
+ wIndexList = new int[3*d];
+
+ int[] ptIndex = new int[d+1];
+ int indexCounter = 0;
+ int newIndex, oldIndexL, oldIndexR;
+
+ ptIndex[0] = 0;
+ ptIndex[1] = d;
+
+ // Careful: mu and lambda are completely different from parent class.
+ imu = new double[d+1];
+ ilam = new double[d+1];
+ imu2 = new double[d+1];
+ imuLambdaZ = new double[d+1];
+ imuOver2LambdaZ= new double[d+1];
+
+ for (int powOfTwo = 1; powOfTwo <= d/2; powOfTwo *= 2) {
+ /* Make room in the indexing array "ptIndex" */
+ for (int j = powOfTwo; j >= 1; j--) ptIndex[2*j] = ptIndex[j];
+
+ /* Insert new indices and Calculate constants */
+ for (int j = 1; j <= powOfTwo; j++) {
+ oldIndexL = 2*j - 2;
+ oldIndexR = 2*j;
+ newIndex = (int) (0.5*(ptIndex[oldIndexL] + ptIndex[oldIndexR]));
+
+ tauX = t[newIndex] - t[ptIndex[oldIndexL]];
+ tauY = t[ptIndex[oldIndexR]] - t[newIndex];
+ mu = tauY/tauX;
+ lambdaZ = delta*delta*tauY*tauY;
+ imu[newIndex] = mu;
+ ilam[newIndex] = lambdaZ;
+ imu2[newIndex] = mu*mu;
+ imuLambdaZ[newIndex] = mu*lambdaZ;
+ imuOver2LambdaZ[newIndex] = mu/2./lambdaZ;
+
+ ptIndex[oldIndexL + 1] = newIndex;
+ wIndexList[indexCounter] = ptIndex[oldIndexL];
+ wIndexList[indexCounter+1] = newIndex;
+ wIndexList[indexCounter+2] = ptIndex[oldIndexR];
+
+ indexCounter += 3;
+ }
+ }
+
+ /* Check if there are holes remaining and fill them */
+ for (int k = 1; k < d; k++) {
+ if (ptIndex[k-1] + 1 < ptIndex[k]) {
+ // there is a hole between (k-1) and k.
+
+ tauX = t[ptIndex[k-1]+1] - t[ptIndex[k-1]];
+ tauY = t[ptIndex[k]] - t[ptIndex[k-1]+1];
+ mu = tauY/tauX;
+ lambdaZ = delta*delta*tauY*tauY;
+ imu[ptIndex[k-1]+1] = mu;
+ ilam[ptIndex[k-1]+1] = lambdaZ;
+ imu2[ptIndex[k-1]+1] = mu*mu;
+ imuLambdaZ[ptIndex[k-1]+1] = mu*lambdaZ;
+ imuOver2LambdaZ[ptIndex[k-1]+1] = mu/2./lambdaZ;
+
+ wIndexList[indexCounter] = ptIndex[k]-2;
+ wIndexList[indexCounter+1] = ptIndex[k]-1;
+ wIndexList[indexCounter+2] = ptIndex[k];
+ indexCounter += 3;
+ }
+ }
+ }
+ }\end{hide}
+
+ public RandomStream getStream() \begin{hide} {
+ if( stream != otherStream)
+ throw new IllegalStateException("Two different streams or more are present");
+ return stream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Only returns a stream if both inner streams are the same.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream, RandomStream otherStream) \begin{hide} {
+ this.stream = stream;
+ this.otherStream = otherStream;
+ normalGen.setStream(stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the streams.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ setStream(stream, stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets both inner streams to the same \texttt{stream}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessMSH.java b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessMSH.java
new file mode 100644
index 0000000..9887399
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessMSH.java
@@ -0,0 +1,291 @@
+
+
+/*
+ * Class: InverseGaussianProcessMSH
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Uses a faster generating method (MSH)
+ * than the simple inversion of the distribution function
+ * used by {@link InverseGaussianProcess}.
+ * It is about 60 times faster.
+ * However it requires two
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s instead
+ * of only one for {@link InverseGaussianProcess}.
+ * The second stream is called <TT>otherStream</TT> below and
+ * it is used to randomly choose between two roots at each time step.
+ *
+ */
+public class InverseGaussianProcessMSH extends InverseGaussianProcess {
+
+ // otherStream is used to decide between the two roots in method MSH.
+ protected RandomStream otherStream;
+ // Needed for the MSH method of generating inverse gaussian.
+ protected NormalGen normalGen;
+
+
+
+ /**
+ * Constructs a new <TT>InverseGaussianProcessMSH</TT>.
+ * The initial value <TT>s0</TT> will be overridden by <SPAN CLASS="MATH"><I>t</I>[0]</SPAN> when
+ * the observation times are set.
+ *
+ */
+ public InverseGaussianProcessMSH (double s0, double delta, double gamma,
+ RandomStream stream,
+ RandomStream otherStream) {
+ super(); // dummy
+ this.x0 = s0;
+ setParams(delta, gamma);
+ this.stream = stream;
+ this.otherStream = otherStream;
+ normalGen = new NormalGen(stream);
+ numberOfRandomStreams = 2;
+ }
+
+
+ /**
+ * Generates the path. It is done by successively calling
+ * <TT>nextObservation()</TT>, therefore the two
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}s are
+ * sampled alternatively.
+ *
+ */
+ public double[] generatePath() {
+ double s = x0;
+ for (int i = 0; i < d; i++)
+ {
+ s += InverseGaussianMSHGen.nextDouble(otherStream,
+ normalGen, imu[i], ilam[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * Instead of using the internal streams to generate the path,
+ * uses two arrays of uniforms <SPAN CLASS="MATH"><I>U</I>[0, 1)</SPAN>. The length of the arrays should be
+ * equal to the number of periods in the observation
+ * times. This method is useful for {@link NormalInverseGaussianProcess}.
+ *
+ */
+ public double[] generatePath (double[] unifNorm, double[] unifOther) {
+ double s = x0;
+ // The class NonRandomStream is defined below.
+ RandomStream nonRandOther = new NonRandomStream(unifOther);
+ // this.stream should keep in memory the original stream of the Normal.
+ normalGen.setStream(new NonRandomStream(unifNorm));
+ for (int i = 0; i < d; i++) {
+ s += InverseGaussianMSHGen.nextDouble(nonRandOther,
+ normalGen, imu[i], ilam[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ normalGen.setStream(stream); // reset to original stream
+ return path;
+ }
+
+
+ /**
+ * Not implemented, requires two
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s.
+ *
+ */
+ public double[] generatePath (double[] uniforms01) {
+ throw new UnsupportedOperationException("Use generatePath with 2 streams");
+ }
+
+
+ public double nextObservation() {
+ double s = path[observationIndex];
+ s += InverseGaussianMSHGen.nextDouble(otherStream, normalGen,
+ imu[observationIndex], ilam[observationIndex]);
+ observationIndex++;
+ observationCounter = observationIndex;
+ path[observationIndex] = s;
+ return s;
+ }
+
+ /**
+ * Only returns a stream if both inner
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s are the same.
+ *
+ */
+ public RandomStream getStream() {
+ if( stream != otherStream)
+ throw new IllegalStateException("Two different streams or more are present");
+ return stream;
+ }
+
+
+ /**
+ * Sets the streams.
+ *
+ */
+ public void setStream (RandomStream stream, RandomStream otherStream) {
+ super.setStream(stream);
+ normalGen.setStream(stream);
+ setOtherStream(otherStream);
+ }
+
+
+ /**
+ * Sets both inner streams to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ super.setStream(stream);
+ normalGen.setStream(stream);
+ setOtherStream(stream);
+ }
+
+
+ /**
+ * Sets the <TT>otherStream</TT>, which is the stream used
+ * to choose between the two roots in the MSH method.
+ *
+ */
+ public void setOtherStream (RandomStream otherStream) {
+ this.otherStream = otherStream;
+ }
+
+
+ /**
+ * Returns the <TT>otherStream</TT>, which is the stream used
+ * to choose between the two quadratic roots from the MSH method.
+ *
+ */
+ public RandomStream getOtherStream() {
+ return otherStream;
+ }
+
+
+ /**
+ * Sets the normal generator. It also sets one of the
+ * two inner streams to the stream of the normal generator.
+ *
+ */
+ public void setNormalGen (NormalGen normalGen) {
+ this.normalGen = normalGen;
+ stream = normalGen.getStream();
+ }
+
+
+ /**
+ * Returns the normal generator.
+ *
+ */
+ public NormalGen getNormalGen() {
+ return normalGen;
+ }
+
+
+
+/* *
+ * NonRandomStream:
+ * Given a double array, this class will return those values
+ * as if it where a random stream.
+ * Careful: Will not hard copy the array given as input.
+ * And not checking for end of array for the time being.
+ * And not checking j>i.
+ */
+ protected class NonRandomStream implements RandomStream
+ {
+ double[] array;
+ int position;
+
+ public NonRandomStream(double[] array)
+ {
+ this.array = array;
+ position = 0;
+ }
+
+ public NonRandomStream(double value)
+ {
+ this.array = new double[]{value};
+ position = 0;
+ }
+
+ public double nextDouble()
+ {
+ return array[position++];
+ }
+
+ public void nextArrayOfDouble(double[] u, int start, int n)
+ {
+ for(int i = 0; i < n; i++)
+ u[start+i] = array[position++];
+ }
+
+ public void nextArrayOfInt(int i, int j, int[] u,
+ int start, int n)
+ {
+ double diff = (double)(j - i);
+ for(int ii = 0; ii < n; ii++)
+ u[start+ii] = i +
+ (int)Math.round(diff * array[position++]);
+ }
+
+ public int nextInt(int i, int j)
+ {
+ return (int)Math.round( (double)(j-i) * array[position]);
+ }
+
+
+ public void resetNextSubstream()
+ {
+ }
+
+ public void resetStartStream()
+ {
+ position = 0;
+ }
+
+ public void resetStartSubstream()
+ {
+ }
+
+ public String toString()
+ {
+ return new String("NonRandomStream of length " +
+ array.length);
+ }
+ }
+
+
+
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessMSH.tex b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessMSH.tex
new file mode 100644
index 0000000..c34021c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessMSH.tex
@@ -0,0 +1,297 @@
+\defmodule {InverseGaussianProcessMSH}
+
+Uses a faster generating method (MSH) \cite{rMIC76a}
+than the simple inversion of the distribution function
+used by \class{InverseGaussianProcess}.
+It is about 60 times faster.
+However it requires two
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s instead
+of only one for \class{InverseGaussianProcess}.
+The second stream is called \texttt{otherStream} below and
+it is used to randomly choose between two roots at each time step.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGaussianProcessMSH
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class InverseGaussianProcessMSH extends InverseGaussianProcess \begin{hide} {
+
+ // otherStream is used to decide between the two roots in method MSH.
+ protected RandomStream otherStream;
+ // Needed for the MSH method of generating inverse gaussian.
+ protected NormalGen normalGen;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseGaussianProcessMSH (double s0, double delta, double gamma,
+ RandomStream stream,
+ RandomStream otherStream) \begin{hide} {
+ super(); // dummy
+ this.x0 = s0;
+ setParams(delta, gamma);
+ this.stream = stream;
+ this.otherStream = otherStream;
+ normalGen = new NormalGen(stream);
+ numberOfRandomStreams = 2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{InverseGaussianProcessMSH}.
+The initial value \texttt{s0} will be overridden by $t[0]$ when
+the observation times are set.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double[] generatePath() \begin{hide} {
+ double s = x0;
+ for (int i = 0; i < d; i++)
+ {
+ s += InverseGaussianMSHGen.nextDouble(otherStream,
+ normalGen, imu[i], ilam[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates the path. It is done by successively calling
+\texttt{nextObservation()}, therefore the two
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}s are
+sampled alternatively.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (double[] unifNorm, double[] unifOther) \begin{hide} {
+ double s = x0;
+ // The class NonRandomStream is defined below.
+ RandomStream nonRandOther = new NonRandomStream(unifOther);
+ // this.stream should keep in memory the original stream of the Normal.
+ normalGen.setStream(new NonRandomStream(unifNorm));
+ for (int i = 0; i < d; i++) {
+ s += InverseGaussianMSHGen.nextDouble(nonRandOther,
+ normalGen, imu[i], ilam[i]);
+ path[i+1] = s;
+ }
+ observationIndex = d;
+ observationCounter = d;
+ normalGen.setStream(stream); // reset to original stream
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Instead of using the internal streams to generate the path,
+uses two arrays of uniforms $U[0,1)$. The length of the arrays should be
+equal to the number of periods in the observation
+times. This method is useful for \class{NormalInverseGaussianProcess}.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (double[] uniforms01) \begin{hide} {
+ throw new UnsupportedOperationException("Use generatePath with 2 streams");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Not implemented, requires two
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ double s = path[observationIndex];
+ s += InverseGaussianMSHGen.nextDouble(otherStream, normalGen,
+ imu[observationIndex], ilam[observationIndex]);
+ observationIndex++;
+ observationCounter = observationIndex;
+ path[observationIndex] = s;
+ return s;
+ }\end{hide}
+
+ public RandomStream getStream() \begin{hide} {
+ if( stream != otherStream)
+ throw new IllegalStateException("Two different streams or more are present");
+ return stream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Only returns a stream if both inner
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s are the same.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream, RandomStream otherStream) \begin{hide} {
+ super.setStream(stream);
+ normalGen.setStream(stream);
+ setOtherStream(otherStream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the streams.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ super.setStream(stream);
+ normalGen.setStream(stream);
+ setOtherStream(stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets both inner streams to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public void setOtherStream (RandomStream otherStream) \begin{hide} {
+ this.otherStream = otherStream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the \texttt{otherStream}, which is the stream used
+to choose between the two roots in the MSH method.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getOtherStream() \begin{hide} {
+ return otherStream;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the \texttt{otherStream}, which is the stream used
+to choose between the two quadratic roots from the MSH method.
+\end{tabb}
+\begin{code}
+
+ public void setNormalGen (NormalGen normalGen) \begin{hide} {
+ this.normalGen = normalGen;
+ stream = normalGen.getStream();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the normal generator. It also sets one of the
+two inner streams to the stream of the normal generator.
+\end{tabb}
+\begin{code}
+
+ public NormalGen getNormalGen() \begin{hide} {
+ return normalGen;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the normal generator.
+\end{tabb}
+
+\begin{code}
+\begin{hide}
+
+/**
+ * NonRandomStream:
+ * Given a double array, this class will return those values
+ * as if it where a random stream.
+ * Careful: Will not hard copy the array given as input.
+ * And not checking for end of array for the time being.
+ * And not checking j>i.
+ */
+ protected class NonRandomStream implements RandomStream
+ {
+ double[] array;
+ int position;
+
+ public NonRandomStream(double[] array)
+ {
+ this.array = array;
+ position = 0;
+ }
+
+ public NonRandomStream(double value)
+ {
+ this.array = new double[]{value};
+ position = 0;
+ }
+
+ public double nextDouble()
+ {
+ return array[position++];
+ }
+
+ public void nextArrayOfDouble(double[] u, int start, int n)
+ {
+ for(int i = 0; i < n; i++)
+ u[start+i] = array[position++];
+ }
+
+ public void nextArrayOfInt(int i, int j, int[] u,
+ int start, int n)
+ {
+ double diff = (double)(j - i);
+ for(int ii = 0; ii < n; ii++)
+ u[start+ii] = i +
+ (int)Math.round(diff * array[position++]);
+ }
+
+ public int nextInt(int i, int j)
+ {
+ return (int)Math.round( (double)(j-i) * array[position]);
+ }
+
+
+ public void resetNextSubstream()
+ {
+ }
+
+ public void resetStartStream()
+ {
+ position = 0;
+ }
+
+ public void resetStartSubstream()
+ {
+ }
+
+ public String toString()
+ {
+ return new String("NonRandomStream of length " +
+ array.length);
+ }
+ }
+
+
+\end{hide}
+\end{code}
+
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessPCA.java b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessPCA.java
new file mode 100644
index 0000000..326bfae
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessPCA.java
@@ -0,0 +1,174 @@
+
+
+/*
+ * Class: InverseGaussianProcessPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Approximates a principal component analysis (PCA)
+ * decomposition of the <TT>InverseGaussianProcess</TT>.
+ * The PCA decomposition of a {@link BrownianMotionPCA}
+ * with a covariance matrix identical to the one
+ * of our <TT>InverseGaussianProcess</TT> is used to
+ * generate the path of our <TT>InverseGaussianProcess</TT>. Such a path is a perfectly random path
+ * and it is hoped that it will provide reduction
+ * in the simulation variance when using quasi-Monte Carlo.
+ *
+ * <P>
+ * The method <TT>nextObservation()</TT> cannot be used with
+ * PCA decompositions since the whole path must be generated at
+ * once.
+ *
+ */
+public class InverseGaussianProcessPCA extends InverseGaussianProcess {
+
+ protected BrownianMotionPCA bmPCA;
+
+
+
+ /**
+ * Constructs a new <TT>InverseGaussianProcessPCA</TT>.
+ * The initial value <TT>s0</TT> will be overridden by <SPAN CLASS="MATH"><I>t</I>[0]</SPAN> when
+ * the observation times are set.
+ *
+ */
+ public InverseGaussianProcessPCA (double s0, double delta, double gamma,
+ RandomStream stream) {
+ super(s0, delta, gamma, stream);
+ bmPCA = new BrownianMotionPCA(0.,0.,delta,stream);
+ numberOfRandomStreams = 1;
+ }
+
+
+ public double[] generatePath () {
+ double[] uniformIncrement = new double[d];
+ double[] BMpath = bmPCA.generatePath();
+
+ for(int i = 0; i < d; i++)
+ {
+ double dt = bmPCA.mudt[i]; //bmTime[i + 1] - bmTime[i];
+ double sigma = bmPCA.sigmasqrdt[i] ;//Math.sqrt(dt) * bmSigma;
+ uniformIncrement[i] =
+ NormalDistQuick.cdf01( (BMpath[i+1] - BMpath[i] - bmPCA.mu * dt)/sigma );
+ }
+ path[0] = x0;
+ for(int i = 0; i < d; i++)
+ path[i+1] = path[i] +
+ InverseGaussianDist.inverseF(imu[i], ilam[i], uniformIncrement[i]);
+
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+ /**
+ * Instead of using the internal stream to generate the path,
+ * uses an array of uniforms <SPAN CLASS="MATH"><I>U</I>[0, 1)</SPAN>. The length of the array should be
+ * equal to the length of the number of periods in the observation
+ * times. This method is useful for {@link NormalInverseGaussianProcess}.
+ *
+ */
+ public double[] generatePath (double[] uniforms01) {
+ double[] uniformIncrement = new double[d];
+ double[] BMpath = bmPCA.generatePath(uniforms01);
+
+ for(int i = 0; i < d; i++)
+ {
+ double dt = bmPCA.mudt[i]; //bmTime[i + 1] - bmTime[i];
+ double sigma = bmPCA.sigmasqrdt[i] ;//Math.sqrt(dt) * bmSigma;
+ uniformIncrement[i] =
+ NormalDistQuick.cdf01( (BMpath[i+1] - BMpath[i] - bmPCA.mu * dt)/sigma );
+ }
+ path[0] = x0;
+ for(int i = 0; i < d; i++)
+ path[i+1] = path[i] +
+ InverseGaussianDist.inverseF(imu[i], ilam[i], uniformIncrement[i]);
+
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * Not implementable for PCA.
+ *
+ */
+ public double nextObservation() {
+ throw new UnsupportedOperationException("Not implementable for PCA.");
+ }
+
+
+ /**
+ * Sets the observation times of both the {@link InverseGaussianProcessPCA}
+ * and the inner
+ * <BR>{@link BrownianMotionPCA}.
+ *
+ */
+ public void setObservationTimes (double t[], int d) {
+ super.setObservationTimes(t,d);
+ bmPCA.setObservationTimes(t,d);
+ }
+
+
+ public RandomStream getStream() {
+ if( stream != bmPCA.getStream() )
+ throw new IllegalStateException("Two different streams or more are present");
+ return stream;
+ }
+
+
+ public void setStream (RandomStream stream) {
+ super.setStream(stream);
+ bmPCA.setStream(stream);
+ }
+
+ /**
+ * Sets the brownian motion PCA. The observation times
+ * will be overriden when the method <TT>observationTimes()</TT> is called on
+ * the {@link InverseGaussianProcessPCA}.
+ *
+ */
+ public void setBrownianMotionPCA (BrownianMotionPCA bmPCA) {
+ this.bmPCA = bmPCA;
+ }
+
+
+ /**
+ * Returns the {@link BrownianMotionPCA}.
+ *
+ */
+ public BrownianMotion getBrownianMotionPCA() {
+ return bmPCA;
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessPCA.tex b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessPCA.tex
new file mode 100644
index 0000000..e5f6dbd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/InverseGaussianProcessPCA.tex
@@ -0,0 +1,180 @@
+\defmodule {InverseGaussianProcessPCA}
+
+Approximates a principal component analysis (PCA)
+decomposition of the \texttt{InverseGaussianProcess}.
+The PCA decomposition of a \class{BrownianMotionPCA}
+with a covariance matrix identical to the one
+of our \texttt{InverseGaussianProcess} is used to
+generate the path of our \texttt{InverseGaussianProcess}
+\cite{fLEC08a}. Such a path is a perfectly random path
+and it is hoped that it will provide reduction
+in the simulation variance when using quasi-Monte Carlo.
+
+The method \texttt{nextObservation()} cannot be used with
+PCA decompositions since the whole path must be generated at
+once.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: InverseGaussianProcessPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class InverseGaussianProcessPCA extends InverseGaussianProcess \begin{hide} {
+
+ protected BrownianMotionPCA bmPCA;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public InverseGaussianProcessPCA (double s0, double delta, double gamma,
+ RandomStream stream) \begin{hide} {
+ super(s0, delta, gamma, stream);
+ bmPCA = new BrownianMotionPCA(0.,0.,delta,stream);
+ numberOfRandomStreams = 1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{InverseGaussianProcessPCA}.
+The initial value \texttt{s0} will be overridden by $t[0]$ when
+the observation times are set.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double[] generatePath () {
+ double[] uniformIncrement = new double[d];
+ double[] BMpath = bmPCA.generatePath();
+
+ for(int i = 0; i < d; i++)
+ {
+ double dt = bmPCA.mudt[i]; //bmTime[i + 1] - bmTime[i];
+ double sigma = bmPCA.sigmasqrdt[i] ;//Math.sqrt(dt) * bmSigma;
+ uniformIncrement[i] =
+ NormalDistQuick.cdf01( (BMpath[i+1] - BMpath[i] - bmPCA.mu * dt)/sigma );
+ }
+ path[0] = x0;
+ for(int i = 0; i < d; i++)
+ path[i+1] = path[i] +
+ InverseGaussianDist.inverseF(imu[i], ilam[i], uniformIncrement[i]);
+
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+
+ public double[] generatePath (double[] uniforms01) \begin{hide} {
+ double[] uniformIncrement = new double[d];
+ double[] BMpath = bmPCA.generatePath(uniforms01);
+
+ for(int i = 0; i < d; i++)
+ {
+ double dt = bmPCA.mudt[i]; //bmTime[i + 1] - bmTime[i];
+ double sigma = bmPCA.sigmasqrdt[i] ;//Math.sqrt(dt) * bmSigma;
+ uniformIncrement[i] =
+ NormalDistQuick.cdf01( (BMpath[i+1] - BMpath[i] - bmPCA.mu * dt)/sigma );
+ }
+ path[0] = x0;
+ for(int i = 0; i < d; i++)
+ path[i+1] = path[i] +
+ InverseGaussianDist.inverseF(imu[i], ilam[i], uniformIncrement[i]);
+
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Instead of using the internal stream to generate the path,
+uses an array of uniforms $U[0,1)$. The length of the array should be
+equal to the length of the number of periods in the observation
+times. This method is useful for \class{NormalInverseGaussianProcess}.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation() \begin{hide} {
+ throw new UnsupportedOperationException("Not implementable for PCA.");
+ }\end{hide}
+\end{code}
+\begin{tabb} Not implementable for PCA.
+\end{tabb}
+\begin{code}
+
+ public void setObservationTimes (double t[], int d) \begin{hide} {
+ super.setObservationTimes(t,d);
+ bmPCA.setObservationTimes(t,d);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Sets the observation times of both the \class{InverseGaussianProcessPCA}
+and the inner \\ \class{BrownianMotionPCA}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ public RandomStream getStream() {
+ if( stream != bmPCA.getStream() )
+ throw new IllegalStateException("Two different streams or more are present");
+ return stream;
+ }
+
+
+ public void setStream (RandomStream stream) {
+ super.setStream(stream);
+ bmPCA.setStream(stream);
+ }\end{hide}
+
+ public void setBrownianMotionPCA (BrownianMotionPCA bmPCA)\begin{hide} {
+ this.bmPCA = bmPCA;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the brownian motion PCA. The observation times
+will be overriden when the method \texttt{observationTimes()} is called on
+the \class{InverseGaussianProcessPCA}.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotion getBrownianMotionPCA() \begin{hide} {
+ return bmPCA;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the \class{BrownianMotionPCA}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/NormalInverseGaussianProcess.java b/source/umontreal/iro/lecuyer/stochprocess/NormalInverseGaussianProcess.java
new file mode 100644
index 0000000..bd03259
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/NormalInverseGaussianProcess.java
@@ -0,0 +1,417 @@
+
+
+/*
+ * Class: NormalInverseGaussianProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.hups.*;
+
+
+
+/**
+ * This class represents a normal inverse gaussian process (NIG).
+ * It obeys the stochastic differential equation
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:nig"></A>
+ * <I>dX</I>(<I>t</I>) = <I>μdt</I> + <I>dB</I>(<I>h</I>(<I>t</I>)),
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH">{<I>B</I>(<I>t</I>), <I>t</I> >= 0}</SPAN> is a {@link BrownianMotion}
+ * with drift <SPAN CLASS="MATH"><I>β</I></SPAN> and variance 1,
+ * and <SPAN CLASS="MATH"><I>h</I>(<I>t</I>)</SPAN> is an {@link InverseGaussianProcess}
+ * <SPAN CLASS="MATH"><I>IG</I>(<I>ν</I>/<I>γ</I>, <I>ν</I><SUP>2</SUP>)</SPAN>, with
+ *
+ * <SPAN CLASS="MATH"><I>ν</I> = <I>δdt</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>γ</I> = (α^2 - β^2)<SUP>1/2</SUP></SPAN>.
+ *
+ * <P>
+ * In this class, the process is generated using the sequential
+ * technique: <SPAN CLASS="MATH"><I>X</I>(0) = <I>x</I><SUB>0</SUB></SPAN> and
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>X</I>(<I>t</I><SUB>j</SUB>) - <I>X</I>(<I>t</I><SUB>j-1</SUB>) = <I>μdt</I> + <I>βY</I><SUB>j</SUB> + (Y_j)<SUP>1/2</SUP><I>Z</I><SUB>j</SUB>,
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB>∼<I>N</I>(0, 1)</SPAN>, and
+ * <SPAN CLASS="MATH"><I>Y</I><SUB>j</SUB>∼<I>IG</I>(<I>ν</I>/<I>γ</I>, <I>ν</I><SUP>2</SUP>)</SPAN>
+ * with
+ * <SPAN CLASS="MATH"><I>ν</I> = <I>δ</I>(<I>t</I><SUB>j</SUB> - <I>t</I><SUB>j-1</SUB>)</SPAN>.
+ *
+ * <P>
+ * There is one {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * used to generate the <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB></SPAN>'s and
+ * there are one or two streams used to generate the underlying
+ * {@link InverseGaussianProcess}, depending on which IG subclass
+ * is used.
+ *
+ * <P>
+ * In finance, a NIG process usually means that
+ * the log-return is given by a NIG process;
+ * {@link GeometricNormalInverseGaussianProcess}
+ * should be used in that case.
+ *
+ */
+public class NormalInverseGaussianProcess extends StochasticProcess {
+ protected RandomStream streamIG1; // U[0,1) gen used in the inverse gaussian
+ protected RandomStream streamIG2; // U[0,1) gen used (maybe) in the inverse gaussian
+ protected RandomStream streamBrownian; // U[0,1) gen for the normal "Brownian"
+
+ protected InverseGaussianProcess igProcess;
+ protected NormalGen normalGen;
+ // Randomized time generated by the InverseGaussianProcess.
+ protected double[] stochTime;
+ double[] dt;
+ double[] mudt;
+
+ // Parameters of the normal inverse gaussian
+ protected double mu;
+ protected double delta;
+ protected double alpha;
+ protected double beta;
+ protected double gamma;
+
+
+
+
+
+ /**
+ * Given an {@link InverseGaussianProcess} <TT>igP</TT>, constructs a
+ * new <TT>NormalInverseGaussianProcess</TT>. The parameters and observation
+ * times of the IG process will be overriden by the parameters
+ * of the NIG process. If there are two
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s in the
+ * {@link InverseGaussianProcess}, this constructor assumes that
+ * both streams have been set to the same stream.
+ *
+ */
+ public NormalInverseGaussianProcess (double x0, double alpha,
+ double beta, double mu,
+ double delta,
+ RandomStream streamBrownian,
+ InverseGaussianProcess igP) {
+ setParams (x0, alpha, beta, mu, delta);
+ this.streamBrownian = streamBrownian;
+ normalGen = new NormalGen(streamBrownian); // N(0,1)
+ igProcess = igP; // its params will be set in init().
+ this.streamIG1 = igProcess.getStream();
+ this.streamIG2 = streamIG1;
+ }
+
+
+ /**
+ * Constructs a new <TT>NormalInverseGaussianProcess</TT>.
+ * The string argument corresponds to the type of underlying
+ * {@link InverseGaussianProcess}. The choices are SEQUENTIAL_SLOW,
+ * SEQUENTIAL_MSH, BRIDGE and PCA, which correspond
+ * respectively to {@link InverseGaussianProcess}, {@link InverseGaussianProcessMSH},
+ * {@link InverseGaussianProcessBridge} and {@link InverseGaussianProcessPCA}.
+ * The third {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}, <TT>streamIG2</TT>,
+ * will not be used at all if the SEQUENTIAL_SLOW or PCA methods are chosen.
+ *
+ */
+ public NormalInverseGaussianProcess (double x0, double alpha,
+ double beta, double mu,
+ double delta,
+ RandomStream streamBrownian,
+ RandomStream streamIG1,
+ RandomStream streamIG2,
+ String igType) {
+ setParams (x0, alpha, beta, mu, delta);
+ this.streamIG1 = streamIG1;
+ this.streamIG2 = streamIG2;
+ this.streamBrownian = streamBrownian;
+ normalGen = new NormalGen(streamBrownian); // N(0,1)
+
+ // The initial time of igProcess here, 0., is arbitrary:
+ // init() sets igProcess.x0 = t[0].
+ if(igType.compareTo("SEQUENTIAL_SLOW") == 0)
+ // the initial value, 0.0, of the IG will be overriden by
+ // the initial time, when it will be set.
+ igProcess = new InverseGaussianProcess(0.0, delta, gamma,
+ streamIG1);
+ else if(igType.compareTo("SEQUENTIAL_MSH") == 0)
+ igProcess = new InverseGaussianProcessMSH (0.0, delta, gamma,
+ streamIG1, streamIG2);
+ else if(igType.compareTo("BRIDGE") == 0)
+ igProcess = new InverseGaussianProcessBridge (0.0, delta, gamma,
+ streamIG1, streamIG2);
+ else if(igType.compareTo("PCA") == 0)
+ igProcess = new InverseGaussianProcessPCA (0.0, delta, gamma,
+ streamIG1);
+ else throw new IllegalArgumentException("Unrecognized igType");
+ }
+
+
+ /**
+ * Same as above, but all {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s
+ * are set to the same stream, <TT>streamAll</TT>.
+ *
+ */
+ public NormalInverseGaussianProcess (double x0, double alpha,
+ double beta, double mu,
+ double delta,
+ RandomStream streamAll,
+ String igType) {
+ this(x0, alpha, beta, mu, delta, streamAll, streamAll, streamAll, igType);
+ }
+
+
+ /**
+ * Generates the path.
+ * This method samples each stream alternatively,
+ * which is useful for quasi-Monte Carlo, where
+ * all streams are in fact the same iterator on
+ * a {@link umontreal.iro.lecuyer.hups.PointSet PointSet}.
+ *
+ */
+ public double[] generatePath() {
+ if(igProcess.getNumberOfRandomStreams() != 1)
+ return generatePathTwoIGStreams();
+
+ double x = x0;
+ double[] randomNormal = new double[d];
+ double[] randomIG1 = new double[d];
+ for (int j = 0; j < d; j++)
+ {
+ randomIG1[j] = streamIG1.nextDouble();
+ randomNormal[j] = streamBrownian.nextDouble();
+ }
+
+ stochTime = igProcess.generatePath(randomIG1);
+ for (int j = 0; j < d; j++)
+ {
+ double dy = stochTime[j + 1] - stochTime[j];
+ x += mudt[j] + beta*dy +
+ Math.sqrt(dy) * NormalDistQuick.inverseF01(randomNormal[j]);
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+
+ protected double[] generatePathTwoIGStreams() {
+ double x = x0;
+ double[] uniformNormal = new double[d];
+ double[] uniformIG1 = new double[d];
+ double[] uniformIG2 = new double[d];
+
+ for (int j = 0; j < d; j++)
+ {
+ uniformIG1[j] = streamIG1.nextDouble();
+ uniformNormal[j] = streamBrownian.nextDouble();
+ uniformIG2[j] = streamIG2.nextDouble();
+ }
+
+ stochTime = igProcess.generatePath(uniformIG1, uniformIG2);
+ for (int j = 0; j < d; j++)
+ {
+ double dy = stochTime[j + 1] - stochTime[j];
+ x += mudt[j] + beta*dy +
+ Math.sqrt(dy) * NormalDistQuick.inverseF01(uniformNormal[j]);
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+ /**
+ * Returns the value of the process for the next time step.
+ * If the underlying {@link InverseGaussianProcess}
+ * is of type {@link InverseGaussianProcessPCA}, this method cannot be
+ * used. It will work with {@link InverseGaussianProcessBridge}, but
+ * the return order of the observations is the bridge order.
+ *
+ */
+ public double nextObservation() {
+ double igNext = igProcess.nextObservation();
+ observationIndex = igProcess.getCurrentObservationIndex();
+ stochTime[observationIndex] = igNext;
+ double dY = igNext - stochTime[0];
+ double dT = t[observationIndex] - t[0];
+ path[observationIndex] = x0 + mu * dT + beta * dY +
+ Math.sqrt(dY) * normalGen.nextDouble();
+ return path[observationIndex];
+ }
+
+
+ protected void init()
+ {
+ super.init();
+ igProcess.setParams(delta, gamma);
+ if(observationTimesSet){
+ stochTime = new double[d+1];
+ stochTime[0] = t[0];
+ dt = new double[d];
+ mudt = new double[d];
+ for(int i = 0; i < d; i++){
+ dt[i] = t[i+1] - t[i];
+ mudt[i] = dt[i] * mu;
+ }
+ }
+ }
+
+ /**
+ * Sets the observation times on the NIG process as usual,
+ * but also sets the observation times of the underlying {@link InverseGaussianProcess}.
+ * It furthermore sets the starting <SPAN CLASS="textit">value</SPAN> of the {@link InverseGaussianProcess}
+ * to <TT>t[0]</TT>.
+ *
+ */
+ public void setObservationTimes(double t[], int d) {
+ super.setObservationTimes(t, d);
+ igProcess.setObservationTimes(t, d);
+ igProcess.x0 = t[0];
+ }
+
+
+ /**
+ * Sets the parameters. Also, computes
+ *
+ * <SPAN CLASS="MATH"><I>γ</I> = (α^2-β^2)<SUP>1/2</SUP></SPAN>.
+ *
+ */
+ public void setParams (double x0, double alpha, double beta,
+ double mu, double delta) {
+ // Initializes the parameters of the process
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ this.x0 = x0;
+ this.mu = mu;
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+
+ gamma = Math.sqrt(alpha*alpha - beta*beta);
+ if (observationTimesSet) init();
+ }
+
+
+ /**
+ * Returns alpha.
+ *
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+
+ /**
+ * Returns beta.
+ *
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+
+ /**
+ * Returns mu.
+ *
+ */
+ public double getMu() {
+ return mu;
+ }
+
+
+ /**
+ * Returns delta.
+ *
+ */
+ public double getDelta() {
+ return delta;
+ }
+
+
+ /**
+ * Returns gamma.
+ *
+ */
+ public double getGamma() {
+ return gamma;
+ }
+
+
+ /**
+ * Returns the analytic average, which
+ * is
+ * <SPAN CLASS="MATH"><I>μt</I> + <I>δtβ</I>/<I>γ</I></SPAN>.
+ *
+ */
+ public double getAnalyticAverage (double time) {
+ return mu*time+delta*time*beta/gamma;
+ }
+
+
+ /**
+ * Returns the analytic variance, which is
+ *
+ * <SPAN CLASS="MATH"><I>δtα</I><SUP>2</SUP>/<I>γ</I><SUP>3</SUP></SPAN>.
+ *
+ */
+ public double getAnalyticVariance (double time) {
+ return delta*time*alpha*alpha/gamma/gamma/gamma;
+ }
+
+
+ /**
+ * Only returns the stream if all streams are equal,
+ * including the stream(s) in the underlying {@link InverseGaussianProcess}.
+ *
+ */
+ public RandomStream getStream() {
+ if( (streamIG1 != streamIG2) ||
+ (streamIG1 != streamBrownian) ||
+ (streamIG1 != normalGen.getStream()) ||
+ (streamIG1 != igProcess.getStream()) )
+ throw new UnsupportedOperationException
+ ("Two different streams or more are present");
+ return streamIG1;
+ }
+
+
+ /**
+ * Sets all internal streams to <TT>stream</TT>,
+ * including the stream(s) of the underlying {@link InverseGaussianProcess}.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ streamIG1 = streamIG2 = streamBrownian = stream;
+ normalGen.setStream(stream);
+ igProcess.setStream(stream);
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/NormalInverseGaussianProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/NormalInverseGaussianProcess.tex
new file mode 100644
index 0000000..30bd3e2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/NormalInverseGaussianProcess.tex
@@ -0,0 +1,398 @@
+\defmodule {NormalInverseGaussianProcess}
+
+This class represents a normal inverse gaussian process (NIG).
+It obeys the stochastic differential equation \cite{pBAR98a}
+\begin{equation}
+ dX(t) = \mu dt + dB(h(t)), \label{eq:nig}
+\end{equation}
+where $\{B(t),\, t\ge 0\}$ is a \class{BrownianMotion}
+with drift $\beta$ and variance 1,
+and $h(t)$ is an \class{InverseGaussianProcess} $IG(\nu/\gamma,\nu^2)$, with
+$\nu = \delta dt$ and $\gamma = \sqrt{\alpha^2 - \beta^2}$.
+
+In this class, the process is generated using the sequential
+technique: $X(0)=x_0$ and
+\begin{equation}
+ X(t_j) - X(t_{j-1}) =\mu dt + \beta Y_j + \sqrt{Y_j} Z_j,
+\end{equation}
+where $Z_j \sim N(0,1)$, and $Y_j \sim IG(\nu/\gamma,\nu^2)$
+with $\nu = \delta(t_j - t_{j-1})$.
+
+There is one \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+used to generate the $Z_j$'s and
+there are one or two streams used to generate the underlying
+\class{InverseGaussianProcess}, depending on which IG subclass
+is used.
+
+In finance, a NIG process usually means that
+the log-return is given by a NIG process;
+\class{GeometricNormalInverseGaussianProcess}
+should be used in that case.
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: NormalInverseGaussianProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+import umontreal.iro.lecuyer.hups.*;
+
+\end{hide}
+
+public class NormalInverseGaussianProcess extends StochasticProcess \begin{hide} {
+ protected RandomStream streamIG1; // U[0,1) gen used in the inverse gaussian
+ protected RandomStream streamIG2; // U[0,1) gen used (maybe) in the inverse gaussian
+ protected RandomStream streamBrownian; // U[0,1) gen for the normal "Brownian"
+
+ protected InverseGaussianProcess igProcess;
+ protected NormalGen normalGen;
+ // Randomized time generated by the InverseGaussianProcess.
+ protected double[] stochTime;
+ double[] dt;
+ double[] mudt;
+
+ // Parameters of the normal inverse gaussian
+ protected double mu;
+ protected double delta;
+ protected double alpha;
+ protected double beta;
+ protected double gamma;
+
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NormalInverseGaussianProcess (double x0, double alpha,
+ double beta, double mu,
+ double delta,
+ RandomStream streamBrownian,
+ InverseGaussianProcess igP) \begin{hide} {
+ setParams (x0, alpha, beta, mu, delta);
+ this.streamBrownian = streamBrownian;
+ normalGen = new NormalGen(streamBrownian); // N(0,1)
+ igProcess = igP; // its params will be set in init().
+ this.streamIG1 = igProcess.getStream();
+ this.streamIG2 = streamIG1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Given an \class{InverseGaussianProcess} \texttt{igP}, constructs a
+new \texttt{NormalInverseGaussianProcess}. The parameters and observation
+times of the IG process will be overriden by the parameters
+of the NIG process. If there are two
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s in the
+\class{InverseGaussianProcess}, this constructor assumes that
+both streams have been set to the same stream.
+\end{tabb}
+\begin{code}
+
+ public NormalInverseGaussianProcess (double x0, double alpha,
+ double beta, double mu,
+ double delta,
+ RandomStream streamBrownian,
+ RandomStream streamIG1,
+ RandomStream streamIG2,
+ String igType) \begin{hide} {
+ setParams (x0, alpha, beta, mu, delta);
+ this.streamIG1 = streamIG1;
+ this.streamIG2 = streamIG2;
+ this.streamBrownian = streamBrownian;
+ normalGen = new NormalGen(streamBrownian); // N(0,1)
+
+ // The initial time of igProcess here, 0., is arbitrary:
+ // init() sets igProcess.x0 = t[0].
+ if(igType.compareTo("SEQUENTIAL_SLOW") == 0)
+ // the initial value, 0.0, of the IG will be overriden by
+ // the initial time, when it will be set.
+ igProcess = new InverseGaussianProcess(0.0, delta, gamma,
+ streamIG1);
+ else if(igType.compareTo("SEQUENTIAL_MSH") == 0)
+ igProcess = new InverseGaussianProcessMSH (0.0, delta, gamma,
+ streamIG1, streamIG2);
+ else if(igType.compareTo("BRIDGE") == 0)
+ igProcess = new InverseGaussianProcessBridge (0.0, delta, gamma,
+ streamIG1, streamIG2);
+ else if(igType.compareTo("PCA") == 0)
+ igProcess = new InverseGaussianProcessPCA (0.0, delta, gamma,
+ streamIG1);
+ else throw new IllegalArgumentException("Unrecognized igType");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{Normal\-Inverse\-Gaussian\-Process}.
+The string argument corresponds to the type of underlying
+\class{Inverse\-Gaussian\-Process}. The choices are SEQUENTIAL\_SLOW,
+SEQUENTIAL\_MSH, BRIDGE and PCA, which correspond
+respectively to \class{Inverse\-Gaussian\-Process}, \class{Inverse\-Gaussian\-ProcessMSH},
+\class{Inverse\-Gaussian\-Process\-Bridge} and \class{Inverse\-Gaussian\-ProcessPCA}.
+The third \externalclass{umontreal.iro.lecuyer.rng}{Random\-Stream}, \texttt{streamIG2},
+will not be used at all if the SEQUENTIAL\_SLOW or PCA methods are chosen.
+\end{tabb}
+\begin{code}
+
+ public NormalInverseGaussianProcess (double x0, double alpha,
+ double beta, double mu,
+ double delta,
+ RandomStream streamAll,
+ String igType) \begin{hide} {
+ this(x0, alpha, beta, mu, delta, streamAll, streamAll, streamAll, igType);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as above, but all \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s
+are set to the same stream, \texttt{streamAll}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double[] generatePath() \begin{hide} {
+ if(igProcess.getNumberOfRandomStreams() != 1)
+ return generatePathTwoIGStreams();
+
+ double x = x0;
+ double[] randomNormal = new double[d];
+ double[] randomIG1 = new double[d];
+ for (int j = 0; j < d; j++)
+ {
+ randomIG1[j] = streamIG1.nextDouble();
+ randomNormal[j] = streamBrownian.nextDouble();
+ }
+
+ stochTime = igProcess.generatePath(randomIG1);
+ for (int j = 0; j < d; j++)
+ {
+ double dy = stochTime[j + 1] - stochTime[j];
+ x += mudt[j] + beta*dy +
+ Math.sqrt(dy) * NormalDistQuick.inverseF01(randomNormal[j]);
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Generates the path.
+This method samples each stream alternatively,
+which is useful for quasi-Monte Carlo, where
+all streams are in fact the same iterator on
+ a \externalclass{umontreal.iro.lecuyer.hups}{PointSet}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected double[] generatePathTwoIGStreams() {
+ double x = x0;
+ double[] uniformNormal = new double[d];
+ double[] uniformIG1 = new double[d];
+ double[] uniformIG2 = new double[d];
+
+ for (int j = 0; j < d; j++)
+ {
+ uniformIG1[j] = streamIG1.nextDouble();
+ uniformNormal[j] = streamBrownian.nextDouble();
+ uniformIG2[j] = streamIG2.nextDouble();
+ }
+
+ stochTime = igProcess.generatePath(uniformIG1, uniformIG2);
+ for (int j = 0; j < d; j++)
+ {
+ double dy = stochTime[j + 1] - stochTime[j];
+ x += mudt[j] + beta*dy +
+ Math.sqrt(dy) * NormalDistQuick.inverseF01(uniformNormal[j]);
+ path[j + 1] = x;
+ }
+ observationIndex = d;
+ return path;
+ }\end{hide}
+
+ public double nextObservation() \begin{hide} {
+ double igNext = igProcess.nextObservation();
+ observationIndex = igProcess.getCurrentObservationIndex();
+ stochTime[observationIndex] = igNext;
+ double dY = igNext - stochTime[0];
+ double dT = t[observationIndex] - t[0];
+ path[observationIndex] = x0 + mu * dT + beta * dY +
+ Math.sqrt(dY) * normalGen.nextDouble();
+ return path[observationIndex];
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the process for the next time step.
+If the underlying \class{InverseGaussian\-Process}
+is of type \class{InverseGaussianProcessPCA}, this method cannot be
+used. It will work with \class{InverseGaussianProcessBridge}, but
+the return order of the observations is the bridge order.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void init()
+ {
+ super.init();
+ igProcess.setParams(delta, gamma);
+ if(observationTimesSet){
+ stochTime = new double[d+1];
+ stochTime[0] = t[0];
+ dt = new double[d];
+ mudt = new double[d];
+ for(int i = 0; i < d; i++){
+ dt[i] = t[i+1] - t[i];
+ mudt[i] = dt[i] * mu;
+ }
+ }
+ }\end{hide}
+
+ public void setObservationTimes(double t[], int d) \begin{hide} {
+ super.setObservationTimes(t, d);
+ igProcess.setObservationTimes(t, d);
+ igProcess.x0 = t[0];
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the observation times on the NIG process as usual,
+but also sets the observation times of the underlying \class{InverseGaussianProcess}.
+It furthermore sets the starting \emph{value} of the \class{InverseGaussianProcess}
+to \texttt{t[0]}.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double x0, double alpha, double beta,
+ double mu, double delta) \begin{hide} {
+ // Initializes the parameters of the process
+ if (delta <= 0.0)
+ throw new IllegalArgumentException ("delta <= 0");
+ if (alpha <= 0.0)
+ throw new IllegalArgumentException ("alpha <= 0");
+ if (Math.abs(beta) >= alpha)
+ throw new IllegalArgumentException ("|beta| >= alpha");
+
+ this.x0 = x0;
+ this.mu = mu;
+ this.delta = delta;
+ this.beta = beta;
+ this.alpha = alpha;
+
+ gamma = Math.sqrt(alpha*alpha - beta*beta);
+ if (observationTimesSet) init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the parameters. Also, computes
+$\gamma = \sqrt{\alpha^2-\beta^2}$.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha() \begin{hide} {
+ return alpha;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns alpha.
+\end{tabb}
+\begin{code}
+
+ public double getBeta() \begin{hide} {
+ return beta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns beta.
+\end{tabb}
+\begin{code}
+
+ public double getMu() \begin{hide} {
+ return mu;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns mu.
+\end{tabb}
+\begin{code}
+
+ public double getDelta() \begin{hide} {
+ return delta;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns delta.
+\end{tabb}
+\begin{code}
+
+ public double getGamma() \begin{hide} {
+ return gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns gamma.
+\end{tabb}
+\begin{code}
+
+ public double getAnalyticAverage (double time) \begin{hide} {
+ return mu*time+delta*time*beta/gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the analytic average, which
+is $\mu t + \delta t \beta/ \gamma$.
+\end{tabb}
+\begin{code}
+
+ public double getAnalyticVariance (double time) \begin{hide} {
+ return delta*time*alpha*alpha/gamma/gamma/gamma;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the analytic variance, which is
+$\delta t \alpha^2 / \gamma^3$.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ if( (streamIG1 != streamIG2) ||
+ (streamIG1 != streamBrownian) ||
+ (streamIG1 != normalGen.getStream()) ||
+ (streamIG1 != igProcess.getStream()) )
+ throw new UnsupportedOperationException
+ ("Two different streams or more are present");
+ return streamIG1;
+ }\end{hide}
+\end{code}
+\begin{tabb} Only returns the stream if all streams are equal,
+including the stream(s) in the underlying \class{InverseGaussianProcess}.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ streamIG1 = streamIG2 = streamBrownian = stream;
+ normalGen.setStream(stream);
+ igProcess.setStream(stream);
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets all internal streams to \texttt{stream},
+including the stream(s) of the underlying \class{Inverse\-Gaussian\-Process}.
+\end{tabb}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcess.java b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcess.java
new file mode 100644
index 0000000..5a61c36
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcess.java
@@ -0,0 +1,260 @@
+
+
+/*
+ * Class: OrnsteinUhlenbeckProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * This class represents an <SPAN CLASS="textit">Ornstein-Uhlenbeck</SPAN> process
+ *
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I>) : <I>t</I> >= 0}</SPAN>, sampled at times
+ * <SPAN CLASS="MATH">0 = <I>t</I><SUB>0</SUB> < <I>t</I><SUB>1</SUB> < <SUP> ... </SUP> < <I>t</I><SUB>d</SUB></SPAN>.
+ * This process obeys the stochastic differential equation
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:ornstein"></A>
+ * <I>dX</I>(<I>t</I>) = <I>α</I>(<I>b</I> - <I>X</I>(<I>t</I>))<I>dt</I> + <I>σ</I> <I>dB</I>(<I>t</I>)
+ * </DIV><P></P>
+ * with initial condition <SPAN CLASS="MATH"><I>X</I>(0) = <I>x</I><SUB>0</SUB></SPAN>,
+ * where <SPAN CLASS="MATH"><I>α</I></SPAN>, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are positive constants,
+ * and
+ * <SPAN CLASS="MATH">{<I>B</I>(<I>t</I>), <I>t</I> >= 0}</SPAN> is a standard Brownian motion
+ * (with drift 0 and volatility 1).
+ * This process is <SPAN CLASS="textit">mean-reverting</SPAN> in the sense that it always tends to
+ * drift toward its general mean <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ * The process is generated using the sequential technique
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:ornstein-seq"></A>
+ * <I>X</I>(<I>t</I><SUB>j</SUB>) = <I>e</I><SUP>-<I>α</I>(t<SUB>j</SUB>-t<SUB>j-1</SUB>)</SUP><I>X</I>(<I>t</I><SUB>j-1</SUB>) + <I>b</I>(1 - <I>e</I><SUP>-<I>α</I>(t<SUB>j</SUB>-t<SUB>j-1</SUB>)</SUP>) + <I>σ</I>(1 - e^-2α(t_j - t_j-1)2α)<SUP>1/2</SUP> <I>Z</I><SUB>j</SUB>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB>∼<I>N</I>(0, 1)</SPAN>. The time intervals
+ * <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB> - <I>t</I><SUB>j-1</SUB></SPAN>
+ * can be arbitrarily large.
+ *
+ */
+public class OrnsteinUhlenbeckProcess extends StochasticProcess {
+ protected NormalGen gen;
+ protected double alpha,
+ beta,
+ sigma;
+ // Precomputed values
+ protected double[] badt,
+ alphadt,
+ sigmasqrdt;
+
+
+
+ /**
+ * Constructs a new <TT>OrnsteinUhlenbeckProcess</TT> with parameters
+ * <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT> and initial value
+ *
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>x0</TT>. The normal variates <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB></SPAN> will be
+ * generated by inversion using the stream <TT>stream</TT>.
+ *
+ */
+ public OrnsteinUhlenbeckProcess (double x0, double alpha, double b,
+ double sigma, RandomStream stream) {
+ this (x0, alpha, b, sigma, new NormalGen (stream));
+ }
+
+
+ /**
+ * Here, the normal variate generator is specified directly
+ * instead of specifying the stream.
+ * The normal generator <TT>gen</TT> can use another method than inversion.
+ *
+ */
+ public OrnsteinUhlenbeckProcess (double x0, double alpha, double b,
+ double sigma, NormalGen gen) {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ this.gen = gen;
+ }
+
+
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double x = badt[observationIndex] + xOld * alphadt[observationIndex]
+ + sigmasqrdt[observationIndex] * gen.nextDouble();
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }
+
+ /**
+ * Generates and returns the next observation at time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB> =</SPAN>
+ * <TT>nextTime</TT>, using the previous observation time <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> defined earlier
+ * (either by this method or by <TT>setObservationTimes</TT>),
+ * as well as the value of the previous observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will reset the observations time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN>
+ * for this process to <TT>nextTime</TT>. The user must make sure that
+ * the <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN> supplied is
+ * <SPAN CLASS="MATH"> >= <I>t</I><SUB>j</SUB></SPAN>.
+ *
+ */
+ public double nextObservation (double nextTime) {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double tem = Math.exp(-alpha * dt);
+ double tem1 = -Math.expm1(-alpha * dt);
+ double x = tem*xOld + beta*tem1 + sigma *
+ Math.sqrt(tem1*(1.0 + tem)/(2.0*alpha)) * gen.nextDouble();
+ path[observationIndex] = x;
+ return x;
+ }
+
+
+ /**
+ * Generates an observation of the process in <TT>dt</TT> time units,
+ * assuming that the process has value <SPAN CLASS="MATH"><I>x</I></SPAN> at the current time.
+ * Uses the process parameters specified in the constructor.
+ * Note that this method does not affect the sample path of the process
+ * stored internally (if any).
+ *
+ *
+ */
+ public double nextObservation (double x, double dt) {
+ double tem = Math.exp(-alpha * dt);
+ double tem1 = -Math.expm1(-alpha * dt);
+ x = tem*x + beta*tem1 + sigma *
+ Math.sqrt(tem1*(1.0 + tem)/(2.0*alpha)) * gen.nextDouble();
+ return x;
+ }
+
+
+ public double[] generatePath() {
+ double x;
+ double xOld = x0;
+ for (int j = 0; j < d; j++) {
+ x = badt[j] + xOld * alphadt[j] + sigmasqrdt[j] * gen.nextDouble();
+ path[j + 1] = x;
+ xOld = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }
+
+
+ /**
+ * Resets the parameters
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>x0</TT>, <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>,
+ * <SPAN CLASS="MATH"><I>b</I> =</SPAN> <TT>b</TT> and <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called too frequently.
+ *
+ */
+ public void setParams (double x0, double alpha, double b, double sigma) {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }
+
+
+ /**
+ * Resets the random stream of the normal generator to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) { gen.setStream (stream); }
+
+
+ /**
+ * Returns the random stream of the normal generator.
+ *
+ */
+ public RandomStream getStream () { return gen.getStream (); }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>α</I></SPAN>.
+ *
+ */
+ public double getAlpha() { return alpha; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>b</I></SPAN>.
+ *
+ */
+ public double getB() { return beta; }
+
+
+ /**
+ * Returns the value of <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() { return sigma; }
+
+
+ /**
+ * Returns the normal random variate generator used.
+ * The <TT>RandomStream</TT> used for that generator can be changed via
+ * <TT>getGen().setStream(stream)</TT>, for example.
+ *
+ */
+ public NormalGen getGen() { return gen; }
+
+
+ protected void initArrays(int d) {
+ double dt, tem, tem1;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ tem = Math.exp(-alpha * dt);
+ tem1 = -Math.expm1(-alpha * dt);
+ badt[j] = beta*tem1;
+ alphadt[j] = tem;
+ sigmasqrdt[j] = sigma * Math.sqrt (tem1*(1.0 + tem)/(2.0*alpha));
+ }
+ }
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ badt = new double[d];
+ alphadt = new double[d];
+ sigmasqrdt = new double[d];
+ initArrays(d);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcess.tex
new file mode 100644
index 0000000..cf45329
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcess.tex
@@ -0,0 +1,266 @@
+\defmodule {OrnsteinUhlenbeckProcess}
+
+This class represents an \emph{Ornstein-Uhlenbeck} process
+ $\{X(t) : t \geq 0 \}$, sampled at times $0 = t_0 < t_1 < \cdots < t_d$.
+This process obeys the stochastic differential equation
+\begin{equation}
+ dX(t) = \alpha(b - X(t)) dt + \sigma\, dB(t)
+ \label{eq:ornstein}
+\end{equation}
+with initial condition $X(0)= x_0$,
+where $\alpha$, $b$ and $\sigma$ are positive constants,
+and $\{B(t),\, t\ge 0\}$ is a standard Brownian motion
+(with drift 0 and volatility 1).
+This process is \emph{mean-reverting} in the sense that it always tends to
+drift toward its general mean $b$.
+The process is generated using the sequential technique \cite[p. 110]{fGLA04a}
+\begin{equation}
+ X(t_j) = e^{-\alpha(t_j - t_{j-1})} X(t_{j-1}) +
+ b\left(1 - e^{-\alpha(t_j - t_{j-1})}\right) +
+ \sigma \sqrt{\frac{1 - e^{-2\alpha(t_j - t_{j-1})}}{2\alpha}}\, Z_j
+ \label{eq:ornstein-seq}
+\end{equation}
+where $Z_j \sim {N}(0,1)$. The time intervals $t_j - t_{j-1}$
+can be arbitrarily large.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: OrnsteinUhlenbeckProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class OrnsteinUhlenbeckProcess extends StochasticProcess \begin{hide} {
+ protected NormalGen gen;
+ protected double alpha,
+ beta,
+ sigma;
+ // Precomputed values
+ protected double[] badt,
+ alphadt,
+ sigmasqrdt;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public OrnsteinUhlenbeckProcess (double x0, double alpha, double b,
+ double sigma, RandomStream stream)\begin{hide} {
+ this (x0, alpha, b, sigma, new NormalGen (stream));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \texttt{OrnsteinUhlenbeckProcess} with parameters
+ $\alpha =$ \texttt{alpha}, $b$, $\sigma =$ \texttt{sigma} and initial value
+$X(t_{0}) =$ \texttt{x0}. The normal variates $Z_j$ will be
+generated by inversion using the stream \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public OrnsteinUhlenbeckProcess (double x0, double alpha, double b,
+ double sigma, NormalGen gen) \begin{hide} {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ this.gen = gen;
+ }\end{hide}
+\end{code}
+\begin{tabb} Here, the normal variate generator is specified directly
+instead of specifying the stream.
+The normal generator \texttt{gen} can use another method than inversion.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double x = badt[observationIndex] + xOld * alphadt[observationIndex]
+ + sigmasqrdt[observationIndex] * gen.nextDouble();
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }\end{hide}
+
+ public double nextObservation (double nextTime) \begin{hide} {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double tem = Math.exp(-alpha * dt);
+ double tem1 = -Math.expm1(-alpha * dt);
+ double x = tem*xOld + beta*tem1 + sigma *
+ Math.sqrt(tem1*(1.0 + tem)/(2.0*alpha)) * gen.nextDouble();
+ path[observationIndex] = x;
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates and returns the next observation at time $t_{j+1} =$
+ \texttt{nextTime}, using the previous observation time $t_{j}$ defined earlier
+(either by this method or by \texttt{setObservationTimes}),
+as well as the value of the previous observation $X(t_j)$.
+\emph{Warning}: This method will reset the observations time $t_{j+1}$
+for this process to \texttt{nextTime}. The user must make sure that
+the $t_{j+1}$ supplied is $\geq t_{j}$.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation (double x, double dt) \begin{hide} {
+ double tem = Math.exp(-alpha * dt);
+ double tem1 = -Math.expm1(-alpha * dt);
+ x = tem*x + beta*tem1 + sigma *
+ Math.sqrt(tem1*(1.0 + tem)/(2.0*alpha)) * gen.nextDouble();
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates an observation of the process in \texttt{dt} time units,
+assuming that the process has value $x$ at the current time.
+Uses the process parameters specified in the constructor.
+Note that this method does not affect the sample path of the process
+stored internally (if any).
+\end{tabb}
+\begin{hide}\begin{code}
+
+ public double[] generatePath() {
+ double x;
+ double xOld = x0;
+ for (int j = 0; j < d; j++) {
+ x = badt[j] + xOld * alphadt[j] + sigmasqrdt[j] * gen.nextDouble();
+ path[j + 1] = x;
+ xOld = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+ public double[] generatePath (RandomStream stream) {
+ gen.setStream (stream);
+ return generatePath();
+ }
+\end{code}
+\begin{tabb} Generates a sample path of the process at all observation times,
+ which are provided in array \texttt{t}.
+ Note that \texttt{t[0]} should be the observation time of \texttt{x0},
+ the initial value of the process, and \texttt{t[]} should have at least $d+1$
+ elements (see the \texttt{setObservationTimes} method).
+\end{tabb}\end{hide}
+\begin{code}
+
+ public void setParams (double x0, double alpha, double b, double sigma) \begin{hide} {
+ this.alpha = alpha;
+ this.beta = b;
+ this.sigma = sigma;
+ this.x0 = x0;
+ if (observationTimesSet) init(); // Otherwise not needed.
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the parameters $X(t_{0}) =$ \texttt{x0}, $\alpha =$ \texttt{alpha},
+ $b =$ \texttt{b} and $\sigma =$ \texttt{sigma} of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called too frequently.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} { gen.setStream (stream); }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the random stream of the normal generator to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream () \begin{hide} { return gen.getStream (); }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the random stream of the normal generator.
+\end{tabb}
+\begin{code}
+
+ public double getAlpha() \begin{hide} { return alpha; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\alpha$.
+\end{tabb}
+\begin{code}
+
+ public double getB() \begin{hide} { return beta; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $b$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma() \begin{hide} { return sigma; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public NormalGen getGen() \begin{hide} { return gen; }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the normal random variate generator used.
+The \texttt{RandomStream} used for that generator can be changed via
+\texttt{getGen().setStream(stream)}, for example.
+\end{tabb}
+\begin{code} \begin{hide}
+
+ protected void initArrays(int d) {
+ double dt, tem, tem1;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ tem = Math.exp(-alpha * dt);
+ tem1 = -Math.expm1(-alpha * dt);
+ badt[j] = beta*tem1;
+ alphadt[j] = tem;
+ sigmasqrdt[j] = sigma * Math.sqrt (tem1*(1.0 + tem)/(2.0*alpha));
+ }
+ }
+
+ // This is called by setObservationTimes to precompute constants
+ // in order to speed up the path generation.
+ protected void init() {
+ super.init();
+ badt = new double[d];
+ alphadt = new double[d];
+ sigmasqrdt = new double[d];
+ initArrays(d);
+ }\end{hide}
+\end{code}
+\begin{code}\begin{hide}
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcessEuler.java b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcessEuler.java
new file mode 100644
index 0000000..fa54f05
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcessEuler.java
@@ -0,0 +1,160 @@
+
+
+/*
+ * Class: OrnsteinUhlenbeckProcessEuler
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * .
+ *
+ * This class represents an <SPAN CLASS="textit">Ornstein-Uhlenbeck</SPAN> process
+ * as in {@link OrnsteinUhlenbeckProcess}, but
+ * the process is generated using the simple Euler scheme
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq:ornstein-seqEuler"></A>
+ * <I>X</I>(<I>t</I><SUB>j</SUB>) - <I>X</I>(<I>t</I><SUB>j-1</SUB>) = <I>α</I>(<I>b</I> - <I>X</I>(<I>t</I><SUB>j-1</SUB>))(<I>t</I><SUB>j</SUB> - <I>t</I><SUB>j-1</SUB>) + <I>σ</I>(t_j - t_j-1)<SUP>1/2</SUP> <I>Z</I><SUB>j</SUB>
+ * </DIV><P></P>
+ * where
+ * <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB>∼<I>N</I>(0, 1)</SPAN>.
+ * This is a good approximation only for small time intervals
+ * <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB> - <I>t</I><SUB>j-1</SUB></SPAN>.
+ *
+ */
+public class OrnsteinUhlenbeckProcessEuler extends OrnsteinUhlenbeckProcess {
+
+
+
+ /**
+ * Constructor with
+ * parameters <SPAN CLASS="MATH"><I>α</I> =</SPAN> <TT>alpha</TT>, <SPAN CLASS="MATH"><I>b</I></SPAN>, <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT> and initial
+ * value
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>x0</TT>. The normal variates <SPAN CLASS="MATH"><I>Z</I><SUB>j</SUB></SPAN> will be
+ * generated by inversion using the stream <TT>stream</TT>.
+ *
+ */
+ public OrnsteinUhlenbeckProcessEuler (double x0, double alpha, double b,
+ double sigma, RandomStream stream)
+ {
+ this (x0, alpha, b, sigma, new NormalGen (stream));
+ }
+
+
+ /**
+ * Here, the normal variate generator is specified directly
+ * instead of specifying the stream.
+ * The normal generator <TT>gen</TT> can use another method than inversion.
+ *
+ */
+ public OrnsteinUhlenbeckProcessEuler (double x0, double alpha, double b,
+ double sigma, NormalGen gen) {
+ super (x0, alpha, b, sigma, gen);
+ }
+
+
+ /**
+ * Generates and returns the next observation at time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB> =</SPAN>
+ * <TT>nextTime</TT>. Assumes the previous observation time is <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> defined
+ * earlier (either by this method or by <TT>setObservationTimes</TT>),
+ * as well as the value of the previous observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will reset the observations time <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN>
+ * for this process to <TT>nextTime</TT>. The user must make sure that
+ * the <SPAN CLASS="MATH"><I>t</I><SUB>j+1</SUB></SPAN> supplied is
+ * <SPAN CLASS="MATH"> >= <I>t</I><SUB>j</SUB></SPAN>.
+ *
+ */
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double x = xOld + (beta - xOld) * alphadt[observationIndex]
+ + sigmasqrdt[observationIndex] * gen.nextDouble();
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }
+
+ public double nextObservation (double nextTime) {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double x = xOld + alpha * (beta - xOld) * dt
+ + sigma * Math.sqrt (dt) * gen.nextDouble();
+ path[observationIndex] = x;
+ return x;
+ }
+
+
+ /**
+ * Generates and returns an observation of the process
+ * in <TT>dt</TT> time units,
+ * assuming that the process has value <SPAN CLASS="MATH"><I>x</I></SPAN> at the current time.
+ * Uses the process parameters specified in the constructor.
+ * Note that this method does not affect the sample path of the process
+ * stored internally (if any).
+ *
+ */
+ public double nextObservation (double x, double dt) {
+ x = x + alpha * (beta - x) * dt
+ + sigma * Math.sqrt (dt) * gen.nextDouble();
+ return x;
+ }
+
+
+ /**
+ * Generates a sample path of the process at all observation times,
+ * which are provided in array <TT>t</TT>.
+ * Note that <TT>t[0]</TT> should be the observation time of <TT>x0</TT>,
+ * the initial value of the process, and <TT>t[]</TT> should have at least <SPAN CLASS="MATH"><I>d</I> + 1</SPAN>
+ * elements (see the <TT>setObservationTimes</TT> method).
+ *
+ */
+ public double[] generatePath() {
+ double x;
+ double xOld = x0;
+ for (int j = 0; j < d; j++) {
+ x = xOld + (beta - xOld)*alphadt[j] + sigmasqrdt[j]*gen.nextDouble();
+ path[j + 1] = x;
+ xOld = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+
+
+ protected void initArrays(int d) {
+ double dt;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ alphadt[j] = alpha * (dt);
+ sigmasqrdt[j] = sigma * Math.sqrt (dt);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcessEuler.tex b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcessEuler.tex
new file mode 100644
index 0000000..2ba5994
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/OrnsteinUhlenbeckProcessEuler.tex
@@ -0,0 +1,160 @@
+\defmodule {OrnsteinUhlenbeckProcessEuler}
+
+This class represents an \emph{Ornstein-Uhlenbeck} process
+as in \class{OrnsteinUhlenbeckProcess}, but
+the process is generated using the simple Euler scheme
+\begin{equation}
+ X(t_j) - X(t_{j-1}) = \alpha(b - X(t_{j-1}))(t_j - t_{j-1}) +
+ \sigma \sqrt{t_j - t_{j-1}}\, Z_j
+ \label{eq:ornstein-seqEuler}
+\end{equation}
+where $Z_j \sim N(0,1)$.
+This is a good approximation only for small time intervals $t_j - t_{j-1}$.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: OrnsteinUhlenbeckProcessEuler
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class OrnsteinUhlenbeckProcessEuler extends OrnsteinUhlenbeckProcess\begin{hide} {
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public OrnsteinUhlenbeckProcessEuler (double x0, double alpha, double b,
+ double sigma, RandomStream stream)
+ \begin{hide} {
+ this (x0, alpha, b, sigma, new NormalGen (stream));
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructor with
+parameters $\alpha =$ \texttt{alpha}, $b$, $\sigma =$ \texttt{sigma} and initial
+value $X(t_{0}) =$ \texttt{x0}. The normal variates $Z_j$ will be
+generated by inversion using the stream \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public OrnsteinUhlenbeckProcessEuler (double x0, double alpha, double b,
+ double sigma, NormalGen gen) \begin{hide} {
+ super (x0, alpha, b, sigma, gen);
+ }\end{hide}
+\end{code}
+\begin{tabb} Here, the normal variate generator is specified directly
+instead of specifying the stream.
+The normal generator \texttt{gen} can use another method than inversion.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%\subsubsection* {Methods}
+\begin{hide}\begin{code}
+
+ public double nextObservation() {
+ double xOld = path[observationIndex];
+ double x = xOld + (beta - xOld) * alphadt[observationIndex]
+ + sigmasqrdt[observationIndex] * gen.nextDouble();
+ observationIndex++;
+ path[observationIndex] = x;
+ return x;
+ }
+
+ public double nextObservation (double nextTime) {
+ double previousTime = t[observationIndex];
+ double xOld = path[observationIndex];
+ observationIndex++;
+ t[observationIndex] = nextTime;
+ double dt = nextTime - previousTime;
+ double x = xOld + alpha * (beta - xOld) * dt
+ + sigma * Math.sqrt (dt) * gen.nextDouble();
+ path[observationIndex] = x;
+ return x;
+ }
+\end{code}
+\begin{tabb} Generates and returns the next observation at time $t_{j+1} =$
+ \texttt{nextTime}. Assumes the previous observation time is $t_{j}$ defined
+earlier (either by this method or by \texttt{setObservationTimes}),
+as well as the value of the previous observation $X(t_j)$.
+\emph{Warning}: This method will reset the observations time $t_{j+1}$
+for this process to \texttt{nextTime}. The user must make sure that
+the $t_{j+1}$ supplied is $\geq t_{j}$.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation (double x, double dt) {
+ x = x + alpha * (beta - x) * dt
+ + sigma * Math.sqrt (dt) * gen.nextDouble();
+ return x;
+ }
+\end{code}
+\begin{tabb} Generates and returns an observation of the process
+in \texttt{dt} time units,
+assuming that the process has value $x$ at the current time.
+Uses the process parameters specified in the constructor.
+Note that this method does not affect the sample path of the process
+stored internally (if any).
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath() {
+ double x;
+ double xOld = x0;
+ for (int j = 0; j < d; j++) {
+ x = xOld + (beta - xOld)*alphadt[j] + sigmasqrdt[j]*gen.nextDouble();
+ path[j + 1] = x;
+ xOld = x;
+ }
+ observationIndex = d;
+ return path;
+ }
+\end{code}
+\begin{tabb} Generates a sample path of the process at all observation times,
+ which are provided in array \texttt{t}.
+ Note that \texttt{t[0]} should be the observation time of \texttt{x0},
+ the initial value of the process, and \texttt{t[]} should have at least $d+1$
+ elements (see the \texttt{setObservationTimes} method).
+\end{tabb}
+\begin{code}
+
+ protected void initArrays(int d) {
+ double dt;
+ for (int j = 0; j < d; j++) {
+ dt = t[j+1] - t[j];
+ alphadt[j] = alpha * (dt);
+ sigmasqrdt[j] = sigma * Math.sqrt (dt);
+ }
+ }
+}\end{code}
+\end{hide}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/StochasticProcess.java b/source/umontreal/iro/lecuyer/stochprocess/StochasticProcess.java
new file mode 100644
index 0000000..145ef3c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/StochasticProcess.java
@@ -0,0 +1,327 @@
+
+
+/*
+ * Class: StochasticProcess
+ * Description: Base class for all stochastic processes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.RandomStream;
+
+
+
+/**
+ * Abstract base class for a stochastic process
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I>) : <I>t</I> >= 0}</SPAN>
+ * sampled (or observed) at a finite number of time points,
+ *
+ * <SPAN CLASS="MATH">0 = <I>t</I><SUB>0</SUB> < <I>t</I><SUB>1</SUB> < <SUP> ... </SUP> < <I>t</I><SUB>d</SUB></SPAN>.
+ * The observation times are usually all specified before generating a sample path.
+ * This can be done via <TT>setObservationTimes</TT>.
+ * The method <TT>generatePath</TT> generates
+ * <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>1</SUB>),..., <I>X</I>(<I>t</I><SUB>d</SUB>)</SPAN> and memorizes
+ * them in a vector, which can be recovered by <TT>getPath</TT>.
+ *
+ * <P>
+ * Alternatively, for some types of processes, the observations <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN> can be
+ * generated sequentially, one at a time, by invoking <TT>resetStartProcess</TT>
+ * first, and then <TT>nextObservation</TT> repeatedly.
+ * For some types of processes, the observation times can be specified one by one
+ * as well, when generating the path. This may be convenient or even necessary
+ * if the observation times are random, for example.
+ *
+ * <P>
+ * <SPAN CLASS="textbf">WARNING: After having called the constructor for one of the subclass,
+ * one must always set the observation times of the process,
+ * by calling method <TT>setObservationTimes</TT> for example or otherwise.</SPAN>
+ *
+ */
+public abstract class StochasticProcess {
+ // Used in some subclasses to make sure the 'setObservationTimes'
+ // method has already been invoked before calling 'init'
+ protected boolean observationTimesSet = false;
+
+ protected double x0 = 0.0; // Default Initial Value of the process
+ protected int d = -1; // Num. of observation times
+ protected int observationIndex = 0; // Index of last generated obs time
+ protected int observationCounter = 0; // Counts how many observations have
+ // been generated so far. Useful when they are not generated in
+ // chronological order.
+ protected double[] t; // Observation times
+ protected double[] path; // Observations of the process
+ //protected RandomStream stream; // Random stream used to generate the process
+ protected int[] observationIndexFromCounter; // Links counter# to index#
+
+
+
+ /**
+ * Sets the observation times of the process to a copy of <TT>T</TT>,
+ * with <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB> =</SPAN> <TT>T[0]</TT> and <SPAN CLASS="MATH"><I>t</I><SUB>d</SUB> =</SPAN> <TT>T[d]</TT>.
+ * The size of <TT>T</TT> must be <SPAN CLASS="MATH"><I>d</I> + 1</SPAN>.
+ *
+ */
+ public void setObservationTimes (double[] T, int d) {
+ if (d <= 0) throw new IllegalArgumentException (
+ "Number of observation times d <= 0");
+ this.d = d;
+ observationTimesSet = true;
+
+ // Copy of the observation times
+ this.t = new double[d+1];
+ System.arraycopy (T, 0, this.t, 0, d+1);
+
+ // Test chronological order
+ for (int i = 0; i < d; i++) {
+ if (T[i+1] < T[i])
+ throw new IllegalArgumentException (
+ "Observation times T[] are not time-ordered");
+ }
+
+ // Construction of 'path' object
+ // We do not do it in 'init()' because we don't have to change the
+ // path object if the user only calls 'setParams'
+ path = new double[d+1];
+
+ /* Process specific initialization; usually precomputes quantities
+ that depend on the observation times. */
+ init();
+ }
+
+
+ /**
+ * Sets equidistant observation times at
+ * <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB> = <I>jδ</I></SPAN>, for
+ * <SPAN CLASS="MATH"><I>j</I> = 0,..., <I>d</I></SPAN>, and <TT>delta</TT> = <SPAN CLASS="MATH"><I>δ</I></SPAN>.
+ *
+ */
+ public void setObservationTimes (double delta, int d) {
+ t = new double[d+1];
+ for (int i=0; i<=d; i++) t[i] = i*delta;
+ setObservationTimes (t, d);
+ }
+
+
+ /**
+ * Returns a reference to the array that contains the observation times
+ *
+ * <SPAN CLASS="MATH">(<I>t</I><SUB>0</SUB>,..., <I>t</I><SUB>d</SUB>)</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method should only be used to <SPAN CLASS="textit">read</SPAN> the observation times.
+ * Changing the values in the array directly may have unexpected consequences.
+ * The method <TT>setObservationTimes</TT> should be used to modify the observation times.
+ *
+ */
+ public double[] getObservationTimes() {
+ return t;
+ }
+
+
+ /**
+ * Returns the number of observation times excluding the time <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB></SPAN>.
+ *
+ */
+ public int getNbObservationTimes() {
+ return d;
+ }
+
+
+ /**
+ * Generates, returns, and saves the sample path
+ *
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I><SUB>0</SUB>), <I>X</I>(<I>t</I><SUB>1</SUB>),…, <I>X</I>(<I>t</I><SUB>d</SUB>)}</SPAN>. It can then be accessed via
+ * <TT>getPath</TT>, <TT>getSubpath</TT>, or <TT>getObservation</TT>.
+ * The generation method depends on the process type.
+ *
+ */
+ public abstract double[] generatePath();
+
+
+ /**
+ * Same as <TT>generatePath()</TT>, but first resets the stream to <TT>stream</TT>.
+ *
+ */
+ public double[] generatePath (RandomStream stream) {
+ setStream (stream);
+ return generatePath();
+ }
+
+
+ /**
+ * Returns a <SPAN CLASS="textit">reference</SPAN> to the last generated sample path
+ *
+ * <SPAN CLASS="MATH">{<I>X</I>(<I>t</I><SUB>0</SUB>),..., <I>X</I>(<I>t</I><SUB>d</SUB>)}</SPAN>.
+ * <SPAN CLASS="textit">Warning</SPAN>: The returned array and its size should not be modified,
+ * because this is the one that memorizes the observations (not a copy of it).
+ * To obtain a copy, use <TT>getSubpath</TT> instead.
+ *
+ */
+ public double[] getPath() {
+ return path;
+ }
+
+
+ /**
+ * Returns in <TT>subpath</TT> the values of the process at a subset of the observation times,
+ * specified as the times <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> whose indices <SPAN CLASS="MATH"><I>j</I></SPAN> are in the array <TT>pathIndices</TT>.
+ * The size of <TT>pathIndices</TT> should be at least as much as that of <TT>subpath</TT>.
+ *
+ */
+ public void getSubpath (double[] subpath, int[] pathIndices) {
+ for (int j=0; j<subpath.length; j++) {
+ subpath[j] = path[pathIndices[j]];
+ }
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN> from the current sample path.
+ * <SPAN CLASS="textit">Warning</SPAN>: If the observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN> for the current path has not yet been
+ * generated, then the value returned is unpredictable.
+ *
+ */
+ public double getObservation (int j) {
+ return path[j];
+ }
+
+
+ /**
+ * Resets the observation counter to its initial value <SPAN CLASS="MATH"><I>j</I> = 0</SPAN>, so
+ * that the current observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN> becomes <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>)</SPAN>. This method should
+ * be invoked before generating observations sequentially one by one
+ * via {@link #nextObservation nextObservation}, for a new sample path.
+ *
+ */
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ }
+
+
+ /**
+ * Returns <TT>true</TT> if <SPAN CLASS="MATH"><I>j</I> < <I>d</I></SPAN>, where <SPAN CLASS="MATH"><I>j</I></SPAN> is the number of observations of the current
+ * sample path generated since the last call to {@link #resetStartProcess resetStartProcess}.
+ * Otherwise returns <TT>false</TT>.
+ *
+ */
+ public boolean hasNextObservation() {
+ if (observationCounter < d) return true;
+ else return false;
+ }
+
+
+ /**
+ * Generates and returns the next observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN> of the stochastic process.
+ * The processes are usually sampled <SPAN CLASS="textit">sequentially</SPAN>, i.e.
+ * if the last observation generated was for time <SPAN CLASS="MATH"><I>t</I><SUB>j-1</SUB></SPAN>, the next observation
+ * returned will be for time <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN>.
+ * In some cases, subclasses extending this abstract class
+ * may use non-sequential sampling algorithms (such as bridge sampling).
+ * The order of generation of the <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN>'s is then specified by the subclass.
+ * All the processes generated using principal components analysis (PCA) do not have
+ * this method.
+ *
+ */
+ public double nextObservation() {
+ throw new UnsupportedOperationException("Method not defined in this class");
+ }
+
+
+ /**
+ * Returns the value of the index <SPAN CLASS="MATH"><I>j</I></SPAN> corresponding to
+ * the time <SPAN CLASS="MATH"><I>t</I><SUB>j</SUB></SPAN> of the last generated observation.
+ *
+ */
+ public int getCurrentObservationIndex() {
+ return observationIndex;
+ }
+
+
+ /**
+ * Returns the value of the last generated observation <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>j</SUB>)</SPAN>.
+ *
+ */
+ public double getCurrentObservation() {
+ return path[observationIndex];
+ }
+
+
+ /**
+ * Returns the initial value <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>)</SPAN> for this process.
+ *
+ */
+ public double getX0() {
+ return x0;
+ }
+
+
+ /**
+ * Sets the initial value <SPAN CLASS="MATH"><I>X</I>(<I>t</I><SUB>0</SUB>)</SPAN> for this process to <TT>s0</TT>,
+ * and reinitializes.
+ *
+ */
+ public void setX0 (double s0) {
+ x0 = s0;
+ init();
+ }
+
+
+ /**
+ * Resets the random stream of the underlying generator to <TT>stream</TT>.
+ *
+ */
+ public abstract void setStream (RandomStream stream);
+
+
+ /**
+ * Returns the random stream of the underlying generator.
+ *
+ */
+ public abstract RandomStream getStream();
+
+
+ /* ** Called by 'setObservationTimes' to initialize arrays and precompute
+ constants to speed up execution. See overriding method 'init'
+ in subclasses for details ***/
+ protected void init() {
+ if (observationTimesSet) // If observation times are not defined, do nothing.
+ path[0] = x0;
+ // We do this here because the s0 parameter may have changed through
+ // a call to the 'setParams' method.
+ }
+
+ /**
+ * Returns a reference to an array that maps an integer <SPAN CLASS="MATH"><I>k</I></SPAN>
+ * to <SPAN CLASS="MATH"><I>i</I><SUB>k</SUB></SPAN>, the index of the observation
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>i<SUB>k</SUB></SUB>)</SPAN> corresponding
+ * to the <SPAN CLASS="MATH"><I>k</I></SPAN>-th observation to be generated for a sample path of this process.
+ * If this process is sampled sequentially, then this map is trivial
+ * (i.e. <SPAN CLASS="MATH"><I>i</I><SUB>k</SUB> = <I>k</I></SPAN>). But it can be useful in a more general setting where
+ * the process is not sampled sequentially
+ * (for example, by a Brownian or gamma bridge) and one wants to know which
+ * observations of the current sample path were previously generated
+ * or will be generated next.
+ *
+ */
+ public int[] getArrayMappingCounterToIndex() {
+ return observationIndexFromCounter;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/StochasticProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/StochasticProcess.tex
new file mode 100644
index 0000000..431a75a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/StochasticProcess.tex
@@ -0,0 +1,316 @@
+\defmodule{StochasticProcess}
+
+Abstract base class for a stochastic process $\{X(t) : t \geq 0 \}$
+sampled (or observed) at a finite number of time points,
+$0 = t_0 < t_1 < \cdots < t_d$.
+The observation times are usually all specified before generating a sample path.
+This can be done via \texttt{setObservationTimes}.
+The method \texttt{generatePath} generates $X(t_1),\dots,X(t_d)$ and memorizes
+them in a vector, which can be recovered by \texttt{getPath}.
+
+Alternatively, for some types of processes, the observations $X(t_j)$ can be
+generated sequentially, one at a time, by invoking \texttt{resetStartProcess}
+first, and then \texttt{nextObservation} repeatedly.
+For some types of processes, the observation times can be specified one by one
+as well, when generating the path. This may be convenient or even necessary
+if the observation times are random, for example.
+
+\textbf{WARNING: After having called the constructor for one of the subclass,
+ one must always set the observation times of the process,
+ by calling method \texttt{setObservationTimes} for example or otherwise.}
+
+
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: StochasticProcess
+ * Description: Base class for all stochastic processes
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.RandomStream;
+\end{hide}
+
+
+public abstract class StochasticProcess \begin{hide} {
+ // Used in some subclasses to make sure the 'setObservationTimes'
+ // method has already been invoked before calling 'init'
+ protected boolean observationTimesSet = false;
+
+ protected double x0 = 0.0; // Default Initial Value of the process
+ protected int d = -1; // Num. of observation times
+ protected int observationIndex = 0; // Index of last generated obs time
+ protected int observationCounter = 0; // Counts how many observations have
+ // been generated so far. Useful when they are not generated in
+ // chronological order.
+ protected double[] t; // Observation times
+ protected double[] path; // Observations of the process
+ //protected RandomStream stream; // Random stream used to generate the process
+ protected int[] observationIndexFromCounter; // Links counter# to index#
+\end{hide}
+\end{code}
+
+\subsubsection* {Methods}
+\begin{code}
+
+ public void setObservationTimes (double[] T, int d)\begin{hide} {
+ if (d <= 0) throw new IllegalArgumentException (
+ "Number of observation times d <= 0");
+ this.d = d;
+ observationTimesSet = true;
+
+ // Copy of the observation times
+ this.t = new double[d+1];
+ System.arraycopy (T, 0, this.t, 0, d+1);
+
+ // Test chronological order
+ for (int i = 0; i < d; i++) {
+ if (T[i+1] < T[i])
+ throw new IllegalArgumentException (
+ "Observation times T[] are not time-ordered");
+ }
+
+ // Construction of 'path' object
+ // We do not do it in 'init()' because we don't have to change the
+ // path object if the user only calls 'setParams'
+ path = new double[d+1];
+
+ /* Process specific initialization; usually precomputes quantities
+ that depend on the observation times. */
+ init();
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Sets the observation times of the process to a copy of \texttt{T},
+with $t_{0} =$ \texttt{T[0]} and $t_{d} =$ \texttt{T[d]}.
+The size of \texttt{T} must be $d+1$.
+\end{tabb}
+\begin{code}
+
+ public void setObservationTimes (double delta, int d)\begin{hide} {
+ t = new double[d+1];
+ for (int i=0; i<=d; i++) t[i] = i*delta;
+ setObservationTimes (t, d);
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets equidistant observation times at $t_j = j\delta$, for $j=0,\dots,d$, and \texttt{delta} = $\delta$.
+\end{tabb}
+\begin{code}
+
+ public double[] getObservationTimes() \begin{hide} {
+ return t;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to the array that contains the observation times
+$(t_0,\dots,t_d)$.
+\emph{Warning}: This method should only be used to \emph{read} the observation times.
+Changing the values in the array directly may have unexpected consequences.
+The method \texttt{setObservationTimes} should be used to modify the observation times.
+\end{tabb}
+\begin{code}
+
+ public int getNbObservationTimes() \begin{hide} {
+ return d;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the number of observation times excluding the time $t_{0}$.
+\end{tabb}
+\begin{code}
+
+ public abstract double[] generatePath();
+\end{code}
+\begin{tabb} Generates, returns, and saves the sample path
+$\{X(t_{0}), X(t_{1}), \ldots, X(t_{d})\}$. It can then be accessed via
+\texttt{getPath}, \texttt{getSubpath}, or \texttt{getObservation}.
+The generation method depends on the process type.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (RandomStream stream) \begin{hide} {
+ setStream (stream);
+ return generatePath();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \texttt{generatePath()}, but first resets the stream to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public double[] getPath() \begin{hide} {
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns a \emph{reference} to the last generated sample path
+$\{X(t_{0}), ... , X(t_{d})\}$.
+\emph{Warning}: The returned array and its size should not be modified,
+because this is the one that memorizes the observations (not a copy of it).
+To obtain a copy, use \texttt{getSubpath} instead.
+\end{tabb}
+\begin{code}
+
+ public void getSubpath (double[] subpath, int[] pathIndices) \begin{hide} {
+ for (int j=0; j<subpath.length; j++) {
+ subpath[j] = path[pathIndices[j]];
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns in \texttt{subpath} the values of the process at a subset of the observation times,
+specified as the times $t_{j}$ whose indices $j$ are in the array \texttt{pathIndices}.
+The size of \texttt{pathIndices} should be at least as much as that of \texttt{subpath}.
+% in order to avoid an \texttt{ArrayIndexOutOfBoundsException}.
+\end{tabb}
+\begin{code}
+
+ public double getObservation (int j) \begin{hide} {
+ return path[j];
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $X(t_{j})$ from the current sample path.
+\emph{Warning}: If the observation $X(t_{j})$ for the current path has not yet been
+generated, then %this method may return the observation from a previous sample path.
+the value returned is unpredictable.
+\end{tabb}
+\begin{code}
+
+ public void resetStartProcess() \begin{hide} {
+ observationIndex = 0;
+ observationCounter = 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Resets the observation counter to its initial value $j=0$, so
+that the current observation $X(t_{j})$ becomes $X(t_{0})$. This method should
+ be invoked before generating observations sequentially one by one
+via \method{nextObservation}{}, for a new sample path.
+\end{tabb}
+\begin{code}
+
+ public boolean hasNextObservation() \begin{hide} {
+ if (observationCounter < d) return true;
+ else return false;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if $j<d$, where $j$ is the number of observations of the current
+sample path generated since the last call to \method{resetStartProcess}{}.
+Otherwise returns \texttt{false}.
+\end{tabb}
+\begin{code}
+
+ public double nextObservation()\begin{hide} {
+ throw new UnsupportedOperationException("Method not defined in this class");
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Generates and returns the next observation $X(t_j)$ of the stochastic process.
+The processes are usually sampled \emph{sequentially}, i.e.
+if the last observation generated was for time $t_{j-1}$, the next observation
+returned will be for time $t_{j}$.
+In some cases, subclasses extending this abstract class
+may use non-sequential sampling algorithms (such as bridge sampling).
+The order of generation of the $t_{j}$'s is then specified by the subclass.
+All the processes generated using principal components analysis (PCA) do not have
+this method.
+\end{tabb}
+\begin{code}
+
+ public int getCurrentObservationIndex() \begin{hide} {
+ return observationIndex;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of the index $j$ corresponding to
+the time $t_{j}$ of the last generated observation.
+\end{tabb}
+\begin{code}
+
+ public double getCurrentObservation() \begin{hide} {
+ return path[observationIndex];
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of the last generated observation $X(t_{j})$.
+\end{tabb}
+\begin{code}
+
+ public double getX0() \begin{hide} {
+ return x0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the initial value $X(t_{0})$ for this process.
+\end{tabb}
+\begin{code}
+
+ public void setX0 (double s0) \begin{hide} {
+ x0 = s0;
+ init();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the initial value $X(t_{0})$ for this process to \texttt{s0},
+and reinitializes.
+\end{tabb}
+\begin{code}
+
+ public abstract void setStream (RandomStream stream);
+\end{code}
+\begin{tabb}
+Resets the random stream of the underlying generator to \texttt{stream}.
+\end{tabb}
+\begin{code}
+
+ public abstract RandomStream getStream();
+\end{code}
+\begin{tabb}
+Returns the random stream of the underlying generator.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ /*** Called by 'setObservationTimes' to initialize arrays and precompute
+ constants to speed up execution. See overriding method 'init'
+ in subclasses for details ***/
+ protected void init() {
+ if (observationTimesSet) // If observation times are not defined, do nothing.
+ path[0] = x0;
+ // We do this here because the s0 parameter may have changed through
+ // a call to the 'setParams' method.
+ }\end{hide}
+
+ public int[] getArrayMappingCounterToIndex() \begin{hide} {
+ return observationIndexFromCounter;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to an array that maps an integer $k$
+to $i_{k}$, the index of the observation $S(t_{i_{k}})$ corresponding
+to the $k$-th observation to be generated for a sample path of this process.
+If this process is sampled sequentially, then this map is trivial
+(i.e. $i_{k} = k$). But it can be useful in a more general setting where
+the process is not sampled sequentially
+(for example, by a Brownian or gamma bridge) and one wants to know which
+observations of the current sample path were previously generated
+or will be generated next.
+\end{tabb}
+\begin{code} \begin{hide}
+} \end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcess.java b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcess.java
new file mode 100644
index 0000000..175ae96
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcess.java
@@ -0,0 +1,314 @@
+
+
+/*
+ * Class: VarianceGammaProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since 2004
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * This class represents a <SPAN CLASS="textit">variance gamma</SPAN> (VG) process
+ *
+ * <SPAN CLASS="MATH">{<I>S</I>(<I>t</I>) = <I>X</I>(<I>t</I>;<I>θ</I>, <I>σ</I>, <I>ν</I>) : <I>t</I> >= 0}</SPAN>. This process is
+ * obtained as a subordinate of the Brownian motion process
+ * <SPAN CLASS="MATH"><I>B</I>(<I>t</I>;<I>θ</I>, <I>σ</I>)</SPAN>
+ * using the operational time
+ * <SPAN CLASS="MATH"><I>G</I>(<I>t</I>;1, <I>ν</I>)</SPAN> (see):
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="VGeqn"></A>
+ * <I>X</I>(<I>t</I>;<I>θ</I>, <I>σ</I>, <I>ν</I>) : = <I>B</I>(<I>G</I>(<I>t</I>;1, <I>ν</I>), <I>θ</I>, <I>σ</I>).
+ * </DIV><P></P>
+ * See also for applications to modelling
+ * asset returns and option pricing.
+ *
+ * <P>
+ * The process is sampled as follows: when <TT>generatePath()</TT> is called, the method
+ * <TT>generatePath()</TT> of the inner {@link GammaProcess} is called;
+ * its path is then used to set the observation times of the
+ * {@link BrownianMotion}. Finally, the method <TT>generatePath()</TT> of
+ * the {@link BrownianMotion} is called.
+ * <SPAN CLASS="textit">Warning</SPAN>: If one wants to reduced the variance as much as possible
+ * in a QMC simulation, this way of proceeding is not optimal. Use
+ * the method <TT>generatePath(uniform01)</TT> instead.
+ *
+ * <P>
+ * If one calls the <TT>nextObservation</TT> method, the operational
+ * time is generated first, followed by the corresponding
+ * brownian motion increment, which is then returned.
+ *
+ * <P>
+ * Note that if one wishes to use <SPAN CLASS="textit">bridge</SPAN> sampling with the
+ * <TT>nextObservation</TT> method, both the gamma process <SPAN CLASS="MATH"><I>G</I></SPAN>
+ * and the Brownian motion process <SPAN CLASS="MATH"><I>B</I></SPAN> should use bridge sampling so that
+ * their observations are synchronized.
+ *
+ */
+public class VarianceGammaProcess extends StochasticProcess {
+
+ protected GammaProcess randomTime; // For the transformed time method
+ protected BrownianMotion BM;
+
+ protected double theta,
+ sigma,
+ nu;
+
+
+
+ public VarianceGammaProcess() {}
+
+ /**
+ * Constructs a new <TT>VarianceGammaProcess</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN>
+ * and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>.
+ * <TT>stream</TT> is used to generate both the {@link BrownianMotion} <SPAN CLASS="MATH"><I>B</I></SPAN> and the
+ * {@link GammaProcess} <SPAN CLASS="MATH"><I>G</I></SPAN> in.
+ *
+ */
+ public VarianceGammaProcess (double s0, double theta, double sigma,
+ double nu, RandomStream stream) {
+ this (s0, new BrownianMotion (s0, theta, sigma, stream),
+ new GammaProcess (0.0, 1.0, nu, stream));
+ }
+
+
+ /**
+ * Constructs a new <TT>VarianceGammaProcess</TT>.
+ * The parameters <SPAN CLASS="MATH"><I>θ</I></SPAN> and <SPAN CLASS="MATH"><I>σ</I></SPAN> are set to the parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and
+ * <SPAN CLASS="MATH"><I>σ</I></SPAN>, respectively, of the {@link BrownianMotion} <TT>BM</TT> and
+ * the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN> is set to the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN> of the {@link GammaProcess}
+ * <TT>Gamma</TT>. The parameters <SPAN CLASS="MATH"><I>μ</I></SPAN> and <SPAN CLASS="MATH"><I>x</I>0</SPAN> of the {@link GammaProcess} are
+ * overwritten to equal 1 and 0 respectively.
+ * The initial value of the process is
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = 1#1</SPAN>.
+ *
+ */
+ public VarianceGammaProcess (double s0, BrownianMotion BM,
+ GammaProcess Gamma) {
+ this.BM = BM;
+ Gamma.setParams(0.0, 1.0, Gamma.getNu()); // forces the average of the GammaProcess
+ randomTime = Gamma; // to be 1.0 and the initial value to be 0.0
+ setParams (s0, BM.getMu(), BM.getSigma(), Gamma.getNu());
+ }
+
+
+ /**
+ * Generates the observation for the next time.
+ * It also works with bridge sampling; however <SPAN CLASS="textit">both</SPAN>
+ * {@link BrownianMotionBridge} and {@link GammaProcessBridge}
+ * must be used in the constructor in that case. Furthermore, for bridge
+ * sampling, the order of the observations is that of the bridge,
+ * not sequential order.
+ *
+ */
+ public double nextObservation() {
+ // We first generate w, then verify what its new counter value is
+ // This is necessary to be general enough to handle bridge sampling
+ double nextBM = BM.nextObservation (randomTime.nextObservation ());
+ observationIndex = BM.getCurrentObservationIndex();
+ path[observationIndex] = nextBM;
+ observationCounter++;
+ return nextBM;
+ }
+
+
+ /**
+ * Generates and returns the path. To do so, it
+ * first generates the complete path of the inner {@link GammaProcess}
+ * and sets the observation times of the inner {@link BrownianMotion}
+ * to this path. This method is not optimal to reduce the variance in
+ * QMC simulations; use <TT>generatePath(double[] uniform01)</TT> for that.
+ *
+ */
+ public double[] generatePath() {
+ BM.setObservationTimes(randomTime.generatePath(), d);
+ path = BM.generatePath();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * Similar to the usual <TT>generatePath()</TT>, but here the uniform
+ * random numbers used for the simulation must be provided to the method. This
+ * allows to properly use the uniform random variates in QMC simulations.
+ * This method divides the table of uniform random
+ * numbers <TT>uniform01</TT> in two smaller tables, the first one, containing the
+ * odd indices of <TT>uniform01</TT> which are used to generate the path of the inner
+ * {@link GammaProcess}, and the even indices (in the second table) are used to
+ * generate the path of the inner {@link BrownianMotion}. This way of proceeding
+ * reduces the variance as much as possible for QMC simulations.
+ *
+ */
+ public double[] generatePath (double[] uniform01) {
+ int dd = uniform01.length;
+ int d = dd / 2;
+
+ if (dd % 2 != 0) {
+ throw new IllegalArgumentException (
+ "The Array uniform01 must have a even length");
+ }
+
+ double[] QMCpointsGP = new double[d];
+ double[] QMCpointsBM = new double[d];
+
+ for(int i = 0; i < d; i++){
+ QMCpointsGP[i] = uniform01[2 * i]; // the odd numbers for the gamma process
+ QMCpointsBM[i] = uniform01[2 * i + 1]; // and the even for the BM process
+ }
+ BM.setObservationTimes(randomTime.generatePath(QMCpointsGP), d);
+
+ path = BM.generatePath(QMCpointsBM);
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * Resets the observation index and counter to 0 and
+ * applies the <TT>resetStartProcess</TT> method to the
+ * {@link BrownianMotion} and the {@link GammaProcess} objects
+ * used to generate this process.
+ *
+ */
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ BM.resetStartProcess();
+ randomTime.resetStartProcess();
+ }
+
+
+ /**
+ * Sets the parameters
+ *
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) =</SPAN> <TT>s0</TT>, <SPAN CLASS="MATH"><I>θ</I> =</SPAN> <TT>theta</TT>, <SPAN CLASS="MATH"><I>σ</I> =</SPAN> <TT>sigma</TT>
+ * and <SPAN CLASS="MATH"><I>ν</I> =</SPAN> <TT>nu</TT> of the process.
+ * <SPAN CLASS="textit">Warning</SPAN>: This method will recompute some quantities stored internally,
+ * which may be slow if called repeatedly.
+ *
+ */
+ public void setParams (double s0, double theta, double sigma, double nu) {
+ this.x0 = s0;
+ this.theta = theta;
+ this.sigma = sigma;
+ this.nu = nu;
+ if (observationTimesSet) init(); // Otherwise no need to.
+ }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>θ</I></SPAN>.
+ *
+ */
+ public double getTheta() { return theta; }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>σ</I></SPAN>.
+ *
+ */
+ public double getSigma() { return sigma; }
+
+
+ /**
+ * Returns the value of the parameter <SPAN CLASS="MATH"><I>ν</I></SPAN>.
+ *
+ */
+ public double getNu() { return nu; }
+
+
+ protected void init() {
+ super.init();
+ if(observationTimesSet){
+ randomTime.setObservationTimes(t, d);
+ randomTime.x0 = t[0];
+ }
+ }
+
+ /**
+ * Sets the observation times on the <TT>VarianceGammaProcess</TT> as
+ * usual, but also sets the observation times of the underlying
+ * {@link GammaProcess}. It furthermore sets the starting <SPAN CLASS="textit">value</SPAN>
+ * of the {@link GammaProcess} to <TT>t[0]</TT>.
+ *
+ */
+ public void setObservationTimes (double t[], int d) {
+ super.setObservationTimes(t, d); //sets the observation times of the GammaProcess by
+ } //calling init()
+
+
+
+ /**
+ * Resets the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}'s.
+ * Warning: this method sets both the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} of the
+ * {@link BrownianMotion} and of the {@link GammaProcess} to
+ * the same {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ BM.setStream (stream);
+ }
+
+
+ /**
+ * Returns the random stream of the {@link BrownianMotion} process, which should
+ * be the same as for the {@link GammaProcess}.
+ *
+ */
+ public RandomStream getStream() {
+ return BM.getStream();
+ }
+
+
+ /**
+ * Returns a reference to the inner {@link BrownianMotion}.
+ *
+ */
+ public BrownianMotion getBrownianMotion() {
+ return BM;
+ }
+
+
+ /**
+ * Returns a reference to the inner {@link GammaProcess}.
+ *
+ */
+ public GammaProcess getGammaProcess() {
+ return randomTime;
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcess.tex b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcess.tex
new file mode 100644
index 0000000..b03a8a5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcess.tex
@@ -0,0 +1,307 @@
+%
+% Pierre Tremblay, February 2004.
+% Jean-Sébastien Parent et Maxime Dion, summer 2008
+%
+
+\defmodule {VarianceGammaProcess}
+
+This class represents a \emph{variance gamma} (VG) process
+$\{S(t) = X(t; \theta, \sigma, \nu) : t \geq 0\}$. This process is
+obtained as a subordinate of the Brownian motion process $B(t;\theta,\sigma)$
+using the operational time $G(t;1,\nu)$ (see \cite{pFEL66a,fAVR06a}):
+\begin{equation}
+X(t; \theta, \sigma, \nu) := B(G(t;1,\nu),\theta, \sigma).
+\label{VGeqn}
+\end{equation}
+See also \cite{fMAD98a,fMAD91a,fMAD90a} for applications to modelling
+asset returns and option pricing.
+
+The process is sampled as follows: when \texttt{generatePath()} is called, the method
+\texttt{generate\-Path()} of the inner \class{GammaProcess} is called;
+its path is then used to set the observation times of the
+\class{BrownianMotion}. Finally, the method \texttt{generatePath()} of
+the \class{BrownianMotion} is called.
+\emph{Warning}: If one wants to reduced the variance as much as possible
+in a QMC simulation, this way of proceeding is not optimal. Use
+the method \texttt{generatePath(uniform01)} instead.
+
+If one calls the \texttt{nextObservation} method, the operational
+time is generated first, followed by the corresponding
+brownian motion increment, which is then returned.
+
+Note that if one wishes to use \emph{bridge} sampling with the
+\texttt{nextObservation} method, both the gamma process $G$
+and the Brownian motion process $B$ should use bridge sampling so that
+their observations are synchronized.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: VarianceGammaProcess
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since 2004
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class VarianceGammaProcess extends StochasticProcess \begin{hide} {
+
+ protected GammaProcess randomTime; // For the transformed time method
+ protected BrownianMotion BM;
+
+ protected double theta,
+ sigma,
+ nu;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}\begin{hide}
+
+ public VarianceGammaProcess() {} \end{hide}
+
+ public VarianceGammaProcess (double s0, double theta, double sigma,
+ double nu, RandomStream stream) \begin{hide} {
+ this (s0, new BrownianMotion (s0, theta, sigma, stream),
+ new GammaProcess (0.0, 1.0, nu, stream));
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{VarianceGammaProcess} with parameters
+$\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$, $\nu = \texttt{nu}$
+and initial value $S(t_{0}) = \texttt{s0}$.
+\texttt{stream} is used to generate both the \class{BrownianMotion} $B$ and the
+\class{GammaProcess} $G$ in (\ref{VGeqn}).
+\end{tabb}
+\begin{code}
+
+ public VarianceGammaProcess (double s0, BrownianMotion BM,
+ GammaProcess Gamma) \begin{hide} {
+ this.BM = BM;
+ Gamma.setParams(0.0, 1.0, Gamma.getNu()); // forces the average of the GammaProcess
+ randomTime = Gamma; // to be 1.0 and the initial value to be 0.0
+ setParams (s0, BM.getMu(), BM.getSigma(), Gamma.getNu());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{VarianceGammaProcess}.
+The parameters $\theta$ and $\sigma$ are set to the parameters $\mu$ and
+$\sigma$, respectively, of the \class{BrownianMotion} \texttt{BM} and
+the parameter $\nu$ is set to the parameter $\nu$ of the \class{GammaProcess}
+\texttt{Gamma}. The parameters $\mu$ and $x0$ of the \class{GammaProcess} are
+overwritten to equal 1 and 0 respectively.
+The initial value of the process is $S(t_{0}) = {\tt s0}$.
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double nextObservation()\begin{hide} {
+ // We first generate w, then verify what its new counter value is
+ // This is necessary to be general enough to handle bridge sampling
+ double nextBM = BM.nextObservation (randomTime.nextObservation ());
+ observationIndex = BM.getCurrentObservationIndex();
+ path[observationIndex] = nextBM;
+ observationCounter++;
+ return nextBM;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates the observation for the next time.
+It also works with bridge sampling; however \emph{both}
+\class{BrownianMotionBridge} and \class{GammaProcessBridge}
+must be used in the constructor in that case. Furthermore, for bridge
+sampling, the order of the observations is that of the bridge,
+not sequential order.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath() \begin{hide} {
+ BM.setObservationTimes(randomTime.generatePath(), d);
+ path = BM.generatePath();
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates and returns the path. To do so, it
+first generates the complete path of the inner \class{GammaProcess}
+and sets the observation times of the inner \class{BrownianMotion}
+to this path. This method is not optimal to reduce the variance in
+QMC simulations; use \texttt{generatePath(double[] uniform01)} for that.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (double[] uniform01) \begin{hide} {
+ int dd = uniform01.length;
+ int d = dd / 2;
+
+ if (dd % 2 != 0) {
+ throw new IllegalArgumentException (
+ "The Array uniform01 must have a even length");
+ }
+
+ double[] QMCpointsGP = new double[d];
+ double[] QMCpointsBM = new double[d];
+
+ for(int i = 0; i < d; i++){
+ QMCpointsGP[i] = uniform01[2 * i]; // the odd numbers for the gamma process
+ QMCpointsBM[i] = uniform01[2 * i + 1]; // and the even for the BM process
+ }
+ BM.setObservationTimes(randomTime.generatePath(QMCpointsGP), d);
+
+ path = BM.generatePath(QMCpointsBM);
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to the usual \texttt{generatePath()}, but here the uniform
+random numbers used for the simulation must be provided to the method. This
+allows to properly use the uniform random variates in QMC simulations.
+This method divides the table of uniform random
+numbers \texttt{uniform01} in two smaller tables, the first one, containing the
+odd indices of \texttt{uniform01} which are used to generate the path of the inner
+\class{GammaProcess}, and the even indices (in the second table) are used to
+generate the path of the inner \class{BrownianMotion}. This way of proceeding
+reduces the variance as much as possible for QMC simulations.
+\end{tabb}
+\begin{code}
+
+ public void resetStartProcess() \begin{hide} {
+ observationIndex = 0;
+ observationCounter = 0;
+ BM.resetStartProcess();
+ randomTime.resetStartProcess();
+ }\end{hide}
+\end{code}
+\begin{tabb} Resets the observation index and counter to 0 and
+applies the \texttt{resetStartProcess} method to the
+\class{BrownianMotion} and the \class{GammaProcess} objects
+used to generate this process.
+\end{tabb}
+\begin{code}
+
+ public void setParams (double s0, double theta, double sigma, double nu) \begin{hide} {
+ this.x0 = s0;
+ this.theta = theta;
+ this.sigma = sigma;
+ this.nu = nu;
+ if (observationTimesSet) init(); // Otherwise no need to.
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Sets the parameters
+$S(t_{0}) =$ \texttt{s0}, $\theta =$ \texttt{theta}, $\sigma =$ \texttt{sigma}
+and $\nu =$ \texttt{nu} of the process.
+\emph{Warning}: This method will recompute some quantities stored internally,
+which may be slow if called repeatedly.
+\end{tabb}
+\begin{code}
+
+ public double getTheta() \begin{hide} { return theta; }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of the parameter $\theta$.
+\end{tabb}
+\begin{code}
+
+ public double getSigma() \begin{hide} { return sigma; }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of the parameter $\sigma$.
+\end{tabb}
+\begin{code}
+
+ public double getNu() \begin{hide} { return nu; }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of the parameter $\nu$.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void init() {
+ super.init();
+ if(observationTimesSet){
+ randomTime.setObservationTimes(t, d);
+ randomTime.x0 = t[0];
+ }
+ }\end{hide}
+
+ public void setObservationTimes (double t[], int d) \begin{hide} {
+ super.setObservationTimes(t, d); //sets the observation times of the GammaProcess by
+ } //calling init()
+\end{hide}
+\end{code}
+\begin{tabb} Sets the observation times on the \texttt{VarianceGammaProcess} as
+usual, but also sets the observation times of the underlying
+\class{GammaProcess}. It furthermore sets the starting \emph{value}
+of the \class{GammaProcess} to \texttt{t[0]}.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ BM.setStream (stream);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Resets the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}'s.
+Warning: this method sets both the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} of the
+\class{BrownianMotion} and of the \class{GammaProcess} to
+the same \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream()\begin{hide} {
+ return BM.getStream();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the random stream of the \class{BrownianMotion} process, which should
+be the same as for the \class{GammaProcess}.
+\end{tabb}
+\begin{code}
+
+ public BrownianMotion getBrownianMotion() \begin{hide} {
+ return BM;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns a reference to the inner \class{BrownianMotion}.
+\end{tabb}
+\begin{code}
+
+ public GammaProcess getGammaProcess() \begin{hide} {
+ return randomTime;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns a reference to the inner \class{GammaProcess}.
+\end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiff.java b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiff.java
new file mode 100644
index 0000000..70a3262
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiff.java
@@ -0,0 +1,323 @@
+
+
+/*
+ * Class: VarianceGammaProcessDiff
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since 2004
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * This class represents a <SPAN CLASS="textit">variance gamma</SPAN> (VG) process
+ *
+ * <SPAN CLASS="MATH">{<I>S</I>(<I>t</I>) = <I>X</I>(<I>t</I>;<I>θ</I>, <I>σ</I>, <I>ν</I>) : <I>t</I> >= 0}</SPAN>.
+ * This process is generated using <SPAN CLASS="textit">difference of gamma sampling</SPAN>
+ * (see), which uses the representation of the VG process
+ * as the difference of two independent {@link GammaProcess}'es (see):
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="dblGammaEqn"></A>
+ * <I>X</I>(<I>t</I>;<I>θ</I>, <I>σ</I>, <I>ν</I>) : = <I>X</I>(0) + <I>Γ</I><SUP>+</SUP>(<I>t</I>;<I>μ</I><SUB>p</SUB>, <I>ν</I><SUB>p</SUB>) - <I>Γ</I><SUP>-</SUP>(<I>t</I>;<I>μ</I><SUB>n</SUB>, <I>ν</I><SUB>n</SUB>)
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>X</I>(0)</SPAN> is a constant corresponding to the initial value of the process
+ * and
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="dblGammaParams"></A>
+ * <TABLE>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>μ</I><SUB>p</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT">(( &thetas;^2 + 2σ^2/ν)<SUP>1/2</SUP> + <I>θ</I>)/2</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>μ</I><SUB>n</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT">(( &thetas;^2 + 2σ^2/ν)<SUP>1/2</SUP> - <I>θ</I>)/2</TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>ν</I><SUB>p</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT"><I>νμ</I><SUB>p</SUB><SUP>2</SUP></TD>
+ * </TR>
+ * <TR VALIGN="MIDDLE"><TD ALIGN="RIGHT"><I>ν</I><SUB>n</SUB></TD>
+ * <TD ALIGN="CENTER">=</TD>
+ * <TD ALIGN="LEFT"><I>νμ</I><SUB>n</SUB><SUP>2</SUP></TD>
+ * </TR>
+ * </TABLE>
+ * </DIV><P></P>
+ *
+ */
+public class VarianceGammaProcessDiff extends VarianceGammaProcess {
+ protected GammaProcess gpos;
+ protected GammaProcess gneg;
+ protected double mup, mun,
+ nup, nun;
+
+
+
+ /**
+ * Constructs a new <TT>VarianceGammaProcessDiff</TT> with parameters
+ *
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN>
+ * and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>. <TT>stream</TT> is
+ * used by two instances of {@link GammaProcess},
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN> and
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP></SPAN>,
+ * respectively. The other parameters are
+ * as in the class {@link VarianceGammaProcess}.
+ * The {@link GammaProcess} objects for
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN> and
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP></SPAN> are
+ * constructed using the parameters from and their
+ * initial values
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP>(<I>t</I><SUB>0</SUB>)</SPAN> and
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP>(<I>t</I><SUB>0</SUB>)</SPAN> are set to <SPAN CLASS="MATH">0</SPAN>.
+ *
+ */
+ public VarianceGammaProcessDiff (double s0, double theta, double sigma,
+ double nu, RandomStream stream) {
+ this (s0, theta, sigma, nu,
+ new GammaProcess (0.0, 1.0, 1.0, stream),
+ new GammaProcess (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+ }
+
+
+ /**
+ * The parameters of the {@link GammaProcess}
+ * objects for
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN> and
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP></SPAN> are
+ * set to those of and their
+ * initial values
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP>(<I>t</I><SUB>0</SUB>)</SPAN> and
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP>(<I>t</I><SUB>0</SUB>)</SPAN> are set to <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB></SPAN>.
+ * The <TT>RandomStream</TT> of the
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP></SPAN> process is overwritten with the
+ * <TT>RandomStream</TT> of the
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN> process.
+ *
+ */
+ public VarianceGammaProcessDiff (double s0, double theta, double sigma,
+ double nu, GammaProcess gpos,
+ GammaProcess gneg) {
+ this.gpos = gpos;
+ this.gneg = gneg;
+ setParams (s0, theta, sigma, nu);
+ gneg.setStream(gpos.getStream()); // to avoid confusion with stream because there is only
+ // one stream in the other constructor
+ }
+
+
+ public double nextObservation() {
+ // This implementation takes possible bridge sampling into account
+ double s = x0 + gpos.nextObservation() - gneg.nextObservation();
+ observationIndex = gpos.getCurrentObservationIndex();
+ path[observationIndex] = s;
+ observationCounter++;
+ return s;
+ }
+
+// no longer useful, this method was created to automaticaly alternate
+// between the two processes the uniform random variables used in the
+// in the simulation. However, this method does not work if the two
+// GammaProcess are PCA...
+// public double[] generatePath() {
+// gpos.resetStartProcess();
+// gneg.resetStartProcess();
+// double s;
+// for (int i=1; i<=d; i++) {
+// s = x0 + gpos.nextObservation() - gneg.nextObservation();
+// path[gpos.getCurrentObservationIndex()] = s;
+// // Note: we must get the observCounter from gpos in the case that
+// // the process is generated by a Gamma bridge
+// // The observCounter of gneg should be the same because both
+// // gamma processes should be generated the same way
+// }
+// observationIndex = d;
+// observationCounter = d;
+// return path;
+// }
+
+
+ /**
+ * Generates, returns and saves the path. To do so, the
+ * path of
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN> is first generated and then the path
+ * of
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP></SPAN>. This is not the optimal way of proceeding
+ * in order to reduce the variance in QMC simulations; for that,
+ * use <TT>generatePath(double[] uniform01)</TT> instead.
+ *
+ */
+ public double[] generatePath() {
+ double[] pathUP = gpos.generatePath();
+ double[] pathDOWN = gneg.generatePath();
+
+ for (int i=0; i<d; i++) {
+ path[i+1] = x0 + pathUP[i+1] - pathDOWN[i+1];
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ /**
+ * Similar to the usual <TT>generatePath()</TT>, but here the uniform
+ * random numbers used for the simulation must be provided to the method. This
+ * allows to properly use the uniform random variates in QMC simulations.
+ * This method divides the table of uniform random
+ * numbers <TT>uniform01</TT> in two smaller tables, the first one containing
+ * the odd indices of <TT>uniform01</TT> are used to generate the path of
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN>
+ * and the even indices are used to generate the path of
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP></SPAN>.
+ * This way of proceeding further reduces the
+ * variance for QMC simulations.
+ *
+ */
+ public double[] generatePath (double[] uniform01) {
+ int dd = uniform01.length;
+ int d = dd / 2;
+
+ if (dd % 2 != 0) {
+ throw new IllegalArgumentException (
+ "The Array uniform01 must have a even length");
+ }
+
+ double[] QMCpointsUP = new double[d];
+ double[] QMCpointsDW = new double[d];
+
+ for(int i = 0; i < d; i++){
+ QMCpointsUP[i] = uniform01[2*i]; // keeps the odd numbers for the gamma process
+ QMCpointsDW[i] = uniform01[2*i + 1]; // and the even for the BM process
+ }
+ gpos.resetStartProcess();
+ gneg.resetStartProcess();
+
+ double[] pathUP = gpos.generatePath(QMCpointsUP);
+ double[] pathDOWN = gneg.generatePath(QMCpointsDW);
+
+ for (int i=0; i<d; i++) {
+ path[i+1] = x0 + pathUP[i+1] - pathDOWN[i+1];
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+
+ /**
+ * Sets the observation times on the <TT>VarianceGammaProcessDiff</TT>
+ * as usual, but also applies the <TT>resetStartProcess</TT> method to the two
+ * {@link GammaProcess} objects used to generate this process.
+ *
+ */
+ public void resetStartProcess() {
+ observationIndex = 0;
+ observationCounter = 0;
+ gpos.resetStartProcess();
+ gneg.resetStartProcess();
+ }
+
+
+ /**
+ * Returns a reference to the {@link GammaProcess} object <TT>gpos</TT>
+ * used to generate the
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN> component of the process.
+ *
+ */
+ public GammaProcess getGpos() {
+ return gpos;
+ }
+
+
+ /**
+ * Returns a reference to the {@link GammaProcess} object <TT>gneg</TT>
+ * used to generate the
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>-</SUP></SPAN> component of the process.
+ *
+ */
+ public GammaProcess getGneg() {
+ return gneg;
+ }
+
+
+ protected void init() {
+ // super.init() is not called because the init() in VarianceGammaProcess
+ // is very different.
+ mup = 0.5 * (Math.sqrt (theta*theta + 2*sigma*sigma/nu) + theta);
+ mun = 0.5 * (Math.sqrt (theta*theta + 2*sigma*sigma/nu) - theta);
+ nup = mup * mup * nu;
+ nun = mun * mun * nu;
+ if (observationTimesSet) {
+ path[0] = x0;
+ gpos.setParams(t[0], mup, nup);
+ gneg.setParams(t[0], mun, nun);
+ }
+ }
+
+ /**
+ * Sets the observation times on the <TT>VarianceGammaProcesDiff</TT>
+ * as usual, but also sets the observation times of the underlying
+ * {@link GammaProcess}'es.
+ *
+ */
+ public void setObservationTimes (double t[], int d) {
+ gpos.setObservationTimes(t, d);
+ gneg.setObservationTimes(t, d);
+ super.setObservationTimes(t, d);
+// the initial value is set to t[0] in the init, which is called in
+// super.setObservationTimes(t, d).
+ }
+
+
+ /**
+ * Returns the <TT>RandomStream</TT> of the
+ * <SPAN CLASS="MATH"><I>Γ</I><SUP>+</SUP></SPAN> process.
+ *
+ */
+ public RandomStream getStream() {
+ return gpos.getStream ();
+ }
+
+
+ /**
+ * Sets the {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream}
+ * of the two {@link GammaProcess}'es to <TT>stream</TT>.
+ *
+ */
+ public void setStream (RandomStream stream) {
+ gpos.setStream(stream);
+ gneg.setStream(stream);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiff.tex b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiff.tex
new file mode 100644
index 0000000..b2559c1
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiff.tex
@@ -0,0 +1,294 @@
+%
+% Pierre Tremblay, February 2004
+%
+
+\defmodule {VarianceGammaProcessDiff}
+
+This class represents a \emph{variance gamma} (VG) process
+$\{S(t) = X(t; \theta, \sigma, \nu) : t \geq 0\}$.
+This process is generated using \emph{difference of gamma sampling}
+(see \cite{fAVR03a,fAVR06a}), which uses the representation of the VG process
+as the difference of two independent \class{GammaProcess}'es (see \cite{fMAD98a}):
+\begin{equation}
+X(t; \theta, \sigma, \nu) := X(0) + \Gamma^{+}(t; \mu_{p}, \nu_{p})
+ - \Gamma^{-}(t; \mu_{n}, \nu_{n})
+\label{dblGammaEqn}
+\end{equation}
+where $X(0)$ is a constant corresponding to the initial value of the process
+and
+\begin{equation}
+\begin{array}{rcl}
+\mu_{p} & = & (\sqrt{ \theta^{2} + 2\sigma^{2}/\nu } + \theta)/2 \\[6pt]
+\mu_{n} & = & (\sqrt{ \theta^{2} + 2\sigma^{2}/\nu } - \theta)/2 \\[6pt]
+\nu_{p} & = & \nu \mu_{p}^{2}\\[8pt]
+\nu_{n} & = & \nu \mu_{n}^{2}
+\end{array}
+\label{dblGammaParams}
+\end{equation}
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: VarianceGammaProcessDiff
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since 2004
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class VarianceGammaProcessDiff extends VarianceGammaProcess \begin{hide} {
+ protected GammaProcess gpos;
+ protected GammaProcess gneg;
+ protected double mup, mun,
+ nup, nun;
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public VarianceGammaProcessDiff (double s0, double theta, double sigma,
+ double nu, RandomStream stream) \begin{hide} {
+ this (s0, theta, sigma, nu,
+ new GammaProcess (0.0, 1.0, 1.0, stream),
+ new GammaProcess (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Constructs a new \texttt{VarianceGammaProcessDiff} with parameters
+$\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$, $\nu = \texttt{nu}$
+and initial value $S(t_{0}) = \texttt{s0}$. \texttt{stream} is
+used by two instances of \class{GammaProcess}, $\Gamma^{+}$ and $\Gamma^{-}$,
+respectively. The other parameters are
+as in the class \class{VarianceGammaProcess}.
+The \class{GammaProcess} objects for $\Gamma^{+}$ and $\Gamma^{-}$ are
+constructed using the parameters from (\ref{dblGammaParams}) and their
+initial values $\Gamma^{+}(t_{0})$ and $\Gamma^{-}(t_{0})$ are set to $0$.
+\end{tabb}
+\begin{code}
+
+ public VarianceGammaProcessDiff (double s0, double theta, double sigma,
+ double nu, GammaProcess gpos,
+ GammaProcess gneg) \begin{hide} {
+ this.gpos = gpos;
+ this.gneg = gneg;
+ setParams (s0, theta, sigma, nu);
+ gneg.setStream(gpos.getStream()); // to avoid confusion with stream because there is only
+ // one stream in the other constructor
+ }\end{hide}
+\end{code}
+\begin{tabb}
+The parameters of the \class{GammaProcess}
+objects for $\Gamma^{+}$ and $\Gamma^{-}$ are
+set to those of (\ref{dblGammaParams}) and their
+initial values $\Gamma^{+}(t_{0})$ and $\Gamma^{-}(t_{0})$ are set to $t_0$.
+The \texttt{RandomStream} of the $\Gamma^{-}$ process is overwritten with the
+\texttt{RandomStream} of the $\Gamma^{+}$ process.
+\end{tabb}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}\begin{hide}
+
+ public double nextObservation() {
+ // This implementation takes possible bridge sampling into account
+ double s = x0 + gpos.nextObservation() - gneg.nextObservation();
+ observationIndex = gpos.getCurrentObservationIndex();
+ path[observationIndex] = s;
+ observationCounter++;
+ return s;
+ }
+
+// no longer useful, this method was created to automaticaly alternate
+// between the two processes the uniform random variables used in the
+// in the simulation. However, this method does not work if the two
+// GammaProcess are PCA...
+// public double[] generatePath() {
+// gpos.resetStartProcess();
+// gneg.resetStartProcess();
+// double s;
+// for (int i=1; i<=d; i++) {
+// s = x0 + gpos.nextObservation() - gneg.nextObservation();
+// path[gpos.getCurrentObservationIndex()] = s;
+// // Note: we must get the observCounter from gpos in the case that
+// // the process is generated by a Gamma bridge
+// // The observCounter of gneg should be the same because both
+// // gamma processes should be generated the same way
+// }
+// observationIndex = d;
+// observationCounter = d;
+// return path;
+// }
+\end{hide}
+
+ public double[] generatePath() \begin{hide} {
+ double[] pathUP = gpos.generatePath();
+ double[] pathDOWN = gneg.generatePath();
+
+ for (int i=0; i<d; i++) {
+ path[i+1] = x0 + pathUP[i+1] - pathDOWN[i+1];
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }\end{hide}
+\end{code}
+\begin{tabb} Generates, returns and saves the path. To do so, the
+path of $\Gamma^{+}$ is first generated and then the path
+of $\Gamma^{-}$. This is not the optimal way of proceeding
+in order to reduce the variance in QMC simulations; for that,
+use \texttt{generatePath(double[] uniform01)} instead.
+\end{tabb}
+\begin{code}
+
+ public double[] generatePath (double[] uniform01) \begin{hide} {
+ int dd = uniform01.length;
+ int d = dd / 2;
+
+ if (dd % 2 != 0) {
+ throw new IllegalArgumentException (
+ "The Array uniform01 must have a even length");
+ }
+
+ double[] QMCpointsUP = new double[d];
+ double[] QMCpointsDW = new double[d];
+
+ for(int i = 0; i < d; i++){
+ QMCpointsUP[i] = uniform01[2*i]; // keeps the odd numbers for the gamma process
+ QMCpointsDW[i] = uniform01[2*i + 1]; // and the even for the BM process
+ }
+ gpos.resetStartProcess();
+ gneg.resetStartProcess();
+
+ double[] pathUP = gpos.generatePath(QMCpointsUP);
+ double[] pathDOWN = gneg.generatePath(QMCpointsDW);
+
+ for (int i=0; i<d; i++) {
+ path[i+1] = x0 + pathUP[i+1] - pathDOWN[i+1];
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+\end{hide}
+\end{code}
+\begin{tabb}
+Similar to the usual \texttt{generatePath()}, but here the uniform
+random numbers used for the simulation must be provided to the method. This
+allows to properly use the uniform random variates in QMC simulations.
+This method divides the table of uniform random
+numbers \texttt{uniform01} in two smaller tables, the first one containing
+the odd indices of \texttt{uniform01} are used to generate the path of $\Gamma^{+}$
+and the even indices are used to generate the path of $\Gamma^{-}$.
+This way of proceeding further reduces the
+variance for QMC simulations.
+\end{tabb}
+\begin{code}
+
+ public void resetStartProcess() \begin{hide} {
+ observationIndex = 0;
+ observationCounter = 0;
+ gpos.resetStartProcess();
+ gneg.resetStartProcess();
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the observation times on the \texttt{VarianceGammaProcessDiff}
+as usual, but also applies the \texttt{resetStartProcess} method to the two
+\class{GammaProcess} objects used to generate this process.
+\end{tabb}
+\begin{code}
+
+ public GammaProcess getGpos() \begin{hide} {
+ return gpos;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to the \class{GammaProcess} object \texttt{gpos}
+used to generate the $\Gamma^{+}$ component of the process.
+\end{tabb}
+\begin{code}
+
+ public GammaProcess getGneg() \begin{hide} {
+ return gneg;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns a reference to the \class{GammaProcess} object \texttt{gneg}
+used to generate the $\Gamma^{-}$ component of the process.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected void init() {
+ // super.init() is not called because the init() in VarianceGammaProcess
+ // is very different.
+ mup = 0.5 * (Math.sqrt (theta*theta + 2*sigma*sigma/nu) + theta);
+ mun = 0.5 * (Math.sqrt (theta*theta + 2*sigma*sigma/nu) - theta);
+ nup = mup * mup * nu;
+ nun = mun * mun * nu;
+ if (observationTimesSet) {
+ path[0] = x0;
+ gpos.setParams(t[0], mup, nup);
+ gneg.setParams(t[0], mun, nun);
+ }
+ }\end{hide}
+
+ public void setObservationTimes (double t[], int d) \begin{hide} {
+ gpos.setObservationTimes(t, d);
+ gneg.setObservationTimes(t, d);
+ super.setObservationTimes(t, d);
+// the initial value is set to t[0] in the init, which is called in
+// super.setObservationTimes(t, d).
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the observation times on the \texttt{VarianceGammaProcesDiff}
+as usual, but also sets the observation times of the underlying
+\class{GammaProcess}'es.
+\end{tabb}
+\begin{code}
+
+ public RandomStream getStream() \begin{hide} {
+ return gpos.getStream ();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the \texttt{RandomStream} of the $\Gamma^{+}$ process.
+\end{tabb}
+\begin{code}
+
+ public void setStream (RandomStream stream) \begin{hide} {
+ gpos.setStream(stream);
+ gneg.setStream(stream);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Sets the \externalclass{umontreal.iro.lecuyer.rng}{RandomStream}
+of the two \class{GammaProcess}'es to \texttt{stream}.
+\end{tabb}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCA.java b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCA.java
new file mode 100644
index 0000000..576e441
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCA.java
@@ -0,0 +1,180 @@
+
+
+/*
+ * Class: VarianceGammaProcessDiffPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+
+
+/**
+ * Same as {@link VarianceGammaProcessDiff}, but the two inner
+ * {@link GammaProcess}'es are of PCA type. Also,
+ * <TT>generatePath(double[] uniforms01)</TT> distributes
+ * the uniform random variates to the {@link GammaProcessPCA}'s according to their
+ * eigenvalues, i.e. the {@link GammaProcessPCA} with the higher eigenvalue
+ * gets the next uniform random number. If one should decide to create a
+ * {@link VarianceGammaProcessDiffPCA} by giving two {@link GammaProcessPCA}'s to an
+ * objet of the class {@link VarianceGammaProcessDiff}, the uniform random
+ * numbers would not be given this way
+ * to the {@link GammaProcessPCA}'s; this might give less variance reduction when
+ * used with QMC.
+ *
+ */
+public class VarianceGammaProcessDiffPCA extends VarianceGammaProcessDiff {
+ int[] indexEigenUp;
+ int[] indexEigenDw;
+
+
+
+
+ /**
+ * Constructs a new {@link VarianceGammaProcessDiffPCA} with
+ * parameters
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN>
+ * and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>. There is only
+ * one {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} here which is
+ * used for the two inner {@link GammaProcessPCA}'s. The other
+ * parameters are set as in {@link VarianceGammaProcessDiff}.
+ *
+ */
+ public VarianceGammaProcessDiffPCA (double s0, double theta,
+ double sigma, double nu,
+ RandomStream stream) {
+ super(s0, theta, sigma, nu,
+ new GammaProcessPCA (0.0, 1.0, 1.0, stream),
+ new GammaProcessPCA (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}
+
+
+ /**
+ * Constructs a new {@link VarianceGammaProcessDiffPCA} with
+ * parameters
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN>
+ * and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>. As in
+ * {@link VarianceGammaProcessDiff}, the
+ * {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} of <TT>gneg</TT> is replaced by
+ * the one of <TT>gpos</TT> to avoid any confusion.
+ *
+ */
+ public VarianceGammaProcessDiffPCA (double s0, double theta,
+ double sigma, double nu,
+ GammaProcessPCA gpos,
+ GammaProcessPCA gneg) {
+ super(s0, theta, sigma, nu, gpos, gneg); // from VarianceGammaProcessDiff
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}
+
+
+ /**
+ * This method is not implemented is this class since
+ * the path cannot be generated sequentially.
+ *
+ */
+ public double nextObservation() {
+ throw new UnsupportedOperationException
+ ("Impossible with PCA, use generatePath() instead.");
+ }
+
+
+
+ public double[] generatePath() {
+ double[] u = new double[2*d];
+ for(int i =0; i < 2*d; i++)
+ u[i] = getStream().nextDouble();
+ return generatePath(u);
+ }
+
+ public double[] generatePath(double[] uniform01) {
+ int dd = uniform01.length;
+ int d = dd / 2;
+
+ if(dd % 2 != 0){
+ throw new IllegalArgumentException (
+ "The Array uniform01 must have a even length");
+ }
+
+ double[] QMCpointsUP = new double[d];
+ double[] QMCpointsDW = new double[d];
+
+ for(int i = 0; i < d; i++){
+ QMCpointsUP[i] = uniform01[ indexEigenUp[i] ];
+ QMCpointsDW[i] = uniform01[ indexEigenDw[i] ];
+ }
+ gpos.resetStartProcess();
+ gneg.resetStartProcess();
+
+ double[] pathUP = gpos.generatePath(QMCpointsUP);
+ double[] pathDOWN = gneg.generatePath(QMCpointsDW);
+
+ for (int i=0; i<d; i++) {
+ path[i+1] = x0 + pathUP[i+1] - pathDOWN[i+1];
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ protected void init() {
+ super.init (); // from VarianceGammaProcessDiff
+ if( observationTimesSet){
+ // Two lines below (casts) should be reinstated after fix inheritance PCA/PCABridge.
+ double[] eigenValUp = ((GammaProcessPCA)gpos).getBMPCA().getSortedEigenvalues();
+ double[] eigenValDw = ((GammaProcessPCA)gneg).getBMPCA().getSortedEigenvalues();
+ indexEigenUp = new int[d];
+ indexEigenDw = new int[d];
+
+ int iUp = 0;
+ int iDw = 0;
+ for(int iQMC = 0; iQMC < 2*d; iQMC++){
+ if(iUp == d) {indexEigenDw[iDw] = iQMC; iDw++;continue;}
+ if(iDw == d) {indexEigenUp[iUp] = iQMC; iUp++;continue;}
+ if( eigenValUp[iUp] >= eigenValDw[iDw] ){
+ indexEigenUp[iUp] = iQMC;
+ iUp++;
+ }
+ else{
+ indexEigenDw[iDw] = iQMC;
+ iDw++;
+ }
+ }
+ }
+
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCA.tex b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCA.tex
new file mode 100644
index 0000000..cfc45aa
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCA.tex
@@ -0,0 +1,184 @@
+%
+% Jean-Sébastien Parent & Maxime Dion, summer 2008
+%
+%
+
+\defmodule {VarianceGammaProcessDiffPCA}
+
+Same as \class{VarianceGammaProcessDiff}, but the two inner
+\class{GammaProcess}'es are of PCA type. Also,
+\texttt{generatePath(double[] uniforms01)} distributes
+the uniform random variates to the \class{GammaProcessPCA}'s according to their
+eigenvalues, i.e. the \class{GammaProcessPCA} with the higher eigenvalue
+gets the next uniform random number. If one should decide to create a
+\class{VarianceGammaProcessDiffPCA} by giving two \class{GammaProcessPCA}'s to an
+objet of the class \class{VarianceGammaProcessDiff}, the uniform random
+numbers would not be given this way
+to the \class{GammaProcessPCA}'s; this might give less variance reduction when
+used with QMC.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: VarianceGammaProcessDiffPCA
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+import umontreal.iro.lecuyer.probdist.*;
+import umontreal.iro.lecuyer.randvar.*;
+
+\end{hide}
+
+public class VarianceGammaProcessDiffPCA extends VarianceGammaProcessDiff \begin{hide} {
+ int[] indexEigenUp;
+ int[] indexEigenDw;
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public VarianceGammaProcessDiffPCA (double s0, double theta,
+ double sigma, double nu,
+ RandomStream stream) \begin{hide} {
+ super(s0, theta, sigma, nu,
+ new GammaProcessPCA (0.0, 1.0, 1.0, stream),
+ new GammaProcessPCA (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \class{VarianceGammaProcessDiffPCA} with
+parameters $\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$, $\nu = \texttt{nu}$
+and initial value $S(t_{0}) = \texttt{s0}$. There is only
+one \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} here which is
+used for the two inner \class{GammaProcessPCA}'s. The other
+parameters are set as in \class{VarianceGammaProcessDiff}.
+\end{tabb}
+\begin{code}
+
+ public VarianceGammaProcessDiffPCA (double s0, double theta,
+ double sigma, double nu,
+ GammaProcessPCA gpos,
+ GammaProcessPCA gneg) \begin{hide} {
+ super(s0, theta, sigma, nu, gpos, gneg); // from VarianceGammaProcessDiff
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}\end{hide}
+\end{code}
+\begin{tabb} Constructs a new \class{VarianceGammaProcessDiffPCA} with
+parameters $\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$, $\nu = \texttt{nu}$
+and initial value $S(t_{0}) = \texttt{s0}$. As in
+\class{VarianceGammaProcessDiff}, the
+\externalclass{umontreal.iro.lecuyer.rng}{RandomStream} of \texttt{gneg} is replaced by
+the one of \texttt{gpos} to avoid any confusion.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public double nextObservation() \begin{hide} {
+ throw new UnsupportedOperationException
+ ("Impossible with PCA, use generatePath() instead.");
+ }\end{hide}
+\end{code}
+\begin{tabb} This method is not implemented is this class since
+the path cannot be generated sequentially.
+\end{tabb}
+\begin{code}
+\begin{hide}
+
+ public double[] generatePath() {
+ double[] u = new double[2*d];
+ for(int i =0; i < 2*d; i++)
+ u[i] = getStream().nextDouble();
+ return generatePath(u);
+ }
+
+ public double[] generatePath(double[] uniform01) {
+ int dd = uniform01.length;
+ int d = dd / 2;
+
+ if(dd % 2 != 0){
+ throw new IllegalArgumentException (
+ "The Array uniform01 must have a even length");
+ }
+
+ double[] QMCpointsUP = new double[d];
+ double[] QMCpointsDW = new double[d];
+
+ for(int i = 0; i < d; i++){
+ QMCpointsUP[i] = uniform01[ indexEigenUp[i] ];
+ QMCpointsDW[i] = uniform01[ indexEigenDw[i] ];
+ }
+ gpos.resetStartProcess();
+ gneg.resetStartProcess();
+
+ double[] pathUP = gpos.generatePath(QMCpointsUP);
+ double[] pathDOWN = gneg.generatePath(QMCpointsDW);
+
+ for (int i=0; i<d; i++) {
+ path[i+1] = x0 + pathUP[i+1] - pathDOWN[i+1];
+ }
+ observationIndex = d;
+ observationCounter = d;
+ return path;
+ }
+
+
+ protected void init() {
+ super.init (); // from VarianceGammaProcessDiff
+ if( observationTimesSet){
+ // Two lines below (casts) should be reinstated after fix inheritance PCA/PCABridge.
+ double[] eigenValUp = ((GammaProcessPCA)gpos).getBMPCA().getSortedEigenvalues();
+ double[] eigenValDw = ((GammaProcessPCA)gneg).getBMPCA().getSortedEigenvalues();
+ indexEigenUp = new int[d];
+ indexEigenDw = new int[d];
+
+ int iUp = 0;
+ int iDw = 0;
+ for(int iQMC = 0; iQMC < 2*d; iQMC++){
+ if(iUp == d) {indexEigenDw[iDw] = iQMC; iDw++;continue;}
+ if(iDw == d) {indexEigenUp[iUp] = iQMC; iUp++;continue;}
+ if( eigenValUp[iUp] >= eigenValDw[iDw] ){
+ indexEigenUp[iUp] = iQMC;
+ iUp++;
+ }
+ else{
+ indexEigenDw[iDw] = iQMC;
+ iDw++;
+ }
+ }
+ }
+
+ }
+
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCABridge.java b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCABridge.java
new file mode 100644
index 0000000..452bb23
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCABridge.java
@@ -0,0 +1,69 @@
+
+
+/*
+ * Class: VarianceGammaProcessDiffPCABridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent & Maxime Dion
+ * @since 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+
+
+
+/**
+ * Same as {@link VarianceGammaProcessDiff}, but the two
+ * inner {@link GammaProcess}'es are of the type PCABridge.
+ * Also, <TT>generatePath(double[] uniform01)</TT> distributes the
+ * lowest coordinates uniforms to the inner
+ * {@link GammaProcessPCABridge} according to their eigenvalues.
+ *
+ */
+public class VarianceGammaProcessDiffPCABridge extends
+ VarianceGammaProcessDiffPCA {
+
+
+
+
+ /**
+ * Constructs a new {@link VarianceGammaProcessDiffPCABridge} with
+ * parameters
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN>
+ * and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>. There is only
+ * one {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} here which is
+ * used for the two inner {@link GammaProcessPCABridge}'s. The other
+ * parameters are set as in {@link VarianceGammaProcessDiff}.
+ *
+ */
+ public VarianceGammaProcessDiffPCABridge (double s0, double theta,
+ double sigma, double nu,
+ RandomStream stream) {
+ super(s0, theta, sigma, nu,
+ new GammaProcessPCABridge (0.0, 1.0, 1.0, stream),
+ new GammaProcessPCABridge (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCABridge.tex b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCABridge.tex
new file mode 100644
index 0000000..fe324bd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCABridge.tex
@@ -0,0 +1,72 @@
+\defmodule {VarianceGammaProcessDiffPCABridge}
+
+Same as \class{VarianceGammaProcessDiff}, but the two
+inner \class{GammaProcess}'es are of the type PCABridge.
+Also, \texttt{generatePath(double[] uniform01)} distributes the
+lowest coordinates uniforms to the inner
+\class{GammaProcessPCABridge} according to their eigenvalues.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: VarianceGammaProcessDiffPCABridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent & Maxime Dion
+ * @since 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+
+\end{hide}
+
+public class VarianceGammaProcessDiffPCABridge extends
+ VarianceGammaProcessDiffPCA \begin{hide} {
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public VarianceGammaProcessDiffPCABridge (double s0, double theta,
+ double sigma, double nu,
+ RandomStream stream) \begin{hide} {
+ super(s0, theta, sigma, nu,
+ new GammaProcessPCABridge (0.0, 1.0, 1.0, stream),
+ new GammaProcessPCABridge (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}\end{hide}
+\end{code}
+\begin{tabb}Constructs a new \class{VarianceGammaProcessDiffPCABridge} with
+parameters $\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$, $\nu = \texttt{nu}$
+and initial value $S(t_{0}) = \texttt{s0}$. There is only
+one \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} here which is
+used for the two inner \class{GammaProcessPCABridge}'s. The other
+parameters are set as in \class{VarianceGammaProcessDiff}.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCASymmetricalBridge.java b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCASymmetricalBridge.java
new file mode 100644
index 0000000..18fe6a7
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCASymmetricalBridge.java
@@ -0,0 +1,73 @@
+
+
+/*
+ * Class: VarianceGammaProcessDiffPCASymmetricalBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent-Chartier and Maxime Dion
+ * @since 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.stochprocess;
+import umontreal.iro.lecuyer.rng.*;
+
+
+
+/**
+ * Same as {@link VarianceGammaProcessDiff}, but the two
+ * inner {@link GammaProcess}'es are of the PCASymmetricalBridge type.
+ * Also, <TT>generatePath(double[] uniform01)</TT> distributes the
+ * lowest coordinates uniforms to the inner
+ * GammaProcessPCA according to their eigenvalues.
+ *
+ */
+public class VarianceGammaProcessDiffPCASymmetricalBridge extends
+ VarianceGammaProcessDiffPCA {
+
+
+
+
+
+
+ /**
+ * Constructs a new
+ * {@link VarianceGammaProcessDiffPCASymmetricalBridge} with
+ * parameters
+ * <SPAN CLASS="MATH"><I>θ</I> = <texttt>theta</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>σ</I> = <texttt>sigma</texttt></SPAN>,
+ * <SPAN CLASS="MATH"><I>ν</I> = <texttt>nu</texttt></SPAN>
+ * and initial value
+ * <SPAN CLASS="MATH"><I>S</I>(<I>t</I><SUB>0</SUB>) = <texttt>s0</texttt></SPAN>. There is only
+ * one {@link umontreal.iro.lecuyer.rng.RandomStream RandomStream} here which is
+ * used for the two inner {@link GammaProcessPCASymmetricalBridge}'s. The other
+ * parameters are set as in {@link VarianceGammaProcessDiff}.
+ *
+ */
+public VarianceGammaProcessDiffPCASymmetricalBridge (
+ double s0, double theta,
+ double sigma, double nu,
+ RandomStream stream) {
+ super(s0, theta, sigma, nu, new GammaProcessPCASymmetricalBridge (0.0, 1.0, 1.0, stream),
+ new GammaProcessPCASymmetricalBridge (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCASymmetricalBridge.tex b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCASymmetricalBridge.tex
new file mode 100644
index 0000000..2e807cd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/VarianceGammaProcessDiffPCASymmetricalBridge.tex
@@ -0,0 +1,79 @@
+\defmodule {VarianceGammaProcessDiffPCASymmetricalBridge}
+
+Same as \class{VarianceGammaProcessDiff}, but the two
+inner \class{GammaProcess}'es are of the PCASymmetricalBridge type.
+Also, \texttt{generatePath(double[] uniform01)} distributes the
+lowest coordinates uniforms to the inner
+GammaProcessPCA according to their eigenvalues.
+
+\bigskip\hrule\bigskip
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+\begin{hide}
+/*
+ * Class: VarianceGammaProcessDiffPCASymmetricalBridge
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @authors Jean-Sebastien Parent-Chartier and Maxime Dion
+ * @since 2008
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.stochprocess;\begin{hide}
+import umontreal.iro.lecuyer.rng.*;
+
+\end{hide}
+
+public class VarianceGammaProcessDiffPCASymmetricalBridge extends
+ VarianceGammaProcessDiffPCA \begin{hide} {
+
+
+
+\end{hide}
+\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+public VarianceGammaProcessDiffPCASymmetricalBridge (
+ double s0, double theta,
+ double sigma, double nu,
+ RandomStream stream) \begin{hide} {
+ super(s0, theta, sigma, nu, new GammaProcessPCASymmetricalBridge (0.0, 1.0, 1.0, stream),
+ new GammaProcessPCASymmetricalBridge (0.0, 1.0, 1.0, stream));
+ // Params mu, nu of the 2 gamma processes are redefined in init()
+ // which will be called after a call to 'setObservTimes'
+}
+\end{hide}
+\end{code}
+\begin{tabb}Constructs a new
+\class{VarianceGammaProcessDiffPCASymmetricalBridge} with
+parameters $\theta = \texttt{theta}$, $\sigma = \texttt{sigma}$, $\nu = \texttt{nu}$
+and initial value $S(t_{0}) = \texttt{s0}$. There is only
+one \externalclass{umontreal.iro.lecuyer.rng}{RandomStream} here which is
+used for the two inner \class{GammaProcessPCASymmetricalBridge}'s. The other
+parameters are set as in \class{VarianceGammaProcessDiff}.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
+
+
+
diff --git a/source/umontreal/iro/lecuyer/stochprocess/guidestochprocess.bbl b/source/umontreal/iro/lecuyer/stochprocess/guidestochprocess.bbl
new file mode 100644
index 0000000..1936090
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/guidestochprocess.bbl
@@ -0,0 +1,99 @@
+\begin{thebibliography}{10}
+
+\bibitem{fALB04a}
+H.~Albrecher and M.~Predota.
+\newblock On {A}sian option pricing for {NIG} {L}\'evy processes.
+\newblock {\em Journal of computational and applied mathematics}, 172:153--168,
+ 2004.
+
+\bibitem{fAVR06a}
+T.~Avramidis and P.~L'Ecuyer.
+\newblock Efficient {M}onte {C}arlo and quasi-{M}onte {C}arlo option pricing
+ under the variance-gamma model.
+\newblock {\em Management Science}, 52(12):1930--1944, 2006.
+
+\bibitem{fAVR03a}
+T.~Avramidis, P.~L'Ecuyer, and P.-A. Tremblay.
+\newblock Efficient simulation of gamma and variance-gamma processes.
+\newblock In {\em Proceedings of the 2003 Winter Simulation Conference}, pages
+ 319--326, Piscataway, New Jersey, 2003. {IEEE} Press.
+
+\bibitem{pBAR98a}
+O.~E. Barndorff-Nielsen.
+\newblock Processes of normal inverse gaussian type.
+\newblock {\em Finance and Stochastics}, 2:41--68, 1998.
+
+\bibitem{fCOX85a}
+J.~C. Cox, J.~E. Ingersoll, and S.~A. Ross.
+\newblock A theory of the term structure of interest rates.
+\newblock {\em Econometrica}, 53:385--407, 1985.
+
+\bibitem{pFEL66a}
+W.~Feller.
+\newblock {\em An Introduction to Probability Theory and Its Applications, Vol.
+ 2}.
+\newblock Wiley, New York, NY, first edition, 1966.
+
+\bibitem{fGLA04a}
+P.~Glasserman.
+\newblock {\em Monte {C}arlo Methods in Financial Engineering}.
+\newblock Springer-Verlag, New York, 2004.
+
+\bibitem{fIMA06a}
+J.~Imai and K.~S. Tan.
+\newblock A general dimension reduction technique for derivative pricing.
+\newblock {\em Journal of Computational Finance}, 10(2):129--155, 2006.
+
+\bibitem{fLEC04a}
+P.~L'Ecuyer.
+\newblock Quasi-{M}onte {C}arlo methods in finance.
+\newblock In R.~G. Ingalls, M.~D. Rossetti, J.~S. Smith, and B.~A Peters,
+ editors, {\em Proceedings of the 2004 Winter Simulation Conference},
+ Piscataway, New Jersey, 2004. {IEEE} Press.
+
+\bibitem{fLEC08a}
+P.~L'Ecuyer, J.~S. Parent-Chartier, and M.~Dion.
+\newblock Simulation of a {L}\'evy process by {PCA} sampling to reduce the
+ effective dimention.
+\newblock In {\em Proceedings of the 2008 Winter Simulation Conference}, pages
+ 436--443, Piscataway, NJ, 2008. IEEE Press.
+
+\bibitem{rLEC06a}
+P.~L'Ecuyer and R.~Simard.
+\newblock Inverting the symmetrical beta distribution.
+\newblock {\em {ACM} Transactions on Mathematical Software}, 32(4):509--520,
+ 2006.
+
+\bibitem{fMAD98a}
+D.~B. Madan, P.~P. Carr, and E.~C. Chang.
+\newblock The variance gamma process and option pricing.
+\newblock {\em European Finance Review}, 2:79--105, 1998.
+
+\bibitem{fMAD91a}
+D.~B. Madan and F.~Milne.
+\newblock Option pricing with {V.G.} martingale components.
+\newblock {\em Mathematical Finance}, 1:39--55, 1991.
+
+\bibitem{fMAD90a}
+D.~B. Madan and E.~Seneta.
+\newblock The variance gamma {(V.G.)} model for share market returns.
+\newblock {\em Journal of Business}, 63:511--524, 1990.
+
+\bibitem{rMIC76a}
+J.~R. Michael, W.~R. Schuchany, and R.~W. Haas.
+\newblock Generating random variates using transformations with multiple roots.
+\newblock {\em The American Statistician}, 30:88--90, 1976.
+
+\bibitem{fRIB02a}
+C.~Ribeiro and N.~Webber.
+\newblock Valuing path-dependent options in the variance-gamma model by {M}onte
+ {C}arlo with a gamma bridge.
+\newblock manuscript, December 2002.
+
+\bibitem{fWEB03a}
+N.~Webber and C.~Ribeiro.
+\newblock A {M}onte {C}arlo method for the normal inverse gaussian option
+ valuation model using an inverse gaussian bridge.
+\newblock Technical Report~5, Society for Computational Economics, 2003.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/guidestochprocess.tex b/source/umontreal/iro/lecuyer/stochprocess/guidestochprocess.tex
new file mode 100644
index 0000000..82fd97d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/guidestochprocess.tex
@@ -0,0 +1,89 @@
+\documentclass[12pt]{article}
+\usepackage{amssymb}
+\usepackage{url}
+\usepackage{alltt}
+\usepackage{html}
+\usepackage{ssj}
+
+\def\bmu{\boldmu}
+\def\bSigma{\boldSigma}
+
+
+
+
+% The symbols for the complex, the real, the integers, the fields.
+%% $\mathbb{C, R, N, Z, F}$
+
+\mytwoheads
+% \dateheadtrue
+% \detailedtrue
+\hyphenation{Inverse-Gaussian-Process Brown-ian-Motion-PCA}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+
+\begin{titlepage}
+
+\title{stochprocess}{Stochastic Processes}
+
+%\footnote{\normalsize
+This package provides tools to generate various stochastic processes.
+
+\vfill
+\end{titlepage}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
+
+\include{overview}
+
+\include{StochasticProcess}
+\include{BrownianMotion}
+\include{BrownianMotionBridge}
+\include{BrownianMotionPCA}
+\include{GeometricBrownianMotion}
+
+\include{GeometricLevyProcess}
+
+
+\include{InverseGaussianProcess}
+\include{InverseGaussianProcessBridge}
+\include{InverseGaussianProcessMSH}
+\include{InverseGaussianProcessPCA}
+\include{NormalInverseGaussianProcess}
+\include{GeometricNormalInverseGaussianProcess}
+
+\include{OrnsteinUhlenbeckProcess}
+\include{OrnsteinUhlenbeckProcessEuler}
+\include{CIRProcess}
+\include{CIRProcessEuler}
+\include{GammaProcess}
+\include{GammaProcessBridge}
+\include{GammaProcessSymmetricalBridge}
+\include{GammaProcessPCA}
+\include{GammaProcessPCABridge}
+\include{GammaProcessPCASymmetricalBridge}
+\include{VarianceGammaProcess}
+\include{VarianceGammaProcessDiff}
+\include{VarianceGammaProcessDiffPCA}
+\include{VarianceGammaProcessDiffPCABridge}
+\include{VarianceGammaProcessDiffPCASymmetricalBridge}
+\include{GeometricVarianceGammaProcess}
+
+
+%\addtocontents{toc}{\bigskip}
+%\include{StringDist}
+%% Discrete distributions
+
+
+\bibliography{fin,prob,random}
+\bibliographystyle{plain}
+
+%\bibliography{stat,random,simul,math,ift,temp2,fin}
+%\include guidefinance.bbl
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/stochprocess/overview.tex b/source/umontreal/iro/lecuyer/stochprocess/overview.tex
new file mode 100644
index 0000000..5840788
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/stochprocess/overview.tex
@@ -0,0 +1,14 @@
+\latex{\section*{Overview}\addcontentsline{toc}{subsection}{Overview}}
+% \latex{\section*{Overview}}
+
+This package provides classes to define stochastic processes
+$\{X(t),\, t\ge 0\}$, and to simulate their sample paths at a finite
+number of (discrete) observation times $t_0 \le t_1 \le \cdots \le t_d$.
+The observation of the generated path is thus the vector
+$(X(t_0),X(t_1),\dots,X(t_d))$.
+
+The observation times $t_0, \dots, t_d$ can be specified (or changed)
+after defining the process, with the method \texttt{setObservationTimes}.
+The random stream used to generate the sample path can also be changed,
+using \texttt{setStream}.
+
diff --git a/source/umontreal/iro/lecuyer/util/AbstractChrono.java b/source/umontreal/iro/lecuyer/util/AbstractChrono.java
new file mode 100644
index 0000000..19ec70b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/AbstractChrono.java
@@ -0,0 +1,203 @@
+
+
+/*
+ * Class: AbstractChrono
+ * Description: calculates CPU time of parts of a program
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+/**
+ * <TT>AbstractChrono</TT> is a class that acts as an interface to
+ * the system clock and calculates the CPU or system time consumed by
+ * parts of a program.
+ *
+ * <P>
+ * Every object of class <TT>AbstractChrono</TT> acts as an independent <EM>stopwatch</EM>.
+ * Several <TT>AbstractChrono</TT> objects can run at any given time.
+ * The method {@link #init init} resets the stopwatch to zero,
+ * {@link #getSeconds getSeconds}, {@link #getMinutes getMinutes} and {@link #getHours getHours}
+ * return its current reading,
+ * and {@link #format format} converts this reading to a {@link String}.
+ * The returned value includes the execution time of the method
+ * from <TT>AbstractChrono</TT>.
+ *
+ * <P>
+ * Below is an example of how it may be used.
+ * A stopwatch named <TT>timer</TT> is constructed (and initialized).
+ * When 2.1 seconds of CPU time have been consumed,
+ * the stopwatch is read and reset to zero.
+ * Then, after an additional 330 seconds (or 5.5 minutes) of CPU time,
+ * the stopwatch is read again and the value is printed to the output
+ * in minutes.
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ *
+ * <BR>
+ * AbstractChrono timer = new Chrono();
+ * <BR></TT>
+ * </DIV>
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ *
+ * <BR>
+ * <BR> suppose 2.1 CPU seconds are used here
+ * <BR>
+ * <BR></TT>
+ * </DIV>
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ * double t = timer.getSeconds(); // Here, t = 2.1
+ * <BR>
+ * timer.init();
+ * <BR>
+ * t = timer.getSeconds(); // Here, t = 0.0
+ * <BR></TT>
+ * </DIV>
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ *
+ * <BR>
+ * <BR> suppose 330 CPU seconds are used here.
+ * <BR>
+ * <BR></TT>
+ * </DIV>
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ * t = timer.getMinutes(); // Here, t = 5.5
+ * <BR>
+ * System.out.println (timer.format()); // Prints: 0:5:30.00
+ * <BR></TT>
+ * </DIV>
+ *
+ */
+public abstract class AbstractChrono {
+
+ private long m_second;
+ private long m_microsec;
+ private long[] now = new long[2];
+ // tab[0] = seconds, tab[1] = microseconds
+
+ protected abstract void getTime (long[] tab);
+
+
+ public AbstractChrono() {
+ }
+
+
+ /**
+ * Initializes this <TT>AbstractChrono</TT> to zero.
+ *
+ */
+ public void init() {
+ getTime (now);
+ m_second = now[0];
+ m_microsec = now[1];
+ }
+
+
+ /**
+ * Returns the CPU time in seconds used by the program since the last call to
+ * {@link #init init} for this <TT>AbstractChrono</TT>.
+ *
+ * @return the number of seconds
+ *
+ */
+ public double getSeconds() {
+ getTime (now);
+ double time = (now[1] - m_microsec)/1000000.0
+ + (now[0] - m_second);
+ return time;
+ }
+
+
+ /**
+ * Returns the CPU time in minutes used by the program since the last call to
+ * {@link #init init} for this <TT>AbstractChrono</TT>.
+ *
+ * @return the number of minutes
+ *
+ */
+ public double getMinutes() {
+ getTime (now);
+ double time = (now[1] - m_microsec)/1000000.0
+ + (now[0] - m_second);
+ return time*1.666666667*0.01;
+ }
+
+
+ /**
+ * Returns the CPU time in hours used by the program since the last call to
+ * {@link #init init} for this <TT>AbstractChrono</TT>.
+ *
+ * @return the number of hours
+ *
+ */
+ public double getHours() {
+ getTime (now);
+ double time = (now[1] - m_microsec)/1000000.0
+ + (now[0] - m_second);
+ return time*2.777777778*0.0001;
+ }
+
+
+ /**
+ * Converts the CPU time used by the program since its last
+ * call to {@link #init init} for this <TT>AbstractChrono</TT> to a
+ * {@link String} in the <TT>HH:MM:SS.xx</TT> format.
+ *
+ * @return the string representation of the CPU time
+ *
+ */
+ public String format() {
+ return format (getSeconds());
+ }
+
+
+ /**
+ * Converts the time <TT>time</TT>, given in seconds, to a
+ * {@link String} in the <TT>HH:MM:SS.xx</TT> format.
+ *
+ * @return the string representation of the time <TT>time</TT>
+ *
+ */
+ public static String format (double time) {
+ int second, hour, min, centieme;
+ hour = (int)(time/3600.0);
+ if (hour > 0) time -= ((double)hour*3600.0);
+ min = (int)(time/60.0);
+ if (min > 0) time -= ((double)min*60.0);
+ second = (int)time;
+ centieme = (int)(100.0*(time - (double)second) + 0.5);
+ return String.valueOf (hour) + ":" +
+ min + ":" +
+ second + "." +
+ centieme;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/AbstractChrono.tex b/source/umontreal/iro/lecuyer/util/AbstractChrono.tex
new file mode 100644
index 0000000..63aff36
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/AbstractChrono.tex
@@ -0,0 +1,226 @@
+\defmodule {AbstractChrono}
+
+\texttt{AbstractChrono} is a class that acts as an interface to
+the system clock and calculates the CPU or system time consumed by
+parts of a program.
+
+Every object of class \texttt{AbstractChrono} acts as an independent {\em stopwatch}.
+Several \texttt{AbstractChrono} objects can run at any given time.
+The method \method{init}{} resets the stopwatch to zero,
+\method{getSeconds}{}, \method{getMinutes}{} and \method{getHours}{}
+ return its current reading,
+and \method{format}{} converts this reading to a \class{String}.
+The returned value includes the execution time of the method
+from \texttt{AbstractChrono}.
+
+Below is an example of how it may be used.
+A stopwatch named \texttt{timer} is constructed (and initialized).
+When 2.1 seconds of CPU time have been consumed,
+the stopwatch is read and reset to zero.
+Then, after an additional 330 seconds (or 5.5 minutes) of CPU time,
+the stopwatch is read again and the value is printed to the output
+in minutes.
+\begin{vcode}
+
+AbstractChrono timer = new Chrono();
+\end{vcode}
+\begin{latexonly}
+\begin{vcode}
+ \vdots \end{vcode}
+\vskip -15pt \hskip 0.8in ({\em suppose 2.1 CPU seconds are used here}.)
+\end{latexonly}
+\begin{htmlonly}
+\begin{vcode}
+
+
+ suppose 2.1 CPU seconds are used here
+
+\end{vcode}
+\end{htmlonly}
+\begin{vcode}
+double t = timer.getSeconds(); // Here, t = 2.1
+timer.init();
+t = timer.getSeconds(); // Here, t = 0.0
+\end{vcode}
+\begin{latexonly}
+\begin{vcode} \vdots \end{vcode}
+\vskip -15pt \hskip 0.8in ({\em suppose 330 CPU seconds are used here}.)
+\end{latexonly}
+\begin{htmlonly}
+\begin{vcode}
+
+
+ suppose 330 CPU seconds are used here.
+
+\end{vcode}
+\end{htmlonly}
+\begin{vcode}
+t = timer.getMinutes(); // Here, t = 5.5
+System.out.println (timer.format()); // Prints: 0:5:30.00
+\end{vcode}
+
+
+\begin{latexonly}
+\begin{hide} %%%%%%
+\paragraph{Warning:}
+ Even though the ANSI/ISO macro \texttt{CLOCKS\_PER\_SEC = 1000000}
+ is the number of clock ticks per second for the value
+ returned by the \texttt{clock} function (so this function returns the
+ number of microseconds), on some systems the value returned by \texttt{clock}
+ wraps around to 0 after about 36 minutes when the type \texttt{long}
+ used to measure time has only 32 bits (on some systems, the clock may
+ wrap around to 0 after about 72 minutes if time is
+ measured using the 32-bit type \texttt{unsigned long}).
+ When the macro \texttt{USE\_ANSI\_CLOCK} in module \texttt{gdef} is undefined,
+ a non-ANSI clock is used. It calls the POSIX function
+ \texttt{times} to get the CPU time used by a program and the
+ non-ANSI macro \texttt{CLK\_TCK} is used to get the real number of
+ clock ticks per second for the system.
+\end{hide} %%%%%
+
+\end{latexonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule\bigskip
+\begin{code}
+\begin{hide}
+/*
+ * Class: AbstractChrono
+ * Description: calculates CPU time of parts of a program
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public abstract class AbstractChrono\begin{hide} {
+
+ private long m_second;
+ private long m_microsec;
+ private long[] now = new long[2];
+ // tab[0] = seconds, tab[1] = microseconds
+
+ protected abstract void getTime (long[] tab);
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Timing functions}
+
+\begin{code}
+ public AbstractChrono()\begin{hide} {
+ }\end{hide}
+
+
+ public void init() \begin{hide} {
+ getTime (now);
+ m_second = now[0];
+ m_microsec = now[1];
+ }\end{hide}
+\end{code}
+ \begin{tabb} Initializes this \texttt{AbstractChrono} to zero.
+ \end{tabb}
+\begin{code}
+
+ public double getSeconds()\begin{hide} {
+ getTime (now);
+ double time = (now[1] - m_microsec)/1000000.0
+ + (now[0] - m_second);
+ return time;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the CPU time in seconds used by the program since the last call to
+ \method{init}{} for this \texttt{AbstractChrono}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the number of seconds}
+\end{htmlonly}
+\begin{code}
+
+ public double getMinutes()\begin{hide} {
+ getTime (now);
+ double time = (now[1] - m_microsec)/1000000.0
+ + (now[0] - m_second);
+ return time*1.666666667*0.01;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the CPU time in minutes used by the program since the last call to
+ \method{init}{} for this \texttt{AbstractChrono}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the number of minutes}
+\end{htmlonly}
+\begin{code}
+
+ public double getHours()\begin{hide} {
+ getTime (now);
+ double time = (now[1] - m_microsec)/1000000.0
+ + (now[0] - m_second);
+ return time*2.777777778*0.0001;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+ Returns the CPU time in hours used by the program since the last call to
+ \method{init}{} for this \texttt{AbstractChrono}.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the number of hours}
+\end{htmlonly}
+\begin{code}
+
+ public String format()\begin{hide} {
+ return format (getSeconds());
+ }\end{hide}
+\end{code}
+ \begin{tabb} Converts the CPU time used by the program since its last
+ call to \method{init}{} for this \texttt{Abstract\-Chrono} to a
+ \class{String} in the \texttt{HH:MM:SS.xx} format.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the string representation of the CPU time}
+\end{htmlonly}
+\begin{code}
+
+ public static String format (double time)\begin{hide} {
+ int second, hour, min, centieme;
+ hour = (int)(time/3600.0);
+ if (hour > 0) time -= ((double)hour*3600.0);
+ min = (int)(time/60.0);
+ if (min > 0) time -= ((double)min*60.0);
+ second = (int)time;
+ centieme = (int)(100.0*(time - (double)second) + 0.5);
+ return String.valueOf (hour) + ":" +
+ min + ":" +
+ second + "." +
+ centieme;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Converts the time \texttt{time}, given in seconds, to a
+ \class{String} in the \texttt{HH:MM:SS.xx} format.
+ \end{tabb}
+\begin{htmlonly}
+ \return{the string representation of the time \texttt{time}}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/ArithmeticMod.java b/source/umontreal/iro/lecuyer/util/ArithmeticMod.java
new file mode 100644
index 0000000..40bef15
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ArithmeticMod.java
@@ -0,0 +1,595 @@
+
+/*
+ * Class: ArithmeticMod
+ * Description: multiplications of scalars, vectors and matrices modulo m
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+/**
+ * This class provides facilities to compute multiplications of scalars, of
+ * vectors and of matrices modulo m. All algorithms are present in three
+ * different versions. These allow operations on <TT>double</TT>, <TT>int</TT> and
+ * <TT>long</TT>. The <TT>int</TT> and <TT>long</TT> versions work exactly like the
+ * <TT>double</TT> ones.
+ *
+ */
+public class ArithmeticMod {
+
+ //private constants
+ private static final double two17 = 131072.0;
+ private static final double two53 = 9007199254740992.0;
+
+ //prevent the creation of the object
+ private ArithmeticMod() {};
+
+
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH">(<I>a</I>×<I>s</I> + <I>c</I>) mod <I>m</I></SPAN>. Where <TT>m</TT> must
+ * be smaller than <SPAN CLASS="MATH">2<SUP>35</SUP></SPAN>. Works also if <TT>s</TT> or <TT>c</TT> are negative.
+ * The result is always positive (and thus always between 0 and <TT>m</TT> - 1).
+ *
+ * @param a the first factor of the multiplication
+ *
+ * @param s the second factor of the multiplication
+ *
+ * @param c the second term of the addition
+ *
+ * @param m the modulus
+ *
+ * @return the result of the multiplication and the addition modulo <TT>m</TT>
+ *
+ */
+ public static double multModM (double a, double s, double c, double m) {
+ int a1;
+ double v = a * s + c;
+ if (v >= two53 || v <= -two53 ) {
+ a1 = (int)(a / two17);
+ a -= a1 * two17;
+ v = a1 * s;
+ a1 = (int)(v / m);
+ v -= a1 * m;
+ v = v * two17 + a * s + c;
+ }
+ a1 = (int)(v / m);
+ if ((v -= a1 * m) < 0.0)
+ return v += m;
+ else
+ return v;
+ }
+
+
+ /**
+ * Computes the result of
+ * <SPAN CLASS="MATH"><TT>A</TT>×<B>s</B> mod <I>m</I></SPAN> and puts the
+ * result in <TT>v</TT>. Where <TT>s</TT> and <TT>v</TT> are both column vectors. This
+ * method works even if <TT>s</TT> = <TT>v</TT>.
+ *
+ * @param A the multiplication matrix
+ *
+ * @param s the multiplied vector
+ *
+ * @param v the result of the multiplication
+ *
+ * @param m the modulus
+ *
+ *
+ */
+ public static void matVecModM (double A[][], double s[], double v[],
+ double m) {
+ int i;
+ double x[] = new double[v.length];
+ for (i = 0; i < v.length; ++i) {
+ x[i] = 0.0;
+ for(int j = 0; j < s.length; j++)
+ x[i] = multModM (A[i][j], s[j], x[i], m);
+ }
+ for (i = 0; i < v.length; ++i)
+ v[i] = x[i];
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><TT>A</TT>×<TT>B</TT> mod <I>m</I></SPAN>
+ * and puts the result in
+ * <TT>C</TT>. Works even if <TT>A</TT> = <TT>C</TT>, <TT>B</TT> = <TT>C</TT> or
+ * <TT>A</TT> = <TT>B</TT> = <TT>C</TT>.
+ *
+ * @param A the first factor of the multiplication
+ *
+ * @param B the second factor of the multiplication
+ *
+ * @param C the result of the multiplication
+ *
+ * @param m the modulus
+ *
+ *
+ */
+ public static void matMatModM (double A[][], double B[][], double C[][],
+ double m) {
+ int i, j;
+ int r = C.length; //# of rows of C
+ int c = C[0].length; //# of columns of C
+ double V[] = new double[r];
+ double W[][] = new double[r][c];
+ for (i = 0; i < c; ++i) {
+ for (j = 0; j < r; ++j)
+ V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < r; ++j)
+ W[j][i] = V[j];
+ }
+ for (i = 0; i < r; ++i) {
+ for (j = 0; j < c; ++j)
+ C[i][j] = W[i][j];
+ }
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><TT>A</TT><SUP>2<SUP><TT>e</TT></SUP></SUP> mod <I>m</I></SPAN> and
+ * puts the result in <TT>B</TT>.
+ * Works even if <TT>A</TT> = <TT>B</TT>.
+ *
+ * @param A the matrix to raise to a power
+ *
+ * @param B the result of exponentiation
+ *
+ * @param m the modulus
+ *
+ * @param e the <SPAN CLASS="MATH">log<SUB>2</SUB></SPAN> of the exponent
+ *
+ *
+ */
+ public static void matTwoPowModM (double A[][], double B[][], double m,
+ int e) {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < A.length; i++) {
+ for (j = 0; j < A.length; ++j) //A is square
+ B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++)
+ matMatModM (B, B, B, m);
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><TT>A</TT><SUP>c</SUP> mod <I>m</I></SPAN>
+ * and puts the result in <TT>B</TT>.
+ * Works even if <TT>A</TT> = <TT>B</TT>.
+ *
+ * @param A the matrix to raise to a power
+ *
+ * @param B the result of the exponentiation
+ *
+ * @param m the modulus
+ *
+ * @param c the exponent
+ *
+ */
+ public static void matPowModM (double A[][], double B[][], double m,
+ int c) {
+ int i, j;
+ int n = c;
+ int s = A.length; //we suppose that A is square
+ double W[][] = new double[s][s];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < s; i++) {
+ for (j = 0; j < s; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0.0;
+ }
+ }
+ for (j = 0; j < s; ++j)
+ B[j][j] = 1.0;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1)
+ matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH">(<I>a</I>×<I>s</I> + <I>c</I>) mod <I>m</I></SPAN>. Works also if <TT>s</TT>
+ * or <TT>c</TT> are negative.
+ * The result is always positive (and thus always between 0 and <TT>m</TT> - 1).
+ *
+ * @param a the first factor of the multiplication
+ *
+ * @param s the second factor of the multiplication
+ *
+ * @param c the second term of the addition
+ *
+ * @param m the modulus
+ *
+ * @return the result of the multiplication and the addition modulo <TT>m</TT>
+ *
+ */
+ public static int multModM (int a, int s, int c, int m) {
+ int r = (int) (((long)a * s + c) % m);
+ return r < 0 ? r + m : r;
+ }
+
+
+ /**
+ * Exactly like {@link #matVecModM(double[][], double[],
+ * double[], double) matVecModM} using <TT>double</TT>, but with <TT>int</TT> instead
+ * of <TT>double</TT>.
+ *
+ * @param A the multiplication matrix
+ *
+ * @param s the multiplied vector
+ *
+ * @param v the result of the multiplication
+ *
+ * @param m the modulus
+ *
+ *
+ */
+ public static void matVecModM (int A[][], int s[], int v[], int m) {
+ int i;
+ int x[] = new int[v.length];
+ for (i = 0; i < v.length; ++i) {
+ x[i] = 0;
+ for(int j = 0; j < s.length; j++)
+ x[i] = multModM(A[i][j], s[j], x[i], m);
+ }
+ for (i = 0; i < v.length; ++i)
+ v[i] = x[i];
+
+ }
+
+
+ /**
+ * Exactly like {@link #matMatModM(double[][], double[][],
+ * double[][], double) matMatModM} using <TT>double</TT>, but with <TT>int</TT> instead
+ * of <TT>double</TT>.
+ *
+ * @param A the first factor of the multiplication
+ *
+ * @param B the second factor of the multiplication
+ *
+ * @param C the result of the multiplication
+ *
+ * @param m the modulus
+ *
+ *
+ */
+ public static void matMatModM (int A[][], int B[][], int C[][], int m) {
+ int i, j;
+ int r = C.length; //# of rows of C
+ int c = C[0].length; //# of columns of C
+ int V[] = new int[r];
+ int W[][] = new int[r][c];
+ for (i = 0; i < c; ++i) {
+ for (j = 0; j < r; ++j)
+ V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < r; ++j)
+ W[j][i] = V[j];
+ }
+ for (i = 0; i < r; ++i) {
+ for (j = 0; j < c; ++j)
+ C[i][j] = W[i][j];
+ }
+ }
+
+
+ /**
+ * Exactly like {@link #matTwoPowModM(double[][], double[][],
+ * double, int) matTwoPowModM} using <TT>double</TT>, but with <TT>int</TT> instead of
+ * <TT>double</TT>.
+ *
+ * @param A the matrix to raise to a power
+ *
+ * @param B the result of exponentiation
+ *
+ * @param m the modulus
+ *
+ * @param e the <SPAN CLASS="MATH">log<SUB>2</SUB></SPAN> of the exponent
+ *
+ *
+ */
+ public static void matTwoPowModM (int A[][], int B[][], int m, int e) {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < A.length; i++) {
+ for (j = 0; j < A.length; ++j) //A is square
+ B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++)
+ matMatModM (B, B, B, m);
+ }
+
+
+ /**
+ * Exactly like {@link #matPowModM(double[][], double[][],
+ * double, int) matPowModM} using <TT>double</TT>, but with <TT>int</TT> instead
+ * of <TT>double</TT>.
+ *
+ * @param A the matrix to raise to a power
+ *
+ * @param B the result of the exponentiation
+ *
+ * @param m the modulus
+ *
+ * @param c the exponent
+ *
+ */
+ public static void matPowModM (int A[][], int B[][], int m, int c) {
+ int i, j;
+ int n = c;
+ int s = A.length; //we suppose that A is square (it must be)
+ int W[][] = new int[s][s];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < s; i++) {
+ for (j = 0; j < s; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0;
+ }
+ }
+ for (j = 0; j < s; ++j)
+ B[j][j] = 1;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1)
+ matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH">(<I>a</I>×<I>s</I> + <I>c</I>) mod <I>m</I></SPAN>. Works also if <TT>s</TT>
+ * or <TT>c</TT> are negative.
+ * The result is always positive (and thus always between 0 and <TT>m</TT> - 1).
+ *
+ * @param a the first factor of the multiplication
+ *
+ * @param s the second factor of the multiplication
+ *
+ * @param c the second term of the addition
+ *
+ * @param m the modulus
+ *
+ * @return the result of the multiplication and the addition modulo <TT>m</TT>
+ *
+ */
+ public static long multModM (long a, long s, long c, long m) {
+
+ /* Suppose que 0 < a < m et 0 < s < m. Retourne (a*s + c) % m.
+ * Cette procedure est tiree de :
+ * L'Ecuyer, P. et Cote, S., A Random Number Package with
+ * Splitting Facilities, ACM TOMS, 1991.
+ * On coupe les entiers en blocs de d bits. H doit etre egal a 2^d. */
+
+ final long H = 2147483648L; // = 2^d used in MultMod
+ long a0, a1, q, qh, rh, k, p;
+ if (a < H) {
+ a0 = a;
+ p = 0;
+ } else {
+ a1 = a / H;
+ a0 = a - H * a1;
+ qh = m / H;
+ rh = m - H * qh;
+ if (a1 >= H) {
+ a1 = a1 - H;
+ k = s / qh;
+ p = H * (s - k * qh) - k * rh;
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ } else /* p = (A2 * s * h) % m. */
+ p = 0;
+ if (a1 != 0) {
+ q = m / a1;
+ k = s / q;
+ p -= k * (m - a1 * q);
+ if (p > 0)
+ p -= m;
+ p += a1 * (s - k * q);
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ } /* p = ((A2 * h + a1) * s) % m. */
+ k = p / qh;
+ p = H * (p - k * qh) - k * rh;
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ } /* p = ((A2 * h + a1) * h * s) % m */
+ if (a0 != 0) {
+ q = m / a0;
+ k = s / q;
+ p -= k * (m - a0 * q);
+ if (p > 0)
+ p -= m;
+ p += a0 * (s - k * q);
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ }
+ p = (p - m) + c;
+ if (p < 0)
+ p += m;
+ return p;
+ }
+
+
+ /**
+ * Exactly like {@link #matVecModM(double[][], double[],
+ * double[], double) matVecModM} using <TT>double</TT>, but with <TT>long</TT> instead
+ * of <TT>double</TT>.
+ *
+ * @param A the multiplication matrix
+ *
+ * @param s the multiplied vector
+ *
+ * @param v the result of the multiplication
+ *
+ * @param m the modulus
+ *
+ *
+ */
+ public static void matVecModM (long A[][], long s[], long v[], long m) {
+ int i;
+ long x[] = new long[v.length];
+ for (i = 0; i < v.length; ++i) {
+ x[i] = 0;
+ for(int j = 0; j < s.length; j++)
+ x[i] = multModM(A[i][j], s[j], x[i], m);
+ }
+ for (i = 0; i < v.length; ++i)
+ v[i] = x[i];
+
+ }
+
+
+ /**
+ * Exactly like {@link #matMatModM(double[][], double[][],
+ * double[][], double) matMatModM} using <TT>double</TT>, but with <TT>long</TT> instead
+ * of <TT>double</TT>.
+ *
+ * @param A the first factor of the multiplication
+ *
+ * @param B the second factor of the multiplication
+ *
+ * @param C the result of the multiplication
+ *
+ * @param m the modulus
+ *
+ *
+ */
+ public static void matMatModM (long A[][], long B[][], long C[][], long m) {
+ int i, j;
+ int r = C.length; //# of rows of C
+ int c = C[0].length; //# of columns of C
+ long V[] = new long[r];
+ long W[][] = new long[r][c];
+ for (i = 0; i < c; ++i) {
+ for (j = 0; j < r; ++j)
+ V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < r; ++j)
+ W[j][i] = V[j];
+ }
+ for (i = 0; i < r; ++i) {
+ for (j = 0; j < c; ++j)
+ C[i][j] = W[i][j];
+ }
+ }
+
+
+ /**
+ * Exactly like {@link #matTwoPowModM(double[][], double[][],
+ * double, int) matTwoPowModM} using <TT>double</TT>, but with <TT>long</TT> instead of
+ * <TT>double</TT>.
+ *
+ * @param A the matrix to raise to a power
+ *
+ * @param B the result of exponentiation
+ *
+ * @param m the modulus
+ *
+ * @param e the <SPAN CLASS="MATH">log<SUB>2</SUB></SPAN> of the exponent
+ *
+ *
+ */
+ public static void matTwoPowModM (long A[][], long B[][], long m, int e) {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < A.length; i++) {
+ for (j = 0; j < A.length; ++j) //A is square
+ B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++)
+ matMatModM (B, B, B, m);
+ }
+
+
+ /**
+ * Exactly like {@link #matPowModM(double[][], double[][],
+ * double, int) matPowModM} using <TT>double</TT>, but with <TT>long</TT> instead
+ * of <TT>double</TT>.
+ *
+ * @param A the matrix to raise to a power
+ *
+ * @param B the result of the exponentiation
+ *
+ * @param m the modulus
+ *
+ * @param c the exponent
+ *
+ *
+ */
+ public static void matPowModM (long A[][], long B[][], long m, int c) {
+ int i, j;
+ int n = c;
+ int s = A.length; //we suppose that A is square (it must be)
+ long W[][] = new long[s][s];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < s; i++) {
+ for (j = 0; j < s; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0;
+ }
+ }
+ for (j = 0; j < s; ++j)
+ B[j][j] = 1;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1)
+ matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/util/ArithmeticMod.tex b/source/umontreal/iro/lecuyer/util/ArithmeticMod.tex
new file mode 100644
index 0000000..11e69a5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ArithmeticMod.tex
@@ -0,0 +1,554 @@
+\defmodule {ArithmeticMod}
+
+This class provides facilities to compute multiplications of scalars, of
+vectors and of matrices modulo m. All algorithms are present in three
+different versions. These allow operations on \texttt{double}, \texttt{int} and
+\texttt{long}. The \texttt{int} and \texttt{long} versions work exactly like the
+\texttt{double} ones.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: ArithmeticMod
+ * Description: multiplications of scalars, vectors and matrices modulo m
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public class ArithmeticMod \begin{hide} {
+
+ //private constants
+ private static final double two17 = 131072.0;
+ private static final double two53 = 9007199254740992.0;
+
+ //prevent the creation of the object
+ private ArithmeticMod() {};
+
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods using \texttt{double}}
+
+\begin{code}
+
+ public static double multModM (double a, double s, double c, double m) \begin{hide} {
+ int a1;
+ double v = a * s + c;
+ if (v >= two53 || v <= -two53 ) {
+ a1 = (int)(a / two17);
+ a -= a1 * two17;
+ v = a1 * s;
+ a1 = (int)(v / m);
+ v -= a1 * m;
+ v = v * two17 + a * s + c;
+ }
+ a1 = (int)(v / m);
+ if ((v -= a1 * m) < 0.0)
+ return v += m;
+ else
+ return v;
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes $(a \times s + c) \bmod m$. Where \texttt{m} must
+ be smaller than $2^{35}$. Works also if \texttt{s} or \texttt{c} are negative.
+ The result is always positive (and thus always between 0 and \texttt{m} - 1).
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the first factor of the multiplication}
+ \param{s}{the second factor of the multiplication}
+ \param{c}{the second term of the addition}
+ \param{m}{the modulus}
+ \return{the result of the multiplication and the addition modulo \texttt{m}}
+\end{htmlonly}
+\begin{code}
+
+ public static void matVecModM (double A[][], double s[], double v[],
+ double m) \begin{hide} {
+ int i;
+ double x[] = new double[v.length];
+ for (i = 0; i < v.length; ++i) {
+ x[i] = 0.0;
+ for(int j = 0; j < s.length; j++)
+ x[i] = multModM (A[i][j], s[j], x[i], m);
+ }
+ for (i = 0; i < v.length; ++i)
+ v[i] = x[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes the result of $\mathtt{A} \times \mathtt{\mathbf{s}}
+ \bmod m$ and puts the
+ result in \texttt{v}. Where \texttt{s} and \texttt{v} are both column vectors. This
+ method works even if \texttt{s} = \texttt{v}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the multiplication matrix}
+ \param{s}{the multiplied vector}
+ \param{v}{the result of the multiplication}
+ \param{m}{the modulus}
+\end{htmlonly}
+\begin{code}
+
+ public static void matMatModM (double A[][], double B[][], double C[][],
+ double m) \begin{hide} {
+ int i, j;
+ int r = C.length; //# of rows of C
+ int c = C[0].length; //# of columns of C
+ double V[] = new double[r];
+ double W[][] = new double[r][c];
+ for (i = 0; i < c; ++i) {
+ for (j = 0; j < r; ++j)
+ V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < r; ++j)
+ W[j][i] = V[j];
+ }
+ for (i = 0; i < r; ++i) {
+ for (j = 0; j < c; ++j)
+ C[i][j] = W[i][j];
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes $\mathtt{A} \times \mathtt{B} \bmod m$
+ and puts the result in
+ \texttt{C}. Works even if \texttt{A} = \texttt{C}, \texttt{B} = \texttt{C} or
+ \texttt{A} = \texttt{B} = \texttt{C}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the first factor of the multiplication}
+ \param{B}{the second factor of the multiplication}
+ \param{C}{the result of the multiplication}
+ \param{m}{the modulus}
+\end{htmlonly}
+\begin{code}
+
+ public static void matTwoPowModM (double A[][], double B[][], double m,
+ int e) \begin{hide} {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < A.length; i++) {
+ for (j = 0; j < A.length; ++j) //A is square
+ B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++)
+ matMatModM (B, B, B, m);
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes $\mathtt{A}^{2^{\mathtt{e}}} \bmod m$ and
+ puts the result in \texttt{B}.
+ Works even if \texttt{A} = \texttt{B}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the matrix to raise to a power}
+ \param{B}{the result of exponentiation}
+ \param{m}{the modulus}
+ \param{e}{the $\log_{2}$ of the exponent}
+\end{htmlonly}
+\begin{code}
+
+ public static void matPowModM (double A[][], double B[][], double m,
+ int c) \begin{hide} {
+ int i, j;
+ int n = c;
+ int s = A.length; //we suppose that A is square
+ double W[][] = new double[s][s];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < s; i++) {
+ for (j = 0; j < s; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0.0;
+ }
+ }
+ for (j = 0; j < s; ++j)
+ B[j][j] = 1.0;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1)
+ matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes $\mathtt{A}^{c} \bmod m$
+ and puts the result in \texttt{B}.
+ Works even if \texttt{A} = \texttt{B}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the matrix to raise to a power}
+ \param{B}{the result of the exponentiation}
+ \param{m}{the modulus}
+ \param{c}{the exponent}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsubsection* {Methods using \texttt{int}}
+\begin{code}
+
+ public static int multModM (int a, int s, int c, int m) \begin{hide} {
+ int r = (int) (((long)a * s + c) % m);
+ return r < 0 ? r + m : r;
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes $(a \times s + c)
+ \bmod m$. Works also if \texttt{s}
+ or \texttt{c} are negative.
+ The result is always positive (and thus always between 0 and \texttt{m} - 1).
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the first factor of the multiplication}
+ \param{s}{the second factor of the multiplication}
+ \param{c}{the second term of the addition}
+ \param{m}{the modulus}
+ \return{the result of the multiplication and the addition modulo \texttt{m}}
+\end{htmlonly}
+\begin{code}
+
+ public static void matVecModM (int A[][], int s[], int v[], int m) \begin{hide} {
+ int i;
+ int x[] = new int[v.length];
+ for (i = 0; i < v.length; ++i) {
+ x[i] = 0;
+ for(int j = 0; j < s.length; j++)
+ x[i] = multModM(A[i][j], s[j], x[i], m);
+ }
+ for (i = 0; i < v.length; ++i)
+ v[i] = x[i];
+
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matVecModM}{double[][], double[],
+ double[], double} using \texttt{double}, but with \texttt{int} instead
+ of \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the multiplication matrix}
+ \param{s}{the multiplied vector}
+ \param{v}{the result of the multiplication}
+ \param{m}{the modulus}
+\end{htmlonly}
+\begin{code}
+
+ public static void matMatModM (int A[][], int B[][], int C[][], int m) \begin{hide} {
+ int i, j;
+ int r = C.length; //# of rows of C
+ int c = C[0].length; //# of columns of C
+ int V[] = new int[r];
+ int W[][] = new int[r][c];
+ for (i = 0; i < c; ++i) {
+ for (j = 0; j < r; ++j)
+ V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < r; ++j)
+ W[j][i] = V[j];
+ }
+ for (i = 0; i < r; ++i) {
+ for (j = 0; j < c; ++j)
+ C[i][j] = W[i][j];
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matMatModM}{double[][], double[][],
+ double[][], double} using \texttt{double}, but with \texttt{int} instead
+ of \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the first factor of the multiplication}
+ \param{B}{the second factor of the multiplication}
+ \param{C}{the result of the multiplication}
+ \param{m}{the modulus}
+\end{htmlonly}
+\begin{code}
+
+ public static void matTwoPowModM (int A[][], int B[][], int m, int e) \begin{hide} {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < A.length; i++) {
+ for (j = 0; j < A.length; ++j) //A is square
+ B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++)
+ matMatModM (B, B, B, m);
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matTwoPowModM}{double[][], double[][],
+ double, int} using \texttt{double}, but with \texttt{int} instead of
+ \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the matrix to raise to a power}
+ \param{B}{the result of exponentiation}
+ \param{m}{the modulus}
+ \param{e}{the $\log_{2}$ of the exponent}
+\end{htmlonly}
+\begin{code}
+
+ public static void matPowModM (int A[][], int B[][], int m, int c) \begin{hide} {
+ int i, j;
+ int n = c;
+ int s = A.length; //we suppose that A is square (it must be)
+ int W[][] = new int[s][s];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < s; i++) {
+ for (j = 0; j < s; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0;
+ }
+ }
+ for (j = 0; j < s; ++j)
+ B[j][j] = 1;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1)
+ matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matPowModM}{double[][], double[][],
+ double, int} using \texttt{double}, but with \texttt{int} instead
+ of \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the matrix to raise to a power}
+ \param{B}{the result of the exponentiation}
+ \param{m}{the modulus}
+ \param{c}{the exponent}
+\end{htmlonly}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods using \texttt{long}}
+\begin{code}
+
+ public static long multModM (long a, long s, long c, long m) \begin{hide} {
+
+ /* Suppose que 0 < a < m et 0 < s < m. Retourne (a*s + c) % m.
+ * Cette procedure est tiree de :
+ * L'Ecuyer, P. et Cote, S., A Random Number Package with
+ * Splitting Facilities, ACM TOMS, 1991.
+ * On coupe les entiers en blocs de d bits. H doit etre egal a 2^d. */
+
+ final long H = 2147483648L; // = 2^d used in MultMod
+ long a0, a1, q, qh, rh, k, p;
+ if (a < H) {
+ a0 = a;
+ p = 0;
+ } else {
+ a1 = a / H;
+ a0 = a - H * a1;
+ qh = m / H;
+ rh = m - H * qh;
+ if (a1 >= H) {
+ a1 = a1 - H;
+ k = s / qh;
+ p = H * (s - k * qh) - k * rh;
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ } else /* p = (A2 * s * h) % m. */
+ p = 0;
+ if (a1 != 0) {
+ q = m / a1;
+ k = s / q;
+ p -= k * (m - a1 * q);
+ if (p > 0)
+ p -= m;
+ p += a1 * (s - k * q);
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ } /* p = ((A2 * h + a1) * s) % m. */
+ k = p / qh;
+ p = H * (p - k * qh) - k * rh;
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ } /* p = ((A2 * h + a1) * h * s) % m */
+ if (a0 != 0) {
+ q = m / a0;
+ k = s / q;
+ p -= k * (m - a0 * q);
+ if (p > 0)
+ p -= m;
+ p += a0 * (s - k * q);
+ if (p < 0)
+ p = (p + 1) % m + m - 1;
+ }
+ p = (p - m) + c;
+ if (p < 0)
+ p += m;
+ return p;
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes $(a \times s + c) \bmod m$. Works also if \texttt{s}
+ or \texttt{c} are negative.
+ The result is always positive (and thus always between 0 and \texttt{m} - 1).
+\end{tabb}
+\begin{htmlonly}
+ \param{a}{the first factor of the multiplication}
+ \param{s}{the second factor of the multiplication}
+ \param{c}{the second term of the addition}
+ \param{m}{the modulus}
+ \return{the result of the multiplication and the addition modulo \texttt{m}}
+\end{htmlonly}
+\begin{code}
+
+ public static void matVecModM (long A[][], long s[], long v[], long m) \begin{hide} {
+ int i;
+ long x[] = new long[v.length];
+ for (i = 0; i < v.length; ++i) {
+ x[i] = 0;
+ for(int j = 0; j < s.length; j++)
+ x[i] = multModM(A[i][j], s[j], x[i], m);
+ }
+ for (i = 0; i < v.length; ++i)
+ v[i] = x[i];
+
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matVecModM}{double[][], double[],
+ double[], double} using \texttt{double}, but with \texttt{long} instead
+ of \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the multiplication matrix}
+ \param{s}{the multiplied vector}
+ \param{v}{the result of the multiplication}
+ \param{m}{the modulus}
+\end{htmlonly}
+\begin{code}
+
+ public static void matMatModM (long A[][], long B[][], long C[][], long m) \begin{hide} {
+ int i, j;
+ int r = C.length; //# of rows of C
+ int c = C[0].length; //# of columns of C
+ long V[] = new long[r];
+ long W[][] = new long[r][c];
+ for (i = 0; i < c; ++i) {
+ for (j = 0; j < r; ++j)
+ V[j] = B[j][i];
+ matVecModM (A, V, V, m);
+ for (j = 0; j < r; ++j)
+ W[j][i] = V[j];
+ }
+ for (i = 0; i < r; ++i) {
+ for (j = 0; j < c; ++j)
+ C[i][j] = W[i][j];
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matMatModM}{double[][], double[][],
+ double[][], double} using \texttt{double}, but with \texttt{long} instead
+ of \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the first factor of the multiplication}
+ \param{B}{the second factor of the multiplication}
+ \param{C}{the result of the multiplication}
+ \param{m}{the modulus}
+\end{htmlonly}
+\begin{code}
+
+ public static void matTwoPowModM (long A[][], long B[][], long m, int e) \begin{hide} {
+ int i, j;
+ /* initialize: B = A */
+ if (A != B) {
+ for (i = 0; i < A.length; i++) {
+ for (j = 0; j < A.length; ++j) //A is square
+ B[i][j] = A[i][j];
+ }
+ }
+ /* Compute B = A^{2^e} */
+ for (i = 0; i < e; i++)
+ matMatModM (B, B, B, m);
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matTwoPowModM}{double[][], double[][],
+ double, int} using \texttt{double}, but with \texttt{long} instead of
+ \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the matrix to raise to a power}
+ \param{B}{the result of exponentiation}
+ \param{m}{the modulus}
+ \param{e}{the $\log_{2}$ of the exponent}
+\end{htmlonly}
+\begin{code}
+
+ public static void matPowModM (long A[][], long B[][], long m, int c) \begin{hide} {
+ int i, j;
+ int n = c;
+ int s = A.length; //we suppose that A is square (it must be)
+ long W[][] = new long[s][s];
+
+ /* initialize: W = A; B = I */
+ for (i = 0; i < s; i++) {
+ for (j = 0; j < s; ++j) {
+ W[i][j] = A[i][j];
+ B[i][j] = 0;
+ }
+ }
+ for (j = 0; j < s; ++j)
+ B[j][j] = 1;
+
+ /* Compute B = A^c mod m using the binary decomp. of c */
+ while (n > 0) {
+ if ((n % 2)==1)
+ matMatModM (W, B, B, m);
+ matMatModM (W, W, W, m);
+ n /= 2;
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Exactly like \method{matPowModM}{double[][], double[][],
+ double, int} using \texttt{double}, but with \texttt{long} instead
+ of \texttt{double}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the matrix to raise to a power}
+ \param{B}{the result of the exponentiation}
+ \param{m}{the modulus}
+ \param{c}{the exponent}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
+
+
diff --git a/source/umontreal/iro/lecuyer/util/BitMatrix.java b/source/umontreal/iro/lecuyer/util/BitMatrix.java
new file mode 100644
index 0000000..72756b3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/BitMatrix.java
@@ -0,0 +1,595 @@
+
+
+/*
+ * Class: BitMatrix
+ * Description: implements matrices of bits and their operations
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.io.Serializable;
+
+
+
+/**
+ * This class implements matrices of bits of arbitrary dimensions. Basic
+ * facilities for bits operations, multiplications and exponentiations are
+ * provided.
+ *
+ */
+public class BitMatrix implements Serializable, Cloneable {
+
+ static final long serialVersionUID = 2472769969919959608L;
+
+ private BitVector[] rows; //rows vectors
+
+ private int r, c; //number of rows / columns
+
+
+
+ /**
+ * Creates a new <TT>BitMatrix</TT> with <TT>r</TT> rows and
+ * <TT>c</TT> columns filled with 0's.
+ *
+ * @param r the number of rows
+ *
+ * @param c the number of columns
+ *
+ *
+ */
+ public BitMatrix (int r, int c) {
+ rows = new BitVector[r];
+ for(int i = 0; i < r; i++)
+ rows[i] = new BitVector(c);
+ this.r = r;
+ this.c = c;
+ }
+
+
+ /**
+ * Creates a new <TT>BitMatrix</TT> using the data in <TT>rows</TT>.
+ * Each of the {@link BitVector} will be one of the rows of the
+ * <TT>BitMatrix</TT>.
+ *
+ * @param rows the rows of the new <TT>BitMatrix</TT>
+ *
+ *
+ */
+ public BitMatrix (BitVector[] rows) {
+ this.rows = new BitVector[rows.length];
+ for(int i = 0; i < rows.length; i++)
+ this.rows[i] = new BitVector(rows[i]);
+ this.r = rows.length;
+ this.c = r > 0 ? rows[0].size() : 0;
+ }
+
+
+ /**
+ * Creates a new <TT>BitMatrix</TT> with <TT>r</TT> rows and <TT>c</TT>
+ * columns using the data in <TT>data</TT>. Note that the orders of the
+ * bits for the rows are using the same order than for the {@link BitVector}.
+ * This does mean that the first bit is the lowest order bit of the last
+ * <TT>int</TT> in the row and the last bit is the highest order bit of the
+ * first <TT>int</TT> int the row.
+ *
+ * @param data the data of the new <TT>BitMatrix</TT>
+ *
+ * @param r the number of rows
+ *
+ * @param c the number of columns
+ *
+ *
+ */
+ public BitMatrix (int[][] data, int r, int c) {
+ this.rows = new BitVector[r];
+ for(int i = 0; i < r; i++)
+ this.rows[i] = new BitVector(data[i],c);
+ this.r = r;
+ this.c = c;
+ }
+
+
+ /**
+ * Copy constructor.
+ *
+ * @param that the <TT>BitMatrix</TT> to copy
+ *
+ */
+ public BitMatrix (BitMatrix that) {
+ this.r = that.r;
+ this.c = that.c;
+ this.rows = new BitVector[this.r];
+ for(int i = 0; i < this.r; i++)
+ this.rows[i] = new BitVector(that.rows[i]);
+ }
+
+ /**
+ * Creates a copy of the <TT>BitMatrix</TT>.
+ *
+ * @return a deep copy of the <TT>BitMatrix</TT>
+ *
+ */
+ public Object clone() {
+ try{
+ BitMatrix c = (BitMatrix)super.clone();
+ c.rows = (BitVector[])rows.clone();
+ for(int i = 0; i < rows.length; i++)
+ c.rows[i] = (BitVector)rows[i].clone();
+ return c;
+ } catch(CloneNotSupportedException e) {
+ IllegalStateException ne = new IllegalStateException();
+ ne.initCause(e);
+ throw ne;
+ }
+ }
+
+
+ /**
+ * Verifies that <TT>this</TT> and <TT>that</TT> are strictly
+ * identical. They must both have the same dimensions and data.
+ *
+ * @param that the <TT>BitMatrix</TT> to compare
+ *
+ * @return if the two <TT>BitMatrix</TT> are identical
+ *
+ */
+ public boolean equals (BitMatrix that) {
+ if(this.r != that.r || this.c != that.c)
+ return false;
+ for(int i = 0; i < r; i++)
+ if(!this.rows[i].equals(that.rows[i]))
+ return false;
+ return true;
+ }
+
+
+ /**
+ * Creates a {@link String} containing all the data of
+ * the <TT>BitMatrix</TT>. The result is displayed in a matrix form, with
+ * each row being put on a different line. Note that the bit at (0,0) is
+ * at the upper left of the matrix, while the bit at (0) in a
+ * {@link BitVector} is the least significant bit.
+ *
+ * @return the content of the <TT>BitMatrix</TT>
+ *
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ // on affiche les bits sur les lignes dans l'order inverse de l'ordre
+ // affiche pour le BitVector pour que le bit a (0,0) soit en haut
+ // a gauche
+
+ sb.append("{ ");
+ for(int i = 0; i < rows.length - 1; i++) {
+ for(int j = 0; j < rows[i].size(); j++)
+ sb.append(rows[i].getBool(j) ? "1" : "0");
+ sb.append(PrintfFormat.NEWLINE + " ");
+ }
+ if(r > 0) {
+ for(int j = 0; j < c; j++)
+ sb.append(rows[r-1].getBool(j) ? "1" : "0");
+ sb.append(" }");
+ } else
+ sb.append(" }");
+
+ return sb.toString();
+ }
+
+
+ /**
+ * Creates a {@link String} containing all the data of
+ * the <TT>BitMatrix</TT>. The data is displayed in the same format as are the
+ * <TT>int[][]</TT> in <TT>Java</TT> code. This allows the user to print the
+ * representation of a <TT>BitMatrix</TT> to be put, directly in the source
+ * code, in the constructor <TT>BitMatrix(int[][], int, int)</TT>. The output
+ * is not designed to be human-readable.
+ *
+ * @return the content of the <TT>BitMatrix</TT>
+ *
+ */
+ public String printData() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("{ ");
+ for(int i = 0; i < r; i++)
+ {
+ sb.append("{");
+ for(int j = 0; j < (c + 31) / 32; j++)
+ {
+ sb.append(rows[i].getInt(j));
+ if(j != (c - 1) / 32)
+ sb.append(", ");
+ }
+ sb.append("}");
+ if(i != r - 1)
+ sb.append("," + PrintfFormat.NEWLINE + " ");
+ }
+ sb.append(" }");
+
+ return sb.toString();
+ }
+
+
+ /**
+ * Returns the number of rows of the <TT>BitMatrix</TT>.
+ *
+ * @return the number of rows
+ *
+ */
+ public int numRows() {
+ return r;
+ }
+
+
+ /**
+ * Returns the number of columns of the <TT>BitMatrix</TT>.
+ *
+ * @return the number of columns
+ *
+ */
+ public int numColumns() {
+ return c;
+ }
+
+
+ /**
+ * Returns the value of the bit in the specified row and column.
+ * If the value is 1, return <TT>true</TT>. If it is 0, return <TT>false</TT>.
+ *
+ * @param row the row of the selected bit
+ *
+ * @param column the column of the selected bit
+ *
+ * @return the value of the bit as a boolean
+ * @exception IndexOutOfBoundsException if the selected bit would
+ * be outside the <TT>BitMatrix</TT>
+ *
+ *
+ */
+ public boolean getBool (int row, int column) {
+ if(row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ return rows[row].getBool(column);
+ }
+
+
+ /**
+ * Changes the value of the bit in the specified row and column.
+ * If <TT>value</TT> is <TT>true</TT>, changes it to 1. If <TT>value</TT> is
+ * <TT>false</TT> changes it to 0.
+ *
+ * @param row the row of the selected bit
+ *
+ * @param column the column of the selected bit
+ *
+ * @param value the new value of the bit as a boolean
+ *
+ * @exception IndexOutOfBoundsException if the selected bit would
+ * be outside the <TT>BitMatrix</TT>
+ *
+ *
+ */
+ public void setBool (int row, int column, boolean value) {
+ if(row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ rows[row].setBool(column,value);
+ }
+
+
+ /**
+ * Returns the transposed matrix. The rows and columns are
+ * interchanged.
+ *
+ * @return the transposed matrix
+ *
+ */
+ public BitMatrix transpose() {
+ BitMatrix result = new BitMatrix(c,r);
+
+ for(int i = 0; i < r; i++)
+ for(int j = 0; j < c; j++)
+ result.rows[j].setBool(i, rows[i].getBool(j));
+
+ return result;
+ }
+
+
+ /**
+ * Returns the <TT>BitMatrix</TT> resulting from the application of
+ * the <TT>not</TT> operator on the original <TT>BitMatrix</TT>. The effect is to
+ * swap all the bits of the <TT>BitMatrix</TT>, turning all 0 into 1 and all 1
+ * into 0.
+ *
+ * @return the result of the <TT>not</TT> operator
+ *
+ */
+ public BitMatrix not() {
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfNot();
+ return result;
+ }
+
+
+ /**
+ * Returns the <TT>BitMatrix</TT> resulting from the application of
+ * the <TT>and</TT> operator on the original <TT>BitMatrix</TT> and <TT>that</TT>.
+ * Only bits which were at 1 in both <TT>BitMatrix</TT> are set at 1 in the
+ * result. All others are set to 0.
+ *
+ * @param that the second operand of the <TT>and</TT> operator
+ *
+ * @return the result of the <TT>and</TT> operator
+ * @exception IncompatibleDimensionException if the two <TT>BitMatrix</TT> are
+ * not of the same dimensions
+ *
+ *
+ */
+ public BitMatrix and (BitMatrix that) {
+ if(this.c != that.c || this.r != that.r)
+ throw new IncompatibleDimensionException
+ ("Both matrices must have the same dimension. " +
+ "this is a " + this.r + "x" + this.c + " matrix " +
+ "while that is a " + that.r + "x" + that.c + " matrix.");
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfAnd(that.rows[i]);
+ return result;
+ }
+
+
+ /**
+ * Returns the <TT>BitMatrix</TT> resulting from the application of
+ * the <TT>or</TT> operator on the original <TT>BitMatrix</TT> and <TT>that</TT>.
+ * Only bits which were at 0 in both <TT>BitMatrix</TT> are set at 0 in the
+ * result. All others are set to 1.
+ *
+ * @param that the second operand of the <TT>or</TT> operator
+ *
+ * @return the result of the <TT>or</TT> operator
+ * @exception IncompatibleDimensionException if the two <TT>BitMatrix</TT> are
+ * not of the same dimensions
+ *
+ *
+ */
+ public BitMatrix or (BitMatrix that) {
+ if(this.c != that.c || this.r != that.r)
+ throw new IncompatibleDimensionException
+ ("Both matrices must have the same dimension. " +
+ "this is a " + this.r + "x" + this.c + " matrix " +
+ "while that is a " + that.r + "x" + that.c + " matrix.");
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfOr(that.rows[i]);
+ return result;
+ }
+
+
+ /**
+ * Returns the <TT>BitMatrix</TT> resulting from the application of
+ * the <TT>xor</TT> operator on the original <TT>BitMatrix</TT> and <TT>that</TT>.
+ * Only bits which were at 1 in only one of the two <TT>BitMatrix</TT> are set
+ * at 1 in the result. All others are set to 0.
+ *
+ * @param that the second operand of the <TT>xor</TT> operator
+ *
+ * @return the result of the <TT>xor</TT> operator
+ * @exception IncompatibleDimensionException if the two <TT>BitMatrix</TT> are
+ * not of the same dimensions
+ *
+ *
+ */
+ public BitMatrix xor (BitMatrix that) {
+ if(this.c != that.c || this.r != that.r)
+ throw new IncompatibleDimensionException
+ ("Both matrices must have the same dimension. " +
+ "this is a " + this.r + "x" + this.c + " matrix " +
+ "while that is a " + that.r + "x" + that.c + " matrix.");
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfXor(that.rows[i]);
+ return result;
+ }
+
+
+ /**
+ * Multiplies the column {@link BitVector} by a <TT>BitMatrix</TT>
+ * and returns the result. The result is
+ * <SPAN CLASS="MATH"><I>A</I>×<I>v</I></SPAN>, where <SPAN CLASS="MATH"><I>A</I></SPAN> is the
+ * <TT>BitMatrix</TT>, and <SPAN CLASS="MATH"><I>v</I></SPAN> is the {@link BitVector}.
+ *
+ * @param vect the vector to multiply
+ *
+ * @return the result of the multiplication
+ *
+ */
+ public BitVector multiply (BitVector vect) {
+ BitVector res = new BitVector(r);
+
+ for(int i = 0; i < r; i++)
+ res.setBool(i, rows[i].scalarProduct(vect));
+
+ return res;
+ }
+
+
+ /**
+ * Multiplies <TT>vect</TT>, seen as a column {@link BitVector}, by
+ * a <TT>BitMatrix</TT>. (See {@link BitVector} to see the conversion between
+ * <TT>int</TT> and {@link BitVector}.) The result is
+ * <SPAN CLASS="MATH"><I>A</I>×<I>v</I></SPAN>,
+ * where <SPAN CLASS="MATH"><I>A</I></SPAN> is the <TT>BitMatrix</TT>, and <SPAN CLASS="MATH"><I>v</I></SPAN> is the {@link BitVector}.
+ *
+ * @param vect the vector to multiply
+ *
+ * @return the result of the multiplication, as an <TT>int</TT>
+ *
+ */
+ public int multiply (int vect) {
+ BitVector temp = new BitVector(new int[]{vect});
+
+ return multiply(temp).getInt(0);
+ }
+
+
+ /**
+ * Multiplies two <TT>BitMatrix</TT>'s together. The result is
+ *
+ * <SPAN CLASS="MATH"><I>A</I>×<I>B</I></SPAN>, where <SPAN CLASS="MATH"><I>A</I></SPAN> is the <TT>this BitMatrix</TT> and <SPAN CLASS="MATH"><I>B</I></SPAN> is the
+ * <TT>that BitMatrix</TT>.
+ *
+ * @param that the other <TT>BitMatrix</TT> to multiply
+ *
+ * @return the result of the multiplication
+ * @exception IncompatibleDimensionException if the number of columns
+ * of the first <TT>BitMatrix</TT> is not equal to the number of rows of
+ * the second <TT>BitMatrix</TT>
+ *
+ *
+ */
+ public BitMatrix multiply (BitMatrix that) {
+ if(this.c != that.r)
+ throw new IncompatibleDimensionException
+ ("The number of columns of this (" + this.c +
+ ") must be equal to the number of rows of that (" +
+ that.r + ").");
+
+ /*
+ BitVector[] res = new BitVector[this.r];
+
+ for(int i = 0; i < this.r; i++) {
+ res[i] = new BitVector(that.c);
+
+ for(int j = 0; j < that.c; j++) {
+ temp = false;
+ for(int k = 0; k < this.c; k++)
+ if(this.rows[i].getBool(k) &&
+ that.rows[k].getBool(j))
+ temp = !temp;
+ res[i].setBool(j,temp);
+ }
+ }
+
+ return BitMatrix(res);
+ */
+
+ // methode plus efficace
+
+ BitMatrix res = new BitMatrix(this.r, that.c);
+
+ for(int i = 0; i < res.r; i++)
+ for(int j = 0; j < res.c; j++)
+ if(this.rows[i].getBool(j))
+ res.rows[i].selfXor(that.rows[j]);
+
+ return res;
+ }
+
+
+ /**
+ * Raises the <TT>BitMatrix</TT> to the power <TT>p</TT>.
+ *
+ * @param p the power up to which to raise the <TT>BitMatrix</TT>
+ *
+ * @return the power of the <TT>BitMatrix</TT>
+ * @exception IncompatibleDimensionException if the <TT>BitMatrix</TT>
+ * is not square
+ *
+ * @exception IllegalArgumentException if <TT>p</TT> is negative
+ *
+ *
+ */
+ public BitMatrix power (long p) {
+ if(c != r)
+ throw new IncompatibleDimensionException
+ ("Only square matrices can be raised to a power.");
+
+ if(p < 0)
+ throw new IllegalArgumentException
+ ("Only non-negative powers are accepted.");
+
+ if(p == 0)
+ {
+ //the identity matrix
+ BitMatrix bm = new BitMatrix(r,r);
+ for(int i = 0; i < r; i++)
+ bm.setBool(i,i,true);
+ return bm;
+ }
+
+ if(p == 1)
+ return this;
+
+ if(p % 2 == 0) {
+ BitMatrix temp = this.power(p/2);
+ return temp.multiply(temp);
+ } else
+ return this.multiply(this.power(p-1));
+ }
+
+
+ /**
+ * Raises the <TT>BitMatrix</TT> to power
+ * <SPAN CLASS="MATH">2<SUP><TT>e</TT></SUP></SPAN>.
+ *
+ * @param e the exponent of the power up to which to raise the <TT>BitMatrix</TT>
+ *
+ * @return the power of the <TT>BitMatrix</TT>
+ * @exception IncompatibleDimensionException if the <TT>BitMatrix</TT> is
+ * not square
+ *
+ */
+ public BitMatrix power2e (int e) {
+ if(c != r)
+ throw new IncompatibleDimensionException
+ ("Only square matrices can be raised to a power.");
+ BitMatrix result = this;
+
+ for(int i = 0; i < e; i++)
+ result = result.multiply(result);
+
+ return result;
+ }
+
+ /**
+ * Runtime exception raised when the dimensions of the
+ * <TT>BitMatrix</TT> are not appropriate for the operation.
+ *
+ */
+ public class IncompatibleDimensionException extends RuntimeException
+
+
+ {
+ private IncompatibleDimensionException() {
+ super();
+ }
+
+ private IncompatibleDimensionException (String message) {
+ super(message);
+ }
+ }
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/util/BitMatrix.tex b/source/umontreal/iro/lecuyer/util/BitMatrix.tex
new file mode 100644
index 0000000..a49a369
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/BitMatrix.tex
@@ -0,0 +1,577 @@
+\defmodule {BitMatrix}
+
+This class implements matrices of bits of arbitrary dimensions. Basic
+facilities for bits operations, multiplications and exponentiations are
+provided.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: BitMatrix
+ * Description: implements matrices of bits and their operations
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util; \begin{hide}
+
+import java.io.Serializable;
+\end{hide}
+
+
+public class BitMatrix implements Serializable, Cloneable \begin{hide} {
+
+ static final long serialVersionUID = 2472769969919959608L;
+
+ private BitVector[] rows; //rows vectors
+
+ private int r, c; //number of rows / columns
+
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public BitMatrix (int r, int c) \begin{hide} {
+ rows = new BitVector[r];
+ for(int i = 0; i < r; i++)
+ rows[i] = new BitVector(c);
+ this.r = r;
+ this.c = c;
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{BitMatrix} with \texttt{r} rows and
+ \texttt{c} columns filled with 0's.
+\end{tabb}
+\begin{htmlonly}
+ \param{r}{the number of rows}
+ \param{c}{the number of columns}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix (BitVector[] rows) \begin{hide} {
+ this.rows = new BitVector[rows.length];
+ for(int i = 0; i < rows.length; i++)
+ this.rows[i] = new BitVector(rows[i]);
+ this.r = rows.length;
+ this.c = r > 0 ? rows[0].size() : 0;
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{BitMatrix} using the data in \texttt{rows}.
+ Each of the \class{BitVector} will be one of the rows of the
+ \texttt{BitMatrix}.
+\end{tabb}
+\begin{htmlonly}
+ \param{rows}{the rows of the new \texttt{BitMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix (int[][] data, int r, int c) \begin{hide} {
+ this.rows = new BitVector[r];
+ for(int i = 0; i < r; i++)
+ this.rows[i] = new BitVector(data[i],c);
+ this.r = r;
+ this.c = c;
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{BitMatrix} with \texttt{r} rows and \texttt{c}
+ columns using the data in \texttt{data}. Note that the orders of the
+ bits for the rows are using the same order than for the \class{BitVector}.
+ This does mean that the first bit is the lowest order bit of the last
+ \texttt{int} in the row and the last bit is the highest order bit of the
+ first \texttt{int} int the row.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{the data of the new \texttt{BitMatrix}}
+ \param{r}{the number of rows}
+ \param{c}{the number of columns}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix (BitMatrix that) \begin{hide} {
+ this.r = that.r;
+ this.c = that.c;
+ this.rows = new BitVector[this.r];
+ for(int i = 0; i < this.r; i++)
+ this.rows[i] = new BitVector(that.rows[i]);
+ } \end{hide}
+\end{code}
+\begin{tabb} Copy constructor.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the \texttt{BitMatrix} to copy}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public Object clone() \begin{hide} {
+ try{
+ BitMatrix c = (BitMatrix)super.clone();
+ c.rows = (BitVector[])rows.clone();
+ for(int i = 0; i < rows.length; i++)
+ c.rows[i] = (BitVector)rows[i].clone();
+ return c;
+ } catch(CloneNotSupportedException e) {
+ IllegalStateException ne = new IllegalStateException();
+ ne.initCause(e);
+ throw ne;
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a copy of the \texttt{BitMatrix}.
+\end{tabb}
+\begin{htmlonly}
+ \return{a deep copy of the \texttt{BitMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public boolean equals (BitMatrix that) \begin{hide} {
+ if(this.r != that.r || this.c != that.c)
+ return false;
+ for(int i = 0; i < r; i++)
+ if(!this.rows[i].equals(that.rows[i]))
+ return false;
+ return true;
+ } \end{hide}
+\end{code}
+\begin{tabb} Verifies that \texttt{this} and \texttt{that} are strictly
+ identical. They must both have the same dimensions and data.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the \texttt{BitMatrix} to compare}
+ \return{if the two \texttt{BitMatrix} are identical}
+\end{htmlonly}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ StringBuffer sb = new StringBuffer();
+
+ // on affiche les bits sur les lignes dans l'order inverse de l'ordre
+ // affiche pour le BitVector pour que le bit a (0,0) soit en haut
+ // a gauche
+
+ sb.append("{ ");
+ for(int i = 0; i < rows.length - 1; i++) {
+ for(int j = 0; j < rows[i].size(); j++)
+ sb.append(rows[i].getBool(j) ? "1" : "0");
+ sb.append(PrintfFormat.NEWLINE + " ");
+ }
+ if(r > 0) {
+ for(int j = 0; j < c; j++)
+ sb.append(rows[r-1].getBool(j) ? "1" : "0");
+ sb.append(" }");
+ } else
+ sb.append(" }");
+
+ return sb.toString();
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a \class{String} containing all the data of
+ the \texttt{BitMatrix}. The result is displayed in a matrix form, with
+ each row being put on a different line. Note that the bit at (0,0) is
+ at the upper left of the matrix, while the bit at (0) in a
+ \class{BitVector} is the least significant bit.
+\end{tabb}
+\begin{htmlonly}
+ \return{the content of the \texttt{BitMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public String printData() \begin{hide} {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("{ ");
+ for(int i = 0; i < r; i++)
+ {
+ sb.append("{");
+ for(int j = 0; j < (c + 31) / 32; j++)
+ {
+ sb.append(rows[i].getInt(j));
+ if(j != (c - 1) / 32)
+ sb.append(", ");
+ }
+ sb.append("}");
+ if(i != r - 1)
+ sb.append("," + PrintfFormat.NEWLINE + " ");
+ }
+ sb.append(" }");
+
+ return sb.toString();
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a \class{String} containing all the data of
+ the \texttt{BitMatrix}. The data is displayed in the same format as are the
+ \texttt{int[][]} in \texttt{Java} code. This allows the user to print the
+ representation of a \texttt{BitMatrix} to be put, directly in the source
+ code, in the constructor \texttt{BitMatrix(int[][], int, int)}. The output
+ is not designed to be human-readable.
+\end{tabb}
+\begin{htmlonly}
+ \return{the content of the \texttt{BitMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public int numRows() \begin{hide} {
+ return r;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the number of rows of the \texttt{BitMatrix}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of rows}
+\end{htmlonly}
+\begin{code}
+
+ public int numColumns() \begin{hide} {
+ return c;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the number of columns of the \texttt{BitMatrix}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of columns}
+\end{htmlonly}
+\begin{code}
+
+ public boolean getBool (int row, int column) \begin{hide} {
+ if(row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ return rows[row].getBool(column);
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the value of the bit in the specified row and column.
+ If the value is 1, return \texttt{true}. If it is 0, return \texttt{false}.
+\end{tabb}
+\begin{htmlonly}
+ \param{row}{the row of the selected bit}
+ \param{column}{the column of the selected bit}
+ \return{the value of the bit as a boolean}
+ \exception{IndexOutOfBoundsException}{if the selected bit would
+ be outside the \texttt{BitMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public void setBool (int row, int column, boolean value) \begin{hide} {
+ if(row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ rows[row].setBool(column,value);
+ } \end{hide}
+\end{code}
+\begin{tabb} Changes the value of the bit in the specified row and column.
+ If \texttt{value} is \texttt{true}, changes it to 1. If \texttt{value} is
+ \texttt{false} changes it to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{row}{the row of the selected bit}
+ \param{column}{the column of the selected bit}
+ \param{value}{the new value of the bit as a boolean}
+ \exception{IndexOutOfBoundsException}{if the selected bit would
+ be outside the \texttt{BitMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix transpose() \begin{hide} {
+ BitMatrix result = new BitMatrix(c,r);
+
+ for(int i = 0; i < r; i++)
+ for(int j = 0; j < c; j++)
+ result.rows[j].setBool(i, rows[i].getBool(j));
+
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the transposed matrix. The rows and columns are
+ interchanged.
+\end{tabb}
+\begin{htmlonly}
+ \return{the transposed matrix}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix not() \begin{hide} {
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfNot();
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the \texttt{BitMatrix} resulting from the application of
+ the \texttt{not} operator on the original \texttt{BitMatrix}. The effect is to
+ swap all the bits of the \texttt{BitMatrix}, turning all 0 into 1 and all 1
+ into 0.
+\end{tabb}
+\begin{htmlonly}
+ \return{the result of the \texttt{not} operator}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix and (BitMatrix that) \begin{hide} {
+ if(this.c != that.c || this.r != that.r)
+ throw new IncompatibleDimensionException
+ ("Both matrices must have the same dimension. " +
+ "this is a " + this.r + "x" + this.c + " matrix " +
+ "while that is a " + that.r + "x" + that.c + " matrix.");
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfAnd(that.rows[i]);
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the \texttt{BitMatrix} resulting from the application of
+ the \texttt{and} operator on the original \texttt{BitMatrix} and \texttt{that}.
+ Only bits which were at 1 in both \texttt{BitMatrix} are set at 1 in the
+ result. All others are set to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand of the \texttt{and} operator}
+ \return{the result of the \texttt{and} operator}
+ \exception{IncompatibleDimensionException}{if the two \texttt{BitMatrix} are
+ not of the same dimensions}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix or (BitMatrix that) \begin{hide} {
+ if(this.c != that.c || this.r != that.r)
+ throw new IncompatibleDimensionException
+ ("Both matrices must have the same dimension. " +
+ "this is a " + this.r + "x" + this.c + " matrix " +
+ "while that is a " + that.r + "x" + that.c + " matrix.");
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfOr(that.rows[i]);
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the \texttt{BitMatrix} resulting from the application of
+ the \texttt{or} operator on the original \texttt{BitMatrix} and \texttt{that}.
+ Only bits which were at 0 in both \texttt{BitMatrix} are set at 0 in the
+ result. All others are set to 1.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand of the \texttt{or} operator}
+ \return{the result of the \texttt{or} operator}
+ \exception{IncompatibleDimensionException}{if the two \texttt{BitMatrix} are
+ not of the same dimensions}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix xor (BitMatrix that) \begin{hide} {
+ if(this.c != that.c || this.r != that.r)
+ throw new IncompatibleDimensionException
+ ("Both matrices must have the same dimension. " +
+ "this is a " + this.r + "x" + this.c + " matrix " +
+ "while that is a " + that.r + "x" + that.c + " matrix.");
+ BitMatrix result = new BitMatrix(this);
+ for(int i = 0; i < r; i++)
+ result.rows[i].selfXor(that.rows[i]);
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the \texttt{BitMatrix} resulting from the application of
+ the \texttt{xor} operator on the original \texttt{BitMatrix} and \texttt{that}.
+ Only bits which were at 1 in only one of the two \texttt{BitMatrix} are set
+ at 1 in the result. All others are set to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand of the \texttt{xor} operator}
+ \return{the result of the \texttt{xor} operator}
+ \exception{IncompatibleDimensionException}{if the two \texttt{BitMatrix} are
+ not of the same dimensions}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector multiply (BitVector vect) \begin{hide} {
+ BitVector res = new BitVector(r);
+
+ for(int i = 0; i < r; i++)
+ res.setBool(i, rows[i].scalarProduct(vect));
+
+ return res;
+ } \end{hide}
+\end{code}
+\begin{tabb} Multiplies the column \class{BitVector} by a \texttt{BitMatrix}
+ and returns the result. The result is $A \times v$, where $A$ is the
+ \texttt{BitMatrix}, and $v$ is the \class{BitVector}.
+\end{tabb}
+\begin{htmlonly}
+ \param{vect}{the vector to multiply}
+ \return{the result of the multiplication}
+\end{htmlonly}
+\begin{code}
+
+ public int multiply (int vect) \begin{hide} {
+ BitVector temp = new BitVector(new int[]{vect});
+
+ return multiply(temp).getInt(0);
+ } \end{hide}
+\end{code}
+\begin{tabb} Multiplies \texttt{vect}, seen as a column \class{BitVector}, by
+ a \texttt{BitMatrix}. (See \class{BitVector} to see the conversion between
+ \texttt{int} and \class{BitVector}.) The result is $A \times v$,
+ where $A$ is the \texttt{BitMatrix}, and $v$ is the \class{BitVector}.
+\end{tabb}
+\begin{htmlonly}
+ \param{vect}{the vector to multiply}
+ \return{the result of the multiplication, as an \texttt{int}}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix multiply (BitMatrix that) \begin{hide} {
+ if(this.c != that.r)
+ throw new IncompatibleDimensionException
+ ("The number of columns of this (" + this.c +
+ ") must be equal to the number of rows of that (" +
+ that.r + ").");
+
+ /*
+ BitVector[] res = new BitVector[this.r];
+
+ for(int i = 0; i < this.r; i++) {
+ res[i] = new BitVector(that.c);
+
+ for(int j = 0; j < that.c; j++) {
+ temp = false;
+ for(int k = 0; k < this.c; k++)
+ if(this.rows[i].getBool(k) &&
+ that.rows[k].getBool(j))
+ temp = !temp;
+ res[i].setBool(j,temp);
+ }
+ }
+
+ return BitMatrix(res);
+ */
+
+ // methode plus efficace
+
+ BitMatrix res = new BitMatrix(this.r, that.c);
+
+ for(int i = 0; i < res.r; i++)
+ for(int j = 0; j < res.c; j++)
+ if(this.rows[i].getBool(j))
+ res.rows[i].selfXor(that.rows[j]);
+
+ return res;
+ } \end{hide}
+\end{code}
+\begin{tabb} Multiplies two \texttt{BitMatrix}'s together. The result is
+ $A \times B$, where $A$ is the \texttt{this BitMatrix} and $B$ is the
+ \texttt{that BitMatrix}.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the other \texttt{BitMatrix} to multiply}
+ \return{the result of the multiplication}
+ \exception{IncompatibleDimensionException}{if the number of columns
+ of the first \texttt{BitMatrix} is not equal to the number of rows of
+ the second \texttt{BitMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix power (long p) \begin{hide} {
+ if(c != r)
+ throw new IncompatibleDimensionException
+ ("Only square matrices can be raised to a power.");
+
+ if(p < 0)
+ throw new IllegalArgumentException
+ ("Only non-negative powers are accepted.");
+
+ if(p == 0)
+ {
+ //the identity matrix
+ BitMatrix bm = new BitMatrix(r,r);
+ for(int i = 0; i < r; i++)
+ bm.setBool(i,i,true);
+ return bm;
+ }
+
+ if(p == 1)
+ return this;
+
+ if(p % 2 == 0) {
+ BitMatrix temp = this.power(p/2);
+ return temp.multiply(temp);
+ } else
+ return this.multiply(this.power(p-1));
+ } \end{hide}
+\end{code}
+\begin{tabb} Raises the \texttt{BitMatrix} to the power \texttt{p}.
+\end{tabb}
+\begin{htmlonly}
+ \param{p}{the power up to which to raise the \texttt{BitMatrix}}
+ \return{the power of the \texttt{BitMatrix}}
+ \exception{IncompatibleDimensionException}{if the \texttt{BitMatrix}
+ is not square}
+ \exception{IllegalArgumentException}{if \texttt{p} is negative}
+\end{htmlonly}
+\begin{code}
+
+ public BitMatrix power2e (int e) \begin{hide} {
+ if(c != r)
+ throw new IncompatibleDimensionException
+ ("Only square matrices can be raised to a power.");
+ BitMatrix result = this;
+
+ for(int i = 0; i < e; i++)
+ result = result.multiply(result);
+
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Raises the \texttt{BitMatrix} to power $2^{\mathtt{e}}$.
+\end{tabb}
+\begin{htmlonly}
+ \param{e}{the exponent of the power up to which to raise the \texttt{BitMatrix}}
+ \return{the power of the \texttt{BitMatrix}}
+ \exception{IncompatibleDimensionException}{if the \texttt{BitMatrix} is
+ not square}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Nested Class}
+
+\begin{code}
+ public class IncompatibleDimensionException extends RuntimeException
+\end{code}
+\begin{tabb} Runtime exception raised when the dimensions of the
+ \texttt{BitMatrix} are not appropriate for the operation.
+\end{tabb}
+\begin{code}
+ \begin{hide}
+ {
+ private IncompatibleDimensionException() {
+ super();
+ }
+
+ private IncompatibleDimensionException (String message) {
+ super(message);
+ }
+ }
+
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/BitVector.java b/source/umontreal/iro/lecuyer/util/BitVector.java
new file mode 100644
index 0000000..21722e6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/BitVector.java
@@ -0,0 +1,657 @@
+
+/*
+ * Class: BitVector
+ * Description: implements vectors of bits and their operations
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.io.Serializable;
+
+
+
+/**
+ * This class implements vectors of bits and the operations needed to use
+ * them. The vectors can be of arbitrary length. The operations provided are
+ * all the binary operations available to the <TT>int</TT> and <TT>long</TT>
+ * primitive types in Java.
+ *
+ * <P>
+ * All bit operations are present in two forms: a normal form and a <TT>self</TT>
+ * form. The normal form returns a newly created object containing the result,
+ * while the <TT>self</TT> form puts the result in the calling object (<TT>this</TT>).
+ * The return value of the <TT>self</TT> form is the calling object itself. This is
+ * done to allow easier manipulation of the results, making it possible to
+ * chain operations.
+ *
+ */
+public class BitVector implements Serializable, Cloneable {
+
+ static final long serialVersionUID = -3448233092524725148L;
+
+ private int[] v; //the bits data
+ private int length; //number of data bits (in bits, not in bytes)
+
+ private final static int all_1 = -1; //integer with all bits set to 1
+ private final static int one_1 = 1; //integer with only his last bit set to 1
+ /*
+ Note sur le format interne du vecteur de bits :
+ On fait toujours en sorte que les bits redondants (ceux qui apparaissent
+ quand length % 32 != 0) soient mis a 0. Ceci permet de faire des
+ operations entre des vecteurs de longeurs differentes en posant que
+ les bits manquants sur le plus petit des deux vecteurs ont la valeur 0.
+ */
+
+
+ /**
+ * Creates a new <TT>BitVector</TT> of length <TT>length</TT> with
+ * all its bits set to 0.
+ *
+ * @param length the length of the <TT>BitVector</TT>
+ *
+ *
+ */
+ public BitVector (int length) {
+ this.length = length;
+ v = new int[(length + 31) / 32];
+ for(int i = 0; i < v.length; i++)
+ v[i] = 0;
+ }
+
+
+ /**
+ * Creates a new <TT>BitVector</TT> of length <TT>length</TT> using
+ * the data in <TT>vect</TT>. Component <TT>vect[0]</TT> makes the 32 lowest order
+ * bits,
+ * with <TT>vect[1]</TT> being the 32 next lowest order bits, and so on.
+ * The normal bit order is then used to fill the 32 bits (the first bit
+ * is the lowest order bit and the last bit is largest order bit).
+ * Note that the sign bit is used as the largest order bit.
+ *
+ * @param vect the bits data
+ *
+ * @param length the length of the vector
+ *
+ * @exception IllegalArgumentException when the length of <TT>vect</TT> is
+ * not compatible with the <TT>length</TT> provided
+ *
+ *
+ */
+ public BitVector (int[] vect, int length) {
+ if (((length + 31)/ 32) != vect.length)
+ throw new IllegalArgumentException
+ ("The int[] length must be equal to the (length + 31) / 32");
+
+ this.length = length;
+ v = new int[vect.length];
+ for(int i = 0; i < vect.length; i++)
+ v[i] = vect[i];
+
+ //the extra bits must be set at zero
+ v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+ }
+
+
+ /**
+ * Creates a new <TT>BitVector</TT> using the data in <TT>vect</TT>.
+ * The length of the <TT>BitVector</TT> is always equals to 32 times the
+ * length of <TT>vect</TT>.
+ *
+ * @param vect the bits data
+ *
+ *
+ */
+ public BitVector (int[] vect) {
+ this(vect, vect.length * 32);
+ }
+
+
+ /**
+ * Creates a copy of the <TT>BitVector that</TT>.
+ *
+ * @param that the <TT>BitVector</TT> to copy
+ *
+ */
+ public BitVector (BitVector that) {
+ this.length = that.length;
+ this.v = new int[that.v.length];
+ for(int i = 0; i < that.v.length; i++)
+ this.v[i] = that.v[i];
+ }
+
+ /**
+ * Creates a copy of the <TT>BitVector</TT>.
+ *
+ * @return a deep copy of the <TT>BitVector</TT>
+ *
+ */
+ public Object clone() {
+ try{
+ BitVector c = (BitVector)super.clone();
+ c.v = (int[])v.clone();
+ return c;
+ }catch(CloneNotSupportedException e) {
+ IllegalStateException ne = new IllegalStateException();
+ ne.initCause(e);
+ throw ne;
+ }
+ }
+
+
+ /**
+ * Verifies if two <TT>BitVector</TT>'s have the same length and
+ * the same data.
+ *
+ * @param that the other <TT>BitVector</TT> to compare to
+ *
+ * @return if the two <TT>BitVector</TT>'s are identiqual
+ *
+ */
+ public boolean equals (BitVector that) {
+ if(this.length != that.length)
+ return false;
+ for(int i = 0; i < this.v.length; i++)
+ if(this.v[i] != that.v[i])
+ return false;
+ return true;
+ }
+
+
+ /**
+ * Returns the length of the <TT>BitVector</TT>.
+ *
+ * @return the length of the <TT>BitVector</TT>
+ *
+ */
+ public int size() {
+ return length;
+ }
+
+
+ /**
+ * Resizes the <TT>BitVector</TT> so that its length is equal
+ * to <TT>size</TT>. If the <TT>BitVector</TT> is enlarged, then the newly
+ * added bits are given the value 1 if <TT>filling</TT> is set to
+ * <TT>true</TT> and 0 otherwise.
+ *
+ * @param size the new size of the <TT>BitVector</TT>
+ *
+ * @param filling the state of the new bits
+ *
+ *
+ */
+ public void enlarge (int size, boolean filling) {
+ if(size < 0)
+ throw new NegativeArraySizeException
+ ("The BitVector must have a non-negative size");
+ if(filling && (length % 32) != 0)
+ v[v.length - 1] ^= all_1 << (length % 32);
+ if((size + 31) / 32 != v.length) {
+ int[] new_v = new int[(size + 31) / 32];
+ int i;
+ for(i = 0; i < new_v.length && i < v.length; i++)
+ new_v[i] = v[i];
+ for(; i < new_v.length; i++)
+ new_v[i] = filling ? all_1 : 0;
+ v = new_v;
+ }
+ length = size;
+
+ //the extra bits must be set at zero
+ v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+ }
+
+
+ /**
+ * Resizes the <TT>BitVector</TT> so that its length is equal
+ * to <TT>size</TT>. Any new bit added is set to 0.
+ *
+ * @param size the new size of the <TT>BitVector</TT>
+ *
+ *
+ */
+ public void enlarge (int size) {
+ enlarge(size, false);
+ }
+
+
+ /**
+ * Gives the value of the bit in position <TT>pos</TT>. If the
+ * value is 1, returns <TT>true</TT>; otherwise, returns <TT>false</TT>.
+ *
+ * @param pos the position of the checked bit
+ *
+ * @return the value of the bit as a boolean
+ * @exception ArrayIndexOutOfBoundsException if <TT>pos</TT> is outside
+ * the range of the <TT>BitVector</TT>
+ *
+ *
+ */
+ public boolean getBool (int pos) {
+ if(pos < 0 || pos >= length)
+ throw new ArrayIndexOutOfBoundsException(pos);
+ return (v[pos >>> 5] & (one_1 << (pos & 31))) != 0;
+ }
+
+
+ /**
+ * Sets the value of the bit in position <TT>pos</TT>. If <TT>value</TT>
+ * is equal to <TT>true</TT>, sets it to 1; otherwise, sets it to 0.
+ *
+ * @param pos the position of the bit to modify
+ *
+ * @param value the new value of the bit as a boolean
+ *
+ * @exception ArrayIndexOutOfBoundsException if <TT>pos</TT> is outside
+ * the range of the <TT>BitVector</TT>
+ *
+ *
+ */
+ public void setBool (int pos, boolean value) {
+ if(pos > length || pos < 0)
+ throw new ArrayIndexOutOfBoundsException(pos);
+ if(value)
+ v[pos / 32] |= (one_1 << (pos % 32));
+ else
+ v[pos / 32] &= (one_1 << (pos % 32)) ^ all_1;
+ }
+
+
+ /**
+ * Returns an <TT>int</TT> containing all the bits in the interval
+ *
+ * <SPAN CLASS="MATH">[<TT>pos</TT>×32,<TT>pos</TT>×32 + 31]</SPAN>.
+ *
+ * @param pos the selected position
+ *
+ * @return the int at the specified position
+ * @exception ArrayIndexOutOfBoundsException if <TT>pos</TT> is outside
+ * the range of the <TT>BitVector</TT>
+ *
+ *
+ */
+ public int getInt (int pos) {
+ if(pos >= v.length || pos < 0)
+ throw new ArrayIndexOutOfBoundsException(pos);
+ return v[pos];
+ }
+
+
+ /**
+ * Returns a string containing all the bits of the <TT>BitVector</TT>,
+ * starting with the highest order bit and finishing with the lowest order bit.
+ * The bits are grouped by groups of 8 bits for ease of reading.
+ *
+ * @return all the bits of the <TT>BitVector</TT>
+ *
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ for(int i = length - 1; i > 0; i--)
+ sb.append(getBool(i) ? "1" : "0").append(i%8 == 0 ? " " : "");
+ sb.append(getBool(0) ? "1" : "0");
+
+ return sb.toString();
+ }
+
+
+ /**
+ * Returns a <TT>BitVector</TT> which is the result of the <TT>not</TT>
+ * operator on the current <TT>BitVector</TT>. The <TT>not</TT> operator is
+ * equivalent to the <code>~</code> operator in Java and thus swap all bits (bits
+ * previously set to 0 become 1 and bits previously set to 1 become 0).
+ *
+ * @return the effect of the <TT>not</TT> operator
+ *
+ */
+ public BitVector not() {
+ BitVector bv = new BitVector(length);
+ for(int i = 0; i < v.length; i++)
+ bv.v[i] = v[i] ^ all_1;
+
+ //the extra bits must be set at zero
+ bv.v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+
+ return bv;
+ }
+
+
+ /**
+ * Applies the <TT>not</TT> operator on the current <TT>BitVector</TT>
+ * and returns it.
+ *
+ * @return the <TT>BitVector</TT> itself
+ *
+ */
+ public BitVector selfNot() {
+ for(int i = 0; i < v.length; i++)
+ v[i] = v[i] ^ all_1;
+
+ //the extra bits must be set at zero
+ v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+
+ return this;
+ }
+
+
+ /**
+ * Returns a <TT>BitVector</TT> which is the result of the <TT>xor</TT>
+ * operator applied on <TT>this</TT> and <TT>that</TT>. The <TT>xor</TT> operator is
+ * equivalent to the <code>^</code> operator in Java. All bits which were set to 0 in
+ * one of the vector and to 1 in the other vector are set to 1. The others
+ * are set to 0. This is equivalent to the addition in modulo 2 arithmetic.
+ *
+ * @param that the second operand to the <TT>xor</TT> operator
+ *
+ * @return the result of the <TT>xor</TT> operation
+ *
+ */
+ public BitVector xor (BitVector that) {
+ //we always consider that this is longer than that
+ if(that.length > this.length)
+ return that.xor(this);
+
+ BitVector bv = new BitVector(this.length);
+
+ int max = this.v.length;
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ bv.v[i] = this.v[i] ^ that.v[i];
+ for(int i = min; i < max; i++)
+ bv.v[i] = this.v[i];
+
+ return bv;
+ }
+
+
+ /**
+ * Applies the <TT>xor</TT> operator on <TT>this</TT> with <TT>that</TT>.
+ * Stores the result in <TT>this</TT> and returns it.
+ *
+ * @param that the second operand to the <TT>xor</TT> operator
+ *
+ * @return <TT>this</TT>
+ *
+ */
+ public BitVector selfXor (BitVector that) {
+ //we assume that this is large enough to contain that
+ if(this.length < that.length)
+ this.enlarge(that.length);
+
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ this.v[i] ^= that.v[i];
+
+ return this;
+ }
+
+
+ /**
+ * Returns a <TT>BitVector</TT> which is the result of the <TT>and</TT>
+ * operator with both the <TT>this</TT> and <TT>that</TT> <TT>BitVector</TT>'s. The
+ * <TT>and</TT> operator is equivalent to the <code>&</code> operator in Java. Only
+ * bits which are set to 1 in both <TT>this</TT> and <TT>that</TT> are set to 1
+ * in the result, all the others are set to 0.
+ *
+ * @param that the second operand to the <TT>and</TT> operator
+ *
+ * @return the result of the <TT>and</TT> operation
+ *
+ */
+ public BitVector and (BitVector that) {
+ //we always consider this as longer than that
+ if(that.length > this.length)
+ return that.and(this);
+
+ BitVector bv = new BitVector(this.length);
+
+ int max = this.v.length;
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ bv.v[i] = this.v[i] & that.v[i];
+ if(that.length % 32 != 0)
+ bv.v[min - 1] |= this.v[min - 1] & (all_1 << (that.length % 32));
+ for(int i = min; i < max; i++)
+ bv.v[i] = 0;
+
+ return bv;
+ }
+
+
+ /**
+ * Applies the <TT>and</TT> operator on <TT>this</TT> with <TT>that</TT>.
+ * Stores the result in <TT>this</TT> and returns it.
+ *
+ * @param that the second operand to the <TT>and</TT> operator
+ *
+ * @return <TT>this</TT>
+ *
+ */
+ public BitVector selfAnd (BitVector that) {
+ //we assume that this is large enough to contain that
+ if(this.length < that.length)
+ this.enlarge(that.length, true);
+
+ int min = that.v.length;
+
+ for(int i = 0; i < min - 1; i++)
+ this.v[i] &= that.v[i];
+ this.v[min - 1] &= (that.v[min - 1] | (all_1 << (that.length % 32)));
+
+ return this;
+ }
+
+
+ /**
+ * Returns a <TT>BitVector</TT> which is the result of the <TT>or</TT>
+ * operator with both the <TT>this</TT> and <TT>that</TT> <TT>BitVector</TT>'s. The
+ * <TT>or</TT> operator is equivalent to the <TT>|</TT> operator in Java. Only
+ * bits which are set to 0 in both <TT>this</TT> and <TT>that</TT> are set to to 0
+ * in the result, all the others are set to 1.
+ *
+ * @param that the second operand to the <TT>or</TT> operator
+ *
+ * @return the result of the <TT>or</TT> operation
+ *
+ */
+ public BitVector or (BitVector that) {
+ //we always consider this is longer than that
+ if(that.length > this.length)
+ return that.or(this);
+
+ BitVector bv = new BitVector(this.length);
+
+ int max = this.v.length;
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ bv.v[i] = this.v[i] | that.v[i];
+ for(int i = min; i < max; i++)
+ bv.v[i] = 0;
+
+ return bv;
+ }
+
+
+ /**
+ * Applies the <TT>or</TT> operator on <TT>this</TT> with <TT>that</TT>.
+ * Stores the result in <TT>this</TT> and returns it.
+ *
+ * @param that the second operand to the <TT>or</TT> operator
+ *
+ * @return <TT>this</TT>
+ *
+ */
+ public BitVector selfOr (BitVector that) {
+ //we assume that this is large enough to contain that
+ if(this.length < that.length)
+ this.enlarge(that.length);
+
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ this.v[i] |= that.v[i];
+
+ return this;
+ }
+
+
+ /**
+ * Returns a <TT>BitVector</TT> equal to the original with
+ * all the bits shifted <TT>j</TT> positions to the right if <TT>j</TT> is
+ * positive, and shifted <TT>j</TT> positions to the left if <TT>j</TT> is negative.
+ * The new bits that appears to the left or to the right are set to 0.
+ * If <TT>j</TT> is positive, this operation is equivalent to the <TT>»></TT>
+ * operator in Java, otherwise, it is equivalent to the <TT>«</TT> operator.
+ *
+ * @param j the size of the shift
+ *
+ * @return the shifted <TT>BitVector</TT>
+ *
+ */
+ public BitVector shift (int j) {
+ BitVector bv = new BitVector(length);
+
+ if(j == 0)
+ return bv;
+ else if(j > 0) {
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = 0; i+a < v.length; i++)
+ bv.v[i] = v[i+a] >>> b;
+ // la retenue
+ for(i = 0; i+a+1 < v.length; i++)
+ bv.v[i] ^= v[i+a+1] << c;
+ } else // if(j < 0)
+ {
+ j = -j;
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = a; i < v.length; i++)
+ bv.v[i] ^= v[i-a] << b;
+ // la retenue
+ for(i = a+1; i < v.length; i++)
+ bv.v[i] ^= v[i-a-1] >>> c;
+ }
+
+ return bv;
+ }
+
+
+ /**
+ * Shift all the bits of the current <TT>BitVector</TT> <TT>j</TT>
+ * positions to the right if <TT>j</TT> is positive, and <TT>j</TT> positions
+ * to the left if <TT>j</TT> is negative.
+ * The new bits that appears to the left or to the rigth are set to 0.
+ * Returns <TT>this</TT>.
+ *
+ * @param j the size of the shift
+ *
+ * @return <TT>this</TT>
+ *
+ */
+ public BitVector selfShift (int j) {
+ if(j == 0)
+ return this;
+ else if(j > length || j < -length) {
+ for(int i = 0; i < v.length; i++)
+ v[i] = 0;
+ } else if(j > 0) {
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = 0; i+a+1 < v.length; i++) {
+ v[i] = v[i+a] >>> b;
+ // la retenue
+ v[i] ^= v[i+a+1] << c;
+ }
+ v[i] = v[i+a] >>> b;
+ for(i += 1; i < v.length; i++)
+ v[i] = 0;
+ } else // if(j < 0)
+ {
+ j = -j;
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = v.length - 1; i > a; i--) {
+ v[i] = v[i-a] << b;
+ // la retenue
+ v[i] ^= v[i-a-1] >>> c;
+ }
+ v[i] = v[i-a] << b;
+ for(i -= 1; i >= 0; i--)
+ v[i] = 0;
+ }
+
+ return this;
+ }
+
+
+ /**
+ * Returns the scalar product of two <TT>BitVector</TT>'s modulo 2.
+ * It returns <TT>true</TT> if there is an odd number of bits with a value of 1
+ * in the result of the <TT>and</TT> operator applied on <TT>this</TT> and
+ * <TT>that</TT>, and returns <TT>false</TT> otherwise.
+ *
+ * @param that the other <TT>BitVector</TT> with which to do the scalar product
+ *
+ * @return the scalar product
+ *
+ */
+ public boolean scalarProduct (BitVector that) {
+ //we must take that is not longer than this
+ if(that.v.length > this.v.length)
+ return that.scalarProduct(this);
+
+ boolean result = false;
+ int prod;
+
+ for(int i = 0; i < that.v.length; i++) {
+ prod = this.v[i] & that.v[i];
+ while(prod != 0) {
+ // a chaque iteration, on enleve le 1 le plus a droite
+ prod &= prod - 1;
+ result = !result;
+ }
+ }
+
+ return result;
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/util/BitVector.tex b/source/umontreal/iro/lecuyer/util/BitVector.tex
new file mode 100644
index 0000000..571644b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/BitVector.tex
@@ -0,0 +1,644 @@
+\defmodule {BitVector}
+
+This class implements vectors of bits and the operations needed to use
+them. The vectors can be of arbitrary length. The operations provided are
+all the binary operations available to the \texttt{int} and \texttt{long}
+primitive types in Java.
+
+All bit operations are present in two forms: a normal form and a \texttt{self}
+form. The normal form returns a newly created object containing the result,
+while the \texttt{self} form puts the result in the calling object (\texttt{this}).
+The return value of the \texttt{self} form is the calling object itself. This is
+done to allow easier manipulation of the results, making it possible to
+chain operations.
+
+%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+\begin{code}\begin{hide}
+/*
+ * Class: BitVector
+ * Description: implements vectors of bits and their operations
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util; \begin{hide}
+
+import java.io.Serializable;
+\end{hide}
+
+
+public class BitVector implements Serializable, Cloneable \begin{hide} {
+
+ static final long serialVersionUID = -3448233092524725148L;
+
+ private int[] v; //the bits data
+ private int length; //number of data bits (in bits, not in bytes)
+
+ private final static int all_1 = -1; //integer with all bits set to 1
+ private final static int one_1 = 1; //integer with only his last bit set to 1
+ /*
+ Note sur le format interne du vecteur de bits :
+ On fait toujours en sorte que les bits redondants (ceux qui apparaissent
+ quand length % 32 != 0) soient mis a 0. Ceci permet de faire des
+ operations entre des vecteurs de longeurs differentes en posant que
+ les bits manquants sur le plus petit des deux vecteurs ont la valeur 0.
+ */
+ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public BitVector (int length) \begin{hide} {
+ this.length = length;
+ v = new int[(length + 31) / 32];
+ for(int i = 0; i < v.length; i++)
+ v[i] = 0;
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{BitVector} of length \texttt{length} with
+ all its bits set to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{length}{the length of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector (int[] vect, int length) \begin{hide} {
+ if (((length + 31)/ 32) != vect.length)
+ throw new IllegalArgumentException
+ ("The int[] length must be equal to the (length + 31) / 32");
+
+ this.length = length;
+ v = new int[vect.length];
+ for(int i = 0; i < vect.length; i++)
+ v[i] = vect[i];
+
+ //the extra bits must be set at zero
+ v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{BitVector} of length \texttt{length} using
+ the data in \texttt{vect}. Component \texttt{vect[0]} makes the 32 lowest order
+ bits,
+ with \texttt{vect[1]} being the 32 next lowest order bits, and so on.
+ The normal bit order is then used to fill the 32 bits (the first bit
+ is the lowest order bit and the last bit is largest order bit).
+ Note that the sign bit is used as the largest order bit.
+\end{tabb}
+\begin{htmlonly}
+ \param{vect}{the bits data}
+ \param{length}{the length of the vector}
+ \exception{IllegalArgumentException}{when the length of \texttt{vect} is
+ not compatible with the \texttt{length} provided}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector (int[] vect) \begin{hide} {
+ this(vect, vect.length * 32);
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{BitVector} using the data in \texttt{vect}.
+ The length of the \texttt{BitVector} is always equals to 32 times the
+ length of \texttt{vect}.
+\end{tabb}
+\begin{htmlonly}
+ \param{vect}{the bits data}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector (BitVector that) \begin{hide} {
+ this.length = that.length;
+ this.v = new int[that.v.length];
+ for(int i = 0; i < that.v.length; i++)
+ this.v[i] = that.v[i];
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a copy of the \texttt{BitVector that}.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the \texttt{BitVector} to copy}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+ public Object clone() \begin{hide} {
+ try{
+ BitVector c = (BitVector)super.clone();
+ c.v = (int[])v.clone();
+ return c;
+ }catch(CloneNotSupportedException e) {
+ IllegalStateException ne = new IllegalStateException();
+ ne.initCause(e);
+ throw ne;
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a copy of the \texttt{BitVector}.
+\end{tabb}
+\begin{htmlonly}
+ \return{a deep copy of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public boolean equals (BitVector that) \begin{hide} {
+ if(this.length != that.length)
+ return false;
+ for(int i = 0; i < this.v.length; i++)
+ if(this.v[i] != that.v[i])
+ return false;
+ return true;
+ } \end{hide}
+\end{code}
+\begin{tabb} Verifies if two \texttt{BitVector}'s have the same length and
+ the same data.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the other \texttt{BitVector} to compare to}
+ \return{if the two \texttt{BitVector}'s are identiqual}
+\end{htmlonly}
+\begin{code}
+
+ public int size() \begin{hide} {
+ return length;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the length of the \texttt{BitVector}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the length of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public void enlarge (int size, boolean filling) \begin{hide} {
+ if(size < 0)
+ throw new NegativeArraySizeException
+ ("The BitVector must have a non-negative size");
+ if(filling && (length % 32) != 0)
+ v[v.length - 1] ^= all_1 << (length % 32);
+ if((size + 31) / 32 != v.length) {
+ int[] new_v = new int[(size + 31) / 32];
+ int i;
+ for(i = 0; i < new_v.length && i < v.length; i++)
+ new_v[i] = v[i];
+ for(; i < new_v.length; i++)
+ new_v[i] = filling ? all_1 : 0;
+ v = new_v;
+ }
+ length = size;
+
+ //the extra bits must be set at zero
+ v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+ } \end{hide}
+\end{code}
+\begin{tabb} Resizes the \texttt{BitVector} so that its length is equal
+ to \texttt{size}. If the \texttt{BitVector} is enlarged, then the newly
+ added bits are given the value 1 if \texttt{filling} is set to
+ \texttt{true} and 0 otherwise.
+\end{tabb}
+\begin{htmlonly}
+ \param{size}{the new size of the \texttt{BitVector}}
+ \param{filling}{the state of the new bits}
+\end{htmlonly}
+\begin{code}
+
+ public void enlarge (int size) \begin{hide} {
+ enlarge(size, false);
+ } \end{hide}
+\end{code}
+\begin{tabb} Resizes the \texttt{BitVector} so that its length is equal
+ to \texttt{size}. Any new bit added is set to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{size}{the new size of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public boolean getBool (int pos) \begin{hide} {
+ if(pos < 0 || pos >= length)
+ throw new ArrayIndexOutOfBoundsException(pos);
+ return (v[pos >>> 5] & (one_1 << (pos & 31))) != 0;
+ } \end{hide}
+\end{code}
+\begin{tabb} Gives the value of the bit in position \texttt{pos}. If the
+ value is 1, returns \texttt{true}; otherwise, returns \texttt{false}.
+\end{tabb}
+\begin{htmlonly}
+ \param{pos}{the position of the checked bit}
+ \return{the value of the bit as a boolean}
+ \exception{ArrayIndexOutOfBoundsException}{if \texttt{pos} is outside
+ the range of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public void setBool (int pos, boolean value) \begin{hide} {
+ if(pos > length || pos < 0)
+ throw new ArrayIndexOutOfBoundsException(pos);
+ if(value)
+ v[pos / 32] |= (one_1 << (pos % 32));
+ else
+ v[pos / 32] &= (one_1 << (pos % 32)) ^ all_1;
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the value of the bit in position \texttt{pos}. If \texttt{value}
+ is equal to \texttt{true}, sets it to 1; otherwise, sets it to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{pos}{the position of the bit to modify}
+ \param{value}{the new value of the bit as a boolean}
+ \exception{ArrayIndexOutOfBoundsException}{if \texttt{pos} is outside
+ the range of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public int getInt (int pos) \begin{hide} {
+ if(pos >= v.length || pos < 0)
+ throw new ArrayIndexOutOfBoundsException(pos);
+ return v[pos];
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns an \texttt{int} containing all the bits in the interval
+ $[\mathtt{pos} \times 32, \mathtt{pos} \times 32 + 31]$.
+\end{tabb}
+\begin{htmlonly}
+ \param{pos}{the selected position}
+ \return{the int at the specified position}
+ \exception{ArrayIndexOutOfBoundsException}{if \texttt{pos} is outside
+ the range of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ StringBuffer sb = new StringBuffer();
+
+ for(int i = length - 1; i > 0; i--)
+ sb.append(getBool(i) ? "1" : "0").append(i%8 == 0 ? " " : "");
+ sb.append(getBool(0) ? "1" : "0");
+
+ return sb.toString();
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns a string containing all the bits of the \texttt{BitVector},
+ starting with the highest order bit and finishing with the lowest order bit.
+ The bits are grouped by groups of 8 bits for ease of reading.
+\end{tabb}
+\begin{htmlonly}
+ \return{all the bits of the \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector not() \begin{hide} {
+ BitVector bv = new BitVector(length);
+ for(int i = 0; i < v.length; i++)
+ bv.v[i] = v[i] ^ all_1;
+
+ //the extra bits must be set at zero
+ bv.v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+
+ return bv;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns a \texttt{BitVector} which is the result of the \texttt{not}
+ operator on the current \texttt{BitVector}. The \texttt{not} operator is
+ equivalent to the \verb!~! operator in Java and thus swap all bits (bits
+ previously set to 0 become 1 and bits previously set to 1 become 0).
+\end{tabb}
+\begin{htmlonly}
+ \return{the effect of the \texttt{not} operator}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector selfNot() \begin{hide} {
+ for(int i = 0; i < v.length; i++)
+ v[i] = v[i] ^ all_1;
+
+ //the extra bits must be set at zero
+ v[v.length - 1] &= (all_1 >>> (31 - (length - 1) % 32));
+
+ return this;
+ } \end{hide}
+\end{code}
+\begin{tabb} Applies the \texttt{not} operator on the current \texttt{BitVector}
+ and returns it.
+%% This gives the same result as \texttt{not()}, but the result is stored
+%% in \texttt{this}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the \texttt{BitVector} itself}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector xor (BitVector that) \begin{hide} {
+ //we always consider that this is longer than that
+ if(that.length > this.length)
+ return that.xor(this);
+
+ BitVector bv = new BitVector(this.length);
+
+ int max = this.v.length;
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ bv.v[i] = this.v[i] ^ that.v[i];
+ for(int i = min; i < max; i++)
+ bv.v[i] = this.v[i];
+
+ return bv;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns a \texttt{BitVector} which is the result of the \texttt{xor}
+ operator applied on \texttt{this} and \texttt{that}. The \texttt{xor} operator is
+ equivalent to the \verb!^! operator in Java. All bits which were set to 0 in
+ one of the vector and to 1 in the other vector are set to 1. The others
+ are set to 0. This is equivalent to the addition in modulo 2 arithmetic.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand to the \texttt{xor} operator}
+ \return{the result of the \texttt{xor} operation}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector selfXor (BitVector that) \begin{hide} {
+ //we assume that this is large enough to contain that
+ if(this.length < that.length)
+ this.enlarge(that.length);
+
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ this.v[i] ^= that.v[i];
+
+ return this;
+ } \end{hide}
+\end{code}
+\begin{tabb} Applies the \texttt{xor} operator on \texttt{this} with \texttt{that}.
+ Stores the result in \texttt{this} and returns it.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand to the \texttt{xor} operator}
+ \return{\texttt{this}}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector and (BitVector that) \begin{hide} {
+ //we always consider this as longer than that
+ if(that.length > this.length)
+ return that.and(this);
+
+ BitVector bv = new BitVector(this.length);
+
+ int max = this.v.length;
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ bv.v[i] = this.v[i] & that.v[i];
+ if(that.length % 32 != 0)
+ bv.v[min - 1] |= this.v[min - 1] & (all_1 << (that.length % 32));
+ for(int i = min; i < max; i++)
+ bv.v[i] = 0;
+
+ return bv;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns a \texttt{BitVector} which is the result of the \texttt{and}
+ operator with both the \texttt{this} and \texttt{that} \texttt{BitVector}'s. The
+ \texttt{and} operator is equivalent to the \verb!&! operator in Java. Only
+ bits which are set to 1 in both \texttt{this} and \texttt{that} are set to 1
+ in the result, all the others are set to 0.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand to the \texttt{and} operator}
+ \return{the result of the \texttt{and} operation}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector selfAnd (BitVector that) \begin{hide} {
+ //we assume that this is large enough to contain that
+ if(this.length < that.length)
+ this.enlarge(that.length, true);
+
+ int min = that.v.length;
+
+ for(int i = 0; i < min - 1; i++)
+ this.v[i] &= that.v[i];
+ this.v[min - 1] &= (that.v[min - 1] | (all_1 << (that.length % 32)));
+
+ return this;
+ } \end{hide}
+\end{code}
+\begin{tabb} Applies the \texttt{and} operator on \texttt{this} with \texttt{that}.
+ Stores the result in \texttt{this} and returns it.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand to the \texttt{and} operator}
+ \return{\texttt{this}}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector or (BitVector that) \begin{hide} {
+ //we always consider this is longer than that
+ if(that.length > this.length)
+ return that.or(this);
+
+ BitVector bv = new BitVector(this.length);
+
+ int max = this.v.length;
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ bv.v[i] = this.v[i] | that.v[i];
+ for(int i = min; i < max; i++)
+ bv.v[i] = 0;
+
+ return bv;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns a \texttt{BitVector} which is the result of the \texttt{or}
+ operator with both the \texttt{this} and \texttt{that} \texttt{BitVector}'s. The
+ \texttt{or} operator is equivalent to the \texttt{|} operator in Java. Only
+ bits which are set to 0 in both \texttt{this} and \texttt{that} are set to to 0
+ in the result, all the others are set to 1.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand to the \texttt{or} operator}
+ \return{the result of the \texttt{or} operation}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector selfOr (BitVector that) \begin{hide} {
+ //we assume that this is large enough to contain that
+ if(this.length < that.length)
+ this.enlarge(that.length);
+
+ int min = that.v.length;
+
+ for(int i = 0; i < min; i++)
+ this.v[i] |= that.v[i];
+
+ return this;
+ } \end{hide}
+\end{code}
+\begin{tabb} Applies the \texttt{or} operator on \texttt{this} with \texttt{that}.
+ Stores the result in \texttt{this} and returns it.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the second operand to the \texttt{or} operator}
+ \return{\texttt{this}}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector shift (int j) \begin{hide} {
+ BitVector bv = new BitVector(length);
+
+ if(j == 0)
+ return bv;
+ else if(j > 0) {
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = 0; i+a < v.length; i++)
+ bv.v[i] = v[i+a] >>> b;
+ // la retenue
+ for(i = 0; i+a+1 < v.length; i++)
+ bv.v[i] ^= v[i+a+1] << c;
+ } else // if(j < 0)
+ {
+ j = -j;
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = a; i < v.length; i++)
+ bv.v[i] ^= v[i-a] << b;
+ // la retenue
+ for(i = a+1; i < v.length; i++)
+ bv.v[i] ^= v[i-a-1] >>> c;
+ }
+
+ return bv;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns a \texttt{BitVector} equal to the original with
+ all the bits shifted \texttt{j} positions to the right if \texttt{j} is
+ positive, and shifted \texttt{j} positions to the left if \texttt{j} is negative.
+ The new bits that appears to the left or to the right are set to 0.
+ If \texttt{j} is positive, this operation is equivalent to the \texttt{>>>}
+ operator in Java, otherwise, it is equivalent to the \texttt{<<} operator.
+\end{tabb}
+\begin{htmlonly}
+ \param{j}{the size of the shift}
+ \return{the shifted \texttt{BitVector}}
+\end{htmlonly}
+\begin{code}
+
+ public BitVector selfShift (int j) \begin{hide} {
+ if(j == 0)
+ return this;
+ else if(j > length || j < -length) {
+ for(int i = 0; i < v.length; i++)
+ v[i] = 0;
+ } else if(j > 0) {
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = 0; i+a+1 < v.length; i++) {
+ v[i] = v[i+a] >>> b;
+ // la retenue
+ v[i] ^= v[i+a+1] << c;
+ }
+ v[i] = v[i+a] >>> b;
+ for(i += 1; i < v.length; i++)
+ v[i] = 0;
+ } else // if(j < 0)
+ {
+ j = -j;
+ int a = j / 32;
+ int b = j % 32;
+ int c = 32 - b;
+
+ int i;
+ for(i = v.length - 1; i > a; i--) {
+ v[i] = v[i-a] << b;
+ // la retenue
+ v[i] ^= v[i-a-1] >>> c;
+ }
+ v[i] = v[i-a] << b;
+ for(i -= 1; i >= 0; i--)
+ v[i] = 0;
+ }
+
+ return this;
+ } \end{hide}
+\end{code}
+\begin{tabb} Shift all the bits of the current \texttt{BitVector} \texttt{j}
+ positions to the right if \texttt{j} is positive, and \texttt{j} positions
+ to the left if \texttt{j} is negative.
+ The new bits that appears to the left or to the rigth are set to 0.
+ Returns \texttt{this}.
+\end{tabb}
+\begin{htmlonly}
+ \param{j}{the size of the shift}
+ \return{\texttt{this}}
+\end{htmlonly}
+\begin{code}
+
+ public boolean scalarProduct (BitVector that) \begin{hide} {
+ //we must take that is not longer than this
+ if(that.v.length > this.v.length)
+ return that.scalarProduct(this);
+
+ boolean result = false;
+ int prod;
+
+ for(int i = 0; i < that.v.length; i++) {
+ prod = this.v[i] & that.v[i];
+ while(prod != 0) {
+ // a chaque iteration, on enleve le 1 le plus a droite
+ prod &= prod - 1;
+ result = !result;
+ }
+ }
+
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the scalar product of two \texttt{BitVector}'s modulo 2.
+ It returns \texttt{true} if there is an odd number of bits with a value of 1
+ in the result of the \texttt{and} operator applied on \texttt{this} and
+ \texttt{that}, and returns \texttt{false} otherwise.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the other \texttt{BitVector} with which to do the scalar product}
+ \return{the scalar product}
+\end{htmlonly}
+
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/Chrono.c b/source/umontreal/iro/lecuyer/util/Chrono.c
new file mode 100644
index 0000000..dc8eee8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Chrono.c
@@ -0,0 +1,81 @@
+/* Interface C --> Java pour GlobalCPUTimeChrono.java */
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <time.h>
+#ifdef _linux
+#include <unistd.h>
+#include <sys/times.h>
+#endif
+#include "umontreal_iro_lecuyer_util_GlobalCPUTimeChrono.h"
+
+#ifdef _WIN32
+static HANDLE currentProcess;
+
+JNIEXPORT jint JNICALL
+JNI_OnLoad (JavaVM* vm, void* reserved) {
+ currentProcess = GetCurrentProcess();
+ return JNI_VERSION_1_2;
+}
+
+/*
+ * A helper function for converting FILETIME to a LONGLONG [safe from memory
+ * alignment point of view].
+ */
+static ULONGLONG
+fileTimeToInt64 (const FILETIME * time)
+{
+ ULARGE_INTEGER _time;
+
+ _time.LowPart = time->dwLowDateTime;
+ _time.HighPart = time->dwHighDateTime;
+
+ return _time.QuadPart;
+}
+#endif
+
+JNIEXPORT void JNICALL
+Java_umontreal_iro_lecuyer_util_GlobalCPUTimeChrono_Heure (JNIEnv *env, jclass class, jlongArray array){
+
+#ifdef _linux
+ struct tms us;
+#endif
+#ifdef _WIN32
+ FILETIME creationTime, exitTime, kernelTime, userTime;
+#endif
+ jlong *jarray = (*env)->GetLongArrayElements(env, array, 0);
+
+#if defined(_linux)
+ long TIC = sysconf(_SC_CLK_TCK);
+
+ times(&us);
+
+ jarray[1] = us.tms_utime + us.tms_stime;
+ jarray[0] = jarray[1] / TIC;
+ jarray[1] = (jarray[1] % TIC) * 1000000 / TIC;
+#elif defined(_WIN32)
+ /* Strongly inspired from
+ * http://www.javaworld.com/javaworld/javaqa/2002-11/01-qa-1108-cpu.html */
+ GetProcessTimes (currentProcess, &creationTime, &exitTime,
+ &kernelTime, &userTime);
+ ULONGLONG rawTime = (ULONGLONG)(fileTimeToInt64 (&kernelTime) +
+ fileTimeToInt64 (&userTime));
+ /* We have to divide by 10000 to get milliseconds out of
+ * the computed time. So we divide by 10000*1000 to get seconds. */
+ jarray[0] = (unsigned long)(rawTime / 10000000);
+ /* One raw time unit corresponds to 10 microseconds.
+ */
+ jarray[1] = (unsigned long)((rawTime % 10000000) / 10);
+#else
+ /* This one is bad but portable across Unixes.
+ * The clock function wraps after approximately 72 minutes. */
+ jarray[1] = clock();
+ jarray[0] = jarray[1] / CLOCKS_PER_SEC;
+ jarray[1] = (jarray[1] % CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
+#endif
+
+ (*env)->ReleaseLongArrayElements(env, array, jarray, 0);
+}
diff --git a/source/umontreal/iro/lecuyer/util/Chrono.java b/source/umontreal/iro/lecuyer/util/Chrono.java
new file mode 100644
index 0000000..194f33c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Chrono.java
@@ -0,0 +1,80 @@
+
+
+/*
+ * Class: Chrono
+ * Description: computes the CPU time for the current thread only
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+/**
+ * The {@link Chrono} class extends the
+ * {@link umontreal.iro.lecuyer.util.AbstractChrono AbstractChrono}
+ * class and computes the CPU time for the current thread only.
+ * This is the simplest way to use chronos. Classes <TT>AbstractChrono</TT>,
+ * {@link umontreal.iro.lecuyer.util.SystemTimeChrono SystemTimeChrono},
+ * {@link umontreal.iro.lecuyer.util.GlobalCPUTimeChrono GlobalCPUTimeChrono} and
+ * {@link umontreal.iro.lecuyer.util.ThreadCPUTimeChrono ThreadCPUTimeChrono}
+ * provide different chronos implementations.
+ * See these classes to learn more about SSJ chronos, if problems appear with
+ * class <TT>Chrono</TT>.
+ *
+ */
+public class Chrono extends AbstractChrono {
+ private ThreadCPUTimeChrono chrono = new ThreadCPUTimeChrono();
+
+ protected void getTime (long[] tab) {
+ chrono.getTime(tab);
+ }
+
+
+ /**
+ * Constructs a <TT>Chrono</TT> object and
+ * initializes it to zero.
+ *
+ */
+ public Chrono() {
+ chrono.init();
+ init();
+ }
+
+
+ /**
+ * Creates a <TT>Chrono</TT> instance adapted for a program
+ * using a single thread. Under Java 1.5, this method returns
+ * an instance of {@link ChronoSingleThread} which can
+ * measure CPU time for one thread. Under Java versions prior to
+ * 1.5, this returns an instance of this class.
+ * This method must not be used to create a timer for a
+ * multi-threaded program, because the obtained CPU times
+ * will differ depending on the used Java version.
+ *
+ * @return the constructed timer.
+ *
+ */
+ public static Chrono createForSingleThread () {
+ return new Chrono();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/Chrono.tex b/source/umontreal/iro/lecuyer/util/Chrono.tex
new file mode 100644
index 0000000..6f77d1c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Chrono.tex
@@ -0,0 +1,93 @@
+\defmodule {Chrono}
+
+The \class{Chrono} class extends the
+\externalclass{umontreal.iro.lecuyer.util}{AbstractChrono}
+class and computes the CPU time for the current thread only.
+This is the simplest way to use chronos. Classes \texttt{AbstractChrono},
+\externalclass{umontreal.iro.lecuyer.util}{SystemTimeChrono},
+\externalclass{umontreal.iro.lecuyer.util}{GlobalCPUTimeChrono} and
+\externalclass{umontreal.iro.lecuyer.util}{ThreadCPUTimeChrono}
+provide different chronos implementations.
+See these classes to learn more about SSJ chronos, if problems appear with
+class \texttt{Chrono}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: Chrono
+ * Description: computes the CPU time for the current thread only
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public class Chrono extends AbstractChrono \begin{hide} {
+ private ThreadCPUTimeChrono chrono = new ThreadCPUTimeChrono();
+
+ protected void getTime (long[] tab) {
+ chrono.getTime(tab);
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public Chrono()\begin{hide} {
+ chrono.init();
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{Chrono} object and
+ initializes it to zero.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+
+ public static Chrono createForSingleThread ()\begin{hide} {
+ return new Chrono();
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Creates a \texttt{Chrono} instance adapted for a program
+ using a single thread. Under Java 1.5, this method returns
+ an instance of \class{ChronoSingleThread} which can
+ measure CPU time for one thread. Under Java versions prior to
+ 1.5, this returns an instance of this class.
+ This method must not be used to create a timer for a
+ multi-threaded program, because the obtained CPU times
+ will differ depending on the used Java version.
+\end{tabb}
+\begin{htmlonly}
+ \return{the constructed timer.}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/ChronoSingleThread.java b/source/umontreal/iro/lecuyer/util/ChronoSingleThread.java
new file mode 100644
index 0000000..b8a0f83
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ChronoSingleThread.java
@@ -0,0 +1,67 @@
+
+
+/*
+ * Class: ChronoSingleThread
+ * Description: deprecated
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+
+
+
+ at Deprecated
+/**
+ * <SPAN CLASS="textbf">This class is deprecated</SPAN> but kept for
+ * compatibility with older versions of SSJ.
+ * {@link Chrono} should be used instead of {@link ChronoSingleThread}.
+ * The {@link ChronoSingleThread} class extends the {@link AbstractChrono}
+ * class and computes the CPU time for the current thread only.
+ * This is the simplest way to use chronos. Classes {@link AbstractChrono},
+ * {@link SystemTimeChrono}, {@link GlobalCPUTimeChrono} and
+ * {@link ThreadCPUTimeChrono} provide different chronos implementations
+ * (see these classes to learn more about SSJ chronos).
+ *
+ */
+public class ChronoSingleThread extends AbstractChrono {
+
+ private ThreadCPUTimeChrono chrono = new ThreadCPUTimeChrono();
+
+ protected void getTime (long[] tab) {
+ chrono.getTime(tab);
+ }
+
+
+ /**
+ * Constructs a <TT>ChronoSingleThread</TT> object and
+ * initializes it to zero.
+ *
+ */
+ public ChronoSingleThread() {
+ chrono.init();
+ init();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/ChronoSingleThread.tex b/source/umontreal/iro/lecuyer/util/ChronoSingleThread.tex
new file mode 100644
index 0000000..482d86d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ChronoSingleThread.tex
@@ -0,0 +1,76 @@
+\defmodule{ChronoSingleThread}
+
+\textbf{This class is deprecated} but kept for
+compatibility with older versions of SSJ.
+\class{Chrono} should be used instead of \class{ChronoSingleThread}.
+The \class{ChronoSingleThread} class extends the \class{AbstractChrono}
+class and computes the CPU time for the current thread only.
+This is the simplest way to use chronos. Classes \class{AbstractChrono},
+\class{SystemTimeChrono}, \class{GlobalCPUTimeChrono} and
+\class{ThreadCPUTimeChrono} provide different chronos implementations
+(see these classes to learn more about SSJ chronos).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: ChronoSingleThread
+ * Description: deprecated
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+\end{hide}
+
+
+ at Deprecated
+public class ChronoSingleThread \begin{hide} extends AbstractChrono {
+
+ private ThreadCPUTimeChrono chrono = new ThreadCPUTimeChrono();
+
+ protected void getTime (long[] tab) {
+ chrono.getTime(tab);
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public ChronoSingleThread()\begin{hide} {
+ chrono.init();
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ChronoSingleThread} object and
+ initializes it to zero.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/ClassFinder.java b/source/umontreal/iro/lecuyer/util/ClassFinder.java
new file mode 100644
index 0000000..f3e3779
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ClassFinder.java
@@ -0,0 +1,369 @@
+
+/*
+ * Class: ClassFinder
+ * Description: Convert a simple class name to a fully qualified class object
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+
+
+/**
+ * Utility class used to convert a simple class name to
+ * a fully qualified class object.
+ * The {@link java.lang.Class Class} class can be used to obtain
+ * information about a class (its name, its fields, methods,
+ * constructors, etc.), and to construct objects, even
+ * if the exact class is known at runtime only.
+ * It provides a {@link java.lang.Class#forName((String)) forName} static method converting
+ * a string to a {@link java.lang.Class Class}, but the given string
+ * must be a fully qualified name.
+ *
+ * <P>
+ * Sometimes, configuration files may need
+ * to contain Java class names. After they are
+ * extracted from the file, these class names are
+ * given to {@link java.lang.Class#forName((String)) forName} to be converted into
+ * {@link java.lang.Class Class} objects.
+ * Unfortunately, only
+ * fully qualified class names will be accepted
+ * as input, which clutters
+ * configuration files, especially if long package names are used.
+ * This class permits the definition of a set of
+ * import declarations in a way similar to the
+ * Java Language Specification.
+ * It provides methods to convert a simple class name
+ * to a {@link java.lang.Class Class} object and to generate a simple name
+ * from a {@link java.lang.Class Class} object, based on the import
+ * rules.
+ *
+ * <P>
+ * The first step for using a class finder is to construct an instance of
+ * this class.
+ * Then, one needs to retrieve the initially empty list of import
+ * declarations by
+ * using {@link #getImports getImports}, and update it with the
+ * actual import declarations.
+ * Then, the method {@link #findClass findClass} can find a class using the
+ * import declarations.
+ * For example, the following code retrieves the class object for the
+ * <TT>List</TT> class in package <TT>java.util</TT>
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ * ClassFinder cf = new ClassFinder();
+ * <BR> cf.getImports().add ("java.util.*");
+ * <BR> Class<?> listClass = cf.findClass ("List");
+ * <BR></TT>
+ * </DIV>
+ *
+ */
+public class ClassFinder implements Cloneable, java.io.Serializable {
+ private static final long serialVersionUID = -4847630831331065792L;
+
+
+ /**
+ * Contains the saved import lists.
+ * Each element of this list is a nested {@link java.util.List List} containing
+ * {@link java.lang.String String}'s, each string containing the fully qualified
+ * name of an imported package or class.
+ *
+ *
+ */
+ private List<List<String>> imports = new LinkedList<List<String>>();
+
+
+
+ /**
+ * Constructs a new class finder with
+ * an empty list of import declarations.
+ *
+ */
+ public ClassFinder() {
+ List<String> imp = new ArrayList<String>();
+ imports.add (imp);
+ }
+
+
+ /**
+ * Returns the current list of import declarations.
+ * This list may contain only {@link java.lang.String String}'s
+ * of the form <TT>java.class.name</TT> or
+ * <TT>java.package.name.*</TT>.
+ *
+ * @return the current list of import declarations.
+ *
+ */
+ public List<String> getImports() {
+ return imports.get (imports.size() - 1);
+ }
+
+
+ /**
+ * Saves the current import list on the import stack.
+ * This method makes a copy of the list returned
+ * by {@link #getImports(()) getImports} and puts it on top
+ * of a stack to be restored later by
+ * {@link #restoreImports(()) restoreImports}.
+ *
+ */
+ public void saveImports() {
+ List<String> imp = getImports();
+ List<String> impBack = new ArrayList<String> (imp);
+ imports.add (impBack);
+ }
+
+
+ /**
+ * Restores the list of import declarations.
+ * This method removes the last list of import
+ * declarations from the stack. If the stack
+ * contains only one list, this list is cleared.
+ *
+ */
+ public void restoreImports() {
+ if (imports.size() == 1)
+ getImports().clear();
+ else
+ imports.remove (imports.size() - 1);
+ }
+
+
+ /**
+ * Tries to find the class corresponding to the simple
+ * name <TT>name</TT>. The method first considers
+ * the argument as a fully qualified class name
+ * and calls {@link java.lang.Class#forName((String)) forName} <TT>(name)</TT>.
+ * If the class cannot be found, it considers the argument
+ * as a simple name. A simple name refers to a class without
+ * specifying the package declaring it. To convert
+ * simple names to qualified names, the method iterates through
+ * all the strings in the list returned by {@link #getImports(()) getImports},
+ * applying
+ * the same rules as a Java compiler to resolve
+ * the class name. However, if an imported package or
+ * class does not exist, it will be ignored whereas
+ * the compiler would stop with an error.
+ *
+ * <P>
+ * For the class with simple name <TT>name</TT> to
+ * be loaded, it must be imported explicitly (single-type import) or
+ * one of the imported packages must contain it (type import on-demand).
+ * If the class with name <TT>name</TT> is imported explicitly,
+ * this import declaration has precedence over
+ * any imported packages.
+ * If several import declaration match the given simple
+ * name, e.g., if several fully qualified names with the same
+ * simple name are imported, or if a class with simple
+ * name <TT>name</TT> exists in several packages,
+ * a {@link NameConflictException} is thrown.
+ *
+ * @param name the simple name of the class.
+ *
+ * @return a reference to the class being loaded.
+ * @exception ClassNotFoundException if the class
+ * cannot be loaded.
+ *
+ * @exception NameConflictException if a name conflict occurred.
+ *
+ *
+ */
+ public Class<?> findClass (String name) throws
+ ClassNotFoundException, NameConflictException {
+ // Try to consider the name as a fully qualified class name
+ try {
+ return Class.forName (name);
+ }
+ catch (ClassNotFoundException cnfe) {}
+
+ List<String> imports = getImports();
+ Class<?> candidate = null;
+ String candidateImportString = "";
+ boolean candidateImportOnDemand = false;
+
+ // Determines the name of the outermost class
+ // if name corresponds to the simple name of a nested class, e.g., for A.B.C
+ // outerName will contain A.
+ int idxOut = name.indexOf ('.');
+ String outerName;
+ if (idxOut == -1)
+ outerName = name;
+ else
+ outerName = name.substring (0, idxOut);
+ for (String importString : imports) {
+ // For each import declaration, we try to load
+ // a class and store the result in cl.
+ // When cl is not null, the Class object is
+ // compared with the best candidate candidate.
+ Class<?> cl = null;
+ boolean onDemand = false;
+ if (!importString.endsWith (".*")) {
+ // Single-type import declaration
+ // We must ensure that this will correspond to
+ // the desired class name. For example, if we
+ // search List and the import java.util.ArrayList
+ // is found in importString, the ArrayList
+ // class must not be returned.
+ if (importString.endsWith ("." + outerName)) {
+ // The name of outer class was found in importString and
+ // a period is found left to it.
+ // So try to load this class.
+ // Simple class names have precedence over
+ // on-demand names.
+
+ // Replace, in importString, the name of
+ // the outer class with the true class name
+ // we want to load. If the simple name
+ // does not refer to a nested class, this
+ // has no effect.
+ String cn = importString.substring
+ (0, importString.length() - outerName.length()) + name;
+ try {
+ cl = Class.forName (cn);
+ }
+ catch (ClassNotFoundException cnfe) {}
+ }
+ }
+ else {
+ // Type import on demand declaration
+ try {
+ // Replace the * with name and
+ // try to load the class.
+ // If that succeeds, our candidate cl
+ // is onDemand.
+ cl = Class.forName
+ (importString.substring (0, importString.length() - 1) + name);
+ onDemand = true;
+ }
+ catch (ClassNotFoundException cnfe) {}
+ }
+ if (cl != null) {
+ // Something was loaded
+ if (candidate == null ||
+ (candidateImportOnDemand && !onDemand)) {
+ // We had no candidate or the candidate was imported
+ // on-demand while this one is a single-type import.
+ candidate = cl;
+ candidateImportString = importString;
+ candidateImportOnDemand = onDemand;
+ }
+ else if (candidate != cl)
+ throw new NameConflictException
+ (this, name,
+ "simple class name " + name +
+ " matches " + candidate.getName() +
+ " (import string " + candidateImportString + ") or " +
+ cl.getName() + " (import string " + importString + ")");
+ }
+ }
+ if (candidate == null)
+ throw new ClassNotFoundException
+ ("Cannot find the class with name " + name);
+ return candidate;
+ }
+
+
+ /**
+ * Returns the simple name of the class <TT>cls</TT> that
+ * can be used when the imports contained
+ * in this class finder are used.
+ * For example, if <TT>java.lang.String.class</TT> is given
+ * to this method, <TT>String</TT> is returned if
+ * <TT>java.lang.*</TT> is among the import declarations.
+ *
+ * <P>
+ * Note: this method does not try to find name conflicts.
+ * This operation is performed by {@link #findClass((String)) findClass} only.
+ * For example, if the list of imported declarations
+ * contains <TT>foo.bar.*</TT> and <TT>test.Foo</TT>, and
+ * the simple name for <TT>test.Foo</TT> is queried,
+ * the method returns <TT>Foo</TT> even if the package
+ * <TT>foo.bar</TT> contains a <TT>Foo</TT> class.
+ *
+ * @param cls the class for which the simple name is queried.
+ *
+ * @return the simple class name.
+ *
+ */
+ public String getSimpleName (Class<?> cls) {
+ if (cls.isArray())
+ return getSimpleName (cls.getComponentType()) + "[]";
+ if (cls.isPrimitive())
+ return cls.getName();
+ Class<?> outer = cls;
+ while (outer.getDeclaringClass() != null)
+ outer = outer.getDeclaringClass();
+ boolean needsFullyQualified = true;
+ for (String importString : getImports()) {
+ if (importString.equals (outer.getName()))
+ // A single-type import is given, can return an unqualified name.
+ needsFullyQualified = false;
+ else if (importString.endsWith (".*")) {
+ // Remove the .* at the end of the import string to get a package name.
+ String pack = importString.substring (0, importString.length() - 2);
+ // Compare this package name.
+ if (pack.equals (cls.getPackage().getName()))
+ needsFullyQualified = false;
+ }
+ }
+ if (needsFullyQualified)
+ return cls.getName();
+ else {
+ String name = cls.getName();
+ String pack = cls.getPackage().getName();
+ if (!name.startsWith (pack))
+ throw new IllegalStateException
+ ("The class name " + name +
+ " does not contain the package name " + pack);
+
+ // Removes the package and the . from the fully qualified class name.
+ return name.substring (pack.length() + 1);
+ }
+ }
+
+
+ /**
+ * Clones this class finder, and copies its lists of
+ * import declarations.
+ *
+ */
+ public ClassFinder clone() {
+ ClassFinder cf;
+ try {
+ cf = (ClassFinder)super.clone();
+ }
+ catch (CloneNotSupportedException cne) {
+ throw new InternalError
+ ("CloneNotSupported thrown for a class implementing Cloneable");
+ }
+ cf.imports = new LinkedList<List<String>>();
+ for (List<String> imp : imports) {
+ List<String> impCpy = new ArrayList<String> (imp);
+ cf.imports.add (impCpy);
+ }
+ return cf;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/util/ClassFinder.tex b/source/umontreal/iro/lecuyer/util/ClassFinder.tex
new file mode 100644
index 0000000..83ebc10
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ClassFinder.tex
@@ -0,0 +1,357 @@
+\defmodule{ClassFinder}
+
+Utility class used to convert a simple class name to
+a fully qualified class object.
+The \externalclass{java.lang}{Class} class can be used to obtain
+information about a class (its name, its fields, methods,
+constructors, etc.), and to construct objects, even
+if the exact class is known at runtime only.
+It provides a \externalmethod{java.lang}{Class}{forName}{(String)} static method converting
+a string to a \externalclass{java.lang}{Class}, but the given string
+must be a fully qualified name.
+
+Sometimes, configuration files may need
+to contain Java class names. After they are
+extracted from the file, these class names are
+given to \externalmethod{java.lang}{Class}{forName}{(String)} to be converted into
+\externalclass{java.lang}{Class} objects.
+Unfortunately, only
+fully qualified class names will be accepted
+as input, which clutters
+configuration files, especially if long package names are used.
+This class permits the definition of a set of
+import declarations in a way similar to the
+Java Language Specification \cite{iGOS00a}.
+It provides methods to convert a simple class name
+to a \externalclass{java.lang}{Class} object and to generate a simple name
+from a \externalclass{java.lang}{Class} object, based on the import
+rules.
+
+The first step for using a class finder is to construct an instance of
+this class.
+Then, one needs to retrieve the initially empty list of import
+declarations by
+using \method{getImports}{}, and update it with the
+actual import declarations.
+Then, the method \method{findClass}{} can find a class using the
+import declarations.
+For example, the following code retrieves the class object for the
+\texttt{List} class in package \texttt{java.util}
+\begin{vcode}
+ ClassFinder cf = new ClassFinder();
+ cf.getImports().add ("java.util.*");
+ Class<?> listClass = cf.findClass ("List");
+\end{vcode}
+
+\bigskip\hrule\bigskip
+
+\begin{code}\begin{hide}
+/*
+ * Class: ClassFinder
+ * Description: Convert a simple class name to a fully qualified class object
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+\end{hide}
+
+
+public class ClassFinder implements Cloneable, java.io.Serializable\begin{hide} {
+ private static final long serialVersionUID = -4847630831331065792L;
+\end{hide}
+
+ private List<List<String>> imports\begin{hide} = new LinkedList<List<String>>();
+\end{hide}
+\end{code}
+\begin{tabb} Contains the saved import lists.
+ Each element of this list is a nested \externalclass{java.util}{List} containing
+ \externalclass{java.lang}{String}'s, each string containing the fully qualified
+ name of an imported package or class.
+\end{tabb}
+\begin{htmlonly}
+ \blocktag{@serial}{}
+\end{htmlonly}
+\begin{code}
+
+ public ClassFinder()\begin{hide} {
+ List<String> imp = new ArrayList<String>();
+ imports.add (imp);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new class finder with
+ an empty list of import declarations.
+\end{tabb}
+\begin{code}
+
+ public List<String> getImports()\begin{hide} {
+ return imports.get (imports.size() - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the current list of import declarations.
+ This list may contain only \externalclass{java.lang}{String}'s
+ of the form \texttt{java.class.name} or
+ \texttt{java.package.name.*}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the current list of import declarations.}
+\end{htmlonly}
+\begin{code}
+
+ public void saveImports()\begin{hide} {
+ List<String> imp = getImports();
+ List<String> impBack = new ArrayList<String> (imp);
+ imports.add (impBack);
+ }\end{hide}
+\end{code}
+\begin{tabb} Saves the current import list on the import stack.
+ This method makes a copy of the list returned
+ by \method{getImports}{()} and puts it on top
+ of a stack to be restored later by
+ \method{restoreImports}{()}.
+\end{tabb}
+\begin{code}
+
+ public void restoreImports()\begin{hide} {
+ if (imports.size() == 1)
+ getImports().clear();
+ else
+ imports.remove (imports.size() - 1);
+ }\end{hide}
+\end{code}
+\begin{tabb} Restores the list of import declarations.
+ This method removes the last list of import
+ declarations from the stack. If the stack
+ contains only one list, this list is cleared.
+\end{tabb}
+\begin{code}
+
+ public Class<?> findClass (String name) throws
+ ClassNotFoundException, NameConflictException\begin{hide} {
+ // Try to consider the name as a fully qualified class name
+ try {
+ return Class.forName (name);
+ }
+ catch (ClassNotFoundException cnfe) {}
+
+ List<String> imports = getImports();
+ Class<?> candidate = null;
+ String candidateImportString = "";
+ boolean candidateImportOnDemand = false;
+
+ // Determines the name of the outermost class
+ // if name corresponds to the simple name of a nested class, e.g., for A.B.C
+ // outerName will contain A.
+ int idxOut = name.indexOf ('.');
+ String outerName;
+ if (idxOut == -1)
+ outerName = name;
+ else
+ outerName = name.substring (0, idxOut);
+ for (String importString : imports) {
+ // For each import declaration, we try to load
+ // a class and store the result in cl.
+ // When cl is not null, the Class object is
+ // compared with the best candidate candidate.
+ Class<?> cl = null;
+ boolean onDemand = false;
+ if (!importString.endsWith (".*")) {
+ // Single-type import declaration
+ // We must ensure that this will correspond to
+ // the desired class name. For example, if we
+ // search List and the import java.util.ArrayList
+ // is found in importString, the ArrayList
+ // class must not be returned.
+ if (importString.endsWith ("." + outerName)) {
+ // The name of outer class was found in importString and
+ // a period is found left to it.
+ // So try to load this class.
+ // Simple class names have precedence over
+ // on-demand names.
+
+ // Replace, in importString, the name of
+ // the outer class with the true class name
+ // we want to load. If the simple name
+ // does not refer to a nested class, this
+ // has no effect.
+ String cn = importString.substring
+ (0, importString.length() - outerName.length()) + name;
+ try {
+ cl = Class.forName (cn);
+ }
+ catch (ClassNotFoundException cnfe) {}
+ }
+ }
+ else {
+ // Type import on demand declaration
+ try {
+ // Replace the * with name and
+ // try to load the class.
+ // If that succeeds, our candidate cl
+ // is onDemand.
+ cl = Class.forName
+ (importString.substring (0, importString.length() - 1) + name);
+ onDemand = true;
+ }
+ catch (ClassNotFoundException cnfe) {}
+ }
+ if (cl != null) {
+ // Something was loaded
+ if (candidate == null ||
+ (candidateImportOnDemand && !onDemand)) {
+ // We had no candidate or the candidate was imported
+ // on-demand while this one is a single-type import.
+ candidate = cl;
+ candidateImportString = importString;
+ candidateImportOnDemand = onDemand;
+ }
+ else if (candidate != cl)
+ throw new NameConflictException
+ (this, name,
+ "simple class name " + name +
+ " matches " + candidate.getName() +
+ " (import string " + candidateImportString + ") or " +
+ cl.getName() + " (import string " + importString + ")");
+ }
+ }
+ if (candidate == null)
+ throw new ClassNotFoundException
+ ("Cannot find the class with name " + name);
+ return candidate;
+ }\end{hide}
+\end{code}
+\begin{tabb} Tries to find the class corresponding to the simple
+ name \texttt{name}. The method first considers
+ the argument as a fully qualified class name
+ and calls \externalmethod{java.lang}{Class}{forName}{(String)} \texttt{(name)}.
+ If the class cannot be found, it considers the argument
+ as a simple name. A simple name refers to a class without
+ specifying the package declaring it. To convert
+ simple names to qualified names, the method iterates through
+ all the strings in the list returned by \method{getImports}{()},
+ applying
+ the same rules as a Java compiler to resolve
+ the class name. However, if an imported package or
+ class does not exist, it will be ignored whereas
+ the compiler would stop with an error.
+
+ For the class with simple name \texttt{name} to
+ be loaded, it must be imported explicitly (single-type import) or
+ one of the imported packages must contain it (type import on-demand).
+ If the class with name \texttt{name} is imported explicitly,
+ this import declaration has precedence over
+ any imported packages.
+ If several import declaration match the given simple
+ name, e.g., if several fully qualified names with the same
+ simple name are imported, or if a class with simple
+ name \texttt{name} exists in several packages,
+ a \class{NameConflictException} is thrown.
+\end{tabb}
+\begin{htmlonly}
+ \param{name}{the simple name of the class.}
+ \return{a reference to the class being loaded.}
+ \exception{ClassNotFoundException}{if the class
+ cannot be loaded.}
+ \exception{NameConflictException}{if a name conflict occurred.}
+\end{htmlonly}
+\begin{code}
+
+ public String getSimpleName (Class<?> cls)\begin{hide} {
+ if (cls.isArray())
+ return getSimpleName (cls.getComponentType()) + "[]";
+ if (cls.isPrimitive())
+ return cls.getName();
+ Class<?> outer = cls;
+ while (outer.getDeclaringClass() != null)
+ outer = outer.getDeclaringClass();
+ boolean needsFullyQualified = true;
+ for (String importString : getImports()) {
+ if (importString.equals (outer.getName()))
+ // A single-type import is given, can return an unqualified name.
+ needsFullyQualified = false;
+ else if (importString.endsWith (".*")) {
+ // Remove the .* at the end of the import string to get a package name.
+ String pack = importString.substring (0, importString.length() - 2);
+ // Compare this package name.
+ if (pack.equals (cls.getPackage().getName()))
+ needsFullyQualified = false;
+ }
+ }
+ if (needsFullyQualified)
+ return cls.getName();
+ else {
+ String name = cls.getName();
+ String pack = cls.getPackage().getName();
+ if (!name.startsWith (pack))
+ throw new IllegalStateException
+ ("The class name " + name +
+ " does not contain the package name " + pack);
+
+ // Removes the package and the . from the fully qualified class name.
+ return name.substring (pack.length() + 1);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the simple name of the class \texttt{cls} that
+ can be used when the imports contained
+ in this class finder are used.
+ For example, if \texttt{java.lang.String.class} is given
+ to this method, \texttt{String} is returned if
+ \texttt{java.lang.*} is among the import declarations.
+
+ Note: this method does not try to find name conflicts.
+ This operation is performed by \method{findClass}{(String)} only.
+ For example, if the list of imported declarations
+ contains \texttt{foo.bar.*} and \texttt{test.Foo}, and
+ the simple name for \texttt{test.Foo} is queried,
+ the method returns \texttt{Foo} even if the package
+ \texttt{foo.bar} contains a \texttt{Foo} class.
+\end{tabb}
+\begin{htmlonly}
+ \param{cls}{the class for which the simple name is queried.}
+ \return{the simple class name.}
+\end{htmlonly}
+\begin{code}
+
+ public ClassFinder clone()\begin{hide} {
+ ClassFinder cf;
+ try {
+ cf = (ClassFinder)super.clone();
+ }
+ catch (CloneNotSupportedException cne) {
+ throw new InternalError
+ ("CloneNotSupported thrown for a class implementing Cloneable");
+ }
+ cf.imports = new LinkedList<List<String>>();
+ for (List<String> imp : imports) {
+ List<String> impCpy = new ArrayList<String> (imp);
+ cf.imports.add (impCpy);
+ }
+ return cf;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Clones this class finder, and copies its lists of
+ import declarations.
+\end{tabb}
diff --git a/source/umontreal/iro/lecuyer/util/DMatrix.java b/source/umontreal/iro/lecuyer/util/DMatrix.java
new file mode 100644
index 0000000..f89df7e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/DMatrix.java
@@ -0,0 +1,1048 @@
+
+/*
+ * Class: DMatrix
+ * Description: Methods for matrix calculations with double numbers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+ import cern.colt.matrix.*;
+ import cern.colt.matrix.impl.*;
+ import cern.colt.matrix.linalg.*;
+ import cern.jet.math.Functions;
+
+
+/**
+ * This class implements a few methods for matrix calculations
+ * with <TT>double</TT> numbers.
+ *
+ */
+public class DMatrix {
+ private double [][] mat; // matrix of double's
+ private int r, c; // number of rows, columns
+
+ // [9/9] Pade numerator coefficients for exp(x);
+ private static double[] cPade = {17643225600d, 8821612800d, 2075673600d,
+ 302702400, 30270240, 2162160, 110880, 3960, 90, 1};
+
+ // [7/7] Pade numerator coefficients for (exp(x) - 1) / x
+ private static double[] p_em1 = {1.0, 1.0/30, 1.0/30, 1.0/936,
+ 1.0/4680, 1.0/171600, 1.0/3603600, 1.0/259459200};
+
+ // [7/7] Pade denominator coefficients for (exp(x) - 1) / x
+ private static double[] q_em1 ={1.0, -(7.0/15), 1.0/10, -(1.0/78),
+ 1.0/936, -(1.0/17160), 1.0/514800, -(1.0/32432400)};
+
+
+ //======================================================================
+
+ /*
+ Matrix multiplication $C = AB$. All three matrices are square, banded,
+ and upper triangular. $A$ has a non-zero diagonal, \texttt{sa} non-zero
+ superdiagonals, and thus a bandwidth of \texttt{sa + 1}. The non-zero
+ elements of $A_{ij}$ are those for which $j - s_a \le i \le j$.
+ Similarly for $B$ which has a bandwidth of \texttt{sb + 1}.
+ The resulting matrix $C$ has \texttt{sa + sb} non-zero superdiagonals,
+ and a bandwidth of \texttt{sa + sb + 1}.
+ */
+ private static void innerMultBand (DoubleMatrix2D A0, int sa,
+ DoubleMatrix2D B0, int sb,
+ DoubleMatrix2D C) {
+ DoubleMatrix2D A, B;
+ if (A0 == C)
+ A = A0.copy ();
+ else
+ A = A0;
+ if (B0 == C)
+ B = B0.copy ();
+ else
+ B = B0;
+ C.assign(0.);
+ final int n = A.rows();
+ int kmin, kmax;
+ double x, y, z;
+ for (int i = 0; i < n; ++i) {
+ int jmax = Math.min(i + sa + sb, n - 1);
+ for (int j = i; j <= jmax; ++j) {
+ kmin = Math.max(i, j - sb);
+ kmax = Math.min(i + sa, j);
+ z = 0;
+ for (int k = kmin; k <= kmax; ++k) {
+ x = A.getQuick (i, k);
+ y = B.getQuick (k, j);
+ z += x * y;
+ }
+ C.setQuick (i, j, z);
+ }
+ }
+ }
+
+
+ //======================================================================
+
+ private static int getScale (final DoubleMatrix2D A, double theta)
+ {
+ // assumes A is an upper bidiagonal matrix
+ final double norm = norm1bidiag(A) / theta;
+ int s;
+ if (norm > 1)
+ s = (int) Math.ceil(Num.log2(norm));
+ else
+ s = 0;
+ return s;
+ }
+
+
+ //======================================================================
+
+ private static DoubleMatrix2D m_taylor (final DoubleMatrix2D A)
+ {
+ // Compute and returns (e^A - I), using the Taylor series around 0
+
+ final double EPS = 1.0e-12;
+ final int k = A.rows();
+ final int JMAX = 2 * k + 100;
+ DoubleMatrix2D Sum = A.copy();
+ DoubleMatrix2D Term = A.copy();
+
+ Functions F = Functions.functions; // alias F
+ Algebra alge = new Algebra();
+ double normS, normT;
+
+ for (int j = 2; j <= JMAX; ++j) {
+ Term = alge.mult(A, Term); // Term <-- A*Term
+ Term.assign (F.mult(1.0 / j)); // Term <-- Term/j
+ Sum.assign (Term, F.plus); // Sum <-- Sum + Term
+ if (j > k + 5) {
+ normS = alge.normInfinity(Sum);
+ normT = alge.normInfinity(Term);
+ if (normT <= normS * EPS)
+ break;
+ }
+ }
+ return Sum;
+ }
+
+ //======================================================================
+
+ private static DoubleMatrix1D m_taylor (final DoubleMatrix2D A, final DoubleMatrix1D b)
+ {
+ // Compute and returns (exp(A) - I)b, using the Taylor series around 0
+
+ final double EPS = 1.0e-12;
+ final int k = A.rows();
+ final int JMAX = 2 * k + 100;
+ DoubleFactory1D factory = DoubleFactory1D.dense;
+ DoubleMatrix1D Term = b.copy();
+ DoubleMatrix1D Sum = factory.make(k);
+
+ Functions F = Functions.functions; // alias F
+ Algebra alge = new Algebra();
+ double Snorm, Tnorm;
+
+ for (int j = 1; j <= JMAX; ++j) {
+ Term = alge.mult(A, Term); // Term <-- A*Term
+ Term.assign (F.mult(1.0 / j)); // Term <-- Term/j
+ Sum.assign (Term, F.plus); // Sum <-- Sum + Term
+ if (j > k + 5) {
+ Tnorm = alge.norm1(Term);
+ Snorm = alge.norm1(Sum);
+ if (Tnorm <= Snorm * EPS)
+ break;
+ }
+ }
+ return Sum;
+ }
+
+
+ //======================================================================
+
+ private static DoubleMatrix2D m_expmiBidiag (final DoubleMatrix2D A)
+ {
+ // Use the diagonal Pade approximant of order [7/7] for (exp(A) - I)/A:
+ // See Higham J.H., Functions of matrices, SIAM, 2008, p. 262.
+ // This method scale A to a matrix with small norm B = A/2^s,
+ // compute U = (exp(B) - I) with Pade approximants, and returns U.
+ // Returns also the scale s in scale[0].
+
+ final int n = A.rows();
+ DoubleMatrix2D B = A.copy();
+ DoubleFactory2D fac = DoubleFactory2D.dense;
+ final DoubleMatrix2D I = fac.identity(n);
+ final DoubleMatrix2D B2 = fac.make(n, n);
+ final DoubleMatrix2D B4 = fac.make(n, n);
+ final DoubleMatrix2D B6 = fac.make(n, n);
+ DMatrix.multBand(B, 1, B, 1, B2); // B^2
+ DMatrix.multBand(B2, 2, B2, 2, B4); // B^4
+ DMatrix.multBand(B4, 4, B2, 2, B6); // B^6
+
+ DoubleMatrix2D V = B6.copy();
+ DoubleMatrix2D U = B4.copy();
+ DMatrix.addMultBand(p_em1[4], U, 4, p_em1[2], B2, 2); // U <-- U*p_em1[4] + B2*p_em1[2]
+ DMatrix.addMultBand(p_em1[6], V, 6, p_em1[0], I, 0); // V <-- V*p_em1[6] + I*p_em1[0]
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 6); // V <-- V + U
+
+ DoubleMatrix2D W = B6.copy();
+ U = B4.copy();
+ DMatrix.addMultBand(p_em1[5], U, 4, p_em1[3], B2, 2); // U <-- U*p_em1[5] + B2*p_em1[3]
+ DMatrix.addMultBand(p_em1[7], W, 6, p_em1[1], I, 0); // W <-- W*p_em1[7] + I*p_em1[1]
+ DMatrix.addMultBand(1.0, W, 6, 1.0, U, 6); // W <-- W + U
+ DMatrix.multBand(W, 6, B, 1, U); // U <-- W*B
+
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 7); // V <-- V + U
+ DoubleMatrix2D N = V.copy(); // numerator Pade
+
+ V = B6.copy();
+ U = B4.copy();
+ DMatrix.addMultBand(q_em1[4], U, 4, q_em1[2], B2, 2); // U <-- U*q_em1[4] + B2*q_em1[2]
+ DMatrix.addMultBand(q_em1[6], V, 6, q_em1[0], I, 0); // V <-- V*q_em1[6] + I*q_em1[0]
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 6); // V <-- V + U
+
+ W = B6.copy();
+ U = B4.copy();
+ DMatrix.addMultBand(q_em1[5], U, 4, q_em1[3], B2, 2); // U <-- U*q_em1[5] + B2*q_em1[3]
+ DMatrix.addMultBand(q_em1[7], W, 6, q_em1[1], I, 0); // W <-- W*q_em1[7] + I*q_em1[1]
+ DMatrix.addMultBand(1.0, W, 6, 1.0, U, 6); // W <-- W + U
+ DMatrix.multBand(W, 6, B, 1, U); // U <-- W*B
+
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 7); // V <-- V + U, denominator Pade
+
+ // Compute Pade approximant W = N/V for (exp(B) - I)/B
+ DMatrix.solveTriangular(V, N, W);
+ DMatrix.multBand(B, 1, W, n - 1, U); // (exp(B) - I) = U <-- B*W
+
+ // calcDiagm1 (B, U);
+ return U;
+ }
+
+
+ static void addMultTriang (final DoubleMatrix2D A, DoubleMatrix1D b, double h) {
+ /* Multiplies the upper triangular matrix A by vector b multiplied by h.
+ Put the result back in b.
+ */
+ final int n = A.rows();
+ double z;
+ for (int i = 0; i < n; ++i) {
+ for (int j = i; j < n; ++j) {
+ z = A.getQuick (i, j) * b.getQuick (j);
+ b.setQuick (i, h*z);
+ }
+ }
+ }
+
+
+ //======================================================================
+ /*
+ * Compute the 1-norm for matrix B, which is bidiagonal. The only non-zero
+ * elements are on the diagonal and the first superdiagonal.
+ *
+ * @param B matrix
+ * @return the norm
+ */
+ private static double norm1bidiag (DoubleMatrix2D B)
+ {
+ final int n = B.rows();
+ double x;
+ double norm = Math.abs(B.getQuick(0, 0));
+ for (int i = 1; i < n; ++i) {
+ x = Math.abs(B.getQuick(i - 1, i)) + Math.abs(B.getQuick(i, i));
+ if (x > norm)
+ norm = x;
+ }
+ return norm;
+ }
+
+ /**
+ * Creates a new <TT>DMatrix</TT> with <TT>r</TT> rows and
+ * <TT>c</TT> columns.
+ *
+ * @param r the number of rows
+ *
+ * @param c the number of columns
+ *
+ *
+ */
+ public DMatrix (int r, int c) {
+ mat = new double[r][c];
+ this.r = r;
+ this.c = c;
+ }
+
+
+ /**
+ * Creates a new <TT>DMatrix</TT> with <TT>r</TT> rows and
+ * <TT>c</TT> columns using the data in <TT>data</TT>.
+ *
+ * @param data the data of the new <TT>DMatrix</TT>
+ *
+ * @param r the number of rows
+ *
+ * @param c the number of columns
+ *
+ *
+ */
+ public DMatrix (double[][] data, int r, int c) {
+ this (r, c);
+ for(int i = 0; i < r; i++)
+ for(int j = 0; j < c; j++)
+ mat[i][j] = data[i][j];
+ }
+
+
+ /**
+ * Copy constructor.
+ *
+ * @param that the <TT>DMatrix</TT> to copy
+ *
+ */
+ public DMatrix (DMatrix that) {
+ this (that.mat, that.r, that.c);
+ }
+
+
+ /**
+ * Given a symmetric positive-definite matrix <SPAN CLASS="MATH"><I>M</I></SPAN>, performs the
+ * Cholesky decomposition of <SPAN CLASS="MATH"><I>M</I></SPAN> and returns the result as a lower triangular
+ * matrix <SPAN CLASS="MATH"><I>L</I></SPAN>, such that <SPAN CLASS="MATH"><I>M</I> = <I>LL</I><SUP>T</SUP></SPAN>.
+ *
+ * @param M the input matrix
+ *
+ * @param L the Cholesky lower triangular matrix
+ *
+ *
+ */
+ public static void CholeskyDecompose (double[][] M, double[][] L) {
+ int d = M.length;
+ DoubleMatrix2D MM = new DenseDoubleMatrix2D (M);
+ DoubleMatrix2D LL = new DenseDoubleMatrix2D (d, d);
+ CholeskyDecomposition decomp = new CholeskyDecomposition (MM);
+ LL = decomp.getL();
+ for(int i = 0; i < L.length; i++)
+ for(int j = 0; j <= i; j++)
+ L[i][j] = LL.get(i,j);
+ for(int i = 0; i < L.length; i++)
+ for(int j = i + 1; j < L.length; j++)
+ L[i][j] = 0.0;
+ }
+
+
+ /**
+ * Given a symmetric positive-definite matrix <SPAN CLASS="MATH"><I>M</I></SPAN>, performs the
+ * Cholesky decomposition of <SPAN CLASS="MATH"><I>M</I></SPAN> and returns the result as a lower triangular
+ * matrix <SPAN CLASS="MATH"><I>L</I></SPAN>, such that <SPAN CLASS="MATH"><I>M</I> = <I>LL</I><SUP>T</SUP></SPAN>.
+ *
+ * @param M the input matrix
+ *
+ * @return the Cholesky lower triangular matrix
+ *
+ */
+ public static DoubleMatrix2D CholeskyDecompose (DoubleMatrix2D M) {
+ CholeskyDecomposition decomp = new CholeskyDecomposition (M);
+ return decomp.getL();
+ }
+
+
+ /**
+ * Computes the principal components decomposition <SPAN CLASS="MATH"><I>M</I></SPAN> =
+ *
+ * <SPAN CLASS="MATH"><I>UΛU</I><SUP>t</SUP></SPAN> by using the singular value decomposition of matrix
+ * <SPAN CLASS="MATH"><I>M</I></SPAN>. Puts the eigenvalues, which are the diagonal elements of matrix <SPAN CLASS="MATH"><I>Λ</I></SPAN>,
+ * sorted by decreasing size, in vector <TT>lambda</TT>, and puts matrix
+ *
+ * <SPAN CLASS="MATH"><I>A</I> = <I>U</I>(Λ)<SUP>1/2</SUP></SPAN> in <TT>A</TT>.
+ *
+ * @param M input matrix
+ *
+ * @param A matrix square root of M
+ *
+ * @param lambda the eigenvalues
+ *
+ *
+ */
+ public static void PCADecompose (double[][] M, double[][] A,
+ double[] lambda) {
+ int d = M.length;
+ DoubleMatrix2D MM = new DenseDoubleMatrix2D (M);
+ DoubleMatrix2D AA = new DenseDoubleMatrix2D (d, d);
+ AA = PCADecompose(MM, lambda);
+
+ for(int i = 0; i < d; i++)
+ for(int j = 0; j < d; j++)
+ A[i][j] = AA.get(i,j);
+ }
+
+
+ /**
+ * Computes the principal components decomposition <SPAN CLASS="MATH"><I>M</I></SPAN> =
+ *
+ * <SPAN CLASS="MATH"><I>UΛU</I><SUP>t</SUP></SPAN> by using the singular value decomposition of matrix
+ * <SPAN CLASS="MATH"><I>M</I></SPAN>. Puts the eigenvalues, which are the diagonal elements of matrix <SPAN CLASS="MATH"><I>Λ</I></SPAN>,
+ * sorted by decreasing size, in vector <TT>lambda</TT>. Returns matrix
+ *
+ * <SPAN CLASS="MATH"><I>A</I> = <I>U</I>(Λ)<SUP>1/2</SUP></SPAN>.
+ *
+ * @param M input matrix
+ *
+ * @param lambda the eigenvalues
+ *
+ * @return matrix square root of M
+ *
+ */
+ public static DoubleMatrix2D PCADecompose (DoubleMatrix2D M,
+ double[] lambda) {
+ // L'objet SingularValueDecomposition permet de recuperer la matrice
+ // des valeurs propres en ordre decroissant et celle des vecteurs propres de
+ // sigma (pour une matrice symetrique et definie-positive seulement).
+
+ SingularValueDecomposition sv = new SingularValueDecomposition(M);
+ // D contient les valeurs propres sur la diagonale
+ DoubleMatrix2D D = sv.getS ();
+
+ for (int i = 0; i < D.rows(); ++i)
+ lambda[i] = D.getQuick (i, i);
+
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); ++i)
+ D.setQuick (i, i, Math.sqrt (lambda[i]));
+ DoubleMatrix2D P = sv.getV();
+ // Multiplier par la matrice orthogonale (ici P)
+ return P.zMult (D, null);
+ }
+
+
+ /**
+ * Solves the matrix equation <SPAN CLASS="MATH"><I>Ax</I> = <I>b</I></SPAN> using LU decomposition.
+ * <SPAN CLASS="MATH"><I>A</I></SPAN> is a square matrix, <SPAN CLASS="MATH"><I>b</I></SPAN> and <SPAN CLASS="MATH"><I>x</I></SPAN> are vectors. Returns the solution <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ * @param A square matrix
+ *
+ * @param b right side vector
+ *
+ * @return the solution vector
+ *
+ */
+ public static double[] solveLU (double[][] A, double[] b) {
+ DoubleMatrix2D M = new DenseDoubleMatrix2D(A);
+ DoubleMatrix1D c = new DenseDoubleMatrix1D(b);
+ LUDecompositionQuick lu = new LUDecompositionQuick();
+ lu.decompose(M);
+ lu.solve(c);
+ return c.toArray();
+ }
+
+
+ /**
+ * Solve the triangular matrix equation <SPAN CLASS="MATH"><I>UX</I> = <I>B</I></SPAN> for <SPAN CLASS="MATH"><I>X</I></SPAN>.
+ * <SPAN CLASS="MATH"><I>U</I></SPAN> is a square upper triangular matrix. <SPAN CLASS="MATH"><I>B</I></SPAN> and <SPAN CLASS="MATH"><I>X</I></SPAN> must have the same
+ * number of columns.
+ *
+ * @param U input matrix
+ *
+ * @param B right-hand side matrix
+ *
+ * @param X output matrix
+ *
+ *
+ */
+ public static void solveTriangular (DoubleMatrix2D U, DoubleMatrix2D B,
+ DoubleMatrix2D X) {
+ final int n = U.rows();
+ final int m = B.columns();
+ double y, z;
+ X.assign(0.);
+ for (int j = 0; j < m; ++j) {
+ for (int i = n - 1; i >= 0; --i) {
+ z = B.getQuick(i, j);
+ for (int k = i + 1; k < n; ++k)
+ z -= U.getQuick(i, k) * X.getQuick(k, j);
+ z /= U.getQuick(i, i);
+ X.setQuick(i, j, z);
+ }
+ }
+ }
+
+
+ /**
+ * Similar to {@link #exp((DoubleMatrix2D)) exp}<TT>(A)</TT>.
+ *
+ * @param A input matrix
+ *
+ * @return the exponential of <SPAN CLASS="MATH"><I>A</I></SPAN>
+ *
+ */
+ public static double[][] exp (double[][] A) {
+ DoubleMatrix2D V = new DenseDoubleMatrix2D(A);
+ DoubleMatrix2D R = exp(V);
+ return R.toArray();
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP></SPAN>, the exponential of the square matrix <SPAN CLASS="MATH"><I>A</I></SPAN>.
+ * The scaling and squaring method is used with
+ * Padé approximants to compute the exponential.
+ *
+ * @param A input matrix
+ *
+ * @return the exponential of <SPAN CLASS="MATH"><I>A</I></SPAN>
+ *
+ */
+ public static DoubleMatrix2D exp (final DoubleMatrix2D A) {
+ /*
+ * Use the diagonal Pade approximant of order [9/9] for exp:
+ * See Higham J.H., Functions of matrices, SIAM, 2008.
+ */
+ DoubleMatrix2D B = A.copy();
+ int n = B.rows();
+ Algebra alge = new Algebra();
+ final double mu = alge.trace(B) / n;
+ double x;
+
+ // B <-- B - mu*I
+ for (int i = 0; i < n; ++i) {
+ x = B.getQuick (i, i);
+ B.setQuick (i, i, x - mu);
+ }
+ /*
+ int bal = 0;
+ if (bal > 0) {
+ throw new UnsupportedOperationException (" balancing");
+ } */
+
+ final double THETA9 = 2.097847961257068; // in Higham
+ int s = getScale (B, THETA9);
+
+ Functions F = Functions.functions; // alias F
+ // B <-- B/2^s
+ double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException (" v <= 0");
+ B.assign (F.mult(v));
+
+ DoubleFactory2D fac = DoubleFactory2D.dense;
+ final DoubleMatrix2D B0 = fac.identity(n); // B^0 = I
+ final DoubleMatrix2D B2 = alge.mult(B, B); // B^2
+ final DoubleMatrix2D B4 = alge.mult(B2, B2); // B^4
+
+ DoubleMatrix2D T = B2.copy(); // T = work matrix
+ DoubleMatrix2D W = B4.copy(); // W = work matrix
+ W.assign (F.mult(cPade[9])); // W <-- W*cPade[9]
+ W.assign (T, F.plusMult(cPade[7])); // W <-- W + T*cPade[7]
+ DoubleMatrix2D U = alge.mult(B4, W); // U <-- B4*W
+
+ // T = B2.copy();
+ W = B4.copy();
+ W.assign (F.mult(cPade[5])); // W <-- W*cPade[5]
+ W.assign (T, F.plusMult(cPade[3])); // W <-- W + T*cPade[3]
+ W.assign (B0, F.plusMult(cPade[1])); // W <-- W + B0*cPade[1]
+ U.assign (W, F.plus); // U <-- U + W
+ U = alge.mult(B, U); // U <-- B*U
+
+ // T = B2.copy();
+ W = B4.copy();
+ W.assign (F.mult(cPade[8])); // W <-- W*cPade[8]
+ W.assign (T, F. plusMult(cPade[6])); // W <-- W + T*cPade[6]
+ DoubleMatrix2D V = alge.mult(B4, W); // V <-- B4*W
+
+ // T = B2.copy();
+ W = B4.copy();
+ W.assign (F.mult(cPade[4])); // W <-- W*cPade[4]
+ W.assign (T, F.plusMult(cPade[2])); // W <-- W + T*cPade[2]
+ W.assign (B0, F.plusMult(cPade[0])); // W <-- W + B0*cPade[0]
+ V.assign (W, F.plus); // V <-- V + W
+
+ W = V.copy();
+ W.assign(U, F.plus); // W = V + U, Pade numerator
+ T = V.copy();
+ T.assign(U, F.minus); // T = V - U, Pade denominator
+
+ // Compute Pade approximant for exponential = W / T
+ LUDecomposition lu = new LUDecomposition(T);
+ B = lu.solve(W);
+
+ if (false) {
+ // This overflows for large |mu|
+ // B <-- B^(2^s)
+ for(int i = 0; i < s; i++)
+ B = alge.mult(B, B);
+ /*
+ if (bal > 0) {
+ throw new UnsupportedOperationException (" balancing");
+ } */
+ v = Math.exp(mu);
+ B.assign (F.mult(v)); // B <-- B*e^mu
+
+ } else {
+ // equivalent to B^(2^s) * e^mu, but only if no balancing
+ double r = mu * v;
+ r = Math.exp(r);
+ B.assign (F.mult(r));
+ for (int i = 0; i < s; i++)
+ B = alge.mult(B, B);
+ }
+
+ return B;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP></SPAN>, the exponential of the <SPAN CLASS="textit">bidiagonal</SPAN>
+ * square matrix <SPAN CLASS="MATH"><I>A</I></SPAN>. The only non-zero elements of <SPAN CLASS="MATH"><I>A</I></SPAN> are on the diagonal and
+ * on the first superdiagonal. This method is faster than
+ * {@link #exp((DoubleMatrix2D)) exp}<TT>(A)</TT> because of the special form of <SPAN CLASS="MATH"><I>A</I></SPAN>.
+ *
+ * @param A bidiagonal matrix
+ *
+ * @return <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP></SPAN>
+ *
+ */
+ public static DoubleMatrix2D expBidiagonal (final DoubleMatrix2D A) {
+ // Use the diagonal Pade approximant of order [9/9] for exp:
+ // See Higham J.H., Functions of matrices, SIAM, 2008.
+ // This method scale A to a matrix with small norm B = A/2^s,
+ // compute U = exp(B) with Pade approximants, and returns U.
+
+ DoubleMatrix2D B = A.copy();
+ final int n = B.rows();
+ Algebra alge = new Algebra();
+ final double mu = alge.trace(B) / n;
+ double x;
+
+ // B <-- B - mu*I
+ for (int i = 0; i < n; ++i) {
+ x = B.getQuick(i, i);
+ B.setQuick(i, i, x - mu);
+ }
+
+ final double THETA9 = 2.097847961257068; // in Higham
+ int s = getScale (B, THETA9);
+ final double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException(" v <= 0");
+ DMatrix.multBand(B, 1, v); // B <-- B/2^s
+
+ DoubleFactory2D fac = DoubleFactory2D.dense;
+ DoubleMatrix2D T = fac.make(n, n);
+ DoubleMatrix2D B4 = fac.make(n, n);
+ DMatrix.multBand(B, 1, B, 1, T); // B^2
+ DMatrix.multBand(T, 2, T, 2, B4); // B^4
+
+ DoubleMatrix2D W = B4.copy(); // W = work matrix
+ DMatrix.addMultBand(cPade[9], W, 4, cPade[7], T, 2); // W <-- W*cPade[9] + T*cPade[7]
+ DoubleMatrix2D U = fac.make(n, n);
+ DMatrix.multBand(W, 4, B4, 4, U); // U <-- B4*W
+
+ W = B4.copy();
+ DMatrix.addMultBand(cPade[5], W, 4, cPade[3], T, 2); // W <-- W*cPade[5] + T*cPade[3]
+ for (int i = 0; i < n; ++i) { // W <-- W + I*cPade[1]
+ x = W.getQuick(i, i);
+ W.setQuick(i, i, x + cPade[1]);
+ }
+ DMatrix.addMultBand(1.0, U, 8, 1.0, W, 4); // U <-- U + W
+ DMatrix.multBand(B, 1, U, 8, U); // U <-- B*U
+
+ W = B4.copy();
+ DMatrix.addMultBand(cPade[8], W, 4, cPade[6], T, 2); // W <-- W*cPade[8] + T*cPade[6]
+ DoubleMatrix2D V = B;
+ DMatrix.multBand(W, 4, B4, 4, V); // V <-- B4*W
+
+ W = B4.copy();
+ DMatrix.addMultBand(cPade[4], W, 4, cPade[2], T, 2); // W <-- W*cPade[4] + T*cPade[2]
+ for (int i = 0; i < n; ++i) { // W <-- W + I*cPade[0]
+ x = W.getQuick(i, i);
+ W.setQuick(i, i, x + cPade[0]);
+ }
+ DMatrix.addMultBand(1.0, V, 8, 1.0, W, 4); // V <-- V + W
+
+ W = V.copy();
+ DMatrix.addMultBand(1.0, W, 9, 1.0, U, 9); // W = V + U, Pade numerator
+ T = V.copy();
+ DMatrix.addMultBand(1.0, T, 9, -1.0, U, 9); // T = V - U, Pade denominator
+
+ // Compute Pade approximant B = W/T for exponential
+ DMatrix.solveTriangular(T, W, B);
+
+ // equivalent to B^(2^s) * e^mu
+ double r = mu * v;
+ r = Math.exp(r);
+ DMatrix.multBand(B, n - 1, r); // B <-- B*r
+
+ T.assign(0.);
+
+ for (int i = 0; i < s; i++) {
+ DMatrix.multBand(B, n - 1, B, n - 1, T);
+ B = T.copy();
+ }
+
+ return B;
+ }
+
+
+ /**
+ * Computes <SPAN CLASS="MATH"><I>c</I> = <I>e</I><SUP>A</SUP><I>b</I></SPAN>, where <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP></SPAN> is the exponential of the <SPAN CLASS="textit">bidiagonal</SPAN>
+ * square matrix <SPAN CLASS="MATH"><I>A</I></SPAN>. The only non-zero elements of <SPAN CLASS="MATH"><I>A</I></SPAN> are on the diagonal and
+ * on the first superdiagonal.
+ * Uses the scaling and squaring method with
+ * Padé approximants. Returns <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ * @param A bidiagonal matrix
+ *
+ * @return <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP></SPAN>
+ *
+ */
+ public static DoubleMatrix1D expBidiagonal (final DoubleMatrix2D A,
+ final DoubleMatrix1D b) {
+ // This is probably not efficient;
+ DoubleMatrix2D U = expBidiagonal (A); // U = exp(A)
+ Algebra alge = new Algebra();
+ return alge.mult(U, b);
+ }
+
+
+ /**
+ * Computes <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP> - <I>I</I></SPAN>, where <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP></SPAN> is the exponential of the <SPAN CLASS="textit">bidiagonal</SPAN>
+ * square matrix <SPAN CLASS="MATH"><I>A</I></SPAN>. The only non-zero elements of <SPAN CLASS="MATH"><I>A</I></SPAN> are on the diagonal and
+ * on the first superdiagonal.
+ * Uses the scaling and squaring method with
+ * Padé approximants. Returns <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP> - <I>I</I></SPAN>.
+ *
+ * @param A bidiagonal matrix
+ *
+ * @return
+ * <SPAN CLASS="MATH">(<I>e</I><SUP>A</SUP> - <I>I</I>)<I>b</I></SPAN>
+ *
+ */
+ public static DoubleMatrix2D expmiBidiagonal (final DoubleMatrix2D A) {
+ // Use the diagonal Pade approximant of order [7/7] for (exp(A) - I)/A:
+ // See Higham J.H., Functions of matrices, SIAM, 2008, p. 262.
+
+ DoubleMatrix2D B = A.copy();
+ final double THETA = 1.13; // theta_{7,1}
+ int s = getScale (B, THETA);
+ final double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException(" v <= 0");
+ DMatrix.multBand(B, 1, v); // B <-- B/2^s
+ DoubleMatrix2D U = m_expmiBidiag (B); // U = exp(B) - I
+
+ DoubleMatrix2D N = U.copy();
+ addIdentity (N); // N <-- exp(B)
+ DoubleMatrix2D V = N.copy(); // V <-- exp(B)
+
+ // Now undo scaling of B = A/2^s using
+ // (exp(A) - I) = (exp(B) - I)(exp(B) + I)(exp(B^2) + I) ... (exp(B^(2^(s-1))) + I)
+
+ Algebra alge = new Algebra();
+ for (int i = 1; i <= s; i++) {
+ addIdentity (N); // N <-- exp(B) + I
+ U = alge.mult(N, U); // U <-- N*U
+ if (i < s) {
+ V = alge.mult(V, V); // V <-- V*V
+ N = V.copy();
+ }
+ }
+
+ return U;
+ }
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH"><I>c</I> = (<I>e</I><SUP>A</SUP> - <I>I</I>)<I>b</I></SPAN>, where <SPAN CLASS="MATH"><I>e</I><SUP>A</SUP></SPAN> is the exponential of the
+ * <SPAN CLASS="textit">bidiagonal</SPAN> square matrix <SPAN CLASS="MATH"><I>A</I></SPAN>.
+ * The only non-zero elements of <SPAN CLASS="MATH"><I>A</I></SPAN> are on the diagonal and
+ * on the first superdiagonal. Uses the scaling and squaring method with a Taylor expansion. Returns <SPAN CLASS="MATH"><I>c</I></SPAN>.
+ *
+ * @param A bidiagonal matrix
+ *
+ * @param b vector
+ *
+ * @return <SPAN CLASS="MATH"><I>c</I></SPAN>
+ *
+ */
+ public static DoubleMatrix1D expmiBidiagonal (final DoubleMatrix2D A,
+ final DoubleMatrix1D b) {
+ DoubleMatrix2D F = A.copy();
+ int s = getScale (F, 1.0 / 16.0);
+ final double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException(" v <= 0");
+ DMatrix.multBand(F, 1, v); // F <-- F/2^s
+
+ DoubleMatrix2D U = expBidiagonal (F); // U = exp(F)
+ DoubleMatrix2D N = U.copy();
+ DoubleMatrix1D C = m_taylor (F, b); // C = (exp(F) - I)b
+ DoubleMatrix1D D = C.copy();
+ Algebra alge = new Algebra();
+
+ // Now undo scaling of F = A/2^s using
+ // (exp(A) - I)b = (exp(F^(2^(s-1))) + I)...(exp(F^2) + I)(exp(F) + I)(exp(F) - I)b
+ for (int i = 1; i <= s; i++) {
+ addIdentity (N); // N <-- exp(F) + I
+ C = alge.mult(N, C); // C <-- N*C
+ if (i < s) {
+ U = alge.mult(U, U); // U <-- U*U
+ N = U.copy();
+ }
+ }
+
+ return C;
+ }
+
+
+ /**
+ * Matrix multiplication <SPAN CLASS="MATH"><I>C</I> = <I>AB</I></SPAN>. All three matrices are square,
+ * banded, and upper triangular. <SPAN CLASS="MATH"><I>A</I></SPAN> has a non-zero diagonal, <TT>sa</TT> non-zero
+ * superdiagonals, and thus a bandwidth of <TT>sa + 1</TT>.
+ * The non-zero elements of <SPAN CLASS="MATH"><I>A</I><SUB>ij</SUB></SPAN> are those for which
+ * <SPAN CLASS="MATH"><I>j</I> - <I>s</I><SUB>a</SUB> <= <I>i</I> <= <I>j</I></SPAN>.
+ * Similarly for <SPAN CLASS="MATH"><I>B</I></SPAN> which has a bandwidth of <TT>sb + 1</TT>.
+ * The resulting matrix <SPAN CLASS="MATH"><I>C</I></SPAN> has <TT>sa + sb</TT> non-zero superdiagonals, and a
+ * bandwidth of <TT>sa + sb + 1</TT>.
+ *
+ * @param A input matrix
+ *
+ * @param sa number of superdiagonals of A
+ *
+ * @param B input matrix
+ *
+ * @param sb number of superdiagonals of B
+ *
+ * @param C result
+ *
+ *
+ */
+ private static void addIdentity (DoubleMatrix2D A) {
+ // add identity matrix to matrix A: A <-- A + I
+ final int n = A.rows();
+ double x;
+ for (int i = 0; i < n; ++i) {
+ x = A.getQuick(i, i);
+ A.setQuick(i, i, x + 1.0);
+ }
+ }
+
+
+ private static void calcDiagm1 (DoubleMatrix2D A, DoubleMatrix2D R) {
+ // calc diagonal of expm1 of triangular matrix A: exp(A) - I
+ final int n = A.rows();
+ double x, v;
+ for (int i = 0; i < n; ++i) {
+ x = A.getQuick(i, i);
+ v = Math.expm1(x); // exp(x) - 1
+ R.setQuick(i, i, v);
+ }
+ }
+
+
+ static void multBand (final DoubleMatrix2D A, int sa,
+ final DoubleMatrix2D B, int sb,
+ DoubleMatrix2D C) {
+ innerMultBand (A, sa, B, sb, C);
+ }
+
+
+ /**
+ * Multiplication of the matrix <SPAN CLASS="MATH"><I>A</I></SPAN> by the scalar <SPAN CLASS="MATH"><I>h</I></SPAN>.
+ * <SPAN CLASS="MATH"><I>A</I></SPAN> is a square banded upper triangular matrix. It has a non-zero diagonal,
+ * <TT>sa</TT> superdiagonals, and thus a bandwidth of <TT>sa + 1</TT>.
+ * The result of the multiplication is put back in <SPAN CLASS="MATH"><I>A</I></SPAN>.
+ *
+ * @param A input and output matrix
+ *
+ * @param sa number of superdiagonals of A
+ *
+ * @param h scalar
+ *
+ *
+ */
+ static void multBand (DoubleMatrix2D A, int sa, double h) {
+ final int n = A.rows();
+ double z;
+ for (int i = 0; i < n; ++i) {
+ int jmax = Math.min(i + sa, n - 1);
+ for (int j = i; j <= jmax; ++j) {
+ z = A.getQuick (i, j);
+ A.setQuick (i, j, z*h);
+ }
+ }
+ }
+
+
+ static void addMultBand (double g, DoubleMatrix2D A, int sa,
+ double h, final DoubleMatrix2D B, int sb) {
+ DoubleMatrix2D S;
+ S = A.copy ();
+ final int n = A.rows();
+ double z;
+ for (int i = 0; i < n; ++i) {
+ int jmax = Math.max(i + sa, i + sb);
+ jmax = Math.min(jmax, n - 1);
+ for (int j = i; j <= jmax; ++j) {
+ z = g*S.getQuick (i, j) + h*B.getQuick (i, j);
+ A.setQuick (i, j, z);
+ }
+ }
+ }
+
+
+ /**
+ * Copies the matrix <SPAN CLASS="MATH"><I>M</I></SPAN> into <SPAN CLASS="MATH"><I>R</I></SPAN>.
+ *
+ * @param M original matrix
+ *
+ * @param R output matrix
+ *
+ *
+ */
+ public static void copy (double[][] M, double[][] R) {
+ for (int i = 0; i < M.length; i++) {
+ for(int j = 0; j < M[i].length; j++) {
+ R[i][j] = M[i][j];
+ }
+ }
+ }
+
+
+ /**
+ * Returns matrix <SPAN CLASS="MATH"><I>M</I></SPAN> as a string.
+ * It is displayed in matrix form, with each row on a line.
+ *
+ * @return the content of <SPAN CLASS="MATH"><I>M</I></SPAN>
+ *
+ */
+ public static String toString(double[][] M) {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("{" + PrintfFormat.NEWLINE);
+ for (int i = 0; i < M.length; i++) {
+ sb.append(" { ");
+ for(int j = 0; j < M[i].length; j++) {
+ sb.append(M[i][j] + " ");
+ if (j < M.length - 1)
+ sb.append(" ");
+ }
+ sb.append("}" + PrintfFormat.NEWLINE);
+ }
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+
+ /**
+ * Creates a {@link String} containing all the data of
+ * the <TT>DMatrix</TT>. The result is displayed in matrix form, with
+ * each row on a line.
+ *
+ * @return the content of the <TT>DMatrix</TT>
+ *
+ */
+ public String toString() {
+ return toString(mat);
+ }
+
+
+ /**
+ * Returns the number of rows of the <TT>DMatrix</TT>.
+ *
+ * @return the number of rows
+ *
+ */
+ public int numRows() {
+ return r;
+ }
+
+
+ /**
+ * Returns the number of columns of the <TT>DMatrix</TT>.
+ *
+ * @return the number of columns
+ *
+ */
+ public int numColumns() {
+ return c;
+ }
+
+
+ /**
+ * Returns the matrix element in the specified row and column.
+ *
+ * @param row the row of the selected element
+ *
+ * @param column the column of the selected element
+ *
+ * @return the value of the element
+ * @exception IndexOutOfBoundsException if the selected element would
+ * be outside the <TT>DMatrix</TT>
+ *
+ *
+ */
+ public double get (int row, int column) {
+ if (row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ return mat[row][column];
+ }
+
+
+ /**
+ * Sets the value of the element in the specified row and column.
+ *
+ * @param row the row of the selected element
+ *
+ * @param column the column of the selected element
+ *
+ * @param value the new value of the element
+ *
+ * @exception IndexOutOfBoundsException if the selected element would
+ * be outside the <TT>DMatrix</TT>
+ *
+ *
+ */
+ public void set (int row, int column, double value) {
+ if (row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ mat[row][column] = value;
+ }
+
+
+ /**
+ * Returns the transposed matrix. The rows and columns are
+ * interchanged.
+ *
+ * @return the transposed matrix
+ *
+ */
+ public DMatrix transpose() {
+ DMatrix result = new DMatrix(c,r);
+
+ for(int i = 0; i < r; i++)
+ for(int j = 0; j < c; j++)
+ result.mat[j][i] = mat[i][j];
+
+ return result;
+ }
+
+
+}
+
diff --git a/source/umontreal/iro/lecuyer/util/DMatrix.tex b/source/umontreal/iro/lecuyer/util/DMatrix.tex
new file mode 100644
index 0000000..9070103
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/DMatrix.tex
@@ -0,0 +1,1039 @@
+\defmodule {DMatrix}
+
+This class implements a few methods for matrix calculations
+with \texttt{double} numbers.
+\hrichard{Cette classe n'a pas \'et\'e beaucoup test\'ee.
+Doit-on enlever toutes les m\'ethodes except\'e les static?}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: DMatrix
+ * Description: Methods for matrix calculations with double numbers
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+ import cern.colt.matrix.*;\begin{hide}
+ import cern.colt.matrix.impl.*;
+ import cern.colt.matrix.linalg.*;
+ import cern.jet.math.Functions;\end{hide}
+
+
+public class DMatrix \begin{hide} {
+ private double [][] mat; // matrix of double's
+ private int r, c; // number of rows, columns
+
+ // [9/9] Pade numerator coefficients for exp(x);
+ private static double[] cPade = {17643225600d, 8821612800d, 2075673600d,
+ 302702400, 30270240, 2162160, 110880, 3960, 90, 1};
+
+ // [7/7] Pade numerator coefficients for (exp(x) - 1) / x
+ private static double[] p_em1 = {1.0, 1.0/30, 1.0/30, 1.0/936,
+ 1.0/4680, 1.0/171600, 1.0/3603600, 1.0/259459200};
+
+ // [7/7] Pade denominator coefficients for (exp(x) - 1) / x
+ private static double[] q_em1 ={1.0, -(7.0/15), 1.0/10, -(1.0/78),
+ 1.0/936, -(1.0/17160), 1.0/514800, -(1.0/32432400)};
+
+
+ //======================================================================
+
+ /*
+ Matrix multiplication $C = AB$. All three matrices are square, banded,
+ and upper triangular. $A$ has a non-zero diagonal, \texttt{sa} non-zero
+ superdiagonals, and thus a bandwidth of \texttt{sa + 1}. The non-zero
+ elements of $A_{ij}$ are those for which $j - s_a \le i \le j$.
+ Similarly for $B$ which has a bandwidth of \texttt{sb + 1}.
+ The resulting matrix $C$ has \texttt{sa + sb} non-zero superdiagonals,
+ and a bandwidth of \texttt{sa + sb + 1}.
+ */
+ private static void innerMultBand (DoubleMatrix2D A0, int sa,
+ DoubleMatrix2D B0, int sb,
+ DoubleMatrix2D C) {
+ DoubleMatrix2D A, B;
+ if (A0 == C)
+ A = A0.copy ();
+ else
+ A = A0;
+ if (B0 == C)
+ B = B0.copy ();
+ else
+ B = B0;
+ C.assign(0.);
+ final int n = A.rows();
+ int kmin, kmax;
+ double x, y, z;
+ for (int i = 0; i < n; ++i) {
+ int jmax = Math.min(i + sa + sb, n - 1);
+ for (int j = i; j <= jmax; ++j) {
+ kmin = Math.max(i, j - sb);
+ kmax = Math.min(i + sa, j);
+ z = 0;
+ for (int k = kmin; k <= kmax; ++k) {
+ x = A.getQuick (i, k);
+ y = B.getQuick (k, j);
+ z += x * y;
+ }
+ C.setQuick (i, j, z);
+ }
+ }
+ }
+
+
+ //======================================================================
+
+ private static int getScale (final DoubleMatrix2D A, double theta)
+ {
+ // assumes A is an upper bidiagonal matrix
+ final double norm = norm1bidiag(A) / theta;
+ int s;
+ if (norm > 1)
+ s = (int) Math.ceil(Num.log2(norm));
+ else
+ s = 0;
+ return s;
+ }
+
+
+ //======================================================================
+
+ private static DoubleMatrix2D m_taylor (final DoubleMatrix2D A)
+ {
+ // Compute and returns (e^A - I), using the Taylor series around 0
+
+ final double EPS = 1.0e-12;
+ final int k = A.rows();
+ final int JMAX = 2 * k + 100;
+ DoubleMatrix2D Sum = A.copy();
+ DoubleMatrix2D Term = A.copy();
+
+ Functions F = Functions.functions; // alias F
+ Algebra alge = new Algebra();
+ double normS, normT;
+
+ for (int j = 2; j <= JMAX; ++j) {
+ Term = alge.mult(A, Term); // Term <-- A*Term
+ Term.assign (F.mult(1.0 / j)); // Term <-- Term/j
+ Sum.assign (Term, F.plus); // Sum <-- Sum + Term
+ if (j > k + 5) {
+ normS = alge.normInfinity(Sum);
+ normT = alge.normInfinity(Term);
+ if (normT <= normS * EPS)
+ break;
+ }
+ }
+ return Sum;
+ }
+
+ //======================================================================
+
+ private static DoubleMatrix1D m_taylor (final DoubleMatrix2D A, final DoubleMatrix1D b)
+ {
+ // Compute and returns (exp(A) - I)b, using the Taylor series around 0
+
+ final double EPS = 1.0e-12;
+ final int k = A.rows();
+ final int JMAX = 2 * k + 100;
+ DoubleFactory1D factory = DoubleFactory1D.dense;
+ DoubleMatrix1D Term = b.copy();
+ DoubleMatrix1D Sum = factory.make(k);
+
+ Functions F = Functions.functions; // alias F
+ Algebra alge = new Algebra();
+ double Snorm, Tnorm;
+
+ for (int j = 1; j <= JMAX; ++j) {
+ Term = alge.mult(A, Term); // Term <-- A*Term
+ Term.assign (F.mult(1.0 / j)); // Term <-- Term/j
+ Sum.assign (Term, F.plus); // Sum <-- Sum + Term
+ if (j > k + 5) {
+ Tnorm = alge.norm1(Term);
+ Snorm = alge.norm1(Sum);
+ if (Tnorm <= Snorm * EPS)
+ break;
+ }
+ }
+ return Sum;
+ }
+
+
+ //======================================================================
+
+ private static DoubleMatrix2D m_expmiBidiag (final DoubleMatrix2D A)
+ {
+ // Use the diagonal Pade approximant of order [7/7] for (exp(A) - I)/A:
+ // See Higham J.H., Functions of matrices, SIAM, 2008, p. 262.
+ // This method scale A to a matrix with small norm B = A/2^s,
+ // compute U = (exp(B) - I) with Pade approximants, and returns U.
+ // Returns also the scale s in scale[0].
+
+ final int n = A.rows();
+ DoubleMatrix2D B = A.copy();
+ DoubleFactory2D fac = DoubleFactory2D.dense;
+ final DoubleMatrix2D I = fac.identity(n);
+ final DoubleMatrix2D B2 = fac.make(n, n);
+ final DoubleMatrix2D B4 = fac.make(n, n);
+ final DoubleMatrix2D B6 = fac.make(n, n);
+ DMatrix.multBand(B, 1, B, 1, B2); // B^2
+ DMatrix.multBand(B2, 2, B2, 2, B4); // B^4
+ DMatrix.multBand(B4, 4, B2, 2, B6); // B^6
+
+ DoubleMatrix2D V = B6.copy();
+ DoubleMatrix2D U = B4.copy();
+ DMatrix.addMultBand(p_em1[4], U, 4, p_em1[2], B2, 2); // U <-- U*p_em1[4] + B2*p_em1[2]
+ DMatrix.addMultBand(p_em1[6], V, 6, p_em1[0], I, 0); // V <-- V*p_em1[6] + I*p_em1[0]
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 6); // V <-- V + U
+
+ DoubleMatrix2D W = B6.copy();
+ U = B4.copy();
+ DMatrix.addMultBand(p_em1[5], U, 4, p_em1[3], B2, 2); // U <-- U*p_em1[5] + B2*p_em1[3]
+ DMatrix.addMultBand(p_em1[7], W, 6, p_em1[1], I, 0); // W <-- W*p_em1[7] + I*p_em1[1]
+ DMatrix.addMultBand(1.0, W, 6, 1.0, U, 6); // W <-- W + U
+ DMatrix.multBand(W, 6, B, 1, U); // U <-- W*B
+
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 7); // V <-- V + U
+ DoubleMatrix2D N = V.copy(); // numerator Pade
+
+ V = B6.copy();
+ U = B4.copy();
+ DMatrix.addMultBand(q_em1[4], U, 4, q_em1[2], B2, 2); // U <-- U*q_em1[4] + B2*q_em1[2]
+ DMatrix.addMultBand(q_em1[6], V, 6, q_em1[0], I, 0); // V <-- V*q_em1[6] + I*q_em1[0]
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 6); // V <-- V + U
+
+ W = B6.copy();
+ U = B4.copy();
+ DMatrix.addMultBand(q_em1[5], U, 4, q_em1[3], B2, 2); // U <-- U*q_em1[5] + B2*q_em1[3]
+ DMatrix.addMultBand(q_em1[7], W, 6, q_em1[1], I, 0); // W <-- W*q_em1[7] + I*q_em1[1]
+ DMatrix.addMultBand(1.0, W, 6, 1.0, U, 6); // W <-- W + U
+ DMatrix.multBand(W, 6, B, 1, U); // U <-- W*B
+
+ DMatrix.addMultBand(1.0, V, 6, 1.0, U, 7); // V <-- V + U, denominator Pade
+
+ // Compute Pade approximant W = N/V for (exp(B) - I)/B
+ DMatrix.solveTriangular(V, N, W);
+ DMatrix.multBand(B, 1, W, n - 1, U); // (exp(B) - I) = U <-- B*W
+
+ // calcDiagm1 (B, U);
+ return U;
+ }
+
+
+ static void addMultTriang (final DoubleMatrix2D A, DoubleMatrix1D b, double h) {
+ /* Multiplies the upper triangular matrix A by vector b multiplied by h.
+ Put the result back in b.
+ */
+ final int n = A.rows();
+ double z;
+ for (int i = 0; i < n; ++i) {
+ for (int j = i; j < n; ++j) {
+ z = A.getQuick (i, j) * b.getQuick (j);
+ b.setQuick (i, h*z);
+ }
+ }
+ }
+
+
+ //======================================================================
+ /*
+ * Compute the 1-norm for matrix B, which is bidiagonal. The only non-zero
+ * elements are on the diagonal and the first superdiagonal.
+ *
+ * @param B matrix
+ * @return the norm
+ */
+ private static double norm1bidiag (DoubleMatrix2D B)
+ {
+ final int n = B.rows();
+ double x;
+ double norm = Math.abs(B.getQuick(0, 0));
+ for (int i = 1; i < n; ++i) {
+ x = Math.abs(B.getQuick(i - 1, i)) + Math.abs(B.getQuick(i, i));
+ if (x > norm)
+ norm = x;
+ }
+ return norm;
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+
+\begin{code}
+ public DMatrix (int r, int c) \begin{hide} {
+ mat = new double[r][c];
+ this.r = r;
+ this.c = c;
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{DMatrix} with \texttt{r} rows and
+ \texttt{c} columns.
+\end{tabb}
+\begin{htmlonly}
+ \param{r}{the number of rows}
+ \param{c}{the number of columns}
+\end{htmlonly}
+\begin{code}
+
+ public DMatrix (double[][] data, int r, int c) \begin{hide} {
+ this (r, c);
+ for(int i = 0; i < r; i++)
+ for(int j = 0; j < c; j++)
+ mat[i][j] = data[i][j];
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a new \texttt{DMatrix} with \texttt{r} rows and
+ \texttt{c} columns using the data in \texttt{data}.
+\end{tabb}
+\begin{htmlonly}
+ \param{data}{the data of the new \texttt{DMatrix}}
+ \param{r}{the number of rows}
+ \param{c}{the number of columns}
+\end{htmlonly}
+\begin{code}
+
+ public DMatrix (DMatrix that) \begin{hide} {
+ this (that.mat, that.r, that.c);
+ } \end{hide}
+\end{code}
+\begin{tabb} Copy constructor.
+\end{tabb}
+\begin{htmlonly}
+ \param{that}{the \texttt{DMatrix} to copy}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static void CholeskyDecompose (double[][] M, double[][] L) \begin{hide} {
+ int d = M.length;
+ DoubleMatrix2D MM = new DenseDoubleMatrix2D (M);
+ DoubleMatrix2D LL = new DenseDoubleMatrix2D (d, d);
+ CholeskyDecomposition decomp = new CholeskyDecomposition (MM);
+ LL = decomp.getL();
+ for(int i = 0; i < L.length; i++)
+ for(int j = 0; j <= i; j++)
+ L[i][j] = LL.get(i,j);
+ for(int i = 0; i < L.length; i++)
+ for(int j = i + 1; j < L.length; j++)
+ L[i][j] = 0.0;
+ } \end{hide}
+\end{code}
+\begin{tabb} Given a symmetric positive-definite matrix $M$, performs the
+Cholesky decomposition of $M$ and returns the result as a lower triangular
+matrix $L$, such that $M = L L^T$.
+\end{tabb}
+\begin{htmlonly}
+ \param{M}{the input matrix}
+ \param{L}{the Cholesky lower triangular matrix}
+\end{htmlonly}
+\begin{code}
+
+ public static DoubleMatrix2D CholeskyDecompose (DoubleMatrix2D M) \begin{hide} {
+ CholeskyDecomposition decomp = new CholeskyDecomposition (M);
+ return decomp.getL();
+ } \end{hide}
+\end{code}
+\begin{tabb} Given a symmetric positive-definite matrix $M$, performs the
+Cholesky decomposition of $M$ and returns the result as a lower triangular
+matrix $L$, such that $M = L L^T$.
+\end{tabb}
+\begin{htmlonly}
+ \param{M}{the input matrix}
+ \return{the Cholesky lower triangular matrix}
+\end{htmlonly}
+\begin{code}
+
+ public static void PCADecompose (double[][] M, double[][] A,
+ double[] lambda)\begin{hide} {
+ int d = M.length;
+ DoubleMatrix2D MM = new DenseDoubleMatrix2D (M);
+ DoubleMatrix2D AA = new DenseDoubleMatrix2D (d, d);
+ AA = PCADecompose(MM, lambda);
+
+ for(int i = 0; i < d; i++)
+ for(int j = 0; j < d; j++)
+ A[i][j] = AA.get(i,j);
+ } \end{hide}
+\end{code}
+\begin{tabb} Computes the principal components decomposition $M$ =
+ $U\Lambda U^{\tr}$ by using the singular value decomposition of matrix
+$M$. Puts the eigenvalues, which are the diagonal elements of matrix $\Lambda$,
+sorted by decreasing size, in vector \texttt{lambda}, and puts matrix
+ $A = U\sqrt{\Lambda}$ in \texttt{A}.
+\end{tabb}
+\begin{htmlonly}
+ \param{M}{input matrix}
+ \param{A}{matrix square root of M}
+ \param{lambda}{the eigenvalues}
+\end{htmlonly}
+\begin{code}
+
+ public static DoubleMatrix2D PCADecompose (DoubleMatrix2D M,
+ double[] lambda)\begin{hide} {
+ // L'objet SingularValueDecomposition permet de recuperer la matrice
+ // des valeurs propres en ordre decroissant et celle des vecteurs propres de
+ // sigma (pour une matrice symetrique et definie-positive seulement).
+
+ SingularValueDecomposition sv = new SingularValueDecomposition(M);
+ // D contient les valeurs propres sur la diagonale
+ DoubleMatrix2D D = sv.getS ();
+
+ for (int i = 0; i < D.rows(); ++i)
+ lambda[i] = D.getQuick (i, i);
+
+ // Calculer la racine carree des valeurs propres
+ for (int i = 0; i < D.rows(); ++i)
+ D.setQuick (i, i, Math.sqrt (lambda[i]));
+ DoubleMatrix2D P = sv.getV();
+ // Multiplier par la matrice orthogonale (ici P)
+ return P.zMult (D, null);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the principal components decomposition $M$ =
+ $U\Lambda U^{\tr}$ by using the singular value decomposition of matrix
+$M$. Puts the eigenvalues, which are the diagonal elements of matrix $\Lambda$,
+sorted by decreasing size, in vector \texttt{lambda}. Returns matrix
+ $A = U\sqrt{\Lambda}$.
+\end{tabb}
+\begin{htmlonly}
+ \param{M}{input matrix}
+ \param{lambda}{the eigenvalues}
+ \return{matrix square root of M}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] solveLU (double[][] A, double[] b) \begin{hide} {
+ DoubleMatrix2D M = new DenseDoubleMatrix2D(A);
+ DoubleMatrix1D c = new DenseDoubleMatrix1D(b);
+ LUDecompositionQuick lu = new LUDecompositionQuick();
+ lu.decompose(M);
+ lu.solve(c);
+ return c.toArray();
+ } \end{hide}
+\end{code}
+\begin{tabb} Solves the matrix equation $Ax = b$ using LU decomposition.
+$A$ is a square matrix, $b$ and $x$ are vectors. Returns the solution $x$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{square matrix}
+ \param{b}{right side vector}
+ \return{the solution vector}
+\end{htmlonly}
+\begin{code}
+
+ public static void solveTriangular (DoubleMatrix2D U, DoubleMatrix2D B,
+ DoubleMatrix2D X) \begin{hide} {
+ final int n = U.rows();
+ final int m = B.columns();
+ double y, z;
+ X.assign(0.);
+ for (int j = 0; j < m; ++j) {
+ for (int i = n - 1; i >= 0; --i) {
+ z = B.getQuick(i, j);
+ for (int k = i + 1; k < n; ++k)
+ z -= U.getQuick(i, k) * X.getQuick(k, j);
+ z /= U.getQuick(i, i);
+ X.setQuick(i, j, z);
+ }
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Solve the triangular matrix equation $UX = B$ for $X$.
+ $U$ is a square upper triangular matrix. $B$ and $X$ must have the same
+number of columns.
+\end{tabb}
+\begin{htmlonly}
+ \param{U}{input matrix}
+ \param{B}{right-hand side matrix}
+ \param{X}{output matrix}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] exp (double[][] A) \begin{hide} {
+ DoubleMatrix2D V = new DenseDoubleMatrix2D(A);
+ DoubleMatrix2D R = exp(V);
+ return R.toArray();
+ } \end{hide}
+\end{code}
+\begin{tabb} Similar to \method{exp}{(DoubleMatrix2D)}\texttt{(A)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{input matrix}
+ \return{the exponential of $A$}
+\end{htmlonly}
+\begin{code}
+
+ public static DoubleMatrix2D exp (final DoubleMatrix2D A) \begin{hide} {
+ /*
+ * Use the diagonal Pade approximant of order [9/9] for exp:
+ * See Higham J.H., Functions of matrices, SIAM, 2008.
+ */
+ DoubleMatrix2D B = A.copy();
+ int n = B.rows();
+ Algebra alge = new Algebra();
+ final double mu = alge.trace(B) / n;
+ double x;
+
+ // B <-- B - mu*I
+ for (int i = 0; i < n; ++i) {
+ x = B.getQuick (i, i);
+ B.setQuick (i, i, x - mu);
+ }
+ /*
+ int bal = 0;
+ if (bal > 0) {
+ throw new UnsupportedOperationException (" balancing");
+ } */
+
+ final double THETA9 = 2.097847961257068; // in Higham
+ int s = getScale (B, THETA9);
+
+ Functions F = Functions.functions; // alias F
+ // B <-- B/2^s
+ double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException (" v <= 0");
+ B.assign (F.mult(v));
+
+ DoubleFactory2D fac = DoubleFactory2D.dense;
+ final DoubleMatrix2D B0 = fac.identity(n); // B^0 = I
+ final DoubleMatrix2D B2 = alge.mult(B, B); // B^2
+ final DoubleMatrix2D B4 = alge.mult(B2, B2); // B^4
+
+ DoubleMatrix2D T = B2.copy(); // T = work matrix
+ DoubleMatrix2D W = B4.copy(); // W = work matrix
+ W.assign (F.mult(cPade[9])); // W <-- W*cPade[9]
+ W.assign (T, F.plusMult(cPade[7])); // W <-- W + T*cPade[7]
+ DoubleMatrix2D U = alge.mult(B4, W); // U <-- B4*W
+
+ // T = B2.copy();
+ W = B4.copy();
+ W.assign (F.mult(cPade[5])); // W <-- W*cPade[5]
+ W.assign (T, F.plusMult(cPade[3])); // W <-- W + T*cPade[3]
+ W.assign (B0, F.plusMult(cPade[1])); // W <-- W + B0*cPade[1]
+ U.assign (W, F.plus); // U <-- U + W
+ U = alge.mult(B, U); // U <-- B*U
+
+ // T = B2.copy();
+ W = B4.copy();
+ W.assign (F.mult(cPade[8])); // W <-- W*cPade[8]
+ W.assign (T, F. plusMult(cPade[6])); // W <-- W + T*cPade[6]
+ DoubleMatrix2D V = alge.mult(B4, W); // V <-- B4*W
+
+ // T = B2.copy();
+ W = B4.copy();
+ W.assign (F.mult(cPade[4])); // W <-- W*cPade[4]
+ W.assign (T, F.plusMult(cPade[2])); // W <-- W + T*cPade[2]
+ W.assign (B0, F.plusMult(cPade[0])); // W <-- W + B0*cPade[0]
+ V.assign (W, F.plus); // V <-- V + W
+
+ W = V.copy();
+ W.assign(U, F.plus); // W = V + U, Pade numerator
+ T = V.copy();
+ T.assign(U, F.minus); // T = V - U, Pade denominator
+
+ // Compute Pade approximant for exponential = W / T
+ LUDecomposition lu = new LUDecomposition(T);
+ B = lu.solve(W);
+
+ if (false) {
+ // This overflows for large |mu|
+ // B <-- B^(2^s)
+ for(int i = 0; i < s; i++)
+ B = alge.mult(B, B);
+ /*
+ if (bal > 0) {
+ throw new UnsupportedOperationException (" balancing");
+ } */
+ v = Math.exp(mu);
+ B.assign (F.mult(v)); // B <-- B*e^mu
+
+ } else {
+ // equivalent to B^(2^s) * e^mu, but only if no balancing
+ double r = mu * v;
+ r = Math.exp(r);
+ B.assign (F.mult(r));
+ for (int i = 0; i < s; i++)
+ B = alge.mult(B, B);
+ }
+
+ return B;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns $e^A$, the exponential of the square matrix $A$.
+The scaling and squaring method \cite{mHIG09a} is used with
+Pad\'e approximants to compute the exponential.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{input matrix}
+ \return{the exponential of $A$}
+\end{htmlonly}
+\begin{code}
+
+ public static DoubleMatrix2D expBidiagonal (final DoubleMatrix2D A) \begin{hide} {
+ // Use the diagonal Pade approximant of order [9/9] for exp:
+ // See Higham J.H., Functions of matrices, SIAM, 2008.
+ // This method scale A to a matrix with small norm B = A/2^s,
+ // compute U = exp(B) with Pade approximants, and returns U.
+
+ DoubleMatrix2D B = A.copy();
+ final int n = B.rows();
+ Algebra alge = new Algebra();
+ final double mu = alge.trace(B) / n;
+ double x;
+
+ // B <-- B - mu*I
+ for (int i = 0; i < n; ++i) {
+ x = B.getQuick(i, i);
+ B.setQuick(i, i, x - mu);
+ }
+
+ final double THETA9 = 2.097847961257068; // in Higham
+ int s = getScale (B, THETA9);
+ final double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException(" v <= 0");
+ DMatrix.multBand(B, 1, v); // B <-- B/2^s
+
+ DoubleFactory2D fac = DoubleFactory2D.dense;
+ DoubleMatrix2D T = fac.make(n, n);
+ DoubleMatrix2D B4 = fac.make(n, n);
+ DMatrix.multBand(B, 1, B, 1, T); // B^2
+ DMatrix.multBand(T, 2, T, 2, B4); // B^4
+
+ DoubleMatrix2D W = B4.copy(); // W = work matrix
+ DMatrix.addMultBand(cPade[9], W, 4, cPade[7], T, 2); // W <-- W*cPade[9] + T*cPade[7]
+ DoubleMatrix2D U = fac.make(n, n);
+ DMatrix.multBand(W, 4, B4, 4, U); // U <-- B4*W
+
+ W = B4.copy();
+ DMatrix.addMultBand(cPade[5], W, 4, cPade[3], T, 2); // W <-- W*cPade[5] + T*cPade[3]
+ for (int i = 0; i < n; ++i) { // W <-- W + I*cPade[1]
+ x = W.getQuick(i, i);
+ W.setQuick(i, i, x + cPade[1]);
+ }
+ DMatrix.addMultBand(1.0, U, 8, 1.0, W, 4); // U <-- U + W
+ DMatrix.multBand(B, 1, U, 8, U); // U <-- B*U
+
+ W = B4.copy();
+ DMatrix.addMultBand(cPade[8], W, 4, cPade[6], T, 2); // W <-- W*cPade[8] + T*cPade[6]
+ DoubleMatrix2D V = B;
+ DMatrix.multBand(W, 4, B4, 4, V); // V <-- B4*W
+
+ W = B4.copy();
+ DMatrix.addMultBand(cPade[4], W, 4, cPade[2], T, 2); // W <-- W*cPade[4] + T*cPade[2]
+ for (int i = 0; i < n; ++i) { // W <-- W + I*cPade[0]
+ x = W.getQuick(i, i);
+ W.setQuick(i, i, x + cPade[0]);
+ }
+ DMatrix.addMultBand(1.0, V, 8, 1.0, W, 4); // V <-- V + W
+
+ W = V.copy();
+ DMatrix.addMultBand(1.0, W, 9, 1.0, U, 9); // W = V + U, Pade numerator
+ T = V.copy();
+ DMatrix.addMultBand(1.0, T, 9, -1.0, U, 9); // T = V - U, Pade denominator
+
+ // Compute Pade approximant B = W/T for exponential
+ DMatrix.solveTriangular(T, W, B);
+
+ // equivalent to B^(2^s) * e^mu
+ double r = mu * v;
+ r = Math.exp(r);
+ DMatrix.multBand(B, n - 1, r); // B <-- B*r
+
+ T.assign(0.);
+
+ for (int i = 0; i < s; i++) {
+ DMatrix.multBand(B, n - 1, B, n - 1, T);
+ B = T.copy();
+ }
+
+ return B;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns $e^A$, the exponential of the \emph{bidiagonal}
+ square matrix $A$. The only non-zero elements of $A$ are on the diagonal and
+ on the first superdiagonal. This method is faster than
+\method{exp}{(DoubleMatrix2D)}\texttt{(A)} because of the special form of $A$.
+%: as an example, for $100\times 100$ matrices, it is about 10 times faster
+% than the general method \texttt{exp}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{bidiagonal matrix}
+ \return{$e^A$}
+\end{htmlonly}
+\begin{code}
+
+ public static DoubleMatrix1D expBidiagonal (final DoubleMatrix2D A,
+ final DoubleMatrix1D b) \begin{hide} {
+ // This is probably not efficient;
+ DoubleMatrix2D U = expBidiagonal (A); // U = exp(A)
+ Algebra alge = new Algebra();
+ return alge.mult(U, b);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes $c = e^A b$, where $e^A$ is the exponential of the \emph{bidiagonal}
+ square matrix $A$. The only non-zero elements of $A$ are on the diagonal and
+ on the first superdiagonal.
+Uses the scaling and squaring method \cite{mHIG09a} with
+Pad\'e approximants. Returns $c$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{bidiagonal matrix}
+ \return{$e^A$}
+\end{htmlonly}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+
+ public static DoubleMatrix2D expmiBidiagonal (final DoubleMatrix2D A) \begin{hide} {
+ // Use the diagonal Pade approximant of order [7/7] for (exp(A) - I)/A:
+ // See Higham J.H., Functions of matrices, SIAM, 2008, p. 262.
+
+ DoubleMatrix2D B = A.copy();
+ final double THETA = 1.13; // theta_{7,1}
+ int s = getScale (B, THETA);
+ final double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException(" v <= 0");
+ DMatrix.multBand(B, 1, v); // B <-- B/2^s
+ DoubleMatrix2D U = m_expmiBidiag (B); // U = exp(B) - I
+
+ DoubleMatrix2D N = U.copy();
+ addIdentity (N); // N <-- exp(B)
+ DoubleMatrix2D V = N.copy(); // V <-- exp(B)
+
+ // Now undo scaling of B = A/2^s using
+ // (exp(A) - I) = (exp(B) - I)(exp(B) + I)(exp(B^2) + I) ... (exp(B^(2^(s-1))) + I)
+
+ Algebra alge = new Algebra();
+ for (int i = 1; i <= s; i++) {
+ addIdentity (N); // N <-- exp(B) + I
+ U = alge.mult(N, U); // U <-- N*U
+ if (i < s) {
+ V = alge.mult(V, V); // V <-- V*V
+ N = V.copy();
+ }
+ }
+
+ return U;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes $e^A - I$, where $e^A$ is the exponential of the \emph{bidiagonal}
+ square matrix $A$. The only non-zero elements of $A$ are on the diagonal and
+ on the first superdiagonal.
+Uses the scaling and squaring method \cite{mSKA09a,mHIG09a} with
+Pad\'e approximants. Returns $e^A - I$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{bidiagonal matrix}
+ \return{$(e^A - I)b$}
+\end{htmlonly}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+
+ public static DoubleMatrix1D expmiBidiagonal (final DoubleMatrix2D A,
+ final DoubleMatrix1D b) \begin{hide} {
+ DoubleMatrix2D F = A.copy();
+ int s = getScale (F, 1.0 / 16.0);
+ final double v = 1.0 / Math.pow(2.0, s);
+ if (v <= 0)
+ throw new IllegalArgumentException(" v <= 0");
+ DMatrix.multBand(F, 1, v); // F <-- F/2^s
+
+ DoubleMatrix2D U = expBidiagonal (F); // U = exp(F)
+ DoubleMatrix2D N = U.copy();
+ DoubleMatrix1D C = m_taylor (F, b); // C = (exp(F) - I)b
+ DoubleMatrix1D D = C.copy();
+ Algebra alge = new Algebra();
+
+ // Now undo scaling of F = A/2^s using
+ // (exp(A) - I)b = (exp(F^(2^(s-1))) + I)...(exp(F^2) + I)(exp(F) + I)(exp(F) - I)b
+ for (int i = 1; i <= s; i++) {
+ addIdentity (N); // N <-- exp(F) + I
+ C = alge.mult(N, C); // C <-- N*C
+ if (i < s) {
+ U = alge.mult(U, U); // U <-- U*U
+ N = U.copy();
+ }
+ }
+
+ return C;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes $c = (e^A - I)b$, where $e^A$ is the exponential of the
+ \emph{bidiagonal} square matrix $A$.
+ The only non-zero elements of $A$ are on the diagonal and
+ on the first superdiagonal. Uses the scaling and squaring method
+ \cite{mSKA09a,mHIG09a} with a Taylor expansion. Returns $c$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{bidiagonal matrix}
+ \param{b}{vector}
+ \return{$c$}
+\end{htmlonly}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{hide}
+\begin{code}
+
+ private static void addIdentity (DoubleMatrix2D A) {
+ // add identity matrix to matrix A: A <-- A + I
+ final int n = A.rows();
+ double x;
+ for (int i = 0; i < n; ++i) {
+ x = A.getQuick(i, i);
+ A.setQuick(i, i, x + 1.0);
+ }
+ }
+
+
+ private static void calcDiagm1 (DoubleMatrix2D A, DoubleMatrix2D R) {
+ // calc diagonal of expm1 of triangular matrix A: exp(A) - I
+ final int n = A.rows();
+ double x, v;
+ for (int i = 0; i < n; ++i) {
+ x = A.getQuick(i, i);
+ v = Math.expm1(x); // exp(x) - 1
+ R.setQuick(i, i, v);
+ }
+ }
+
+
+ static void multBand (final DoubleMatrix2D A, int sa,
+ final DoubleMatrix2D B, int sb,
+ DoubleMatrix2D C) \begin{hide} {
+ innerMultBand (A, sa, B, sb, C);
+ } \end{hide}
+\end{code}
+\begin{tabb} Matrix multiplication $C = AB$. All three matrices are square,
+banded, and upper triangular. $A$ has a non-zero diagonal, \texttt{sa} non-zero
+ superdiagonals, and thus a bandwidth of \texttt{sa + 1}.
+The non-zero elements of $A_{ij}$ are those for which $j - s_a \le i \le j$.
+Similarly for $B$ which has a bandwidth of \texttt{sb + 1}.
+The resulting matrix $C$ has \texttt{sa + sb} non-zero superdiagonals, and a
+bandwidth of \texttt{sa + sb + 1}.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{input matrix}
+ \param{sa}{number of superdiagonals of A}
+ \param{B}{input matrix}
+ \param{sb}{number of superdiagonals of B}
+ \param{C}{result}
+\end{htmlonly}
+\begin{code}
+
+ static void multBand (DoubleMatrix2D A, int sa, double h) \begin{hide} {
+ final int n = A.rows();
+ double z;
+ for (int i = 0; i < n; ++i) {
+ int jmax = Math.min(i + sa, n - 1);
+ for (int j = i; j <= jmax; ++j) {
+ z = A.getQuick (i, j);
+ A.setQuick (i, j, z*h);
+ }
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Multiplication of the matrix $A$ by the scalar $h$.
+$A$ is a square banded upper triangular matrix. It has a non-zero diagonal,
+\texttt{sa} superdiagonals, and thus a bandwidth of \texttt{sa + 1}.
+The result of the multiplication is put back in $A$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{input and output matrix}
+ \param{sa}{number of superdiagonals of A}
+ \param{h}{scalar}
+\end{htmlonly}
+\begin{code}
+
+ static void addMultBand (double g, DoubleMatrix2D A, int sa,
+ double h, final DoubleMatrix2D B, int sb) \begin{hide} {
+ DoubleMatrix2D S;
+ S = A.copy ();
+ final int n = A.rows();
+ double z;
+ for (int i = 0; i < n; ++i) {
+ int jmax = Math.max(i + sa, i + sb);
+ jmax = Math.min(jmax, n - 1);
+ for (int j = i; j <= jmax; ++j) {
+ z = g*S.getQuick (i, j) + h*B.getQuick (i, j);
+ A.setQuick (i, j, z);
+ }
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Addition of the matrices $gA + hB$ after multiplication with
+the scalars $g$ and $h$. $A$ is a square banded upper triangular matrix.
+It has a non-zero diagonal, \texttt{sa} superdiagonals, and thus a bandwidth
+of \texttt{sa + 1}. Similarly for $B$ which has a bandwidth of \texttt{sb + 1}.
+The result is put back in $A$.
+\end{tabb}
+\begin{htmlonly}
+ \param{g}{coefficient multiplying A}
+ \param{A}{input and output matrix}
+ \param{sa}{number of superdiagonals of A}
+ \param{h}{coefficient multiplying B}
+ \param{B}{input matrix}
+ \param{sb}{number of superdiagonals of B}
+\end{htmlonly}
+ \end{hide}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{code}
+
+ public static void copy (double[][] M, double[][] R) \begin{hide} {
+ for (int i = 0; i < M.length; i++) {
+ for(int j = 0; j < M[i].length; j++) {
+ R[i][j] = M[i][j];
+ }
+ }
+ } \end{hide}
+\end{code}
+\begin{tabb} Copies the matrix $M$ into $R$.
+\end{tabb}
+\begin{htmlonly}
+ \param{M}{original matrix}
+ \param{R}{output matrix}
+\end{htmlonly}
+\begin{code}
+
+ public static String toString(double[][] M) \begin{hide} {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("{" + PrintfFormat.NEWLINE);
+ for (int i = 0; i < M.length; i++) {
+ sb.append(" { ");
+ for(int j = 0; j < M[i].length; j++) {
+ sb.append(M[i][j] + " ");
+ if (j < M.length - 1)
+ sb.append(" ");
+ }
+ sb.append("}" + PrintfFormat.NEWLINE);
+ }
+ sb.append("}");
+
+ return sb.toString();
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns matrix $M$ as a string.
+It is displayed in matrix form, with each row on a line.
+\end{tabb}
+\begin{htmlonly}
+ \return{the content of $M$}
+\end{htmlonly}
+\begin{code}
+
+ public String toString() \begin{hide} {
+ return toString(mat);
+ } \end{hide}
+\end{code}
+\begin{tabb} Creates a \class{String} containing all the data of
+ the \texttt{DMatrix}. The result is displayed in matrix form, with
+ each row on a line.
+\end{tabb}
+\begin{htmlonly}
+ \return{the content of the \texttt{DMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public int numRows() \begin{hide} {
+ return r;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the number of rows of the \texttt{DMatrix}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of rows}
+\end{htmlonly}
+\begin{code}
+
+ public int numColumns() \begin{hide} {
+ return c;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the number of columns of the \texttt{DMatrix}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the number of columns}
+\end{htmlonly}
+\begin{code}
+
+ public double get (int row, int column) \begin{hide} {
+ if (row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ return mat[row][column];
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the matrix element in the specified row and column.
+\end{tabb}
+\begin{htmlonly}
+ \param{row}{the row of the selected element}
+ \param{column}{the column of the selected element}
+ \return{the value of the element}
+ \exception{IndexOutOfBoundsException}{if the selected element would
+ be outside the \texttt{DMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public void set (int row, int column, double value) \begin{hide} {
+ if (row >= r || column >= c)
+ throw new IndexOutOfBoundsException();
+
+ mat[row][column] = value;
+ } \end{hide}
+\end{code}
+\begin{tabb} Sets the value of the element in the specified row and column.
+\end{tabb}
+\begin{htmlonly}
+ \param{row}{the row of the selected element}
+ \param{column}{the column of the selected element}
+ \param{value}{the new value of the element}
+ \exception{IndexOutOfBoundsException}{if the selected element would
+ be outside the \texttt{DMatrix}}
+\end{htmlonly}
+\begin{code}
+
+ public DMatrix transpose() \begin{hide} {
+ DMatrix result = new DMatrix(c,r);
+
+ for(int i = 0; i < r; i++)
+ for(int j = 0; j < c; j++)
+ result.mat[j][i] = mat[i][j];
+
+ return result;
+ } \end{hide}
+\end{code}
+\begin{tabb} Returns the transposed matrix. The rows and columns are
+ interchanged.
+\end{tabb}
+\begin{htmlonly}
+ \return{the transposed matrix}
+\end{htmlonly}
+
+\begin{code}
+\begin{hide}
+}
+\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/DoubleArrayComparator.java b/source/umontreal/iro/lecuyer/util/DoubleArrayComparator.java
new file mode 100644
index 0000000..35b9a44
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/DoubleArrayComparator.java
@@ -0,0 +1,75 @@
+
+/*
+ * Class: DoubleArrayComparator
+ * Description: Compares two double's arrays
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.util.Comparator;
+
+
+/**
+ * An implementation of {@link java.util.Comparator} which compares two
+ * <TT>double</TT> arrays by comparing their <TT>i</TT>-<SPAN CLASS="textit">th</SPAN> element,
+ * where <TT>i</TT> is given in the constructor.
+ * Method <TT>compare(d1, d2)</TT> returns <SPAN CLASS="MATH">-1</SPAN>, <SPAN CLASS="MATH">0</SPAN>, or <SPAN CLASS="MATH">1</SPAN> depending on
+ * whether <TT>d1[i]</TT> is less than, equal to, or greater than
+ * <TT>d2[i]</TT>.
+ *
+ */
+public class DoubleArrayComparator implements Comparator<double[]> {
+ private int i;
+
+
+
+ /**
+ * Constructs a comparator, where <TT>i</TT> is the index
+ * used for the comparisons.
+ *
+ * @param i index used for comparison
+ *
+ */
+ public DoubleArrayComparator (int i) {
+ this.i = i;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH">-1</SPAN>, <SPAN CLASS="MATH">0</SPAN>, or <SPAN CLASS="MATH">1</SPAN> depending on
+ * whether <TT>d1[i]</TT> is less than, equal
+ * to, or greater than <TT>d2[i]</TT>.
+ *
+ * @param d1 first array
+ *
+ * @param d2 second array
+ *
+ *
+ */
+ public int compare (double[] d1, double[] d2) {
+ if (i >= d1.length || i >= d2.length)
+ throw new IllegalArgumentException("Comparing in a"+
+ "dimension larger than arary dimension");
+ return (d1[i]< d2[i] ? -1 : (d1[i] > d2[i] ? 1 : 0));
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/DoubleArrayComparator.tex b/source/umontreal/iro/lecuyer/util/DoubleArrayComparator.tex
new file mode 100644
index 0000000..1cfd93a
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/DoubleArrayComparator.tex
@@ -0,0 +1,86 @@
+\defmodule{DoubleArrayComparator}
+
+An implementation of \class{java.util.Comparator} which compares two
+\texttt{double} arrays by comparing their \texttt{i}-\emph{th} element,
+where \texttt{i} is given in the constructor.
+Method \texttt{compare(d1, d2)} returns $-1$, $0$, or $1$ depending on
+whether \texttt{d1[i]} is less than, equal to, or greater than
+\texttt{d2[i]}.
+
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: DoubleArrayComparator
+ * Description: Compares two double's arrays
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+\begin{hide}
+import java.util.Comparator;
+\end{hide}
+
+public class DoubleArrayComparator implements Comparator<double[]> \begin{hide} {
+ private int i;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructor}
+\begin{code}
+
+ public DoubleArrayComparator (int i) \begin{hide} {
+ this.i = i;
+ }
+\end{hide}\end{code}
+\begin{tabb}
+ Constructs a comparator, where \texttt{i} is the index
+ used for the comparisons.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{index used for comparison}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public int compare (double[] d1, double[] d2) \begin{hide} {
+ if (i >= d1.length || i >= d2.length)
+ throw new IllegalArgumentException("Comparing in a"+
+ "dimension larger than arary dimension");
+ return (d1[i]< d2[i] ? -1 : (d1[i] > d2[i] ? 1 : 0));
+ } \end{hide}
+\end{code}
+\begin{tabb}
+Returns $-1$, $0$, or $1$ depending on
+whether \texttt{d1[i]} is less than, equal
+to, or greater than \texttt{d2[i]}.
+\end{tabb}
+\begin{htmlonly}
+ \param{d1}{first array}
+ \param{d2}{second array}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/GlobalCPUTimeChrono.java b/source/umontreal/iro/lecuyer/util/GlobalCPUTimeChrono.java
new file mode 100644
index 0000000..f0cd33c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/GlobalCPUTimeChrono.java
@@ -0,0 +1,72 @@
+
+
+/*
+ * Class: GlobalCPUTimeChrono
+ * Description: Compute the global CPU time used by the Java Virtual Machine
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+/**
+ * Extends the {@link AbstractChrono} class to compute the global CPU time used
+ * by the Java Virtual Machine. This includes CPU time taken by any thread,
+ * including the garbage collector, class loader, etc.
+ *
+ * <P>
+ * Part of this class is implemented in the C language and the
+ * implementation is unfortunately operating system-dependent.
+ * The C functions for the current class have been compiled on a 32-bit machine
+ * running Linux. For a <EM>platform-independent</EM> CPU timer (valid only with Java-1.5 or later),
+ * one should use the class {@link ThreadCPUTimeChrono} which is programmed
+ * directly in Java.
+ *
+ */
+public class GlobalCPUTimeChrono extends AbstractChrono {
+
+ private static boolean ssjUtilLoaded = false;
+ private static native void Heure (long[] tab);
+
+ protected void getTime (long[] tab) {
+ if (!ssjUtilLoaded) {
+ // We cannot load the library in a static block
+ // since the subclass ChronoSingleThread does not
+ // need the native method.
+ System.loadLibrary ("ssjutil");
+
+ ssjUtilLoaded = true;
+ }
+
+ Heure (tab);
+ }
+
+
+
+ /**
+ * Constructs a <TT>Chrono</TT> object and initializes it to zero.
+ *
+ */
+ public GlobalCPUTimeChrono() {
+ init();
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/GlobalCPUTimeChrono.tex b/source/umontreal/iro/lecuyer/util/GlobalCPUTimeChrono.tex
new file mode 100644
index 0000000..ecbe333
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/GlobalCPUTimeChrono.tex
@@ -0,0 +1,80 @@
+\defmodule {GlobalCPUTimeChrono}
+
+Extends the \class{AbstractChrono} class to compute the global CPU time used
+by the Java Virtual Machine. This includes CPU time taken by any thread,
+including the garbage collector, class loader, etc.
+
+Part of this class is implemented in the C language and the
+implementation is unfortunately operating system-dependent.
+The C functions for the current class have been compiled on a 32-bit machine
+running Linux. % and will not work on 64-bit machines.
+For a {\em platform-independent} CPU timer (valid only with Java--1.5 or later),
+ one should use the class \class{ThreadCPUTimeChrono} which is programmed
+directly in Java.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: GlobalCPUTimeChrono
+ * Description: Compute the global CPU time used by the Java Virtual Machine
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public class GlobalCPUTimeChrono extends AbstractChrono\begin{hide} {
+
+ private static boolean ssjUtilLoaded = false;
+ private static native void Heure (long[] tab);
+
+ protected void getTime (long[] tab) {
+ if (!ssjUtilLoaded) {
+ // We cannot load the library in a static block
+ // since the subclass ChronoSingleThread does not
+ // need the native method.
+ System.loadLibrary ("ssjutil");
+
+ ssjUtilLoaded = true;
+ }
+
+ Heure (tab);
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public GlobalCPUTimeChrono()\begin{hide} {
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{Chrono} object and initializes it to zero.
+ \end{tabb}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/Introspection.java b/source/umontreal/iro/lecuyer/util/Introspection.java
new file mode 100644
index 0000000..6c1025e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Introspection.java
@@ -0,0 +1,394 @@
+
+/*
+ * Class: Introspection
+ * Description: Methods for introspection using Java Reflection API.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+
+
+/**
+ * Provides utility methods for introspection using
+ * Java Reflection API.
+ *
+ */
+public class Introspection {
+ private Introspection() {}
+
+ /**
+ * Returns all the methods declared and inherited
+ * by a class. This is similar to {@link java.lang.Class#getMethods(()) getMethods}
+ * except that it enumerates non-public methods as well as public ones.
+ * This method uses {@link java.lang.Class#getDeclaredMethods(()) getDeclaredMethods}
+ * to get the declared methods of <TT>c</TT>.
+ * It also gets the declared methods of superclasses.
+ * If a method is defined in a superclass and overriden
+ * in a subclass, only the overriden method will be
+ * in the returned array.
+ *
+ * <P>
+ * Note that since this method uses
+ * {@link java.lang.Class#getDeclaredMethods(()) getDeclaredMethods},
+ * it can throw a {@link java.lang.SecurityException SecurityException} if
+ * a security manager is present.
+ *
+ * @param c the class being processed.
+ *
+ * @return the array of methods.
+ *
+ */
+ public static Method[] getMethods (Class<?> c) {
+ // Creates the set of methods for the class.
+ List<Method> lst = internalGetMethods (c);
+
+ // Copy the methods to the array that will be returned.
+ return lst.toArray (new Method[lst.size()]);
+ }
+
+
+ private static List<Method> internalGetMethods (Class<?> c) {
+ // Inspired from java.lang.Class
+ List<Method> methods = new ArrayList<Method>();
+ Method[] mt = c.getDeclaredMethods();
+ for (int i = 0; i < mt.length; i++)
+ methods.add (mt[i]);
+
+ List<Method> inheritedMethods = new ArrayList<Method>();
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++)
+ inheritedMethods.addAll (internalGetMethods (iface[i]));
+ if (!c.isInterface()) {
+ Class<?> s = c.getSuperclass();
+ if (s != null) {
+ List<Method> supers = internalGetMethods (s);
+ for (Method m : supers) {
+ // Filter out concrete implementations of any interface
+ // methods.
+ if (m != null && !Modifier.isAbstract (m.getModifiers()))
+ removeByNameAndSignature (inheritedMethods, m);
+ }
+ supers.addAll (inheritedMethods);
+ inheritedMethods = supers;
+ }
+ }
+
+ // Filter out all local methods from inherited ones
+ for (Method m : methods)
+ removeByNameAndSignature (inheritedMethods, m);
+
+ for (Method m : inheritedMethods) {
+ if (m == null)
+ continue;
+ if (!methods.contains (m))
+ methods.add (m);
+ }
+ return methods;
+ }
+
+ private static void removeByNameAndSignature (List<Method> methods, Method m) {
+ for (ListIterator<Method> it = methods.listIterator(); it.hasNext(); ) {
+ Method tst = it.next();
+ if (tst == null)
+ continue;
+ if (tst.getName().equals (m.getName()) &&
+ tst.getReturnType() == m.getReturnType() &&
+ sameSignature (tst, m))
+ it.set (null);
+ }
+ }
+
+ /**
+ * Determines if two methods <TT>m1</TT> and <TT>m2</TT>
+ * share the same signature. For the signature to be identical,
+ * methods must have the same
+ * number of parameters and the same parameter types.
+ *
+ * @param m1 the first method.
+ *
+ * @param m2 the second method.
+ *
+ * @return <TT>true</TT> if the signatures are the same, <TT>false</TT>
+ * otherwise.
+ *
+ */
+ public static boolean sameSignature (Method m1, Method m2) {
+ Class[] pt1 = m1.getParameterTypes();
+ Class[] pt2 = m2.getParameterTypes();
+ if (pt1.length != pt2.length)
+ return false;
+ for (int i = 0; i < pt1.length; i++)
+ if (pt1[i] != pt2[i])
+ return false;
+ return true;
+ }
+
+
+ /**
+ * Returns all the fields declared and inherited
+ * by a class. This is similar to {@link java.lang.Class#getFields(()) getFields}
+ * except that it enumerates non-public fields as well as public ones.
+ * This method uses {@link java.lang.Class#getDeclaredFields(()) getDeclaredFields}
+ * to get the declared fields of <TT>c</TT>.
+ * It also gets the declared fields of superclasses and implemented
+ * interfaces.
+ *
+ * <P>
+ * Note that since this method uses
+ * {@link java.lang.Class#getDeclaredFields(()) getDeclaredFields},
+ * it can throw a {@link java.lang.SecurityException SecurityException} if
+ * a security manager is present.
+ *
+ * @param c the class being processed.
+ *
+ * @return the array of fields.
+ *
+ */
+ public static Field[] getFields (Class<?> c) {
+ // Creates the set of fields for the class.
+ List<Field> lst = new ArrayList<Field>();
+ processFields (c, lst);
+ Set<Class<?>> traversedInterfaces = new HashSet<Class<?>>();
+ processInterfaceFields (c, lst, traversedInterfaces);
+
+ if (!c.isInterface()) {
+ Class<?> s = c.getSuperclass();
+ while (s != null) {
+ processFields (s, lst);
+ processInterfaceFields (s, lst, traversedInterfaces);
+ s = s.getSuperclass();
+ }
+ }
+
+ // Copy the fields to the array that will be returned.
+ return lst.toArray (new Field[lst.size()]);
+ }
+
+
+ private static void processFields (final Class<?> c, final List<Field> lst) {
+ Field[] f = c.getDeclaredFields();
+ for (int i = 0; i < f.length; i++)
+ lst.add (f[i]);
+ }
+
+ private static void processInterfaceFields (final Class<?> c, final List<Field> lst, final Set<Class<?>> traversedInterfaces) {
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++) {
+ if (traversedInterfaces.contains (iface[i]))
+ continue;
+ traversedInterfaces.add (iface[i]);
+ processFields (iface[i], lst);
+ processInterfaceFields (iface[i], lst, traversedInterfaces);
+ }
+ }
+
+ /**
+ * This is like {@link java.lang.Class#getMethod((String, Class...)) getMethod}, except that it can return non-public methods.
+ *
+ * @param c the class being processed.
+ *
+ * @param name the name of the method.
+ *
+ * @param pt the parameter types.
+ *
+ * @exception NoSuchMethodException if the method cannot be found.
+ *
+ *
+ */
+ public static Method getMethod (Class<?> c, String name, Class[] pt)
+ throws NoSuchMethodException {
+ try {
+ return c.getDeclaredMethod (name, pt);
+ }
+ catch (NoSuchMethodException nme) {}
+ if (!c.isInterface())
+ try {
+ Class<?> s = c.getSuperclass();
+ if (s != null)
+ return getMethod (s, name, pt);
+ }
+ catch (NoSuchMethodException nme) {}
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++)
+ try {
+ return getMethod (iface[i], name, pt);
+ }
+ catch (NoSuchMethodException nme) {}
+ throw new NoSuchMethodException
+ ("Cannot find method " + name + " in class " + c.getName());
+ }
+
+
+ /**
+ * This is like {@link java.lang.Class#getField((String)) getField},
+ * except that it can return non-public fields.
+ *
+ * <P>
+ * Note that since this method uses
+ * {@link java.lang.Class#getDeclaredField((String)) getDeclaredField},
+ * it can throw a {@link java.lang.SecurityException SecurityException} if
+ * a security manager is present.
+ *
+ * @param c the class being processed.
+ *
+ * @param name the name of the method.
+ *
+ * @exception NoSuchFieldException if the field cannot be found.
+ *
+ *
+ */
+ public static Field getField (Class<?> c, String name)
+ throws NoSuchFieldException {
+ try {
+ return c.getDeclaredField (name);
+ }
+ catch (NoSuchFieldException nfe) {}
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++)
+ try {
+ return getField (iface[i], name);
+ }
+ catch (NoSuchFieldException nme) {}
+ if (!c.isInterface())
+ try {
+ Class s = c.getSuperclass();
+ if (s != null)
+ return getField (s, name);
+ }
+ catch (NoSuchFieldException nfe) {}
+ throw new NoSuchFieldException
+ ("Cannot find field " + name + " in " + c.getName());
+ }
+
+
+ /**
+ * Returns the field name corresponding to the value of
+ * an enumerated type <TT>val</TT>.
+ * This method gets the class of <TT>val</TT> and
+ * scans its fields to find a public static and final field
+ * containing <TT>val</TT>. If such a field is found,
+ * its name is returned. Otherwise, <TT>null</TT> is returned.
+ *
+ * @param val the value of the enumerated type.
+ *
+ * @return the field name or <TT>null</TT>.
+ *
+ */
+ public static String getFieldName (Object val) {
+ Class<?> enumType = val.getClass();
+ Field[] f = enumType.getFields();
+ for (int i = 0; i < f.length; i++) {
+ if (Modifier.isPublic (f[i].getModifiers()) &&
+ Modifier.isStatic (f[i].getModifiers()) &&
+ Modifier.isFinal (f[i].getModifiers())) {
+ try {
+ if (f[i].get (null) == val)
+ return f[i].getName();
+ }
+ catch (IllegalAccessException iae) {}
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Returns the field of class <TT>cls</TT> corresponding to
+ * the name <TT>name</TT>. This method looks for
+ * a public, static, and final field with name <TT>name</TT> and
+ * returns its value. If no appropriate field can be
+ * found, an {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ *
+ * @param cls the class to look for a field in.
+ *
+ * @param name the name of field.
+ *
+ * @return the object in the field.
+ * @exception IllegalArgumentException if <TT>name</TT> does
+ * not correspond to a valid field name.
+ *
+ *
+ */
+ public static <T> T valueOf (Class<T> cls, String name) {
+ try {
+ Field field = cls.getField (name);
+ if (Modifier.isStatic (field.getModifiers()) &&
+ Modifier.isFinal (field.getModifiers()) &&
+ cls.isAssignableFrom (field.getType()))
+ return (T)field.get (null);
+ }
+ catch (NoSuchFieldException nfe) {}
+ catch (IllegalAccessException iae) {}
+ throw new IllegalArgumentException ("Invalid field name: " + name);
+ }
+
+
+ /**
+ * Similar to {@link #valueOf((Class, String)) valueOf} <TT>(cls, name)</TT>,
+ * with case insensitive field name look-up.
+ * If <TT>cls</TT> defines several fields with the same case insensitive
+ * name <TT>name</TT>, an {@link java.lang.IllegalArgumentException IllegalArgumentException} is thrown.
+ *
+ * @param cls the class to look for a field in.
+ *
+ * @param name the name of field.
+ *
+ * @return the object in the field.
+ * @exception IllegalArgumentException if <TT>name</TT> does
+ * not correspond to a valid field name, or if
+ * the class defines several fields with the same name.
+ *
+ */
+ public static <T> T valueOfIgnoreCase (Class<T> cls, String name) {
+ Field[] fields = cls.getFields();
+ T res = null;
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (field.getName().equalsIgnoreCase (name) &&
+ Modifier.isStatic (field.getModifiers()) &&
+ Modifier.isFinal (field.getModifiers()) &&
+ cls.isAssignableFrom (field.getType()))
+ try {
+ T res2 = (T)field.get (null);
+ if (res != null && res2 != null)
+ throw new IllegalArgumentException
+ ("Found more than one field with the same name in class " +
+ cls.getName() +
+ " if case is ignored");
+ res = res2;
+ }
+ catch (IllegalAccessException iae) {}
+ }
+ if (res == null)
+ throw new IllegalArgumentException ("Invalid field name: " + name);
+ return res;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/util/Introspection.tex b/source/umontreal/iro/lecuyer/util/Introspection.tex
new file mode 100644
index 0000000..4f0ddb0
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Introspection.tex
@@ -0,0 +1,376 @@
+\defmodule{Introspection}
+
+Provides utility methods for introspection using
+Java Reflection API.
+
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: Introspection
+ * Description: Methods for introspection using Java Reflection API.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+\end{hide}
+
+
+public class Introspection\begin{hide} {
+ private Introspection() {}\end{hide}
+
+ public static Method[] getMethods (Class<?> c)\begin{hide} {
+ // Creates the set of methods for the class.
+ List<Method> lst = internalGetMethods (c);
+
+ // Copy the methods to the array that will be returned.
+ return lst.toArray (new Method[lst.size()]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns all the methods declared and inherited
+ by a class. This is similar to \externalmethod{java.lang}{Class}{getMethods}{()}
+ except that it enumerates non-public methods as well as public ones.
+ This method uses \externalmethod{java.lang}{Class}{getDeclaredMethods}{()}
+ to get the declared methods of \texttt{c}.
+ It also gets the declared methods of superclasses.
+ If a method is defined in a superclass and overriden
+ in a subclass, only the overriden method will be
+ in the returned array.
+
+ Note that since this method uses
+ \externalmethod{java.lang}{Class}{getDeclaredMethods}{()},
+ it can throw a \externalclass{java.lang}{SecurityException} if
+ a security manager is present.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the class being processed.}
+ \return{the array of methods.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private static List<Method> internalGetMethods (Class<?> c) {
+ // Inspired from java.lang.Class
+ List<Method> methods = new ArrayList<Method>();
+ Method[] mt = c.getDeclaredMethods();
+ for (int i = 0; i < mt.length; i++)
+ methods.add (mt[i]);
+
+ List<Method> inheritedMethods = new ArrayList<Method>();
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++)
+ inheritedMethods.addAll (internalGetMethods (iface[i]));
+ if (!c.isInterface()) {
+ Class<?> s = c.getSuperclass();
+ if (s != null) {
+ List<Method> supers = internalGetMethods (s);
+ for (Method m : supers) {
+ // Filter out concrete implementations of any interface
+ // methods.
+ if (m != null && !Modifier.isAbstract (m.getModifiers()))
+ removeByNameAndSignature (inheritedMethods, m);
+ }
+ supers.addAll (inheritedMethods);
+ inheritedMethods = supers;
+ }
+ }
+
+ // Filter out all local methods from inherited ones
+ for (Method m : methods)
+ removeByNameAndSignature (inheritedMethods, m);
+
+ for (Method m : inheritedMethods) {
+ if (m == null)
+ continue;
+ if (!methods.contains (m))
+ methods.add (m);
+ }
+ return methods;
+ }
+
+ private static void removeByNameAndSignature (List<Method> methods, Method m) {
+ for (ListIterator<Method> it = methods.listIterator(); it.hasNext(); ) {
+ Method tst = it.next();
+ if (tst == null)
+ continue;
+ if (tst.getName().equals (m.getName()) &&
+ tst.getReturnType() == m.getReturnType() &&
+ sameSignature (tst, m))
+ it.set (null);
+ }
+ }\end{hide}
+
+ public static boolean sameSignature (Method m1, Method m2)\begin{hide} {
+ Class[] pt1 = m1.getParameterTypes();
+ Class[] pt2 = m2.getParameterTypes();
+ if (pt1.length != pt2.length)
+ return false;
+ for (int i = 0; i < pt1.length; i++)
+ if (pt1[i] != pt2[i])
+ return false;
+ return true;
+ }\end{hide}
+\end{code}
+\begin{tabb} Determines if two methods \texttt{m1} and \texttt{m2}
+ share the same signature. For the signature to be identical,
+ methods must have the same
+ number of parameters and the same parameter types.
+\end{tabb}
+\begin{htmlonly}
+ \param{m1}{the first method.}
+ \param{m2}{the second method.}
+ \return{\texttt{true} if the signatures are the same, \texttt{false}
+ otherwise.}
+\end{htmlonly}
+\begin{code}
+
+ public static Field[] getFields (Class<?> c)\begin{hide} {
+ // Creates the set of fields for the class.
+ List<Field> lst = new ArrayList<Field>();
+ processFields (c, lst);
+ Set<Class<?>> traversedInterfaces = new HashSet<Class<?>>();
+ processInterfaceFields (c, lst, traversedInterfaces);
+
+ if (!c.isInterface()) {
+ Class<?> s = c.getSuperclass();
+ while (s != null) {
+ processFields (s, lst);
+ processInterfaceFields (s, lst, traversedInterfaces);
+ s = s.getSuperclass();
+ }
+ }
+
+ // Copy the fields to the array that will be returned.
+ return lst.toArray (new Field[lst.size()]);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns all the fields declared and inherited
+ by a class. This is similar to \externalmethod{java.lang}{Class}{getFields}{()}
+ except that it enumerates non-public fields as well as public ones.
+ This method uses \externalmethod{java.lang}{Class}{getDeclaredFields}{()}
+ to get the declared fields of \texttt{c}.
+ It also gets the declared fields of superclasses and implemented
+ interfaces.
+
+ Note that since this method uses
+ \externalmethod{java.lang}{Class}{getDeclaredFields}{()},
+ it can throw a \externalclass{java.lang}{SecurityException} if
+ a security manager is present.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the class being processed.}
+ \return{the array of fields.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private static void processFields (final Class<?> c, final List<Field> lst) {
+ Field[] f = c.getDeclaredFields();
+ for (int i = 0; i < f.length; i++)
+ lst.add (f[i]);
+ }
+
+ private static void processInterfaceFields (final Class<?> c, final List<Field> lst, final Set<Class<?>> traversedInterfaces) {
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++) {
+ if (traversedInterfaces.contains (iface[i]))
+ continue;
+ traversedInterfaces.add (iface[i]);
+ processFields (iface[i], lst);
+ processInterfaceFields (iface[i], lst, traversedInterfaces);
+ }
+ }\end{hide}
+
+ public static Method getMethod (Class<?> c, String name, Class[] pt)
+ throws NoSuchMethodException \begin{hide} {
+ try {
+ return c.getDeclaredMethod (name, pt);
+ }
+ catch (NoSuchMethodException nme) {}
+ if (!c.isInterface())
+ try {
+ Class<?> s = c.getSuperclass();
+ if (s != null)
+ return getMethod (s, name, pt);
+ }
+ catch (NoSuchMethodException nme) {}
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++)
+ try {
+ return getMethod (iface[i], name, pt);
+ }
+ catch (NoSuchMethodException nme) {}
+ throw new NoSuchMethodException
+ ("Cannot find method " + name + " in class " + c.getName());
+ }\end{hide}
+\end{code}
+\begin{tabb} This is like \externalmethod{java.lang}{Class}{getMethod}{(String, Class...)}, except that it can return non-public methods.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the class being processed.}
+ \param{name}{the name of the method.}
+ \param{pt}{the parameter types.}
+ \exception{NoSuchMethodException}{if the method cannot be found.}
+\end{htmlonly}
+\begin{code}
+
+ public static Field getField (Class<?> c, String name)
+ throws NoSuchFieldException\begin{hide} {
+ try {
+ return c.getDeclaredField (name);
+ }
+ catch (NoSuchFieldException nfe) {}
+ Class[] iface = c.getInterfaces();
+ for (int i = 0; i < iface.length; i++)
+ try {
+ return getField (iface[i], name);
+ }
+ catch (NoSuchFieldException nme) {}
+ if (!c.isInterface())
+ try {
+ Class s = c.getSuperclass();
+ if (s != null)
+ return getField (s, name);
+ }
+ catch (NoSuchFieldException nfe) {}
+ throw new NoSuchFieldException
+ ("Cannot find field " + name + " in " + c.getName());
+ }\end{hide}
+\end{code}
+\begin{tabb} This is like \externalmethod{java.lang}{Class}{getField}{(String)},
+ except that it can return non-public fields.
+
+ Note that since this method uses
+ \externalmethod{java.lang}{Class}{getDeclaredField}{(String)},
+ it can throw a \externalclass{java.lang}{SecurityException} if
+ a security manager is present.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{the class being processed.}
+ \param{name}{the name of the method.}
+ \exception{NoSuchFieldException}{if the field cannot be found.}
+\end{htmlonly}
+\begin{code}
+
+ public static String getFieldName (Object val)\begin{hide} {
+ Class<?> enumType = val.getClass();
+ Field[] f = enumType.getFields();
+ for (int i = 0; i < f.length; i++) {
+ if (Modifier.isPublic (f[i].getModifiers()) &&
+ Modifier.isStatic (f[i].getModifiers()) &&
+ Modifier.isFinal (f[i].getModifiers())) {
+ try {
+ if (f[i].get (null) == val)
+ return f[i].getName();
+ }
+ catch (IllegalAccessException iae) {}
+ }
+ }
+ return null;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the field name corresponding to the value of
+ an enumerated type \texttt{val}.
+ This method gets the class of \texttt{val} and
+ scans its fields to find a public static and final field
+ containing \texttt{val}. If such a field is found,
+ its name is returned. Otherwise, \texttt{null} is returned.
+\end{tabb}
+\begin{htmlonly}
+ \param{val}{the value of the enumerated type.}
+ \return{the field name or \texttt{null}.}
+\end{htmlonly}
+\begin{code}
+
+ public static <T> T valueOf (Class<T> cls, String name)\begin{hide} {
+ try {
+ Field field = cls.getField (name);
+ if (Modifier.isStatic (field.getModifiers()) &&
+ Modifier.isFinal (field.getModifiers()) &&
+ cls.isAssignableFrom (field.getType()))
+ return (T)field.get (null);
+ }
+ catch (NoSuchFieldException nfe) {}
+ catch (IllegalAccessException iae) {}
+ throw new IllegalArgumentException ("Invalid field name: " + name);
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the field of class \texttt{cls} corresponding to
+ the name \texttt{name}. This method looks for
+ a public, static, and final field with name \texttt{name} and
+ returns its value. If no appropriate field can be
+ found, an \externalclass{java.lang}{IllegalArgumentException} is thrown.
+\end{tabb}
+\begin{htmlonly}
+ \param{cls}{the class to look for a field in.}
+ \param{name}{the name of field.}
+ \return{the object in the field.}
+ \exception{IllegalArgumentException}{if \texttt{name} does
+ not correspond to a valid field name.}
+\end{htmlonly}
+\begin{code}
+
+ public static <T> T valueOfIgnoreCase (Class<T> cls, String name)\begin{hide} {
+ Field[] fields = cls.getFields();
+ T res = null;
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (field.getName().equalsIgnoreCase (name) &&
+ Modifier.isStatic (field.getModifiers()) &&
+ Modifier.isFinal (field.getModifiers()) &&
+ cls.isAssignableFrom (field.getType()))
+ try {
+ T res2 = (T)field.get (null);
+ if (res != null && res2 != null)
+ throw new IllegalArgumentException
+ ("Found more than one field with the same name in class " +
+ cls.getName() +
+ " if case is ignored");
+ res = res2;
+ }
+ catch (IllegalAccessException iae) {}
+ }
+ if (res == null)
+ throw new IllegalArgumentException ("Invalid field name: " + name);
+ return res;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Similar to \method{valueOf}{(Class, String)} \texttt{(cls, name)},
+ with case insensitive field name look-up.
+ If \texttt{cls} defines several fields with the same case insensitive
+ name \texttt{name}, an \externalclass{java.lang}{IllegalArgumentException} is thrown.
+\end{tabb}
+\begin{htmlonly}
+ \param{cls}{the class to look for a field in.}
+ \param{name}{the name of field.}
+ \return{the object in the field.}
+ \exception{IllegalArgumentException}{if \texttt{name} does
+ not correspond to a valid field name, or if
+ the class defines several fields with the same name.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/util/JDBCManager.java b/source/umontreal/iro/lecuyer/util/JDBCManager.java
new file mode 100644
index 0000000..cf299cc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/JDBCManager.java
@@ -0,0 +1,859 @@
+
+/*
+ * Class: JDBCManager
+ * Description: Interface to access databases
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.io.*;
+import java.net.URL;
+import java.sql.*;
+import javax.sql.DataSource;
+import java.util.Properties;
+import javax.naming.*;
+
+
+/**
+ * This class provides some facilities to connect to a SQL database and to
+ * retrieve data stored in it.
+ * JDBC provides a standardized interface for accessing a database
+ * independently of a specific database management system (DBMS).
+ * The user of JDBC must create a {@link Connection} object
+ * used to send SQL queries to the underlying DBMS, but
+ * the creation of the connection adds a DBMS-specific portion
+ * in the application.
+ * This class helps the developer in moving the DBMS-specific
+ * information out of the source code by storing it in
+ * a properties file. The methods in this class can
+ * read such a properties file and establish the JDBC connection.
+ * The connection can be made by using a {@link DataSource} obtained
+ * through a JNDI server, or by a JDBC URI associated with a
+ * driver class.
+ * Therefore, the properties used to connect to the
+ * database must be a JNDI name (<TT>jdbc.jndi-name</TT>),
+ * or a driver to load
+ * (<TT>jdbc.driver</TT>) with the URI of a database (<TT>jdbc.uri</TT>).
+ *
+ * <DIV CLASS="vcode" ALIGN="LEFT">
+ * <TT>
+ *
+ * <BR> jdbc.driver=com.mysql.jdbc.Driver
+ * <BR>
+ * <BR> jdbc.uri=jdbc:mysql://mysql.iro.umontreal.ca/database?user=foo&password=bar
+ * <BR></TT>
+ * </DIV>
+ *
+ * <P>
+ * The connection is established using the
+ * {@link #connectToDatabase((Properties)) connectToDatabase}
+ * method. Shortcut methods are also available to read the
+ * properties from a file or a resource before establishing the connection.
+ * This class also provides shortcut methods to read data from a database
+ * and to copy the data into Java arrays.
+ *
+ */
+public class JDBCManager {
+
+
+ /**
+ * Connects to the database using the properties <TT>prop</TT> and returns the
+ * an object representing the connection.
+ * The properties stored in <TT>prop</TT> must be a JNDI name
+ * (<TT>jdbc.jndi-name</TT>), or the name of a driver
+ * (<TT>jdbc.driver</TT>) to load and the URI of the database (<TT>jdbc.uri</TT>).
+ * When a JNDI name is given, this method constructs a
+ * context using the nullary constructor of {@link InitialContext},
+ * uses the context to get a {@link DataSource} object,
+ * and uses the data source to obtain a connection.
+ * This method assumes that JNDI is configured correctly;
+ * see the class {@link InitialContext} for more information about configuring
+ * JNDI.
+ * If no JNDI name is specified, the method looks for a JDBC URI.
+ * If a driver class name is specified along with the URI, the corresponding driver
+ * is loaded and registered with the JDBC {@link DriverManager}.
+ * The driver manager is then used to obtain the connection using the URI.
+ * This method throws
+ * an {@link SQLException} if the connection failed and an
+ * {@link IllegalArgumentException}
+ * if the properties do not contain the required values.
+ *
+ * @param prop the properties to connect to the database.
+ *
+ * @return the connection to the database.
+ * @exception SQLException if the connection failed.
+ *
+ * @exception IllegalArgumentException if the properties do not contain the require values.
+ *
+ *
+ */
+ public static Connection connectToDatabase (Properties prop)
+ throws SQLException {
+ Connection connection = null;
+ String jndiName;
+
+ if ((jndiName = prop.getProperty ("jdbc.jndi-name")) != null)
+ {
+ try
+ {
+ InitialContext context = new InitialContext();
+ connection = ((DataSource) context.lookup (jndiName)).getConnection();
+ }
+ catch (NamingException e)
+ {
+ throw new IllegalArgumentException
+ ("The jdbc.jndi-name property refers to the invalid name " + jndiName);
+ }
+ }
+ else
+ {
+ String driver = prop.getProperty ("jdbc.driver");
+ String uri = prop.getProperty ("jdbc.uri");
+ if (uri != null)
+ {
+ if (driver != null) {
+ try
+ {
+ Class driverClass = Class.forName (driver);
+ if (!Driver.class.isAssignableFrom (driverClass))
+ throw new IllegalArgumentException
+ ("The driver name " + driver +
+ " does not correspond to a class implementing the java.sql.Driver interface");
+ // Needed by some buggy drivers
+ driverClass.newInstance();
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new IllegalArgumentException ("Could not find the driver class " + driver);
+ }
+ catch (IllegalAccessException iae) {
+ throw new IllegalArgumentException
+ ("An illegal access prevented the instantiation of driver class " + driver);
+ }
+ catch (InstantiationException ie) {
+ throw new IllegalArgumentException
+ ("An instantiation exception prevented the instantiation of driver class " + driver +
+ ": " + ie.getMessage());
+ }
+ }
+ connection = DriverManager.getConnection (uri);
+ }
+ else
+ {
+ throw new IllegalArgumentException
+ ("The jdbc.driver and jdbc.uri properties must be given if jdbc.jndi-name is not set");
+ }
+ }
+
+ return connection;
+ }
+
+
+ /**
+ * Returns a connection to the database using the properties read from stream <TT>is</TT>.
+ * This method loads the properties from the given stream, and
+ * calls {@link #connectToDatabase((Properties)) connectToDatabase} to establish the connection.
+ *
+ * @param is the stream to read for the properties.
+ *
+ * @return the connection to the database.
+ * @exception SQLException if the connection failed.
+ *
+ * @exception IOException if the stream can not be read correctly.
+ *
+ * @exception IllegalArgumentException if the properties do not contain the require values.
+ *
+ *
+ */
+ public static Connection connectToDatabase (InputStream is)
+ throws IOException, SQLException {
+ Properties prop = new Properties();
+
+ prop.load (is);
+
+ return connectToDatabase (prop);
+ }
+
+
+ /**
+ * Equivalent to {@link #connectToDatabase((InputStream)) connectToDatabase} <TT>(url.openStream())</TT>.
+ *
+ */
+ public static Connection connectToDatabase (URL url)
+ throws IOException, SQLException {
+
+ InputStream is = url.openStream();
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }
+
+
+ /**
+ * Equivalent to {@link #connectToDatabase((InputStream)) connectToDatabase} <TT>(new FileInputStream (file))</TT>.
+ *
+ */
+ public static Connection connectToDatabase (File file)
+ throws IOException, SQLException {
+
+ FileInputStream is = new FileInputStream (file);
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }
+
+
+ /**
+ * Equivalent to {@link #connectToDatabase((InputStream)) connectToDatabase} <TT>(new FileInputStream (fileName))</TT>.
+ *
+ */
+ public static Connection connectToDatabase (String fileName)
+ throws IOException, SQLException {
+
+ FileInputStream is = new FileInputStream (fileName);
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }
+
+
+ /**
+ * Uses {@link #connectToDatabase((InputStream)) connectToDatabase} with the stream obtained from
+ * the resource <TT>resource</TT>.
+ * This method searches the file <TT>resource</TT> on the class path, opens
+ * the first resource found, and extracts properties from it.
+ * It then uses {@link #connectToDatabase((Properties)) connectToDatabase}
+ * to establish the connection.
+ *
+ */
+ public static Connection connectToDatabaseFromResource (String resource)
+ throws IOException, SQLException {
+
+ InputStream is = JDBCManager.class.getClassLoader().getResourceAsStream (resource);
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into an array of double-precision values.
+ * This method uses the statement <TT>stmt</TT> to execute the given query, and
+ * assumes that the first column of the result set contains double-precision values.
+ * Each row of the result set then becomes an element of an array of double-precision
+ * values which is returned by this method.
+ * This method throws an {@link SQLException} if the query is not valid.
+ *
+ * @param stmt the statement used to make the query.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the first column of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static double[] readDoubleData (Statement stmt, String query)
+ throws SQLException {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ double[] res = new double[rs.getRow()];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++)
+ {
+ res[i] = rs.getDouble (1);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into an array of double-precision values.
+ * This method uses the active connection <TT>connection</TT> to create
+ * a statement, and passes this statement, with the query, to
+ * {@link #readDoubleData((Statement,String)) readDoubleData}, which returns an
+ * array of double-precision values.
+ *
+ * @param connection the active connection to the database.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the first column of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static double[] readDoubleData (Connection connection,
+ String query)
+ throws SQLException {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readDoubleData (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }
+
+
+ /**
+ * Returns the values of the column <TT>column</TT> of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readDoubleData((Statement, String)) readDoubleData}
+ * <TT>(stmt, "SELECT column FROM table")</TT>.
+ *
+ */
+ public static double[] readDoubleData (Statement stmt, String table,
+ String column)
+ throws SQLException {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readDoubleData (stmt, query);
+ }
+
+
+ /**
+ * Returns the values of the column <TT>column</TT> of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readDoubleData((Connection, String)) readDoubleData}
+ * <TT>(connection, "SELECT column FROM table")</TT>.
+ *
+ */
+ public static double[] readDoubleData (Connection connection,
+ String table, String column)
+ throws SQLException {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readDoubleData (connection, query);
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into an array of integers.
+ * This method uses the statement <TT>stmt</TT> to execute the given query, and
+ * assumes that the first column of the result set contains integer values.
+ * Each row of the result set then becomes an element of an array of integers
+ * which is returned by this method.
+ * This method throws an {@link SQLException} if the query is not valid.
+ * The given statement <TT>stmt</TT> must not be set up to
+ * produce forward-only result sets.
+ *
+ * @param stmt the statement used to make the query.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the first column of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static int[] readIntData (Statement stmt, String query)
+ throws SQLException {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int[] res = new int[rs.getRow()];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++)
+ {
+ res[i] = rs.getInt (1);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into an array of integers.
+ * This method uses the active connection <TT>connection</TT> to create
+ * a statement, and passes this statement, with the query, to
+ * {@link #readIntData((Statement,String)) readIntData}, which returns an
+ * array of integers.
+ *
+ * @param connection the active connection to the database.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the first column of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static int[] readIntData (Connection connection, String query)
+ throws SQLException {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readIntData (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }
+
+
+ /**
+ * Returns the values of the column <TT>column</TT> of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readIntData((Statement, String)) readIntData}
+ * <TT>(stmt, "SELECT column FROM table")</TT>.
+ *
+ */
+ public static int[] readIntData (Statement stmt, String table,
+ String column)
+ throws SQLException {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readIntData (stmt, query);
+ }
+
+
+ /**
+ * Returns the values of the column <TT>column</TT> of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readIntData((Connection, String)) readIntData}
+ * <TT>(connection, "SELECT column FROM table")</TT>.
+ *
+ */
+ public static int[] readIntData (Connection connection, String table,
+ String column)
+ throws SQLException {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readIntData (connection, query);
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into an array of objects.
+ * This method uses the statement <TT>stmt</TT> to execute the given query, and
+ * extracts values from the first column of the obtained result set by
+ * using the <TT>getObject</TT> method.
+ * Each row of the result set then becomes an element of an array of objects
+ * which is returned by this method.
+ * The type of the objects in the array depends on the column type of
+ * the result set, which depends on the database and query.
+ * This method throws an {@link SQLException} if the query is not valid.
+ * The given statement <TT>stmt</TT> must not be set up to
+ * produce forward-only result sets.
+ *
+ * @param stmt the statement used to make the query.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the first column of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static Object[] readObjectData (Statement stmt, String query)
+ throws SQLException {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ Object[] res = new Object[rs.getRow()];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++)
+ {
+ res[i] = rs.getObject (1);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into an array of objects.
+ * This method uses the active connection <TT>connection</TT> to create
+ * a statement, and passes this statement, with the query, to
+ * {@link #readObjectData((Statement,String)) readObjectData}, which returns an
+ * array of integers.
+ *
+ * @param connection the active connection to the database.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the first column of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static Object[] readObjectData (Connection connection,
+ String query)
+ throws SQLException {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readObjectData (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }
+
+
+ /**
+ * Returns the values of the column <TT>column</TT> of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readObjectData((Statement, String)) readObjectData}
+ * <TT>(stmt, "SELECT column FROM table")</TT>.
+ *
+ */
+ public static Object[] readObjectData (Statement stmt,
+ String table, String column)
+ throws SQLException {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readObjectData (stmt, query);
+ }
+
+
+ /**
+ * Returns the values of the column <TT>column</TT> of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readObjectData((Connection, String)) readObjectData}
+ * <TT>(connection, "SELECT column FROM table")</TT>.
+ *
+ */
+ public static Object[] readObjectData (Connection connection,
+ String table, String column)
+ throws SQLException {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readObjectData (connection, query);
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into a
+ * rectangular 2D array of double-precision values.
+ * This method uses the statement <TT>stmt</TT> to execute the given query, and
+ * assumes that the columns of the result set contain double-precision values.
+ * Each row of the result set then becomes a row of a 2D array of double-precision
+ * values which is returned by this method.
+ * This method throws an {@link SQLException} if the query is not valid.
+ * The given statement <TT>stmt</TT> must not be set up to
+ * produce forward-only result sets.
+ *
+ * @param stmt the statement used to make the query.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the columns of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static double[][] readDoubleData2D (Statement stmt, String query)
+ throws SQLException {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int c = rs.getMetaData().getColumnCount();
+ double[][] res = new double[rs.getRow()][c];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++) {
+ for (int j = 0; j < res[i].length; j++)
+ res[i][j] = rs.getDouble (1 + j);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into a rectangular 2D array
+ * of double-precision values.
+ * This method uses the active connection <TT>connection</TT> to create
+ * a statement, and passes this statement, with the query, to
+ * {@link #readDoubleData2D((Statement,String)) readDoubleData2D}, which returns a 2D
+ * array of double-precision values.
+ *
+ * @param connection the active connection to the database.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the columns of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static double[][] readDoubleData2D (Connection connection,
+ String query)
+ throws SQLException {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readDoubleData2D (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }
+
+
+ /**
+ * Returns the values of the columns of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readDoubleData2D((Statement, String)) readDoubleData2D}
+ * <TT>(stmt, "SELECT * FROM table")</TT>.
+ *
+ */
+ public static double[][] readDoubleData2DTable (Statement stmt,
+ String table)
+ throws SQLException {
+ final String query = "SELECT * FROM " + table;
+
+ return readDoubleData2D (stmt, query);
+ }
+
+
+ /**
+ * Returns the values of the columns of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readDoubleData2D((Connection, String)) readDoubleData2D}
+ * <TT>(connection, "SELECT * FROM table")</TT>.
+ *
+ */
+ public static double[][] readDoubleData2DTable (Connection connection,
+ String table)
+ throws SQLException {
+ final String query = "SELECT * FROM " + table;
+
+ return readDoubleData2D (connection, query);
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into a
+ * rectangular 2D array of integers.
+ * This method uses the statement <TT>stmt</TT> to execute the given query, and
+ * assumes that the columns of the result set contain integers.
+ * Each row of the result set then becomes a row of a 2D array of integers
+ * which is returned by this method.
+ * This method throws an {@link SQLException} if the query is not valid.
+ * The given statement <TT>stmt</TT> must not be set up to
+ * produce forward-only result sets.
+ *
+ * @param stmt the statement used to make the query.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the columns of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static int[][] readIntData2D (Statement stmt, String query)
+ throws SQLException {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int c = rs.getMetaData().getColumnCount();
+ int[][] res = new int[rs.getRow()][c];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++) {
+ for (int j = 0; j < res[i].length; j++)
+ res[i][j] = rs.getInt (1 + j);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into a rectangular 2D array
+ * of integers.
+ * This method uses the active connection <TT>connection</TT> to create
+ * a statement, and passes this statement, with the query, to
+ * {@link #readIntData2D((Statement,String)) readIntData2D}, which returns a 2D
+ * array of integers.
+ *
+ * @param connection the active connection to the database.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the columns of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static int[][] readIntData2D (Connection connection, String query)
+ throws SQLException {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readIntData2D (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }
+
+
+ /**
+ * Returns the values of the columns of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readIntData2D((Statement, String)) readIntData2D}
+ * <TT>(stmt, "SELECT * FROM table")</TT>.
+ *
+ */
+ public static int[][] readIntData2DTable (Statement stmt, String table)
+ throws SQLException {
+ final String query = "SELECT * FROM " + table;
+
+ return readIntData2D (stmt, query);
+ }
+
+
+ /**
+ * Returns the values of the columns of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readIntData2D((Connection, String)) readIntData2D}
+ * <TT>(connection, "SELECT * FROM table")</TT>.
+ *
+ */
+ public static int[][] readIntData2DTable (Connection connection,
+ String table)
+ throws SQLException {
+ final String query = "SELECT * FROM " + table;
+
+ return readIntData2D (connection, query);
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into a
+ * rectangular 2D array of objects.
+ * This method uses the statement <TT>stmt</TT> to execute the given query, and
+ * extracts values from the obtained result set by using the <TT>getObject</TT> method.
+ * Each row of the result set then becomes a row of a 2D array of objects
+ * which is returned by this method.
+ * The type of the objects in the 2D array depends on the column types of
+ * the result set, which depend on the database and query.
+ * This method throws an {@link SQLException} if the query is not valid.
+ * The given statement <TT>stmt</TT> must not be set up to
+ * produce forward-only result sets.
+ *
+ * @param stmt the statement used to make the query.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the columns of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static Object[][] readObjectData2D (Statement stmt, String query)
+ throws SQLException {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int c = rs.getMetaData().getColumnCount();
+ Object[][] res = new Object[rs.getRow()][c];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++) {
+ for (int j = 0; j < res[i].length; j++)
+ res[i][j] = rs.getObject (1 + j);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }
+
+
+ /**
+ * Copies the result of the SQL query <TT>query</TT> into a rectangular 2D array
+ * of integers.
+ * This method uses the active connection <TT>connection</TT> to create
+ * a statement, and passes this statement, with the query, to
+ * {@link #readObjectData2D((Statement,String)) readObjectData2D}, which returns a 2D
+ * array of integers.
+ *
+ * @param connection the active connection to the database.
+ *
+ * @param query the SQL query to execute.
+ *
+ * @return the columns of the result set.
+ * @exception SQLException if the query is not valid.
+ *
+ *
+ */
+ public static Object[][] readObjectData2D (Connection connection,
+ String query)
+ throws SQLException {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readObjectData2D (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }
+
+
+ /**
+ * Returns the values of the columns of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readObjectData2D((Statement, String)) readObjectData2D}
+ * <TT>(stmt, "SELECT * FROM table")</TT>.
+ *
+ */
+ public static Object[][] readObjectData2DTable (Statement stmt,
+ String table)
+ throws SQLException {
+ final String query = "SELECT * FROM " + table;
+
+ return readObjectData2D (stmt, query);
+ }
+
+
+ /**
+ * Returns the values of the columns of the table <TT>table</TT>.
+ * This method is equivalent to {@link #readObjectData2D((Connection, String)) readObjectData2D}
+ * <TT>(connection, "SELECT * FROM table")</TT>.
+ *
+ */
+ public static Object[][] readObjectData2DTable (Connection connection,
+ String table)
+ throws SQLException {
+ final String query = "SELECT * FROM " + table;
+
+ return readObjectData2D (connection, query);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/JDBCManager.tex b/source/umontreal/iro/lecuyer/util/JDBCManager.tex
new file mode 100644
index 0000000..9c8e3af
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/JDBCManager.tex
@@ -0,0 +1,842 @@
+\defmodule{JDBCManager}
+
+This class provides some facilities to connect to a SQL database and to
+retrieve data stored in it.
+JDBC provides a standardized interface for accessing a database
+independently of a specific database management system (DBMS).
+The user of JDBC must create a \class{Connection} object
+used to send SQL queries to the underlying DBMS, but
+the creation of the connection adds a DBMS-specific portion
+in the application.
+This class helps the developer in moving the DBMS-specific
+information out of the source code by storing it in
+a properties file. The methods in this class can
+read such a properties file and establish the JDBC connection.
+The connection can be made by using a \class{DataSource} obtained
+through a JNDI server, or by a JDBC URI associated with a
+driver class.
+Therefore, the properties used to connect to the
+database must be a JNDI name (\texttt{jdbc.jndi-name}),
+or a driver to load
+(\texttt{jdbc.driver}) with the URI of a database (\texttt{jdbc.uri}).
+\begin{vcode}
+
+ jdbc.driver=com.mysql.jdbc.Driver\\
+ jdbc.uri=jdbc:mysql://mysql.iro.umontreal.ca/database?user=foo&password=bar
+\end{vcode}
+
+The connection is established using the
+\method{connectToDatabase}{(Properties)}
+method. Shortcut methods are also available to read the
+properties from a file or a resource before establishing the connection.
+This class also provides shortcut methods to read data from a database
+and to copy the data into Java arrays.
+
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: JDBCManager
+ * Description: Interface to access databases
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+\begin{hide}
+import java.io.*;
+import java.net.URL;
+import java.sql.*;
+import javax.sql.DataSource;
+import java.util.Properties;
+import javax.naming.*;
+\end{hide}
+
+public class JDBCManager\begin{hide} {
+\end{hide}\end{code}
+\subsubsection*{Methods}
+\begin{code}
+
+ public static Connection connectToDatabase (Properties prop)
+ throws SQLException \begin{hide} {
+ Connection connection = null;
+ String jndiName;
+
+ if ((jndiName = prop.getProperty ("jdbc.jndi-name")) != null)
+ {
+ try
+ {
+ InitialContext context = new InitialContext();
+ connection = ((DataSource) context.lookup (jndiName)).getConnection();
+ }
+ catch (NamingException e)
+ {
+ throw new IllegalArgumentException
+ ("The jdbc.jndi-name property refers to the invalid name " + jndiName);
+ }
+ }
+ else
+ {
+ String driver = prop.getProperty ("jdbc.driver");
+ String uri = prop.getProperty ("jdbc.uri");
+ if (uri != null)
+ {
+ if (driver != null) {
+ try
+ {
+ Class driverClass = Class.forName (driver);
+ if (!Driver.class.isAssignableFrom (driverClass))
+ throw new IllegalArgumentException
+ ("The driver name " + driver +
+ " does not correspond to a class implementing the java.sql.Driver interface");
+ // Needed by some buggy drivers
+ driverClass.newInstance();
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new IllegalArgumentException ("Could not find the driver class " + driver);
+ }
+ catch (IllegalAccessException iae) {
+ throw new IllegalArgumentException
+ ("An illegal access prevented the instantiation of driver class " + driver);
+ }
+ catch (InstantiationException ie) {
+ throw new IllegalArgumentException
+ ("An instantiation exception prevented the instantiation of driver class " + driver +
+ ": " + ie.getMessage());
+ }
+ }
+ connection = DriverManager.getConnection (uri);
+ }
+ else
+ {
+ throw new IllegalArgumentException
+ ("The jdbc.driver and jdbc.uri properties must be given if jdbc.jndi-name is not set");
+ }
+ }
+
+ return connection;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Connects to the database using the properties \texttt{prop} and returns the
+ an object representing the connection.
+ The properties stored in \texttt{prop} must be a JNDI name
+ (\texttt{jdbc.jndi-name}), or the name of a driver
+ (\texttt{jdbc.driver}) to load and the URI of the database (\texttt{jdbc.uri}).
+ When a JNDI name is given, this method constructs a
+ context using the nullary constructor of \class{InitialContext},
+ uses the context to get a \class{DataSource} object,
+ and uses the data source to obtain a connection.
+ This method assumes that JNDI is configured correctly;
+ see the class \class{InitialContext} for more information about configuring
+ JNDI.
+ If no JNDI name is specified, the method looks for a JDBC URI.
+ If a driver class name is specified along with the URI, the corresponding driver
+ is loaded and registered with the JDBC \class{DriverManager}.
+ The driver manager is then used to obtain the connection using the URI.
+ This method throws
+ an \class{SQLException} if the connection failed and an
+ \class{IllegalArgumentException}
+ if the properties do not contain the required values.
+\end{tabb}
+\begin{htmlonly}
+ \param{prop}{the properties to connect to the database.}
+ \return{the connection to the database.}
+ \exception{SQLException}{if the connection failed.}
+ \exception{IllegalArgumentException}{if the properties do not contain the require values.}
+\end{htmlonly}
+\begin{code}
+
+ public static Connection connectToDatabase (InputStream is)
+ throws IOException, SQLException \begin{hide} {
+ Properties prop = new Properties();
+
+ prop.load (is);
+
+ return connectToDatabase (prop);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a connection to the database using the properties read from stream \texttt{is}.
+ This method loads the properties from the given stream, and
+ calls \method{connectToDatabase}{(Properties)} to establish the connection.
+\end{tabb}
+\begin{htmlonly}
+ \param{is}{the stream to read for the properties.}
+ \return{the connection to the database.}
+ \exception{SQLException}{if the connection failed.}
+ \exception{IOException}{if the stream can not be read correctly.}
+ \exception{IllegalArgumentException}{if the properties do not contain the require values.}
+\end{htmlonly}
+\begin{code}
+
+ public static Connection connectToDatabase (URL url)
+ throws IOException, SQLException\begin{hide} {
+
+ InputStream is = url.openStream();
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Equivalent to \method{connectToDatabase}{(InputStream)} \texttt{(url.openStream())}.
+\end{tabb}
+\begin{code}
+
+ public static Connection connectToDatabase (File file)
+ throws IOException, SQLException\begin{hide} {
+
+ FileInputStream is = new FileInputStream (file);
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Equivalent to \method{connectToDatabase}{(InputStream)} \texttt{(new FileInputStream (file))}.
+\end{tabb}
+\begin{code}
+
+ public static Connection connectToDatabase (String fileName)
+ throws IOException, SQLException\begin{hide} {
+
+ FileInputStream is = new FileInputStream (fileName);
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Equivalent to \method{connectToDatabase}{(InputStream)} \texttt{(new FileInputStream (fileName))}.
+\end{tabb}
+\begin{code}
+
+ public static Connection connectToDatabaseFromResource (String resource)
+ throws IOException, SQLException\begin{hide} {
+
+ InputStream is = JDBCManager.class.getClassLoader().getResourceAsStream (resource);
+ try {
+ return connectToDatabase (is);
+ }
+ finally {
+ is.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Uses \method{connectToDatabase}{(InputStream)} with the stream obtained from
+ the resource \texttt{resource}.
+ This method searches the file \texttt{resource} on the class path, opens
+ the first resource found, and extracts properties from it.
+ It then uses \method{connectToDatabase}{(Properties)}
+ to establish the connection.
+\end{tabb}
+\begin{code}
+
+ public static double[] readDoubleData (Statement stmt, String query)
+ throws SQLException\begin{hide} {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ double[] res = new double[rs.getRow()];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++)
+ {
+ res[i] = rs.getDouble (1);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into an array of double-precision values.
+ This method uses the statement \texttt{stmt} to execute the given query, and
+ assumes that the first column of the result set contains double-precision values.
+ Each row of the result set then becomes an element of an array of double-precision
+ values which is returned by this method.
+ This method throws an \class{SQLException} if the query is not valid.
+\end{tabb}
+\begin{htmlonly}
+ \param{stmt}{the statement used to make the query.}
+ \param{query}{the SQL query to execute.}
+ \return{the first column of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] readDoubleData (Connection connection,
+ String query)
+ throws SQLException\begin{hide} {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readDoubleData (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into an array of double-precision values.
+ This method uses the active connection \texttt{connection} to create
+ a statement, and passes this statement, with the query, to
+ \method{readDoubleData}{(Statement,String)}, which returns an
+ array of double-precision values.
+\end{tabb}
+\begin{htmlonly}
+ \param{connection}{the active connection to the database.}
+ \param{query}{the SQL query to execute.}
+ \return{the first column of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] readDoubleData (Statement stmt, String table,
+ String column)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readDoubleData (stmt, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the column \texttt{column} of the table \texttt{table}.
+ % in an array of \texttt{double}'s.
+ This method is equivalent to \method{readDoubleData}{(Statement, String)}
+ \texttt{(stmt, "SELECT column FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static double[] readDoubleData (Connection connection,
+ String table, String column)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readDoubleData (connection, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the column \texttt{column} of the table \texttt{table}.
+ % in an array of \texttt{double}'s.
+ This method is equivalent to \method{readDoubleData}{(Connection, String)}
+ \texttt{(connection, "SELECT column FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static int[] readIntData (Statement stmt, String query)
+ throws SQLException\begin{hide} {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int[] res = new int[rs.getRow()];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++)
+ {
+ res[i] = rs.getInt (1);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into an array of integers.
+ This method uses the statement \texttt{stmt} to execute the given query, and
+ assumes that the first column of the result set contains integer values.
+ Each row of the result set then becomes an element of an array of integers
+ which is returned by this method.
+ This method throws an \class{SQLException} if the query is not valid.
+ The given statement \texttt{stmt} must not be set up to
+ produce forward-only result sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{stmt}{the statement used to make the query.}
+ \param{query}{the SQL query to execute.}
+ \return{the first column of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[] readIntData (Connection connection, String query)
+ throws SQLException\begin{hide} {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readIntData (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into an array of integers.
+ This method uses the active connection \texttt{connection} to create
+ a statement, and passes this statement, with the query, to
+ \method{readIntData}{(Statement,String)}, which returns an
+ array of integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{connection}{the active connection to the database.}
+ \param{query}{the SQL query to execute.}
+ \return{the first column of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[] readIntData (Statement stmt, String table,
+ String column)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readIntData (stmt, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the column \texttt{column} of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readIntData}{(Statement, String)}
+ \texttt{(stmt, "SELECT column FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static int[] readIntData (Connection connection, String table,
+ String column)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readIntData (connection, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the column \texttt{column} of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readIntData}{(Connection, String)}
+ \texttt{(connection, "SELECT column FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static Object[] readObjectData (Statement stmt, String query)
+ throws SQLException\begin{hide} {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ Object[] res = new Object[rs.getRow()];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++)
+ {
+ res[i] = rs.getObject (1);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into an array of objects.
+ This method uses the statement \texttt{stmt} to execute the given query, and
+ extracts values from the first column of the obtained result set by
+ using the \texttt{getObject} method.
+ Each row of the result set then becomes an element of an array of objects
+ which is returned by this method.
+ The type of the objects in the array depends on the column type of
+ the result set, which depends on the database and query.
+ This method throws an \class{SQLException} if the query is not valid.
+ The given statement \texttt{stmt} must not be set up to
+ produce forward-only result sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{stmt}{the statement used to make the query.}
+ \param{query}{the SQL query to execute.}
+ \return{the first column of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static Object[] readObjectData (Connection connection,
+ String query)
+ throws SQLException\begin{hide} {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readObjectData (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into an array of objects.
+ This method uses the active connection \texttt{connection} to create
+ a statement, and passes this statement, with the query, to
+ \method{readObjectData}{(Statement,String)}, which returns an
+ array of integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{connection}{the active connection to the database.}
+ \param{query}{the SQL query to execute.}
+ \return{the first column of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static Object[] readObjectData (Statement stmt,
+ String table, String column)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readObjectData (stmt, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the column \texttt{column} of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readObjectData}{(Statement, String)}
+ \texttt{(stmt, "SELECT column FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static Object[] readObjectData (Connection connection,
+ String table, String column)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT " + column + " FROM " + table;
+
+ return readObjectData (connection, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the column \texttt{column} of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readObjectData}{(Connection, String)}
+ \texttt{(connection, "SELECT column FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static double[][] readDoubleData2D (Statement stmt, String query)
+ throws SQLException\begin{hide} {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int c = rs.getMetaData().getColumnCount();
+ double[][] res = new double[rs.getRow()][c];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++) {
+ for (int j = 0; j < res[i].length; j++)
+ res[i][j] = rs.getDouble (1 + j);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into a
+ rectangular 2D array of double-precision values.
+ This method uses the statement \texttt{stmt} to execute the given query, and
+ assumes that the columns of the result set contain double-precision values.
+ Each row of the result set then becomes a row of a 2D array of double-precision
+ values which is returned by this method.
+ This method throws an \class{SQLException} if the query is not valid.
+ The given statement \texttt{stmt} must not be set up to
+ produce forward-only result sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{stmt}{the statement used to make the query.}
+ \param{query}{the SQL query to execute.}
+ \return{the columns of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] readDoubleData2D (Connection connection,
+ String query)
+ throws SQLException\begin{hide} {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readDoubleData2D (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into a rectangular 2D array
+ of double-precision values.
+ This method uses the active connection \texttt{connection} to create
+ a statement, and passes this statement, with the query, to
+ \method{readDoubleData2D}{(Statement,String)}, which returns a 2D
+ array of double-precision values.
+\end{tabb}
+\begin{htmlonly}
+ \param{connection}{the active connection to the database.}
+ \param{query}{the SQL query to execute.}
+ \return{the columns of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] readDoubleData2DTable (Statement stmt,
+ String table)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT * FROM " + table;
+
+ return readDoubleData2D (stmt, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the columns of the table \texttt{table}.
+ % in an array of \texttt{double}'s.
+ This method is equivalent to \method{readDoubleData2D}{(Statement, String)}
+ \texttt{(stmt, "SELECT * FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static double[][] readDoubleData2DTable (Connection connection,
+ String table)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT * FROM " + table;
+
+ return readDoubleData2D (connection, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the columns of the table \texttt{table}.
+ % in an array of \texttt{double}'s.
+ This method is equivalent to \method{readDoubleData2D}{(Connection, String)}
+ \texttt{(connection, "SELECT * FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static int[][] readIntData2D (Statement stmt, String query)
+ throws SQLException\begin{hide} {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int c = rs.getMetaData().getColumnCount();
+ int[][] res = new int[rs.getRow()][c];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++) {
+ for (int j = 0; j < res[i].length; j++)
+ res[i][j] = rs.getInt (1 + j);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into a
+ rectangular 2D array of integers.
+ This method uses the statement \texttt{stmt} to execute the given query, and
+ assumes that the columns of the result set contain integers.
+ Each row of the result set then becomes a row of a 2D array of integers
+ which is returned by this method.
+ This method throws an \class{SQLException} if the query is not valid.
+ The given statement \texttt{stmt} must not be set up to
+ produce forward-only result sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{stmt}{the statement used to make the query.}
+ \param{query}{the SQL query to execute.}
+ \return{the columns of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[][] readIntData2D (Connection connection, String query)
+ throws SQLException\begin{hide} {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readIntData2D (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into a rectangular 2D array
+ of integers.
+ This method uses the active connection \texttt{connection} to create
+ a statement, and passes this statement, with the query, to
+ \method{readIntData2D}{(Statement,String)}, which returns a 2D
+ array of integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{connection}{the active connection to the database.}
+ \param{query}{the SQL query to execute.}
+ \return{the columns of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[][] readIntData2DTable (Statement stmt, String table)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT * FROM " + table;
+
+ return readIntData2D (stmt, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the columns of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readIntData2D}{(Statement, String)}
+ \texttt{(stmt, "SELECT * FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static int[][] readIntData2DTable (Connection connection,
+ String table)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT * FROM " + table;
+
+ return readIntData2D (connection, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the columns of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readIntData2D}{(Connection, String)}
+ \texttt{(connection, "SELECT * FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static Object[][] readObjectData2D (Statement stmt, String query)
+ throws SQLException\begin{hide} {
+ ResultSet rs = stmt.executeQuery (query);
+ rs.last();
+ int c = rs.getMetaData().getColumnCount();
+ Object[][] res = new Object[rs.getRow()][c];
+ rs.first();
+
+ for (int i = 0; i < res.length; i++) {
+ for (int j = 0; j < res[i].length; j++)
+ res[i][j] = rs.getObject (1 + j);
+ rs.next();
+ }
+ rs.close();
+
+ return res;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into a
+ rectangular 2D array of objects.
+ This method uses the statement \texttt{stmt} to execute the given query, and
+ extracts values from the obtained result set by using the \texttt{getObject} method.
+ Each row of the result set then becomes a row of a 2D array of objects
+ which is returned by this method.
+ The type of the objects in the 2D array depends on the column types of
+ the result set, which depend on the database and query.
+ This method throws an \class{SQLException} if the query is not valid.
+ The given statement \texttt{stmt} must not be set up to
+ produce forward-only result sets.
+\end{tabb}
+\begin{htmlonly}
+ \param{stmt}{the statement used to make the query.}
+ \param{query}{the SQL query to execute.}
+ \return{the columns of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static Object[][] readObjectData2D (Connection connection,
+ String query)
+ throws SQLException\begin{hide} {
+ Statement stmt = connection.createStatement
+ (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ try {
+ return readObjectData2D (stmt, query);
+ }
+ finally {
+ stmt.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Copies the result of the SQL query \texttt{query} into a rectangular 2D array
+ of integers.
+ This method uses the active connection \texttt{connection} to create
+ a statement, and passes this statement, with the query, to
+ \method{readObjectData2D}{(Statement,String)}, which returns a 2D
+ array of integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{connection}{the active connection to the database.}
+ \param{query}{the SQL query to execute.}
+ \return{the columns of the result set.}
+ \exception{SQLException}{if the query is not valid.}
+\end{htmlonly}
+\begin{code}
+
+ public static Object[][] readObjectData2DTable (Statement stmt,
+ String table)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT * FROM " + table;
+
+ return readObjectData2D (stmt, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the columns of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readObjectData2D}{(Statement, String)}
+ \texttt{(stmt, "SELECT * FROM table")}.
+\end{tabb}
+\begin{code}
+
+ public static Object[][] readObjectData2DTable (Connection connection,
+ String table)
+ throws SQLException\begin{hide} {
+ final String query = "SELECT * FROM " + table;
+
+ return readObjectData2D (connection, query);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the values of the columns of the table \texttt{table}.
+ % in an array of \texttt{int}'s.
+ This method is equivalent to \method{readObjectData2D}{(Connection, String)}
+ \texttt{(connection, "SELECT * FROM table")}.
+\end{tabb}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/Misc.java b/source/umontreal/iro/lecuyer/util/Misc.java
new file mode 100644
index 0000000..f6fb58c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Misc.java
@@ -0,0 +1,330 @@
+
+/*
+ * Class: Misc
+ * Description: Miscellaneous functions that are hard to classify.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+ import umontreal.iro.lecuyer.functions.MathFunction;
+
+/**
+ * This class provides miscellaneous functions that are hard to classify.
+ * Some may be moved to another class in the future.
+ *
+ */
+public class Misc {
+ private Misc() {}
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>k</I><SUP>th</SUP></SPAN> smallest item of the array <SPAN CLASS="MATH"><I>A</I></SPAN> of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Array <SPAN CLASS="MATH"><I>A</I></SPAN> is unchanged by the method.
+ * Restriction:
+ * <SPAN CLASS="MATH">1 <= <I>k</I> <= <I>n</I></SPAN>.
+ *
+ * @param A the array which contain the items
+ *
+ * @param n the number of items in the array
+ *
+ * @param k the index of the smallest item
+ *
+ * @return the kth smallest item of the array <SPAN CLASS="MATH"><I>A</I></SPAN>
+ *
+ */
+ public static double quickSelect (double[] A, int n, int k) {
+ double[] U = new double[n];
+ double[] V = new double[n];
+ double p = A[k - 1];
+ int u = 0;
+ int v = 0;
+ int indV = 0;
+
+ for (int i = 0; i < n; i++) {
+ if (A[i] <= p) {
+ v++;
+ if (A[i] != p) {
+ U[u++] = A[i];
+ }
+ } else
+ V[indV++] = A[i];
+ }
+
+ if (k <= u)
+ return quickSelect (U, u, k);
+ else if (k > v)
+ return quickSelect (V, indV, k - v);
+ else return p;
+ }
+
+
+ /**
+ * Returns the <SPAN CLASS="MATH"><I>k</I><SUP>th</SUP></SPAN> smallest item of the array <SPAN CLASS="MATH"><I>A</I></SPAN> of size <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Array <SPAN CLASS="MATH"><I>A</I></SPAN> is unchanged by the method.
+ * Restriction:
+ * <SPAN CLASS="MATH">1 <= <I>k</I> <= <I>n</I></SPAN>.
+ *
+ * @param A the array which contain the items
+ *
+ * @param n the number of items in the array
+ *
+ * @param k the index of the smallest item
+ *
+ * @return the kth smallest item of the array <SPAN CLASS="MATH"><I>A</I></SPAN>
+ *
+ */
+ public static int quickSelect (int[] A, int n, int k) {
+ int[] U = new int[n];
+ int[] V = new int[n];
+ int p = A[k - 1];
+ int u = 0;
+ int v = 0;
+ int indV = 0;
+
+ for (int i = 0; i < n; i++) {
+ if (A[i] <= p) {
+ v++;
+ if (A[i] != p) {
+ U[u++] = A[i];
+ }
+ } else
+ V[indV++] = A[i];
+ }
+
+ if (k <= u)
+ return quickSelect (U, u, k);
+ else if (k > v)
+ return quickSelect (V, indV, k - v);
+ else return p;
+ }
+
+
+ /**
+ * Returns the median of the first <SPAN CLASS="MATH"><I>n</I></SPAN> elements of array <SPAN CLASS="MATH"><I>A</I></SPAN>.
+ *
+ * @param A the array
+ *
+ * @param n the number of used elements
+ *
+ * @return the median of <SPAN CLASS="MATH"><I>A</I></SPAN>
+ *
+ */
+ public static double getMedian (double[] A, int n) {
+ int k = (n+1)/2; // median index
+ double med = quickSelect(A, n, k);
+ double y;
+ if ((n & 1) == 0) {
+ y = quickSelect(A, n, k + 1);
+ med = (med + y) / 2.0;
+ }
+ return med;
+ }
+
+
+ /**
+ * Returns the median of the first <SPAN CLASS="MATH"><I>n</I></SPAN> elements of array <SPAN CLASS="MATH"><I>A</I></SPAN>.
+ *
+ * @param A the array
+ *
+ * @param n the number of used elements
+ *
+ * @return the median of <SPAN CLASS="MATH"><I>A</I></SPAN>
+ *
+ */
+ public static double getMedian (int[] A, int n) {
+ int k = (n+1)/2; // median index
+ double med = quickSelect(A, n, k);
+ double y;
+ if ((n & 1) == 0) {
+ y = quickSelect(A, n, k + 1);
+ med = (med + y) / 2.0;
+ }
+ return med;
+ }
+
+
+ /**
+ * Returns the index of the time interval corresponding to time <TT>t</TT>.
+ * Let
+ * <SPAN CLASS="MATH"><I>t</I><SUB>0</SUB> <= <SUP> ... </SUP> <= <I>t</I><SUB>n</SUB></SPAN> be simulation times stored in a subset of
+ * <TT>times</TT>. This method uses binary search to determine the
+ * smallest value <SPAN CLASS="MATH"><I>i</I></SPAN> for which
+ * <SPAN CLASS="MATH"><I>t</I><SUB>i</SUB> <= <I>t</I> < <I>t</I><SUB>i+1</SUB></SPAN>, and returns <SPAN CLASS="MATH"><I>i</I></SPAN>.
+ * The value of <SPAN CLASS="MATH"><I>t</I><SUB>i</SUB></SPAN> is stored in <TT>times[start+i]</TT> whereas
+ * <SPAN CLASS="MATH"><I>n</I></SPAN> is defined as <TT>end - start</TT>.
+ * If <SPAN CLASS="MATH"><I>t</I> < <I>t</I><SUB>0</SUB></SPAN>, this returns <SPAN CLASS="MATH">-1</SPAN>. If <SPAN CLASS="MATH"><I>t</I> >= <I>t</I><SUB>n</SUB></SPAN>, this returns <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * Otherwise, the returned value is greater than or equal to 0, and
+ * smaller than or equal to <SPAN CLASS="MATH"><I>n</I> - 1</SPAN>. <TT>start</TT> and <TT>end</TT> are only used
+ * to set lower and upper limits of the search in the <TT>times</TT>
+ * array; the index space of the returned value always starts at 0.
+ * Note that if the elements of <TT>times</TT> with indices <TT>start</TT>,
+ * ..., <TT>end</TT> are not sorted in non-decreasing order,
+ * the behavior of this method is undefined.
+ *
+ * @param times an array of simulation times.
+ *
+ * @param start the first index in the array to consider.
+ *
+ * @param end the last index (inclusive) in the array to consider.
+ *
+ * @param t the queried simulation time.
+ *
+ * @return the index of the interval.
+ * @exception NullPointerException if <TT>times</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>start</TT> is negative,
+ * or if <TT>end</TT> is smaller than <TT>start</TT>.
+ *
+ * @exception ArrayIndexOutOfBoundsException if <TT>start + end</TT>
+ * is greater than or equal to the length of <TT>times</TT>.
+ *
+ *
+ */
+ public static int getTimeInterval (double[] times, int start, int end,
+ double t) {
+ if (start < 0)
+ throw new IllegalArgumentException
+ ("The starting index must not be negative");
+ int n = end - start;
+ if (n < 0)
+ throw new IllegalArgumentException
+ ("The ending index must be greater than or equal to the starting index");
+ if (t < times[start])
+ return -1;
+ if (t >= times[end])
+ return n;
+
+ int start0 = start;
+ // Perform binary search to find the interval index
+ int mid = (start + end)/2;
+ // Test if t is inside the interval mid.
+ // The interval mid starts at times[mid],
+ // and the interval mid+1 starts at times[mid + 1].
+ while (t < times[mid] || t >= times[mid + 1]) {
+ if (start == end)
+ // Should not happen, safety check to avoid infinite loops.
+ throw new IllegalStateException();
+ if (t < times[mid])
+ // time corresponds to an interval before mid.
+ end = mid - 1;
+ else
+ // time corresponds to an interval after mid.
+ start = mid + 1;
+ mid = (start + end)/2;
+ }
+ return mid - start0;
+ }
+
+
+ /**
+ * Computes the Newton interpolating polynomial. Given the <SPAN CLASS="MATH"><I>n</I> + 1</SPAN>
+ * real distinct points
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>0</SUB>, <I>y</I><SUB>0</SUB>),</SPAN>
+ * <SPAN CLASS="MATH">(<I>x</I><SUB>1</SUB>, <I>y</I><SUB>1</SUB>),…,(<I>x</I><SUB>n</SUB>, <I>y</I><SUB>n</SUB>)</SPAN>,
+ * with <TT>X[i]</TT> <SPAN CLASS="MATH">= <I>x</I><SUB>i</SUB></SPAN>, <TT>Y[i]</TT> <SPAN CLASS="MATH">= <I>y</I><SUB>i</SUB></SPAN>, this function computes
+ * the <SPAN CLASS="MATH"><I>n</I> + 1</SPAN> coefficients <TT>C[i]</TT> <SPAN CLASS="MATH">= <I>c</I><SUB>i</SUB></SPAN> of the Newton
+ * interpolating polynomial <SPAN CLASS="MATH"><I>P</I>(<I>x</I>)</SPAN> of degree <SPAN CLASS="MATH"><I>n</I></SPAN> passing through these points,
+ * i.e. such that
+ * <SPAN CLASS="MATH"><I>y</I><SUB>i</SUB> = <I>P</I>(<I>x</I><SUB>i</SUB>)</SPAN>, given by
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq.newton.interpol"></A>
+ * <I>P</I>(<I>x</I>) = <I>c</I><SUB>0</SUB> + <I>c</I><SUB>1</SUB>(<I>x</I> - <I>x</I><SUB>0</SUB>) + <I>c</I><SUB>2</SUB>(<I>x</I> - <I>x</I><SUB>0</SUB>)(<I>x</I> - <I>x</I><SUB>1</SUB>) + <SUP> ... </SUP> + <I>c</I><SUB>n</SUB>(<I>x</I> - <I>x</I><SUB>0</SUB>)(<I>x</I> - <I>x</I><SUB>1</SUB>)<SUP> ... </SUP>(<I>x</I> - <I>x</I><SUB>n-1</SUB>).
+ * </DIV><P></P>
+ *
+ * @param n degree of the interpolating polynomial
+ *
+ * @param X <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates of points
+ *
+ * @param Y <SPAN CLASS="MATH"><I>y</I></SPAN>-coordinates of points
+ *
+ * @param C Coefficients of the interpolating polynomial
+ *
+ *
+ */
+ public static void interpol (int n, double[] X, double[] Y, double[] C) {
+ int j;
+ // Compute divided differences for the Newton interpolating polynomial
+ for (j = 0; j <= n; ++j)
+ C[j] = Y[j];
+ for (int i = 1; i <= n; ++i)
+ for (j = n; j >= i; --j) {
+ if (X[j] == X[j-i])
+ C[j] = 0;
+ else
+ C[j] = (C[j] - C[j-1]) / (X[j] - X[j-i]);
+ }
+ }
+
+
+ /**
+ * Given <SPAN CLASS="MATH"><I>n</I></SPAN>, <SPAN CLASS="MATH"><I>X</I></SPAN> and <SPAN CLASS="MATH"><I>C</I></SPAN> as described in
+ * {@link #interpol(int,double[],double[],double[]) interpol}<TT>(n, X, Y, C)</TT>, this
+ * function returns the value of the interpolating polynomial <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)</SPAN> evaluated
+ * at <SPAN CLASS="MATH"><I>z</I></SPAN> (see eq. ).
+ *
+ * @param n degree of the interpolating polynomial
+ *
+ * @param X <SPAN CLASS="MATH"><I>x</I></SPAN>-coordinates of points
+ *
+ * @param C Coefficients of the interpolating polynomial
+ *
+ * @param z argument where polynomial is evaluated
+ *
+ * @return Value of the interpolating polynomial <SPAN CLASS="MATH"><I>P</I>(<I>z</I>)</SPAN>
+ *
+ */
+ public static double evalPoly (int n, double[] X, double[] C, double z) {
+ double v = C[n];
+ for (int j = n-1; j >= 0; --j)
+ v = v*(z - X[j]) + C[j];
+ return v;
+ }
+
+
+ /**
+ * .
+ *
+ * Evaluates the polynomial <SPAN CLASS="MATH"><I>P</I>(<I>x</I>)</SPAN>
+ * of degree <SPAN CLASS="MATH"><I>n</I></SPAN> with coefficients <SPAN CLASS="MATH"><I>c</I><SUB>j</SUB> =</SPAN> <TT>C[j]</TT> at <SPAN CLASS="MATH"><I>x</I></SPAN>:
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="eq.horner"></A>
+ * <I>P</I>(<I>x</I>) = <I>c</I><SUB>0</SUB> + <I>c</I><SUB>1</SUB><I>x</I> + <I>c</I><SUB>2</SUB><I>x</I><SUP>2</SUP> + <SUP> ... </SUP> + <I>c</I><SUB>n</SUB><I>x</I><SUP>n</SUP>
+ * </DIV><P></P>
+ *
+ * @param C Coefficients of the polynomial
+ *
+ * @param n degree of the polynomial
+ *
+ * @param x argument where polynomial is evaluated
+ *
+ * @return Value of the polynomial <SPAN CLASS="MATH"><I>P</I>(<I>x</I>)</SPAN>
+ *
+ */
+ public static double evalPoly (double[] C, int n, double x) {
+ double v = C[n];
+ for (int j = n-1; j >= 0; --j)
+ v = v*x + C[j];
+ return v;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/Misc.tex b/source/umontreal/iro/lecuyer/util/Misc.tex
new file mode 100644
index 0000000..50262d9
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Misc.tex
@@ -0,0 +1,302 @@
+\defmodule{Misc}
+
+This class provides miscellaneous functions that are hard to classify.
+Some may be moved to another class in the future.
+
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: Misc
+ * Description: Miscellaneous functions that are hard to classify.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+ import umontreal.iro.lecuyer.functions.MathFunction;\end{hide}
+
+public class Misc\begin{hide} {
+ private Misc() {}\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double quickSelect (double[] A, int n, int k)\begin{hide} {
+ double[] U = new double[n];
+ double[] V = new double[n];
+ double p = A[k - 1];
+ int u = 0;
+ int v = 0;
+ int indV = 0;
+
+ for (int i = 0; i < n; i++) {
+ if (A[i] <= p) {
+ v++;
+ if (A[i] != p) {
+ U[u++] = A[i];
+ }
+ } else
+ V[indV++] = A[i];
+ }
+
+ if (k <= u)
+ return quickSelect (U, u, k);
+ else if (k > v)
+ return quickSelect (V, indV, k - v);
+ else return p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $k^{th}$ smallest item of the array $A$ of size $n$.
+ Array $A$ is unchanged by the method.
+ Restriction: $1 \le k \le n$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{A}{the array which contain the items}
+ \param{n}{the number of items in the array}
+ \param{k}{the index of the smallest item}
+ \return{the kth smallest item of the array $A$}
+\end{htmlonly}
+\begin{code}
+
+ public static int quickSelect (int[] A, int n, int k)\begin{hide} {
+ int[] U = new int[n];
+ int[] V = new int[n];
+ int p = A[k - 1];
+ int u = 0;
+ int v = 0;
+ int indV = 0;
+
+ for (int i = 0; i < n; i++) {
+ if (A[i] <= p) {
+ v++;
+ if (A[i] != p) {
+ U[u++] = A[i];
+ }
+ } else
+ V[indV++] = A[i];
+ }
+
+ if (k <= u)
+ return quickSelect (U, u, k);
+ else if (k > v)
+ return quickSelect (V, indV, k - v);
+ else return p;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the $k^{th}$ smallest item of the array $A$ of size $n$.
+ Array $A$ is unchanged by the method.
+ Restriction: $1 \le k \le n$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the array which contain the items}
+ \param{n}{the number of items in the array}
+ \param{k}{the index of the smallest item}
+ \return{the kth smallest item of the array $A$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMedian (double[] A, int n)\begin{hide} {
+ int k = (n+1)/2; // median index
+ double med = quickSelect(A, n, k);
+ double y;
+ if ((n & 1) == 0) {
+ y = quickSelect(A, n, k + 1);
+ med = (med + y) / 2.0;
+ }
+ return med;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the median of the first $n$ elements of array $A$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the array}
+ \param{n}{the number of used elements}
+ \return{the median of $A$}
+\end{htmlonly}
+\begin{code}
+
+ public static double getMedian (int[] A, int n)\begin{hide} {
+ int k = (n+1)/2; // median index
+ double med = quickSelect(A, n, k);
+ double y;
+ if ((n & 1) == 0) {
+ y = quickSelect(A, n, k + 1);
+ med = (med + y) / 2.0;
+ }
+ return med;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the median of the first $n$ elements of array $A$.
+\end{tabb}
+\begin{htmlonly}
+ \param{A}{the array}
+ \param{n}{the number of used elements}
+ \return{the median of $A$}
+\end{htmlonly}
+\begin{code}
+
+ public static int getTimeInterval (double[] times, int start, int end,
+ double t)\begin{hide} {
+ if (start < 0)
+ throw new IllegalArgumentException
+ ("The starting index must not be negative");
+ int n = end - start;
+ if (n < 0)
+ throw new IllegalArgumentException
+ ("The ending index must be greater than or equal to the starting index");
+ if (t < times[start])
+ return -1;
+ if (t >= times[end])
+ return n;
+
+ int start0 = start;
+ // Perform binary search to find the interval index
+ int mid = (start + end)/2;
+ // Test if t is inside the interval mid.
+ // The interval mid starts at times[mid],
+ // and the interval mid+1 starts at times[mid + 1].
+ while (t < times[mid] || t >= times[mid + 1]) {
+ if (start == end)
+ // Should not happen, safety check to avoid infinite loops.
+ throw new IllegalStateException();
+ if (t < times[mid])
+ // time corresponds to an interval before mid.
+ end = mid - 1;
+ else
+ // time corresponds to an interval after mid.
+ start = mid + 1;
+ mid = (start + end)/2;
+ }
+ return mid - start0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the index of the time interval corresponding to time \texttt{t}.
+ Let $t_0\le\cdots\le t_n$ be simulation times stored in a subset of
+ \texttt{times}. This method uses binary search to determine the
+ smallest value $i$ for which $t_i\le t < t_{i+1}$, and returns $i$.
+ The value of $t_i$ is stored in \texttt{times[start+i]} whereas
+ $n$ is defined as \texttt{end - start}.
+ If $t<t_0$, this returns $-1$. If $t\ge t_n$, this returns $n$.
+ Otherwise, the returned value is greater than or equal to 0, and
+ smaller than or equal to $n-1$. \texttt{start} and \texttt{end} are only used
+ to set lower and upper limits of the search in the \texttt{times}
+ array; the index space of the returned value always starts at 0.
+ Note that if the elements of \texttt{times} with indices \texttt{start},
+ \ldots, \texttt{end} are not sorted in non-decreasing order,
+ the behavior of this method is undefined.
+\end{tabb}
+\begin{htmlonly}
+ \param{times}{an array of simulation times.}
+ \param{start}{the first index in the array to consider.}
+ \param{end}{the last index (inclusive) in the array to consider.}
+ \param{t}{the queried simulation time.}
+ \return{the index of the interval.}
+ \exception{NullPointerException}{if \texttt{times} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{start} is negative,
+ or if \texttt{end} is smaller than \texttt{start}.}
+ \exception{ArrayIndexOutOfBoundsException}{if \texttt{start + end}
+ is greater than or equal to the length of \texttt{times}.}
+\end{htmlonly}
+\begin{code}
+
+ public static void interpol (int n, double[] X, double[] Y, double[] C)\begin{hide} {
+ int j;
+ // Compute divided differences for the Newton interpolating polynomial
+ for (j = 0; j <= n; ++j)
+ C[j] = Y[j];
+ for (int i = 1; i <= n; ++i)
+ for (j = n; j >= i; --j) {
+ if (X[j] == X[j-i])
+ C[j] = 0;
+ else
+ C[j] = (C[j] - C[j-1]) / (X[j] - X[j-i]);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the Newton interpolating polynomial. Given the $n+1$
+ real distinct points $(x_0, y_0),$ $(x_1, y_1),\ldots, (x_n, y_n)$,
+ with \texttt{X[i]} $= x_i$, \texttt{Y[i]} $= y_i$, this function computes
+ the $n+1$ coefficients \texttt{C[i]} $= c_i$ of the Newton
+ interpolating polynomial $P(x)$ of degree $n$ passing through these points,
+ i.e. such that $y_i= P(x_i)$, given by
+\begin{equation}
+\qquad P(x) = c_0 + c_1(x-x_0) + c_2(x-x_0)(x-x_1) + \cdots +
+ c_n(x-x_0)(x-x_1) \cdots(x-x_{n-1}). \label{eq.newton.interpol}
+\end{equation}
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{degree of the interpolating polynomial}
+ \param{X}{$x$-coordinates of points}
+ \param{Y}{$y$-coordinates of points}
+ \param{C}{Coefficients of the interpolating polynomial}
+\end{htmlonly}
+\begin{code}
+
+ public static double evalPoly (int n, double[] X, double[] C, double z) \begin{hide} {
+ double v = C[n];
+ for (int j = n-1; j >= 0; --j)
+ v = v*(z - X[j]) + C[j];
+ return v;
+ }\end{hide}
+\end{code}
+\begin{tabb} Given $n$, $X$ and $C$ as described in
+ \method{interpol}{int,double[],double[],double[]}\texttt{(n, X, Y, C)}, this
+function returns the value of the interpolating polynomial $P(z)$ evaluated
+ at $z$ (see eq. \ref{eq.newton.interpol}).
+\end{tabb}
+\begin{htmlonly}
+ \param{n}{degree of the interpolating polynomial}
+ \param{X}{$x$-coordinates of points}
+ \param{C}{Coefficients of the interpolating polynomial}
+ \param{z}{argument where polynomial is evaluated}
+ \return{Value of the interpolating polynomial $P(z)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double evalPoly (double[] C, int n, double x) \begin{hide} {
+ double v = C[n];
+ for (int j = n-1; j >= 0; --j)
+ v = v*x + C[j];
+ return v;
+ }\end{hide}
+\end{code}
+\begin{tabb} Evaluates the polynomial $P(x)$
+of degree $n$ with coefficients $c_j =$ \texttt{C[j]} at $x$:
+\begin{equation}
+\qquad P(x) = c_0 + c_1 x + c_2 x^2 + \cdots + c_n x^n \label{eq.horner}
+\end{equation}
+\end{tabb}
+\begin{htmlonly}
+ \param{C}{Coefficients of the polynomial}
+ \param{n}{degree of the polynomial}
+ \param{x}{argument where polynomial is evaluated}
+ \return{Value of the polynomial $P(x)$}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/MultivariateFunction.java b/source/umontreal/iro/lecuyer/util/MultivariateFunction.java
new file mode 100644
index 0000000..e45d435
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/MultivariateFunction.java
@@ -0,0 +1,125 @@
+
+/*
+ * Class: MultivariateFunction
+ * Description: Represents a function of multiple variables.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+/**
+ * Represents a function of multiple variables.
+ * This interface specifies a method <TT>evaluate</TT> that computes
+ * a
+ * <SPAN CLASS="MATH"><I>g</I>(<B>x</B>)</SPAN> function, where
+ * <SPAN CLASS="MATH"><B>x</B> = (<I>x</I><SUB>0</SUB>,…, <I>x</I><SUB>d-1</SUB>)∈<B>R</B><SUP>d</SUP></SPAN>. It also specifies
+ * a method <TT>evaluateGradient</TT> for computing
+ * its gradient
+ * <SPAN CLASS="MATH">∇<I>g</I>(<B>x</B>)</SPAN>.
+ *
+ * <P>
+ * The dimension <SPAN CLASS="MATH"><I>d</I></SPAN> can be fixed or variable. When <SPAN CLASS="MATH"><I>d</I></SPAN> is fixed, the
+ * methods specified by this interface always take the same number of
+ * arguments. This is the case, for example, with a ratio of two
+ * variables.
+ * When <SPAN CLASS="MATH"><I>d</I></SPAN> is variable, the implementation can compute the
+ * function for a vector
+ * <SPAN CLASS="MATH"><B>x</B></SPAN> of any length. This can happen for a
+ * product or sum of variables.
+ *
+ * <P>
+ * The methods of this interface take a variable number of arguments to
+ * accomodate the common case of fixed dimension with more convenience;
+ * the programmer can call the method without creating an array.
+ * For the generic case, however, one can replace the arguments with an
+ * array.
+ *
+ */
+public interface MultivariateFunction {
+
+ /**
+ * Returns <SPAN CLASS="MATH"><I>d</I></SPAN>, the dimension of the function computed
+ * by this implementation. If the dimension is not fixed,
+ * this method must return a negative value.
+ *
+ * @return the dimension.
+ *
+ */
+ public int getDimension();
+
+
+ /**
+ * Computes the function
+ * <SPAN CLASS="MATH"><I>g</I>(<B>x</B>)</SPAN>
+ * for the vector <TT>x</TT>. The length of the
+ * given array must correspond to the dimension of
+ * this function. The method must compute and return the result
+ * of the function without modifying the elements
+ * in <TT>x</TT> since the array can be reused for
+ * further computation.
+ *
+ * @param x a vector
+ * <SPAN CLASS="MATH"><B>x</B></SPAN>.
+ *
+ * @return the value of
+ * <SPAN CLASS="MATH"><I>g</I>(<B>x</B>)</SPAN>.
+ * @exception NullPointerException if <TT>x</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>x.length</TT>
+ * does not correspond to the dimension of this function.
+ *
+ *
+ */
+ public double evaluate (double... x);
+
+
+ /**
+ * Computes
+ * <SPAN CLASS="MATH">∂<I>g</I>(<B>x</B>)/∂<I>x</I><SUB>i</SUB></SPAN>,
+ * the derivative of
+ * <SPAN CLASS="MATH"><I>g</I>(<B>x</B>)</SPAN>
+ * with respect to <SPAN CLASS="MATH"><I>x</I><SUB>i</SUB></SPAN>. The length of the
+ * given array must correspond to the dimension of
+ * this function. The method must compute and return the result
+ * of the derivative without modifying the elements
+ * in <TT>x</TT> since the array can be reused for
+ * further computations, e.g., the gradient
+ * <SPAN CLASS="MATH">∇<I>g</I>(<B>x</B>)</SPAN>.
+ *
+ * @param i the variable to derive with respect to.
+ *
+ * @param x a vector
+ * <SPAN CLASS="MATH"><B>x</B></SPAN>.
+ *
+ * @return the value of the partial derivative.
+ * @exception NullPointerException if <TT>x</TT> is <TT>null</TT>.
+ *
+ * @exception IllegalArgumentException if <TT>x.length</TT>
+ * does not correspond to the dimension of this function.
+ *
+ * @exception IndexOutOfBoundsException if <TT>i</TT> is negative
+ * or greater than or equal to the dimension of this function.
+ *
+ *
+ */
+ public double evaluateGradient (int i, double... x);
+}
diff --git a/source/umontreal/iro/lecuyer/util/MultivariateFunction.tex b/source/umontreal/iro/lecuyer/util/MultivariateFunction.tex
new file mode 100644
index 0000000..3846383
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/MultivariateFunction.tex
@@ -0,0 +1,107 @@
+\defmodule{MultivariateFunction}
+
+Represents a function of multiple variables.
+This interface specifies a method \texttt{evaluate} that computes
+a $g(\mathbf{x})$ function, where $\mathbf{x}=(x_0,\ldots,x_{d-1})\in\RR^d$. It also specifies
+a method \texttt{evaluateGradient} for computing
+its gradient $\nabla g(\mathbf{x})$.
+
+The dimension $d$ can be fixed or variable. When $d$ is fixed, the
+methods specified by this interface always take the same number of
+arguments. This is the case, for example, with a ratio of two
+variables.
+When $d$ is variable, the implementation can compute the
+function for a vector $\mathbf{x}$ of any length. This can happen for a
+product or sum of variables.
+
+The methods of this interface take a variable number of arguments to
+accomodate the common case of fixed dimension with more convenience;
+the programmer can call the method without creating an array.
+For the generic case, however, one can replace the arguments with an
+array.
+
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: MultivariateFunction
+ * Description: Represents a function of multiple variables.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public interface MultivariateFunction\begin{hide} {\end{hide}
+
+ public int getDimension();
+\end{code}
+\begin{tabb} Returns $d$, the dimension of the function computed
+ by this implementation. If the dimension is not fixed,
+ this method must return a negative value.
+\end{tabb}
+\begin{htmlonly}
+ \return{the dimension.}
+\end{htmlonly}
+\begin{code}
+
+ public double evaluate (double... x);
+\end{code}
+\begin{tabb} Computes the function $g(\mathbf{x})$
+ for the vector \texttt{x}. The length of the
+ given array must correspond to the dimension of
+ this function. The method must compute and return the result
+ of the function without modifying the elements
+ in \texttt{x} since the array can be reused for
+ further computation.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{a vector $\mathbf{x}$.}
+ \return{the value of $g(\mathbf{x})$.}
+ \exception{NullPointerException}{if \texttt{x} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{x.length}
+ does not correspond to the dimension of this function.}
+\end{htmlonly}
+\begin{code}
+
+ public double evaluateGradient (int i, double... x);\end{code}
+\begin{tabb} Computes $\partial g(\mathbf{x})/\partial x_i$,
+ the derivative of $g(\mathbf{x})$
+ with respect to $x_i$. The length of the
+ given array must correspond to the dimension of
+ this function. The method must compute and return the result
+ of the derivative without modifying the elements
+ in \texttt{x} since the array can be reused for
+ further computations, e.g., the gradient $\nabla g(\mathbf{x})$.
+\end{tabb}
+\begin{htmlonly}
+ \param{i}{the variable to derive with respect to.}
+ \param{x}{a vector $\mathbf{x}$.}
+ \return{the value of the partial derivative.}
+ \exception{NullPointerException}{if \texttt{x} is \texttt{null}.}
+ \exception{IllegalArgumentException}{if \texttt{x.length}
+ does not correspond to the dimension of this function.}
+ \exception{IndexOutOfBoundsException}{if \texttt{i} is negative
+ or greater than or equal to the dimension of this function.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/NameConflictException.java b/source/umontreal/iro/lecuyer/util/NameConflictException.java
new file mode 100644
index 0000000..898c9e6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/NameConflictException.java
@@ -0,0 +1,102 @@
+
+/*
+ * Class: NameConflictException
+ * Description: An exception
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+package umontreal.iro.lecuyer.util;
+
+
+/**
+ * This exception is thrown by a {@link ClassFinder}
+ * when two or more fully qualified class names can be
+ * associated with a simple class name.
+ *
+ */
+public class NameConflictException extends Exception {
+ private static final long serialVersionUID = -5124156035520217708L;
+ private ClassFinder finder;
+ private String name;
+
+
+
+ /**
+ * Constructs a new name conflict exception.
+ *
+ */
+ public NameConflictException() {}
+
+
+ /**
+ * Constructs a new name conflict exception with
+ * message <TT>message</TT>.
+ *
+ * @param message the error message.
+ *
+ *
+ */
+ public NameConflictException (String message) {
+ super (message);
+ }
+
+
+ /**
+ * Constructs a new name conflict exception with class
+ * finder <TT>finder</TT>, simple name <TT>name</TT>,
+ * and message <TT>message</TT>.
+ *
+ * @param finder the class finder in which the name conflict occurred.
+ *
+ * @param name the simple conflicting name.
+ *
+ * @param message the message describint the conflict.
+ *
+ */
+ public NameConflictException (ClassFinder finder, String name,
+ String message) {
+ super (message);
+ this.finder = finder;
+ this.name = name;
+ }
+
+
+ /**
+ * Returns the class finder associated with
+ * this exception.
+ *
+ * @return the associated class finder.
+ *
+ */
+ public ClassFinder getClassFinder() {
+ return finder;
+ }
+
+
+ /**
+ * Returns the simple name associated with
+ * this exception.
+ *
+ * @return the associated simple name.
+ */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/util/NameConflictException.tex b/source/umontreal/iro/lecuyer/util/NameConflictException.tex
new file mode 100644
index 0000000..5f27838
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/NameConflictException.tex
@@ -0,0 +1,107 @@
+\defmodule{NameConflictException}
+
+This exception is thrown by a \class{ClassFinder}
+when two or more fully qualified class names can be
+associated with a simple class name.
+
+\bigskip\hrule
+\begin{code}\begin{hide}
+/*
+ * Class: NameConflictException
+ * Description: An exception
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public class NameConflictException extends Exception\begin{hide} {
+ private static final long serialVersionUID = -5124156035520217708L;
+ private ClassFinder finder;
+ private String name;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constructors}
+\begin{code}
+
+ public NameConflictException()\begin{hide} {}\end{hide}
+\end{code}
+\begin{tabb} Constructs a new name conflict exception.
+\end{tabb}
+\begin{code}
+
+ public NameConflictException (String message)\begin{hide} {
+ super (message);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new name conflict exception with
+ message \texttt{message}.
+\end{tabb}
+\begin{htmlonly}
+ \param{message}{the error message.}
+\end{htmlonly}
+\begin{code}
+
+ public NameConflictException (ClassFinder finder, String name,
+ String message)\begin{hide} {
+ super (message);
+ this.finder = finder;
+ this.name = name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new name conflict exception with class
+ finder \texttt{finder}, simple name \texttt{name},
+ and message \texttt{message}.
+\end{tabb}
+\begin{htmlonly}
+ \param{finder}{the class finder in which the name conflict occurred.}
+ \param{name}{the simple conflicting name.}
+ \param{message}{the message describint the conflict.}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public ClassFinder getClassFinder()\begin{hide} {
+ return finder;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the class finder associated with
+ this exception.
+\end{tabb}
+\begin{htmlonly}
+ \return{the associated class finder.}
+\end{htmlonly}
+\begin{code}
+
+ public String getName()\begin{hide} {
+ return name;
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Returns the simple name associated with
+ this exception.
+\end{tabb}
+\begin{htmlonly}
+ \return{the associated simple name.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/util/Num.java b/source/umontreal/iro/lecuyer/util/Num.java
new file mode 100644
index 0000000..4033f9d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Num.java
@@ -0,0 +1,1385 @@
+
+
+/*
+ * Class: Num
+ * Description: Provides methods to compute some special functions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+ import cern.jet.math.Bessel;
+
+/**
+ * This class provides a few constants and some methods to compute numerical
+ * quantities such as factorials, combinations, gamma functions, and so on.
+ *
+ */
+public class Num {
+ private static final double XBIG = 40.0;
+ private static final double SQPI_2 = 0.88622692545275801; // Sqrt(Pi)/2
+ private static final double RACPI = 1.77245385090551602729; // sqrt(Pi)
+ private static final double LOG_SQPI_2 = -0.1207822376352453; // Ln(Sqrt(Pi)/2)
+ private static final double LOG4 = 1.3862943611198906; // Ln(4)
+ private static final double LOG_PI = 1.14472988584940017413; // Ln(Pi)
+ private static final double PIsur2 = Math.PI/2.0;
+
+ private static final double UNSIX = 1.0/6.0;
+ private static final double QUARAN = 1.0/42.0;
+ private static final double UNTRENTE = 1.0 / 30.0;
+ private static final double DTIERS = 2.0 / 3.0;
+ private static final double CTIERS = 5.0 / 3.0;
+ private static final double STIERS = 7.0 / 3.0;
+ private static final double QTIERS = 14.0 / 3.0;
+
+
+ private static final double[] AERF = {
+ // used in erf(x)
+ 1.4831105640848036E0,
+ -3.0107107338659494E-1,
+ 6.8994830689831566E-2,
+ -1.3916271264722188E-2,
+ 2.4207995224334637E-3,
+ -3.6586396858480864E-4,
+ 4.8620984432319048E-5,
+ -5.7492565580356848E-6,
+ 6.1132435784347647E-7,
+ -5.8991015312958434E-8,
+ 5.2070090920686482E-9,
+ -4.2329758799655433E-10,
+ 3.188113506649174974E-11,
+ -2.2361550188326843E-12,
+ 1.46732984799108492E-13,
+ -9.044001985381747E-15,
+ 5.25481371547092E-16
+ };
+
+ private static final double[] AERFC = {
+ // used in erfc(x)
+ 6.10143081923200418E-1,
+ -4.34841272712577472E-1,
+ 1.76351193643605501E-1,
+ -6.07107956092494149E-2,
+ 1.77120689956941145E-2,
+ -4.32111938556729382E-3,
+ 8.54216676887098679E-4,
+ -1.27155090609162743E-4,
+ 1.12481672436711895E-5,
+ 3.13063885421820973E-7,
+ -2.70988068537762022E-7,
+ 3.07376227014076884E-8,
+ 2.51562038481762294E-9,
+ -1.02892992132031913E-9,
+ 2.99440521199499394E-11,
+ 2.60517896872669363E-11,
+ -2.63483992417196939E-12,
+ -6.43404509890636443E-13,
+ 1.12457401801663447E-13,
+ 1.7281533389986098E-14,
+ -4.2641016949424E-15,
+ -5.4537197788E-16,
+ 1.5869760776E-16,
+ 2.08998378E-17,
+ -0.5900E-17
+ };
+
+
+ private static final double[] AlnGamma = {
+ /* Chebyshev coefficients for lnGamma (x + 3), 0 <= x <= 1 In Yudell
+ Luke: The special functions and their approximations, Vol. II,
+ Academic Press, p. 301, 1969. There is an error in the additive
+ constant in the formula: (Ln (2)). */
+ 0.52854303698223459887,
+ 0.54987644612141411418,
+ 0.02073980061613665136,
+ -0.00056916770421543842,
+ 0.00002324587210400169,
+ -0.00000113060758570393,
+ 0.00000006065653098948,
+ -0.00000000346284357770,
+ 0.00000000020624998806,
+ -0.00000000001266351116,
+ 0.00000000000079531007,
+ -0.00000000000005082077,
+ 0.00000000000000329187,
+ -0.00000000000000021556,
+ 0.00000000000000001424,
+ -0.00000000000000000095
+ };
+
+ private Num() {}
+
+
+ /**
+ * Difference between 1.0 and the smallest <TT>double</TT> greater than 1.0.
+ *
+ */
+ public static final double DBL_EPSILON = 2.2204460492503131e-16;
+
+
+ /**
+ * Largest <TT>int</TT> <SPAN CLASS="MATH"><I>x</I></SPAN> such that <SPAN CLASS="MATH">2<SUP>x-1</SUP></SPAN> is representable
+ * (approximately) as a <TT>double</TT>.
+ *
+ */
+ public static final int DBL_MAX_EXP = 1024;
+
+
+ /**
+ * Smallest <TT>int</TT> <SPAN CLASS="MATH"><I>x</I></SPAN> such that <SPAN CLASS="MATH">2<SUP>x-1</SUP></SPAN> is representable
+ * (approximately) as a normalised <TT>double</TT>.
+ *
+ */
+ public static final int DBL_MIN_EXP = -1021;
+
+
+ /**
+ * Largest <TT>int</TT> <SPAN CLASS="MATH"><I>x</I></SPAN> such that <SPAN CLASS="MATH">10<SUP>x</SUP></SPAN> is representable
+ * (approximately) as a <TT>double</TT>.
+ *
+ */
+ public static final int DBL_MAX_10_EXP = 308;
+
+
+ /**
+ * Smallest normalized positive floating-point <TT>double</TT>.
+ *
+ */
+ public static final double DBL_MIN = 2.2250738585072014e-308;
+
+
+ /**
+ * Natural logarithm of <TT>DBL_MIN</TT>.
+ *
+ */
+ public static final double LN_DBL_MIN = -708.3964185322641;
+
+
+ /**
+ * Number of decimal digits of precision in a <TT>double</TT>.
+ *
+ */
+ public static final int DBL_DIG = 15;
+
+
+ /**
+ * The constant <SPAN CLASS="MATH"><I>e</I></SPAN>.
+ *
+ */
+ public static final double EBASE = 2.7182818284590452354;
+
+
+ /**
+ * The Euler-Mascheroni constant.
+ *
+ */
+ public static final double EULER = 0.57721566490153286;
+
+
+ /**
+ * The value of <SPAN CLASS="MATH">(2)<SUP>1/2</SUP></SPAN>.
+ *
+ */
+ public static final double RAC2 = 1.41421356237309504880;
+
+
+ /**
+ * The value of
+ * <SPAN CLASS="MATH">1/(2)<SUP>1/2</SUP></SPAN>.
+ *
+ */
+ public static final double IRAC2 = 0.70710678118654752440;
+
+
+ /**
+ * The values of <SPAN CLASS="MATH">ln 2</SPAN>.
+ *
+ */
+ public static final double LN2 = 0.69314718055994530941;
+
+
+ /**
+ * The values of <SPAN CLASS="MATH">1/ln 2</SPAN>.
+ *
+ */
+ public static final double ILN2 = 1.44269504088896340737;
+
+
+ /**
+ * Largest integer
+ * <SPAN CLASS="MATH"><I>n</I><SUB>0</SUB> = 2<SUP>53</SUP></SPAN> such that any integer
+ * <SPAN CLASS="MATH"><I>n</I> <= <I>n</I><SUB>0</SUB></SPAN> is represented exactly as a <TT>double</TT>.
+ *
+ */
+ public static final double MAXINTDOUBLE = 9007199254740992.0;
+
+
+ /**
+ * Powers of 2 up to <TT>MAXTWOEXP</TT> are stored exactly
+ * in the array <TT>TWOEXP</TT>.
+ *
+ */
+ public static final double MAXTWOEXP = 64;
+
+
+ /**
+ * Contains the precomputed positive powers of 2.
+ * One has <TT>TWOEXP[j]</TT><SPAN CLASS="MATH">= 2<SUP>j</SUP></SPAN>, for
+ * <SPAN CLASS="MATH"><I>j</I> = 0,..., 64</SPAN>.
+ *
+ */
+ public static final double TWOEXP[] = {
+ 1.0, 2.0, 4.0, 8.0, 1.6e1, 3.2e1,
+ 6.4e1, 1.28e2, 2.56e2, 5.12e2, 1.024e3,
+ 2.048e3, 4.096e3, 8.192e3, 1.6384e4, 3.2768e4,
+ 6.5536e4, 1.31072e5, 2.62144e5, 5.24288e5,
+ 1.048576e6, 2.097152e6, 4.194304e6, 8.388608e6,
+ 1.6777216e7, 3.3554432e7, 6.7108864e7,
+ 1.34217728e8, 2.68435456e8, 5.36870912e8,
+ 1.073741824e9, 2.147483648e9, 4.294967296e9,
+ 8.589934592e9, 1.7179869184e10, 3.4359738368e10,
+ 6.8719476736e10, 1.37438953472e11, 2.74877906944e11,
+ 5.49755813888e11, 1.099511627776e12, 2.199023255552e12,
+ 4.398046511104e12, 8.796093022208e12,
+ 1.7592186044416e13, 3.5184372088832e13,
+ 7.0368744177664e13, 1.40737488355328e14,
+ 2.81474976710656e14, 5.62949953421312e14,
+ 1.125899906842624e15, 2.251799813685248e15,
+ 4.503599627370496e15, 9.007199254740992e15,
+ 1.8014398509481984e16, 3.6028797018963968e16,
+ 7.2057594037927936e16, 1.44115188075855872e17,
+ 2.88230376151711744e17, 5.76460752303423488e17,
+ 1.152921504606846976e18, 2.305843009213693952e18,
+ 4.611686018427387904e18, 9.223372036854775808e18,
+ 1.8446744073709551616e19
+ };
+
+
+
+ /**
+ * Contains the precomputed negative powers of 10.
+ * One has <TT>TEN_NEG_POW[j]</TT><SPAN CLASS="MATH">= 10<SUP>-j</SUP></SPAN>, for
+ * <SPAN CLASS="MATH"><I>j</I> = 0,…, 16</SPAN>.
+ *
+ */
+ public static final double TEN_NEG_POW[] = {
+ 1.0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8,
+ 1.0e-9, 1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16
+ };
+
+
+
+ /**
+ * Returns the greatest common divisor (gcd) of <SPAN CLASS="MATH"><I>x</I></SPAN> and <SPAN CLASS="MATH"><I>y</I></SPAN>.
+ *
+ * @param x integer
+ *
+ * @param y integer
+ *
+ * @return the GCD of <SPAN CLASS="MATH"><I>x</I></SPAN> and <SPAN CLASS="MATH"><I>y</I></SPAN>
+ *
+ */
+ public static int gcd (int x, int y) {
+ if (x < 0) x = -x;
+ if (y < 0) y = -y;
+ int r;
+ while (y != 0) {
+ r = x % y;
+ x = y;
+ y = r;
+ }
+ return x;
+ }
+
+
+ /**
+ * Returns the greatest common divisor (gcd) of <SPAN CLASS="MATH"><I>x</I></SPAN> and <SPAN CLASS="MATH"><I>y</I></SPAN>.
+ *
+ * @param x integer
+ *
+ * @param y integer
+ *
+ * @return the GCD of <SPAN CLASS="MATH"><I>x</I></SPAN> and <SPAN CLASS="MATH"><I>y</I></SPAN>
+ *
+ */
+ public static long gcd (long x, long y) {
+ if (x < 0) x = -x;
+ if (y < 0) y = -y;
+ long r;
+ while (y != 0) {
+ r = x % y;
+ x = y;
+ y = r;
+ }
+ return x;
+ }
+
+
+ /**
+ * Returns the number of different combinations
+ * of <SPAN CLASS="MATH"><I>s</I></SPAN> objects amongst <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ * @param n total number of objects
+ *
+ * @param s number of chosen objects on a combination
+ *
+ * @return the combination of <SPAN CLASS="MATH"><I>s</I></SPAN> objects amongst <SPAN CLASS="MATH"><I>n</I></SPAN>
+ *
+ */
+ public static double combination (int n, int s) {
+ final int NLIM = 100; // pour eviter les debordements
+ int i;
+ if (s == 0 || s == n)
+ return 1;
+ if (s < 0) {
+ System.err.println ("combination: s < 0");
+ return 0;
+ }
+ if (s > n) {
+ System.err.println ("combination: s > n");
+ return 0;
+ }
+ if (s > (n/2))
+ s = n - s;
+ if (n <= NLIM) {
+ double Res = 1.0;
+ int Diff = n - s;
+ for (i = 1; i <= s; i++) {
+ Res *= (double)(Diff + i)/(double)i;
+ }
+ return Res;
+ }
+ else {
+ double Res = (lnFactorial (n) - lnFactorial (s))
+ - lnFactorial (n - s);
+ return Math.exp (Res);
+ }
+ }
+
+
+
+ /**
+ * Returns the natural logarithm ofnumber of different combinations
+ * of <SPAN CLASS="MATH"><I>s</I></SPAN> objects amongst <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @param n total number of objects
+ *
+ * @param s number of chosen objects on a combination
+ *
+ * @return the natural log of the combination
+ *
+ */
+ public static double lnCombination (int n, int s) {
+ if (s == 0 || s == n)
+ return 0;
+ if (s < 0 || s > n)
+ return Double.NEGATIVE_INFINITY;
+
+ double res = (lnFactorial (n) - lnFactorial (s)) - lnFactorial (n - s);
+ return res;
+ }
+
+
+
+ /**
+ * Returns the value of factorial <SPAN CLASS="MATH"><I>n</I></SPAN>.
+ *
+ * @param n the integer for which the factorial must be computed
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>n</I>!</SPAN>
+ *
+ */
+ public static double factorial (int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("factorial: n < 0");
+ double T = 1;
+ for (int j = 2; j <= n; j++)
+ T *= j;
+ return T;
+ }
+
+
+ /**
+ * Returns the value of the natural logarithm of
+ * factorial <SPAN CLASS="MATH"><I>n</I></SPAN>. Gives 16 decimals of precision
+ * (relative error
+ * <SPAN CLASS="MATH">< 0.5×10<SUP>-15</SUP></SPAN>).
+ *
+ * @param n argument of the log-factorial
+ *
+ * @return natural logarithm of <SPAN CLASS="MATH"><I>n</I></SPAN> factorial
+ *
+ */
+ public static double lnFactorial (int n) {
+ return lnFactorial ((long) n);
+ }
+
+
+ /**
+ * Returns the value of the natural logarithm of
+ * factorial <SPAN CLASS="MATH"><I>n</I></SPAN>. Gives 16 decimals of precision
+ * (relative error
+ * <SPAN CLASS="MATH">< 0.5×10<SUP>-15</SUP></SPAN>).
+ *
+ * @param n argument of the log-factorial
+ *
+ * @return natural logarithm of <SPAN CLASS="MATH"><I>n</I></SPAN> factorial
+ *
+ */
+ public static double lnFactorial (long n) {
+ final int NLIM = 14;
+
+ if (n < 0)
+ throw new IllegalArgumentException ("lnFactorial: n < 0");
+
+ if (n == 0 || n == 1)
+ return 0.0;
+ if (n <= NLIM) {
+ long z = 1;
+ long x = 1;
+ for (int i = 2; i <= n; i++) {
+ ++x;
+ z *= x;
+ }
+ return Math.log (z);
+ }
+ else {
+ double x = (double)(n + 1);
+ double y = 1.0/(x*x);
+ double z = ((-(5.95238095238E-4*y) + 7.936500793651E-4)*y -
+ 2.7777777777778E-3)*y + 8.3333333333333E-2;
+ z = ((x - 0.5)*Math.log (x) - x) + 9.1893853320467E-1 + z/x;
+ return z;
+ }
+ }
+
+
+ /**
+ * Returns the value of factorial(<SPAN CLASS="MATH"><I>n</I></SPAN>)<SPAN CLASS="MATH">/<I>n</I><SUP>n</SUP></SPAN>.
+ *
+ * @param n integer
+ *
+ * @return the value of <SPAN CLASS="MATH"><I>n</I>!/<I>n</I><SUP>n</SUP></SPAN>
+ *
+ */
+ public static double factoPow (int n) {
+ if (n < 0)
+ throw new IllegalArgumentException ("factoPow : n < 0");
+ double res = 1.0 / n;
+ for (int i = 2; i <= n; i++) {
+ res *= (double) i / n;
+ }
+ return res;
+ }
+
+
+ /**
+ * Computes and returns the Stirling numbers of the second kind
+ *
+ * @param m number of rows of the allocated matrix
+ *
+ * @param n number of columns of the allocated matrix
+ *
+ * @return the matrix of Stirling numbers
+ *
+ */
+ public static double[][] calcMatStirling (int m, int n) {
+ int i, j, k;
+ double[][] M = new double[m+1][n+1];
+
+ for (i = 0; i <= m; i++)
+ for (j = 0; j <= n; j++)
+ M[i][j] = 0.0;
+
+ M[0][0] = 1.0;
+ for (j = 1; j <= n; j++) {
+ M[0][j] = 0.0;
+ if (j <= m) {
+ k = j - 1;
+ M[j][j] = 1.0;
+ }
+ else
+ k = m;
+ for (i = 1; i <= k; i++)
+ M[i][j] = (double)i*M[i][j - 1] + M[i - 1][j - 1];
+ }
+ return M;
+ }
+
+
+ /**
+ * Returns <SPAN CLASS="MATH">log<SUB>2</SUB>(</SPAN><TT>x</TT><SPAN CLASS="MATH">)</SPAN>.
+ *
+ * @param x the value for which the logarithm must be computed
+ *
+ * @return the value of <SPAN CLASS="MATH">log<SUB>2</SUB>(</SPAN><TT>x</TT><SPAN CLASS="MATH">)</SPAN>
+ *
+ */
+ public static double log2 (double x) {
+ return ILN2*Math.log (x);
+ }
+
+
+ /**
+ * Returns the natural logarithm of the gamma function <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I>)</SPAN>
+ * evaluated at <TT>x</TT>.
+ * Gives 16 decimals of precision, but is implemented only for <SPAN CLASS="MATH"><I>x</I> > 0</SPAN>.
+ *
+ * @param x the value for which the lnGamma function must be computed
+ *
+ * @return the natural logarithm of the gamma function
+ *
+ */
+ public static double lnGamma (double x) {
+ if (x <= 0.0)
+ throw new IllegalArgumentException ("lnGamma: x <= 0");
+ if (Double.isNaN(x))
+ return Double.NaN;
+ final double XLIMBIG = 1.0/DBL_EPSILON;
+ final double XLIM1 = 18.0;
+ final double DK2 = 0.91893853320467274177; // Ln (sqrt (2 Pi))
+ final double DK1 = 0.9574186990510627;
+ final int N = 15; // Degree of Chebyshev polynomial
+ double y, z;
+ int i, k;
+
+/*
+ if (x <= 0.0) {
+ double f = (1.0 - x) - Math.floor (1.0 - x);
+ return LOG_PI - lnGamma (1.0 - x) - Math.log (Math.sin (Math.PI * f));
+ }
+*/
+ if (x > XLIM1) {
+ if (x > XLIMBIG)
+ y = 0.0;
+ else
+ y = 1.0/(x*x);
+ z = ((-(5.95238095238E-4*y) + 7.936500793651E-4)*y -
+ 2.7777777777778E-3)*y + 8.3333333333333E-2;
+ z = ((x - 0.5)*Math.log (x) - x) + DK2 + z/x;
+ return z;
+
+ } else if (x > 4.0) {
+ k = (int)x;
+ z = x - k;
+ y = 1.0;
+ for (i = 3; i < k; i++)
+ y *= z + i;
+ y = Math.log (y);
+
+ } else if (x <= 0.0)
+ return Double.MAX_VALUE;
+
+ else if (x < 3.0) {
+ k = (int)x;
+ z = x - k;
+ y = 1.0;
+ for (i = 2; i >= k; i--)
+ y *= z + i;
+ y = -Math.log (y);
+ }
+ else { // 3 <= x <= 4
+ z = x - 3.0;
+ y = 0.0;
+ }
+ z = evalCheby (AlnGamma, N, 2.0*z - 1.0);
+ return z + DK1 + y;
+ }
+
+
+ /**
+ * Computes the natural logarithm of the Beta function
+ *
+ * <SPAN CLASS="MATH"><I>B</I>(<I>λ</I>, <I>ν</I>)</SPAN>. It is defined in terms of the Gamma function as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>B</I>(<I>λ</I>, <I>ν</I>) = 1#1
+ * </DIV><P></P>
+ * with <TT>lam</TT> <SPAN CLASS="MATH">= <I>λ</I></SPAN> and <TT>nu</TT> <SPAN CLASS="MATH">= <I>ν</I></SPAN>.
+ *
+ */
+ public static double lnBeta (double lam, double nu) {
+ if (0. == lam || 0. == nu)
+ return Double.POSITIVE_INFINITY;
+ if (Double.isInfinite (lam) || Double.isInfinite (nu))
+ return Double.NEGATIVE_INFINITY;
+ return lnGamma (lam) + lnGamma (nu) - lnGamma (lam + nu);
+ }
+
+
+ /**
+ * Returns the value of the logarithmic derivative of the Gamma function
+ *
+ * <SPAN CLASS="MATH"><I>ψ</I>(<I>x</I>) = <I>Γ</I>'(<I>x</I>)/<I>Γ</I>(<I>x</I>)</SPAN>.
+ *
+ */
+ public static double digamma (double x) {
+ final double C7[][] = {
+ {1.3524999667726346383e4, 4.5285601699547289655e4, 4.5135168469736662555e4,
+ 1.8529011818582610168e4, 3.3291525149406935532e3, 2.4068032474357201831e2,
+ 5.1577892000139084710, 6.2283506918984745826e-3},
+ {6.9389111753763444376e-7, 1.9768574263046736421e4, 4.1255160835353832333e4,
+ 2.9390287119932681918e4, 9.0819666074855170271e3,
+ 1.2447477785670856039e3, 6.7429129516378593773e1, 1.0}
+ };
+ final double C4[][] = {
+ {-2.728175751315296783e-15, -6.481571237661965099e-1, -4.486165439180193579,
+ -7.016772277667586642, -2.129404451310105168},
+ {7.777885485229616042, 5.461177381032150702e1,
+ 8.929207004818613702e1, 3.227034937911433614e1, 1.0}
+ };
+
+ if (Double.isNaN(x))
+ return Double.NaN;
+ double prodPj = 0.0;
+ double prodQj = 0.0;
+ double digX = 0.0;
+
+ if (x >= 3.0) {
+ double x2 = 1.0 / (x * x);
+ for (int j = 4; j >= 0; j--) {
+ prodPj = prodPj * x2 + C4[0][j];
+ prodQj = prodQj * x2 + C4[1][j];
+ }
+ digX = Math.log (x) - (0.5 / x) + (prodPj / prodQj);
+
+ } else if (x >= 0.5) {
+ final double X0 = 1.46163214496836234126;
+ for (int j = 7; j >= 0; j--) {
+ prodPj = x * prodPj + C7[0][j];
+ prodQj = x * prodQj + C7[1][j];
+ }
+ digX = (x - X0) * (prodPj / prodQj);
+
+ } else {
+ double f = (1.0 - x) - Math.floor (1.0 - x);
+ digX = digamma (1.0 - x) + Math.PI / Math.tan (Math.PI * f);
+ }
+
+ return digX;
+ }
+
+
+ /**
+ * Returns the value of the trigamma function
+ * <SPAN CLASS="MATH"><I>dψ</I>(<I>x</I>)/<I>dx</I></SPAN>, the derivative of
+ * the digamma function, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double trigamma (double x) {
+ double y, sum;
+ if (Double.isNaN(x))
+ return Double.NaN;
+
+ if (x < 0.5) {
+ y = (1.0 - x) - Math.floor (1.0 - x);
+ sum = Math.PI / Math.sin (Math.PI * y);
+ return sum * sum - trigamma (1.0 - x);
+ }
+
+ if (x >= 40.0) {
+ // Asymptotic series
+ y = 1.0/(x*x);
+ sum = 1.0 + y*(1.0/6.0 - y*(1.0/30.0 - y*(1.0/42.0 - 1.0/30.0*y)));
+ sum += 0.5/x;
+ return sum/x;
+ }
+
+ int i;
+ int p = (int) x;
+ y = x - p;
+ sum = 0.0;
+
+ if (p > 3) {
+ for (i = 3; i < p; i++)
+ sum -= 1.0/((y + i)*(y + i));
+
+ } else if (p < 3) {
+ for (i = 2; i >= p; i--)
+ sum += 1.0/((y + i)*(y + i));
+ }
+
+ /* Chebyshev coefficients for trigamma (x + 3), 0 <= x <= 1. In Yudell
+ Luke: The special functions and their approximations, Vol. II,
+ Academic Press, p. 301, 1969. */
+ final int N = 15;
+ final double A[] = { 2.0*0.33483869791094938576, -0.05518748204873009463,
+ 0.00451019073601150186, -0.00036570588830372083,
+ 2.943462746822336e-5, -2.35277681515061e-6, 1.8685317663281e-7,
+ -1.475072018379e-8, 1.15799333714e-9, -9.043917904e-11,
+ 7.029627e-12, -5.4398873e-13, 0.4192525e-13, -3.21903e-15, 0.2463e-15,
+ -1.878e-17, 0., 0. };
+
+ return sum + evalChebyStar (A, N, y);
+ }
+
+
+ /**
+ * Returns the value of the tetragamma function
+ * <SPAN CLASS="MATH"><I>d</I><SUP>2</SUP><I>ψ</I>(<I>x</I>)/<I>d</I><SUP>2</SUP><I>x</I></SPAN>, the second
+ * derivative of the digamma function, evaluated at <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ *
+ */
+ public static double tetragamma (double x) {
+ double y, sum;
+ if (Double.isNaN(x))
+ return Double.NaN;
+
+ if (x < 0.5) {
+ y = (1.0 - x) - Math.floor (1.0 - x);
+ sum = Math.PI / Math.sin (Math.PI * y);
+ return 2.0 * Math.cos (Math.PI * y) * sum * sum * sum +
+ tetragamma (1.0 - x);
+ }
+
+ if (x >= 20.0) {
+ // Asymptotic series
+ y = 1.0/(x*x);
+ sum = y*(0.5 - y*(1.0/6.0 - y*(1.0/6.0 - y*(0.3 - 5.0/6.0*y))));
+ sum += 1.0 + 1.0/x;
+ return -sum*y;
+ }
+
+ int i;
+ int p = (int) x;
+ y = x - p;
+ sum = 0.0;
+
+ if (p > 3) {
+ for (i = 3; i < p; i++)
+ sum += 1.0 / ((y + i) * (y + i) * (y + i));
+
+ } else if (p < 3) {
+ for (i = 2; i >= p; i--)
+ sum -= 1.0 / ((y + i) * (y + i) * (y + i));
+ }
+
+ /* Chebyshev coefficients for tetragamma (x + 3), 0 <= x <= 1. In Yudell
+ Luke: The special functions and their approximations, Vol. II,
+ Academic Press, p. 301, 1969. */
+ final int N = 16;
+ final double A[] = { -0.11259293534547383037*2.0, 0.03655700174282094137,
+ -0.00443594249602728223, 0.00047547585472892648,
+ -4.747183638263232e-5, 4.52181523735268e-6, -4.1630007962011e-7,
+ 3.733899816535e-8, -3.27991447410e-9, 2.8321137682e-10,
+ -2.410402848e-11, 2.02629690e-12, -1.6852418e-13, 1.388481e-14,
+ -1.13451e-15, 9.201e-17, -7.41e-18, 5.9e-19, -5.0e-20 };
+
+ return 2.0 * sum + evalChebyStar (A, N, y);
+ }
+
+
+ /**
+ * Returns the value of the ratio
+ * <SPAN CLASS="MATH"><I>Γ</I>(<I>x</I> + 1/2)/<I>Γ</I>(<I>x</I>)</SPAN> of two gamma
+ * functions. This ratio is evaluated in a numerically stable way.
+ * Restriction: <SPAN CLASS="MATH"><I>x</I> > 0</SPAN>.
+ *
+ */
+ public static double gammaRatioHalf (double x) {
+ if (x <= 0.0)
+ throw new IllegalArgumentException ("gammaRatioHalf: x <= 0");
+ if (Double.isNaN(x))
+ return Double.NaN;
+
+ if (x <= 10.0) {
+ double y = lnGamma (x + 0.5) - lnGamma (x);
+ return Math.exp(y);
+ }
+
+ double sum;
+ if (x <= 300.0) {
+ // The sum converges very slowly for small x, but faster as x increases
+ final double EPSILON = 1.0e-15;
+ double term = 1.0;
+ sum = 1.0;
+ int i = 1;
+ while (term > EPSILON*sum) {
+ term *= (i - 1.5)*(i - 1.5) /(i*(x + i - 1.5));
+ sum += term;
+ i++;
+ }
+ return Math.sqrt ((x - 0.5)*sum);
+ }
+
+ // Asymptotic series for Gamma(x + 0.5) / (Gamma(x) * Sqrt(x))
+ // Comparer la vitesse de l'asymptotique avec la somme ci-dessus !!!
+ double y = 1.0 / (8.0*x);
+ sum = 1.0 + y*(-1.0 + y*(0.5 + y*(2.5 - y*(2.625 + 49.875*y))));
+ return sum*Math.sqrt(x);
+ }
+
+
+ /**
+ * Implementation of the Kahan summation algorithm.
+ * Sums the first <SPAN CLASS="MATH"><I>n</I></SPAN> elements of <SPAN CLASS="MATH"><I>A</I></SPAN> and returns the sum.
+ * This algorithm is more precise than the naive algorithm.
+ * See <TT><A NAME="tex2html1"
+ * HREF="http://en.wikipedia.org/wiki/Kahan_summation_algorithm">http://en.wikipedia.org/wiki/Kahan_summation_algorithm</A></TT>.
+ *
+ */
+ public static double sumKahan (double[] A, int n) {
+ if (A.length < n)
+ n = A.length;
+ double sum = 0;
+ double c = 0;
+ double y, t;
+
+ for (int i = 0; i < n; i++) {
+ y = A[i] - c;
+ t = sum + y;
+ c = (t - sum) - y;
+ sum = t;
+ }
+
+ return sum;
+ }
+
+
+ /**
+ * Computes the <SPAN CLASS="MATH"><I>n</I></SPAN>-th harmonic number
+ * <SPAN CLASS="MATH"><I>H</I><SUB>n</SUB> = ∑<SUB>j=1</SUB><SUP>n</SUP>1/<I>j</I></SPAN>.
+ *
+ */
+ public static double harmonic (long n) {
+ if (n < 1)
+ throw new IllegalArgumentException ("n < 1");
+ return digamma(n + 1) + EULER;
+ }
+
+
+ /**
+ * .
+ *
+ * Computes the sum
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * 2#2 3#3,
+ * </DIV><P></P>
+ * where the symbol
+ * <SPAN CLASS="MATH">∑<SUP>′</SUP></SPAN> means that the term with <SPAN CLASS="MATH"><I>j</I> = 0</SPAN> is excluded
+ * from the sum.
+ *
+ */
+ public static double harmonic2 (long n) {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (1 == n)
+ return 0.0;
+ if (2 == n)
+ return 1.0;
+
+ long k = n / 2;
+ if ((n & 1) == 1)
+ return 2.0*harmonic(k); // n odd
+ return 1.0/k + 2.0*harmonic(k-1); // n even
+ }
+
+
+ /**
+ * Returns the volume <SPAN CLASS="MATH"><I>V</I></SPAN> of a sphere of radius 1 in <SPAN CLASS="MATH"><I>t</I></SPAN> dimensions
+ * using the norm <SPAN CLASS="MATH"><I>L</I><SUB>p</SUB></SPAN>. It is given by the formula
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>V</I> = ([2<I>Γ</I>(1 + 1/<I>p</I>)]<SUP>t</SUP>)/<I>Γ</I>(1 + <I>t</I>/<I>p</I>), <I>p</I> > 0,
+ * </DIV><P></P>
+ * where <SPAN CLASS="MATH"><I>Γ</I></SPAN> is the gamma function.
+ * The case of the sup norm <SPAN CLASS="MATH"><I>L</I><SUB>∞</SUB></SPAN> is obtained by choosing <SPAN CLASS="MATH"><I>p</I> = 0</SPAN>.
+ * Restrictions: <SPAN CLASS="MATH"><I>p</I> >= 0</SPAN> and <SPAN CLASS="MATH"><I>t</I> >= 1</SPAN>.
+ *
+ * @param p index of the used norm
+ *
+ * @param t number of dimensions
+ *
+ * @return the volume of a sphere
+ *
+ */
+ public static double volumeSphere (double p, int t) {
+ final double EPS = 2.0*DBL_EPSILON;
+ int pLR = (int)p;
+ double kLR = (double)t;
+ double Vol;
+ int s;
+
+ if (p < 0)
+ throw new IllegalArgumentException ("volumeSphere: p < 0");
+
+ if (Math.abs (p - pLR) <= EPS) {
+ switch (pLR) {
+ case 0:
+ return TWOEXP[t];
+ case 1:
+ return TWOEXP[t]/(double)factorial (t);
+ case 2:
+ if ((t % 2) == 0)
+ return Math.pow (Math.PI, kLR/2.0)/(double)factorial (t/2);
+ else {
+ s = (t + 1)/2;
+ return Math.pow (Math.PI, (double)s - 1.0)*factorial (s)*
+ TWOEXP[2*s]/(double)factorial (2*s);
+ }
+ default:
+ }
+ }
+ Vol = kLR*(LN2 + lnGamma (1.0 + 1.0/p)) -
+ lnGamma (1.0 + kLR/p);
+ return Math.exp (Vol);
+ }
+
+
+ /**
+ * Evaluates the Bernoulli polynomial <SPAN CLASS="MATH"><I>B</I><SUB>n</SUB>(<I>x</I>)</SPAN> of degree <SPAN CLASS="MATH"><I>n</I></SPAN>
+ * at <SPAN CLASS="MATH"><I>x</I></SPAN>. Only degrees <SPAN CLASS="MATH"><I>n</I> <= 8</SPAN> are programmed for now.
+ * The first Bernoulli polynomials of even degree are:
+ * <BR>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay"><A NAME="bernoulli"></A>
+ * <TABLE CELLPADDING="0" ALIGN="CENTER" WIDTH="100%">
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>B</I><SUB>0</SUB>(<I>x</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP>1</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>B</I><SUB>2</SUB>(<I>x</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>x</I><SUP>2</SUP> - <I>x</I> + 1/6</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>B</I><SUB>4</SUB>(<I>x</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>x</I><SUP>4</SUP> -2<I>x</I><SUP>3</SUP> + <I>x</I><SUP>2</SUP> - 1/30</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>B</I><SUB>6</SUB>(<I>x</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>x</I><SUP>6</SUP> -3<I>x</I><SUP>5</SUP> +5<I>x</I><SUP>4</SUP>/2 - <I>x</I><SUP>2</SUP>/2 + 1/42</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * <TR VALIGN="MIDDLE"><TD NOWRAP ALIGN="RIGHT"><I>B</I><SUB>8</SUB>(<I>x</I>)</TD>
+ * <TD WIDTH="10" ALIGN="CENTER" NOWRAP>=</TD>
+ * <TD ALIGN="LEFT" NOWRAP><I>x</I><SUP>8</SUP> -4<I>x</I><SUP>7</SUP> +14<I>x</I><SUP>6</SUP>/3 - 7<I>x</I><SUP>4</SUP>/3 + 2<I>x</I><SUP>2</SUP>/3 - 1/30.</TD>
+ * <TD CLASS="eqno" WIDTH=10 ALIGN="RIGHT">
+ * </TD></TR>
+ * </TABLE></DIV>
+ * <BR CLEAR="ALL">
+ *
+ */
+ public static double bernoulliPoly (int n, double x) {
+ switch (n) {
+ case 0:
+ return 1.0;
+ case 1:
+ return x - 0.5;
+ case 2:
+ return x*(x - 1.0) + UNSIX;
+ case 3:
+ return ((2.0*x - 3.0) * x + 1.0)*x*0.5;
+ case 4:
+ return ((x - 2.0) * x + 1.0)*x*x - UNTRENTE;
+ case 5:
+ return (((x - 2.5) * x + CTIERS) *x*x - UNSIX) * x;
+ case 6:
+ return (((x - 3.0) * x + 2.5) * x*x - 0.5) * x*x + QUARAN;
+ case 7:
+ return ((((x - 3.5) * x + 3.5) *x*x - 7.0/6.0) * x*x + UNSIX) * x;
+ case 8:
+ return ((((x - 4.0) * x +
+ QTIERS) * x*x - STIERS) * x*x + DTIERS) * x*x - UNTRENTE;
+ default:
+ throw new IllegalArgumentException("n must be <= 8");
+ }
+ // return 0;
+ }
+
+
+ /**
+ * Evaluates a series of Chebyshev polynomials <SPAN CLASS="MATH"><I>T</I><SUB>j</SUB></SPAN> at
+ * <SPAN CLASS="MATH"><I>x</I></SPAN> over the basic interval <SPAN CLASS="MATH">[- 1, 1]</SPAN>. It uses
+ * the method of Clenshaw, i.e., computes and returns
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>y</I> = 4#4 + ∑<SUB>j=1</SUB><SUP>n</SUP><I>a</I><SUB>j</SUB><I>T</I><SUB>j</SUB>(<I>x</I>).
+ * </DIV><P></P>
+ * @param a coefficients of the polynomials
+ *
+ * @param n largest degree of polynomials
+ *
+ * @param x the parameter of the <SPAN CLASS="MATH"><I>T</I><SUB>j</SUB></SPAN> functions
+ *
+ * @return the value of a series of Chebyshev polynomials <SPAN CLASS="MATH"><I>T</I><SUB>j</SUB></SPAN>.
+ *
+ */
+ public static double evalCheby (double a[], int n, double x) {
+ if (Math.abs (x) > 1.0)
+ System.err.println ("Chebychev polynomial evaluated "+
+ "at x outside [-1, 1]");
+ final double xx = 2.0*x;
+ double b0 = 0.0;
+ double b1 = 0.0;
+ double b2 = 0.0;
+ for (int j = n; j >= 0; j--) {
+ b2 = b1;
+ b1 = b0;
+ b0 = (xx*b1 - b2) + a[j];
+ }
+ return (b0 - b2)/2.0;
+ }
+
+
+ /**
+ * Evaluates a series of shifted Chebyshev polynomials <SPAN CLASS="MATH"><I>T</I><SUB>j</SUB><SUP>*</SUP></SPAN>
+ * at <SPAN CLASS="MATH"><I>x</I></SPAN> over the basic interval <SPAN CLASS="MATH">[0, 1]</SPAN>. It uses
+ * the method of Clenshaw, i.e., computes and returns
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * <I>y</I> = [tex2html_wrap_indisplay816] + ∑<SUB>j=1</SUB><SUP>n</SUP><I>a</I><SUB>j</SUB><I>T</I><SUB>j</SUB><SUP>*</SUP>(<I>x</I>).
+ * </DIV><P></P>
+ * @param a coefficients of the polynomials
+ *
+ * @param n largest degree of polynomials
+ *
+ * @param x the parameter of the <SPAN CLASS="MATH"><I>T</I><SUB>j</SUB><SUP>*</SUP></SPAN> functions
+ *
+ * @return the value of a series of Chebyshev polynomials <SPAN CLASS="MATH"><I>T</I><SUB>j</SUB><SUP>*</SUP></SPAN>.
+ *
+ */
+ public static double evalChebyStar (double a[], int n, double x) {
+ if (x > 1.0 || x < 0.0)
+ System.err.println ("Shifted Chebychev polynomial evaluated " +
+ "at x outside [0, 1]");
+ final double xx = 2.0*(2.0*x - 1.0);
+ double b0 = 0.0;
+ double b1 = 0.0;
+ double b2 = 0.0;
+ for (int j = n; j >= 0; j--) {
+ b2 = b1;
+ b1 = b0;
+ b0 = xx*b1 - b2 + a[j];
+ }
+ return (b0 - b2)/2.0;
+ }
+
+
+ /**
+ * Returns the value of
+ * <SPAN CLASS="MATH"><I>K</I><SUB>1/4</SUB>(<I>x</I>)</SPAN>, where <SPAN CLASS="MATH"><I>K</I><SUB>a</SUB></SPAN> is the modified
+ * Bessel's function of the second kind.
+ * The relative error on the returned value is less than
+ *
+ * <SPAN CLASS="MATH">0.5×10<SUP>-6</SUP></SPAN> for
+ * <SPAN CLASS="MATH"><I>x</I> > 10<SUP>-300</SUP></SPAN>.
+ *
+ * @param x value at which the function is calculated
+ *
+ * @return the value of
+ * <SPAN CLASS="MATH"><I>K</I><SUB>1/4</SUB>(<I>x</I>)</SPAN>
+ *
+ */
+ public static double besselK025 (double x) {
+ if (Double.isNaN(x))
+ return Double.NaN;
+ if (x < 1.E-300)
+ return Double.MIN_VALUE;
+
+ final int DEG = 6;
+ final double c[] = {
+ 32177591145.0,
+ 2099336339520.0,
+ 16281990144000.0,
+ 34611957596160.0,
+ 26640289628160.0,
+ 7901666082816.0,
+ 755914244096.0
+ };
+
+ final double b[] = {
+ 75293843625.0,
+ 2891283595200.0,
+ 18691126272000.0,
+ 36807140966400.0,
+ 27348959232000.0,
+ 7972533043200.0,
+ 755914244096.0
+ };
+
+ /*------------------------------------------------------------------
+ * x > 0.6 => approximation asymptotique rationnelle dans Luke:
+ * Yudell L.Luke "Mathematical functions and their approximations",
+ * Academic Press Inc. New York, 1975, p.371
+ *------------------------------------------------------------------*/
+ if (x >= 0.6) {
+ double B = b[DEG];
+ double C = c[DEG];
+ for (int j = DEG; j >= 1; j--) {
+ B = B * x + b[j - 1];
+ C = C * x + c[j - 1];
+ }
+ double Res1 = Math.sqrt(Math.PI / (2.0*x)) * Math.exp(-x)*(C/B);
+ return Res1;
+ }
+
+ /*------------------------------------------------------------------
+ * x < 0.6 => la serie de K_{1/4} = Pi/Sqrt (2) [I_{-1/4} - I_{1/4}]
+ *------------------------------------------------------------------*/
+ double xx = x * x;
+ double rac = Math.pow (x/2.0, 0.25);
+ double Res = (((xx/1386.0 + 1.0/42.0)*xx + 1.0/3.0)*xx + 1.0)/
+ (1.225416702465177*rac);
+ double temp = (((xx/3510.0 + 1.0/90.0)*xx + 0.2)*xx + 1.0)*rac/
+ 0.906402477055477;
+ Res = Math.PI*(Res - temp)/RAC2;
+ return Res;
+ }
+
+
+ /**
+ * Returns the value of
+ * <SPAN CLASS="MATH"><I>e</I><SUP>x</SUP><I>K</I><SUB>1</SUB>(<I>y</I>)</SPAN>, where <SPAN CLASS="MATH"><I>K</I><SUB>1</SUB></SPAN> is the modified Bessel
+ * function of the second kind of order 1. Restriction: <SPAN CLASS="MATH"><I>y</I> > 0</SPAN>.
+ *
+ */
+ public static double expBesselK1 (double x, double y) {
+ if (Double.isNaN(x) || Double.isNaN(y))
+ return Double.NaN;
+ if (y > 500.0) {
+ double sum = 1 + 3.0/(8.0*y) - 15.0 / (128.0*y*y) + 105.0 /(1024.0*y*y*y);
+ return Math.sqrt(PIsur2/ y) * sum * Math.exp(x - y);
+ } else if (Math.abs(x) > 500.0) {
+ double b = Bessel.k1(y);
+ return Math.exp(x + Math.log(b));
+ } else {
+ return Math.exp(x) * Bessel.k1(y);
+ }
+ }
+
+
+ /**
+ * Returns the value of <TT>erf</TT>(<SPAN CLASS="MATH"><I>x</I></SPAN>), the error function. It is defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * erf(<I>x</I>) = 2/[(π)<SUP>1/2</SUP>]∫<SUB>0</SUB><SUP>x</SUP><I>dt</I> <I>e</I><SUP>-t<SUP>2</SUP></SUP>.
+ * </DIV><P></P>
+ * @param x value at which the function is calculated
+ *
+ * @return the value of <TT>erf</TT><SPAN CLASS="MATH">(<I>x</I>)</SPAN>
+ *
+ */
+ public static double erf (double x) {
+ if (Double.isNaN(x))
+ return Double.NaN;
+ if (x < 0.0)
+ return -erf(-x);
+ if (x >= 6.0)
+ return 1.0;
+ if (x >= 2.0)
+ return 1.0 - erfc(x);
+
+ double t = 0.5*x*x - 1.0;
+ double y = Num.evalCheby (AERF, 16, t);
+ return x*y;
+ }
+
+
+ /**
+ * Returns the value of <TT>erfc</TT>(<SPAN CLASS="MATH"><I>x</I></SPAN>), the complementary error function.
+ * It is defined as
+ *
+ * <P></P>
+ * <DIV ALIGN="CENTER" CLASS="mathdisplay">
+ * erfc(<I>x</I>) = 2/[(π)<SUP>1/2</SUP>]∫<SUB>x</SUB><SUP>∞</SUP><I>dt</I> <I>e</I><SUP>-t<SUP>2</SUP></SUP>.
+ * </DIV><P></P>
+ *
+ * @param x value at which the function is calculated
+ *
+ * @return the value of <TT>erfc</TT><SPAN CLASS="MATH">(<I>x</I>)</SPAN>
+ *
+ */
+ public static double erfc (double x) {
+ if (Double.isNaN(x))
+ return Double.NaN;
+ if (x < 0.0)
+ return 2.0 - erfc(-x);
+ if (x >= XBIG)
+ return 0.0;
+ double t = (x - 3.75)/(x + 3.75);
+ double y = Num.evalCheby (AERFC, 24, t);
+ y *= Math.exp(-x*x);
+ return y;
+ }
+
+
+ private static final double[] InvP1 = {
+ 0.160304955844066229311e2,
+ -0.90784959262960326650e2,
+ 0.18644914861620987391e3,
+ -0.16900142734642382420e3,
+ 0.6545466284794487048e2,
+ -0.864213011587247794e1,
+ 0.1760587821390590
+ };
+
+ private static final double[] InvQ1 = {
+ 0.147806470715138316110e2,
+ -0.91374167024260313396e2,
+ 0.21015790486205317714e3,
+ -0.22210254121855132366e3,
+ 0.10760453916055123830e3,
+ -0.206010730328265443e2,
+ 0.1e1
+ };
+
+ private static final double[] InvP2 = {
+ -0.152389263440726128e-1,
+ 0.3444556924136125216,
+ -0.29344398672542478687e1,
+ 0.11763505705217827302e2,
+ -0.22655292823101104193e2,
+ 0.19121334396580330163e2,
+ -0.5478927619598318769e1,
+ 0.237516689024448000
+ };
+
+ private static final double[] InvQ2 = {
+ -0.108465169602059954e-1,
+ 0.2610628885843078511,
+ -0.24068318104393757995e1,
+ 0.10695129973387014469e2,
+ -0.23716715521596581025e2,
+ 0.24640158943917284883e2,
+ -0.10014376349783070835e2,
+ 0.1e1
+ };
+
+ private static final double[] InvP3 = {
+ 0.56451977709864482298e-4,
+ 0.53504147487893013765e-2,
+ 0.12969550099727352403,
+ 0.10426158549298266122e1,
+ 0.28302677901754489974e1,
+ 0.26255672879448072726e1,
+ 0.20789742630174917228e1,
+ 0.72718806231556811306,
+ 0.66816807711804989575e-1,
+ -0.17791004575111759979e-1,
+ 0.22419563223346345828e-2
+ };
+
+ private static final double[] InvQ3 = {
+ 0.56451699862760651514e-4,
+ 0.53505587067930653953e-2,
+ 0.12986615416911646934,
+ 0.10542932232626491195e1,
+ 0.30379331173522206237e1,
+ 0.37631168536405028901e1,
+ 0.38782858277042011263e1,
+ 0.20372431817412177929e1,
+ 0.1e1
+ };
+
+ /**
+ * Returns the value of <TT>erf</TT>
+ * <SPAN CLASS="MATH"><SUP>-1</SUP>(<I>u</I>)</SPAN>, the inverse of the error
+ * function. If <SPAN CLASS="MATH"><I>u</I> = </SPAN><TT>erf</TT><SPAN CLASS="MATH">(<I>x</I>)</SPAN>, then <SPAN CLASS="MATH"><I>x</I> = </SPAN><TT>erf</TT>
+ * <SPAN CLASS="MATH"><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ *
+ * @param u value at which the function is calculated
+ *
+ * @return the value of erfInv<SPAN CLASS="MATH">(<I>u</I>)</SPAN>
+ *
+ */
+ public static double erfInv (double u) {
+ if (Double.isNaN(u))
+ return Double.NaN;
+ if (u < 0.0)
+ return -erfInv (-u);
+
+ if (u > 1.0)
+ throw new IllegalArgumentException ("u is not in [-1, 1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ double t, z, v, w;
+
+ if (u <= 0.75) {
+ t = u * u - 0.5625;
+ v = Misc.evalPoly (InvP1, 6, t);
+ w = Misc.evalPoly (InvQ1, 6, t);
+ z = (v / w) * u;
+
+ } else if (u <= 0.9375) {
+ t = u * u - 0.87890625;
+ v = Misc.evalPoly (InvP2, 7, t);
+ w = Misc.evalPoly (InvQ2, 7, t);
+ z = (v / w) * u;
+
+ } else {
+ t = 1.0 / Math.sqrt (-Math.log (1.0 - u));
+ v = Misc.evalPoly (InvP3, 10, t);
+ w = Misc.evalPoly (InvQ3, 8, t);
+ z = (v / w) / t;
+ }
+
+ return z;
+ }
+
+
+ /**
+ * Returns the value of <TT>erfc</TT>
+ * <SPAN CLASS="MATH"><SUP>-1</SUP>(<I>u</I>)</SPAN>, the inverse of the
+ * complementary error function. If <SPAN CLASS="MATH"><I>u</I> = </SPAN><TT>erfc</TT><SPAN CLASS="MATH">(<I>x</I>)</SPAN>,
+ * then <SPAN CLASS="MATH"><I>x</I> = </SPAN><TT>erfc</TT>
+ * <SPAN CLASS="MATH"><SUP>-1</SUP>(<I>u</I>)</SPAN>.
+ *
+ * @param u value at which the function is calculated
+ *
+ * @return the value of erfcInv<SPAN CLASS="MATH">(<I>u</I>)</SPAN>
+ *
+ */
+ public static double erfcInv (double u) {
+ if (Double.isNaN(u))
+ return Double.NaN;
+ if (u < 0 || u > 2)
+ throw new IllegalArgumentException ("u is not in [0, 2]");
+
+ if (u > 0.005)
+ return erfInv (1.0 - u);
+
+ if (u <= 0)
+ return Double.POSITIVE_INFINITY;
+
+ double x, w;
+
+ // first term of asymptotic series
+ x = Math.sqrt (-Math.log (RACPI * u));
+ x = Math.sqrt (-Math.log (RACPI * u * x));
+
+ // Newton's method
+ for (int i = 0; i < 3; ++i) {
+ w = -2.0*Math.exp(-x * x) / RACPI;
+ x += (u - erfc(x))/w;
+ }
+ return x;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/Num.tex b/source/umontreal/iro/lecuyer/util/Num.tex
new file mode 100644
index 0000000..4468726
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Num.tex
@@ -0,0 +1,1332 @@
+\defmodule {Num}
+
+This class provides a few constants and some methods to compute numerical
+quantities such as factorials, combinations, gamma functions, and so on.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: Num
+ * Description: Provides methods to compute some special functions
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+\begin{hide} import cern.jet.math.Bessel;\end{hide}
+
+public class Num\begin{hide} {
+ private static final double XBIG = 40.0;
+ private static final double SQPI_2 = 0.88622692545275801; // Sqrt(Pi)/2
+ private static final double RACPI = 1.77245385090551602729; // sqrt(Pi)
+ private static final double LOG_SQPI_2 = -0.1207822376352453; // Ln(Sqrt(Pi)/2)
+ private static final double LOG4 = 1.3862943611198906; // Ln(4)
+ private static final double LOG_PI = 1.14472988584940017413; // Ln(Pi)
+ private static final double PIsur2 = Math.PI/2.0;
+
+ private static final double UNSIX = 1.0/6.0;
+ private static final double QUARAN = 1.0/42.0;
+ private static final double UNTRENTE = 1.0 / 30.0;
+ private static final double DTIERS = 2.0 / 3.0;
+ private static final double CTIERS = 5.0 / 3.0;
+ private static final double STIERS = 7.0 / 3.0;
+ private static final double QTIERS = 14.0 / 3.0;
+
+
+ private static final double[] AERF = {
+ // used in erf(x)
+ 1.4831105640848036E0,
+ -3.0107107338659494E-1,
+ 6.8994830689831566E-2,
+ -1.3916271264722188E-2,
+ 2.4207995224334637E-3,
+ -3.6586396858480864E-4,
+ 4.8620984432319048E-5,
+ -5.7492565580356848E-6,
+ 6.1132435784347647E-7,
+ -5.8991015312958434E-8,
+ 5.2070090920686482E-9,
+ -4.2329758799655433E-10,
+ 3.188113506649174974E-11,
+ -2.2361550188326843E-12,
+ 1.46732984799108492E-13,
+ -9.044001985381747E-15,
+ 5.25481371547092E-16
+ };
+
+ private static final double[] AERFC = {
+ // used in erfc(x)
+ 6.10143081923200418E-1,
+ -4.34841272712577472E-1,
+ 1.76351193643605501E-1,
+ -6.07107956092494149E-2,
+ 1.77120689956941145E-2,
+ -4.32111938556729382E-3,
+ 8.54216676887098679E-4,
+ -1.27155090609162743E-4,
+ 1.12481672436711895E-5,
+ 3.13063885421820973E-7,
+ -2.70988068537762022E-7,
+ 3.07376227014076884E-8,
+ 2.51562038481762294E-9,
+ -1.02892992132031913E-9,
+ 2.99440521199499394E-11,
+ 2.60517896872669363E-11,
+ -2.63483992417196939E-12,
+ -6.43404509890636443E-13,
+ 1.12457401801663447E-13,
+ 1.7281533389986098E-14,
+ -4.2641016949424E-15,
+ -5.4537197788E-16,
+ 1.5869760776E-16,
+ 2.08998378E-17,
+ -0.5900E-17
+ };
+
+
+ private static final double[] AlnGamma = {
+ /* Chebyshev coefficients for lnGamma (x + 3), 0 <= x <= 1 In Yudell
+ Luke: The special functions and their approximations, Vol. II,
+ Academic Press, p. 301, 1969. There is an error in the additive
+ constant in the formula: (Ln (2)). */
+ 0.52854303698223459887,
+ 0.54987644612141411418,
+ 0.02073980061613665136,
+ -0.00056916770421543842,
+ 0.00002324587210400169,
+ -0.00000113060758570393,
+ 0.00000006065653098948,
+ -0.00000000346284357770,
+ 0.00000000020624998806,
+ -0.00000000001266351116,
+ 0.00000000000079531007,
+ -0.00000000000005082077,
+ 0.00000000000000329187,
+ -0.00000000000000021556,
+ 0.00000000000000001424,
+ -0.00000000000000000095
+ };
+
+ private Num() {}\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Constants}
+
+\begin{code}
+
+ public static final double DBL_EPSILON = 2.2204460492503131e-16;
+\end{code}
+ \begin{tabb} Difference between 1.0 and the smallest \texttt{double} greater than 1.0.
+ \end{tabb}
+\begin{code}
+
+ public static final int DBL_MAX_EXP = 1024;
+\end{code}
+ \begin{tabb} Largest \texttt{int} $x$ such that $2^{x-1}$ is representable
+ (approximately) as a \texttt{double}.
+ \end{tabb}
+\begin{code}
+
+ public static final int DBL_MIN_EXP = -1021;
+\end{code}
+ \begin{tabb} Smallest \texttt{int} $x$ such that $2^{x-1}$ is representable
+ (approximately) as a normalised \texttt{double}.
+ \end{tabb}
+\begin{code}
+
+ public static final int DBL_MAX_10_EXP = 308;
+\end{code}
+ \begin{tabb} Largest \texttt{int} $x$ such that $10^x$ is representable
+ (approximately) as a \texttt{double}.
+ \end{tabb}
+\begin{code}
+
+ public static final double DBL_MIN = 2.2250738585072014e-308;
+\end{code}
+ \begin{tabb} Smallest normalized positive floating-point \texttt{double}.
+ \end{tabb}
+\begin{code}
+
+ public static final double LN_DBL_MIN = -708.3964185322641;
+\end{code}
+ \begin{tabb} Natural logarithm of \texttt{DBL\_MIN}.
+ \end{tabb}
+\begin{code}
+
+ public static final int DBL_DIG = 15;
+\end{code}
+ \begin{tabb} Number of decimal digits of precision in a \texttt{double}.
+ \end{tabb}
+\begin{code}
+
+ public static final double EBASE = 2.7182818284590452354;
+\end{code}
+ \begin{tabb} The constant $e$.
+ \end{tabb}
+\begin{code}
+
+ public static final double EULER = 0.57721566490153286;
+\end{code}
+ \begin{tabb} The Euler-Mascheroni constant.
+ \end{tabb}
+\begin{code}
+
+ public static final double RAC2 = 1.41421356237309504880;
+\end{code}
+ \begin{tabb} The value of $\sqrt{2}$.
+ \end{tabb}
+\begin{code}
+
+ public static final double IRAC2 = 0.70710678118654752440;
+\end{code}
+ \begin{tabb} The value of $1/\sqrt{2}$.
+ \end{tabb}
+\begin{code}
+
+ public static final double LN2 = 0.69314718055994530941;
+\end{code}
+ \begin{tabb} The values of $\ln 2$.
+ \end{tabb}
+\begin{code}
+
+ public static final double ILN2 = 1.44269504088896340737;
+\end{code}
+ \begin{tabb} The values of $1/\ln 2$.
+ \end{tabb}
+\begin{code}
+
+ public static final double MAXINTDOUBLE = 9007199254740992.0;
+\end{code}
+ \begin{tabb} Largest integer $n_0 = 2^{53}$ such that any integer
+ $n \le n_0$ is represented exactly as a \texttt{double}.
+ \end{tabb}
+\begin{code}
+
+ public static final double MAXTWOEXP = 64;
+\end{code}
+ \begin{tabb} Powers of 2 up to \texttt{MAXTWOEXP} are stored exactly
+ in the array \texttt{TWOEXP}.
+ \end{tabb}
+\begin{code}
+
+ public static final double TWOEXP[]\begin{hide} = {
+ 1.0, 2.0, 4.0, 8.0, 1.6e1, 3.2e1,
+ 6.4e1, 1.28e2, 2.56e2, 5.12e2, 1.024e3,
+ 2.048e3, 4.096e3, 8.192e3, 1.6384e4, 3.2768e4,
+ 6.5536e4, 1.31072e5, 2.62144e5, 5.24288e5,
+ 1.048576e6, 2.097152e6, 4.194304e6, 8.388608e6,
+ 1.6777216e7, 3.3554432e7, 6.7108864e7,
+ 1.34217728e8, 2.68435456e8, 5.36870912e8,
+ 1.073741824e9, 2.147483648e9, 4.294967296e9,
+ 8.589934592e9, 1.7179869184e10, 3.4359738368e10,
+ 6.8719476736e10, 1.37438953472e11, 2.74877906944e11,
+ 5.49755813888e11, 1.099511627776e12, 2.199023255552e12,
+ 4.398046511104e12, 8.796093022208e12,
+ 1.7592186044416e13, 3.5184372088832e13,
+ 7.0368744177664e13, 1.40737488355328e14,
+ 2.81474976710656e14, 5.62949953421312e14,
+ 1.125899906842624e15, 2.251799813685248e15,
+ 4.503599627370496e15, 9.007199254740992e15,
+ 1.8014398509481984e16, 3.6028797018963968e16,
+ 7.2057594037927936e16, 1.44115188075855872e17,
+ 2.88230376151711744e17, 5.76460752303423488e17,
+ 1.152921504606846976e18, 2.305843009213693952e18,
+ 4.611686018427387904e18, 9.223372036854775808e18,
+ 1.8446744073709551616e19
+ };
+\end{hide}
+\end{code}
+ \begin{tabb} Contains the precomputed positive powers of 2.
+ One has \texttt{TWOEXP[j]}$ = 2^j$, for $j=0,\dots,64$.
+ \end{tabb}
+\begin{code}
+
+ public static final double TEN_NEG_POW[]\begin{hide} = {
+ 1.0, 1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6, 1.0e-7, 1.0e-8,
+ 1.0e-9, 1.0e-10, 1.0e-11, 1.0e-12, 1.0e-13, 1.0e-14, 1.0e-15, 1.0e-16
+ };
+\end{hide}
+\end{code}
+ \begin{tabb} Contains the precomputed negative powers of 10.
+ One has \texttt{TEN\_NEG\_POW[j]}$ = 10^{-j}$, for $j=0,\ldots,16$.
+ \end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static int gcd (int x, int y)\begin{hide} {
+ if (x < 0) x = -x;
+ if (y < 0) y = -y;
+ int r;
+ while (y != 0) {
+ r = x % y;
+ x = y;
+ y = r;
+ }
+ return x;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the greatest common divisor (gcd) of $x$ and $y$.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{x}{integer}
+ \param{y}{integer}
+ \return{the GCD of $x$ and $y$}
+ \end{htmlonly}
+\begin{code}
+
+ public static long gcd (long x, long y)\begin{hide} {
+ if (x < 0) x = -x;
+ if (y < 0) y = -y;
+ long r;
+ while (y != 0) {
+ r = x % y;
+ x = y;
+ y = r;
+ }
+ return x;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the greatest common divisor (gcd) of $x$ and $y$.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{x}{integer}
+ \param{y}{integer}
+ \return{the GCD of $x$ and $y$}
+ \end{htmlonly}
+\begin{code}
+
+ public static double combination (int n, int s)\begin{hide} {
+ final int NLIM = 100; // pour eviter les debordements
+ int i;
+ if (s == 0 || s == n)
+ return 1;
+ if (s < 0) {
+ System.err.println ("combination: s < 0");
+ return 0;
+ }
+ if (s > n) {
+ System.err.println ("combination: s > n");
+ return 0;
+ }
+ if (s > (n/2))
+ s = n - s;
+ if (n <= NLIM) {
+ double Res = 1.0;
+ int Diff = n - s;
+ for (i = 1; i <= s; i++) {
+ Res *= (double)(Diff + i)/(double)i;
+ }
+ return Res;
+ }
+ else {
+ double Res = (lnFactorial (n) - lnFactorial (s))
+ - lnFactorial (n - s);
+ return Math.exp (Res);
+ }
+ }
+\end{hide}
+\end{code}
+ \begin{tabb} Returns the \begin{latexonly}value of $\binom{n}{s}$, the
+ \end{latexonly} number of different combinations
+ of $s$ objects amongst $n$. % Uses an algorithm that prevents overflows
+ % (when computing factorials), if possible.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{n}{total number of objects}
+ \param{s}{number of chosen objects on a combination}
+ \return{the combination of $s$ objects amongst $n$}
+ \end{htmlonly}
+\begin{code}
+
+ public static double lnCombination (int n, int s)\begin{hide} {
+ if (s == 0 || s == n)
+ return 0;
+ if (s < 0 || s > n)
+ return Double.NEGATIVE_INFINITY;
+
+ double res = (lnFactorial (n) - lnFactorial (s)) - lnFactorial (n - s);
+ return res;
+ }
+\end{hide}
+\end{code}
+ \begin{tabb} Returns the natural logarithm of\begin{latexonly} $\binom{n}{s}$, the
+ \end{latexonly} number of different combinations
+ of $s$ objects amongst $n$.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{n}{total number of objects}
+ \param{s}{number of chosen objects on a combination}
+ \return{the natural log of the combination}
+ \end{htmlonly}
+\begin{code}
+
+ public static double factorial (int n)\begin{hide} {
+ if (n < 0)
+ throw new IllegalArgumentException ("factorial: n < 0");
+ double T = 1;
+ for (int j = 2; j <= n; j++)
+ T *= j;
+ return T;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the value of\latex{ $n!$}\html{ factorial $n$.}
+ \end{tabb}
+ \begin{htmlonly}
+ \param{n}{the integer for which the factorial must be computed}
+ \return{the value of $n!$}
+ \end{htmlonly}
+\begin{code}
+
+ public static double lnFactorial (int n)\begin{hide} {
+ return lnFactorial ((long) n);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the value of\latex{ $\ln (n!)$,} the natural logarithm of
+ factorial $n$. Gives 16 decimals of precision
+ (relative error $< 0.5\times 10^{-15}$).
+ \end{tabb}
+ \begin{htmlonly}
+ \param{n}{argument of the log-factorial}
+ \return{natural logarithm of $n$ factorial}
+ \end{htmlonly}
+\begin{code}
+
+ public static double lnFactorial (long n)\begin{hide} {
+ final int NLIM = 14;
+
+ if (n < 0)
+ throw new IllegalArgumentException ("lnFactorial: n < 0");
+
+ if (n == 0 || n == 1)
+ return 0.0;
+ if (n <= NLIM) {
+ long z = 1;
+ long x = 1;
+ for (int i = 2; i <= n; i++) {
+ ++x;
+ z *= x;
+ }
+ return Math.log (z);
+ }
+ else {
+ double x = (double)(n + 1);
+ double y = 1.0/(x*x);
+ double z = ((-(5.95238095238E-4*y) + 7.936500793651E-4)*y -
+ 2.7777777777778E-3)*y + 8.3333333333333E-2;
+ z = ((x - 0.5)*Math.log (x) - x) + 9.1893853320467E-1 + z/x;
+ return z;
+ }
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the value of\latex{ $\ln (n!)$,} the natural logarithm of
+ factorial $n$. Gives 16 decimals of precision
+ (relative error $< 0.5\times 10^{-15}$).
+ \end{tabb}
+ \begin{htmlonly}
+ \param{n}{argument of the log-factorial}
+ \return{natural logarithm of $n$ factorial}
+ \end{htmlonly}
+\begin{code}
+
+ public static double factoPow (int n)\begin{hide} {
+ if (n < 0)
+ throw new IllegalArgumentException ("factoPow : n < 0");
+ double res = 1.0 / n;
+ for (int i = 2; i <= n; i++) {
+ res *= (double) i / n;
+ }
+ return res;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the value of\latex{ $n!/n^n$.}\html{ factorial($n$)$/n^n$.}
+ \end{tabb}
+ \begin{htmlonly}
+ \param{n}{integer}
+ \return{the value of $n!/n^n$}
+ \end{htmlonly}
+\begin{code}
+
+ public static double[][] calcMatStirling (int m, int n)\begin{hide} {
+ int i, j, k;
+ double[][] M = new double[m+1][n+1];
+
+ for (i = 0; i <= m; i++)
+ for (j = 0; j <= n; j++)
+ M[i][j] = 0.0;
+
+ M[0][0] = 1.0;
+ for (j = 1; j <= n; j++) {
+ M[0][j] = 0.0;
+ if (j <= m) {
+ k = j - 1;
+ M[j][j] = 1.0;
+ }
+ else
+ k = m;
+ for (i = 1; i <= k; i++)
+ M[i][j] = (double)i*M[i][j - 1] + M[i - 1][j - 1];
+ }
+ return M;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Computes and returns the Stirling numbers of the second kind
+\begin{latexonly}
+\eq
+ M[i,j] = \left\{\begin{matrix}j \\ i\end{matrix}\right\}
+ \quad \mbox { for $0\le i\le m$ and $0\le i\le j\le n$}.
+ \label{Stirling2}
+\endeq
+See \cite[Section 1.2.6]{iKNU73a}.
+% See D. E. Knuth, {\em The Art of Computer Programming\/}, vol.~1,
+% second ed., 1973, Section 1.2.6.
+ The matrix $M$ is the transpose of Knuth's (1973).
+\end{latexonly}
+\end{tabb}
+\begin{htmlonly}
+ \param{m}{number of rows of the allocated matrix}
+ \param{n}{number of columns of the allocated matrix}
+ \return{the matrix of Stirling numbers}
+ \end{htmlonly}
+\begin{code}
+
+ public static double log2 (double x)\begin{hide} {
+ return ILN2*Math.log (x);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns $\log_2 ($\texttt{x}$)$.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{x}{the value for which the logarithm must be computed}
+ \return{the value of $\log_2 ($\texttt{x}$)$}
+ \end{htmlonly}
+\begin{code}
+
+ public static double lnGamma (double x)\begin{hide} {
+ if (x <= 0.0)
+ throw new IllegalArgumentException ("lnGamma: x <= 0");
+ if (Double.isNaN(x))
+ return Double.NaN;
+ final double XLIMBIG = 1.0/DBL_EPSILON;
+ final double XLIM1 = 18.0;
+ final double DK2 = 0.91893853320467274177; // Ln (sqrt (2 Pi))
+ final double DK1 = 0.9574186990510627;
+ final int N = 15; // Degree of Chebyshev polynomial
+ double y, z;
+ int i, k;
+
+/*
+ if (x <= 0.0) {
+ double f = (1.0 - x) - Math.floor (1.0 - x);
+ return LOG_PI - lnGamma (1.0 - x) - Math.log (Math.sin (Math.PI * f));
+ }
+*/
+ if (x > XLIM1) {
+ if (x > XLIMBIG)
+ y = 0.0;
+ else
+ y = 1.0/(x*x);
+ z = ((-(5.95238095238E-4*y) + 7.936500793651E-4)*y -
+ 2.7777777777778E-3)*y + 8.3333333333333E-2;
+ z = ((x - 0.5)*Math.log (x) - x) + DK2 + z/x;
+ return z;
+
+ } else if (x > 4.0) {
+ k = (int)x;
+ z = x - k;
+ y = 1.0;
+ for (i = 3; i < k; i++)
+ y *= z + i;
+ y = Math.log (y);
+
+ } else if (x <= 0.0)
+ return Double.MAX_VALUE;
+
+ else if (x < 3.0) {
+ k = (int)x;
+ z = x - k;
+ y = 1.0;
+ for (i = 2; i >= k; i--)
+ y *= z + i;
+ y = -Math.log (y);
+ }
+ else { // 3 <= x <= 4
+ z = x - 3.0;
+ y = 0.0;
+ }
+ z = evalCheby (AlnGamma, N, 2.0*z - 1.0);
+ return z + DK1 + y;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the natural logarithm of the gamma function $\Gamma(x)$
+ evaluated at \texttt{x}.
+ Gives 16 decimals of precision, but is implemented only for $x>0$.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{x}{the value for which the lnGamma function must be computed}
+ \return{the natural logarithm of the gamma function}
+ \end{htmlonly}
+\begin{code}
+
+ public static double lnBeta (double lam, double nu)\begin{hide} {
+ if (0. == lam || 0. == nu)
+ return Double.POSITIVE_INFINITY;
+ if (Double.isInfinite (lam) || Double.isInfinite (nu))
+ return Double.NEGATIVE_INFINITY;
+ return lnGamma (lam) + lnGamma (nu) - lnGamma (lam + nu);
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the natural logarithm of the Beta function
+ $B(\lambda, \nu)$. It is defined in terms of the Gamma function as
+ $$
+ B(\lambda, \nu) = \frac{\Gamma(\lambda)\Gamma(\nu)}{\Gamma(\lambda + \nu)}
+ $$
+ with \texttt{lam} $=\lambda$ and \texttt{nu} $=\nu$.
+\end{tabb}
+\begin{code}
+
+ public static double digamma (double x)\begin{hide} {
+ final double C7[][] = {
+ {1.3524999667726346383e4, 4.5285601699547289655e4, 4.5135168469736662555e4,
+ 1.8529011818582610168e4, 3.3291525149406935532e3, 2.4068032474357201831e2,
+ 5.1577892000139084710, 6.2283506918984745826e-3},
+ {6.9389111753763444376e-7, 1.9768574263046736421e4, 4.1255160835353832333e4,
+ 2.9390287119932681918e4, 9.0819666074855170271e3,
+ 1.2447477785670856039e3, 6.7429129516378593773e1, 1.0}
+ };
+ final double C4[][] = {
+ {-2.728175751315296783e-15, -6.481571237661965099e-1, -4.486165439180193579,
+ -7.016772277667586642, -2.129404451310105168},
+ {7.777885485229616042, 5.461177381032150702e1,
+ 8.929207004818613702e1, 3.227034937911433614e1, 1.0}
+ };
+
+ if (Double.isNaN(x))
+ return Double.NaN;
+ double prodPj = 0.0;
+ double prodQj = 0.0;
+ double digX = 0.0;
+
+ if (x >= 3.0) {
+ double x2 = 1.0 / (x * x);
+ for (int j = 4; j >= 0; j--) {
+ prodPj = prodPj * x2 + C4[0][j];
+ prodQj = prodQj * x2 + C4[1][j];
+ }
+ digX = Math.log (x) - (0.5 / x) + (prodPj / prodQj);
+
+ } else if (x >= 0.5) {
+ final double X0 = 1.46163214496836234126;
+ for (int j = 7; j >= 0; j--) {
+ prodPj = x * prodPj + C7[0][j];
+ prodQj = x * prodQj + C7[1][j];
+ }
+ digX = (x - X0) * (prodPj / prodQj);
+
+ } else {
+ double f = (1.0 - x) - Math.floor (1.0 - x);
+ digX = digamma (1.0 - x) + Math.PI / Math.tan (Math.PI * f);
+ }
+
+ return digX;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the value of the logarithmic derivative of the Gamma function
+ $\psi(x) = \Gamma'(x) / \Gamma(x)$.
+\end{tabb}
+\begin{code}
+
+ public static double trigamma (double x)\begin{hide} {
+ double y, sum;
+ if (Double.isNaN(x))
+ return Double.NaN;
+
+ if (x < 0.5) {
+ y = (1.0 - x) - Math.floor (1.0 - x);
+ sum = Math.PI / Math.sin (Math.PI * y);
+ return sum * sum - trigamma (1.0 - x);
+ }
+
+ if (x >= 40.0) {
+ // Asymptotic series
+ y = 1.0/(x*x);
+ sum = 1.0 + y*(1.0/6.0 - y*(1.0/30.0 - y*(1.0/42.0 - 1.0/30.0*y)));
+ sum += 0.5/x;
+ return sum/x;
+ }
+
+ int i;
+ int p = (int) x;
+ y = x - p;
+ sum = 0.0;
+
+ if (p > 3) {
+ for (i = 3; i < p; i++)
+ sum -= 1.0/((y + i)*(y + i));
+
+ } else if (p < 3) {
+ for (i = 2; i >= p; i--)
+ sum += 1.0/((y + i)*(y + i));
+ }
+
+ /* Chebyshev coefficients for trigamma (x + 3), 0 <= x <= 1. In Yudell
+ Luke: The special functions and their approximations, Vol. II,
+ Academic Press, p. 301, 1969. */
+ final int N = 15;
+ final double A[] = { 2.0*0.33483869791094938576, -0.05518748204873009463,
+ 0.00451019073601150186, -0.00036570588830372083,
+ 2.943462746822336e-5, -2.35277681515061e-6, 1.8685317663281e-7,
+ -1.475072018379e-8, 1.15799333714e-9, -9.043917904e-11,
+ 7.029627e-12, -5.4398873e-13, 0.4192525e-13, -3.21903e-15, 0.2463e-15,
+ -1.878e-17, 0., 0. };
+
+ return sum + evalChebyStar (A, N, y);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the value of the trigamma function $d\psi(x)/dx$, the derivative of
+ the digamma function, evaluated at $x$.
+\end{tabb}
+\begin{code}
+
+ public static double tetragamma (double x)\begin{hide} {
+ double y, sum;
+ if (Double.isNaN(x))
+ return Double.NaN;
+
+ if (x < 0.5) {
+ y = (1.0 - x) - Math.floor (1.0 - x);
+ sum = Math.PI / Math.sin (Math.PI * y);
+ return 2.0 * Math.cos (Math.PI * y) * sum * sum * sum +
+ tetragamma (1.0 - x);
+ }
+
+ if (x >= 20.0) {
+ // Asymptotic series
+ y = 1.0/(x*x);
+ sum = y*(0.5 - y*(1.0/6.0 - y*(1.0/6.0 - y*(0.3 - 5.0/6.0*y))));
+ sum += 1.0 + 1.0/x;
+ return -sum*y;
+ }
+
+ int i;
+ int p = (int) x;
+ y = x - p;
+ sum = 0.0;
+
+ if (p > 3) {
+ for (i = 3; i < p; i++)
+ sum += 1.0 / ((y + i) * (y + i) * (y + i));
+
+ } else if (p < 3) {
+ for (i = 2; i >= p; i--)
+ sum -= 1.0 / ((y + i) * (y + i) * (y + i));
+ }
+
+ /* Chebyshev coefficients for tetragamma (x + 3), 0 <= x <= 1. In Yudell
+ Luke: The special functions and their approximations, Vol. II,
+ Academic Press, p. 301, 1969. */
+ final int N = 16;
+ final double A[] = { -0.11259293534547383037*2.0, 0.03655700174282094137,
+ -0.00443594249602728223, 0.00047547585472892648,
+ -4.747183638263232e-5, 4.52181523735268e-6, -4.1630007962011e-7,
+ 3.733899816535e-8, -3.27991447410e-9, 2.8321137682e-10,
+ -2.410402848e-11, 2.02629690e-12, -1.6852418e-13, 1.388481e-14,
+ -1.13451e-15, 9.201e-17, -7.41e-18, 5.9e-19, -5.0e-20 };
+
+ return 2.0 * sum + evalChebyStar (A, N, y);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns the value of the tetragamma function $d^{2}\psi(x)/d^{2}x$, the second
+ derivative of the digamma function, evaluated at $x$.
+\end{tabb}
+\begin{code}
+
+ public static double gammaRatioHalf (double x)\begin{hide} {
+ if (x <= 0.0)
+ throw new IllegalArgumentException ("gammaRatioHalf: x <= 0");
+ if (Double.isNaN(x))
+ return Double.NaN;
+
+ if (x <= 10.0) {
+ double y = lnGamma (x + 0.5) - lnGamma (x);
+ return Math.exp(y);
+ }
+
+ double sum;
+ if (x <= 300.0) {
+ // The sum converges very slowly for small x, but faster as x increases
+ final double EPSILON = 1.0e-15;
+ double term = 1.0;
+ sum = 1.0;
+ int i = 1;
+ while (term > EPSILON*sum) {
+ term *= (i - 1.5)*(i - 1.5) /(i*(x + i - 1.5));
+ sum += term;
+ i++;
+ }
+ return Math.sqrt ((x - 0.5)*sum);
+ }
+
+ // Asymptotic series for Gamma(x + 0.5) / (Gamma(x) * Sqrt(x))
+ // Comparer la vitesse de l'asymptotique avec la somme ci-dessus !!!
+ double y = 1.0 / (8.0*x);
+ sum = 1.0 + y*(-1.0 + y*(0.5 + y*(2.5 - y*(2.625 + 49.875*y))));
+ return sum*Math.sqrt(x);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the ratio $\Gamma(x+1/2)/\Gamma(x)$ of two gamma
+functions. This ratio is evaluated in a numerically stable way.
+Restriction: $x>0$.
+\end{tabb}
+\begin{code}
+
+ public static double sumKahan (double[] A, int n) \begin{hide} {
+ if (A.length < n)
+ n = A.length;
+ double sum = 0;
+ double c = 0;
+ double y, t;
+
+ for (int i = 0; i < n; i++) {
+ y = A[i] - c;
+ t = sum + y;
+ c = (t - sum) - y;
+ sum = t;
+ }
+
+ return sum;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Implementation of the Kahan summation algorithm.
+Sums the first $n$ elements of $A$ and returns the sum.
+This algorithm is more precise than the naive algorithm.
+See \url{http://en.wikipedia.org/wiki/Kahan_summation_algorithm}.
+\end{tabb}
+\begin{code}
+
+ public static double harmonic (long n)\begin{hide} {
+ if (n < 1)
+ throw new IllegalArgumentException ("n < 1");
+ return digamma(n + 1) + EULER;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the $n$-th harmonic number $H_n = \sum_{j=1}^n 1/j$.
+\end{tabb}
+\begin{code}
+
+ public static double harmonic2 (long n)\begin{hide} {
+ if (n <= 0)
+ throw new IllegalArgumentException ("n <= 0");
+ if (1 == n)
+ return 0.0;
+ if (2 == n)
+ return 1.0;
+
+ long k = n / 2;
+ if ((n & 1) == 1)
+ return 2.0*harmonic(k); // n odd
+ return 1.0/k + 2.0*harmonic(k-1); // n even
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes the sum
+\[
+\sideset{}{'}\sum_{-n/2<j\le n/2}\; \frac 1{|j|},
+\]
+ where the symbol $\sum^\prime$ means that the term with $j=0$ is excluded
+ from the sum.
+\end{tabb}
+\begin{code}
+
+ public static double volumeSphere (double p, int t)\begin{hide} {
+ final double EPS = 2.0*DBL_EPSILON;
+ int pLR = (int)p;
+ double kLR = (double)t;
+ double Vol;
+ int s;
+
+ if (p < 0)
+ throw new IllegalArgumentException ("volumeSphere: p < 0");
+
+ if (Math.abs (p - pLR) <= EPS) {
+ switch (pLR) {
+ case 0:
+ return TWOEXP[t];
+ case 1:
+ return TWOEXP[t]/(double)factorial (t);
+ case 2:
+ if ((t % 2) == 0)
+ return Math.pow (Math.PI, kLR/2.0)/(double)factorial (t/2);
+ else {
+ s = (t + 1)/2;
+ return Math.pow (Math.PI, (double)s - 1.0)*factorial (s)*
+ TWOEXP[2*s]/(double)factorial (2*s);
+ }
+ default:
+ }
+ }
+ Vol = kLR*(LN2 + lnGamma (1.0 + 1.0/p)) -
+ lnGamma (1.0 + kLR/p);
+ return Math.exp (Vol);
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the volume $V$ of a sphere of radius 1 in $t$ dimensions
+ using the norm $L_p$. It is given by the formula
+\begin{htmlonly}
+\[
+ V = ([2\Gamma (1 + 1/p)]^t)/\Gamma(1 + t/p), \qquad p > 0,
+\]
+\end{htmlonly}
+\begin{latexonly}
+\[
+ V = \frac{\left[2 \Gamma (1 + 1/p)\right]^t}
+ {\Gamma\left (1 + t/p\right)}, \qquad p > 0,
+\]
+\end{latexonly}%
+where $\Gamma$ is the gamma function.
+ The case of the sup norm $L_\infty$ is obtained by choosing $p=0$.
+ Restrictions: $p\ge 0$ and $t\ge 1$.
+ \end{tabb}
+ \begin{htmlonly}
+ \param{p}{index of the used norm}
+ \param{t}{number of dimensions}
+ \return{the volume of a sphere}
+ \end{htmlonly}
+\begin{code}
+
+ public static double bernoulliPoly (int n, double x) \begin{hide} {
+ switch (n) {
+ case 0:
+ return 1.0;
+ case 1:
+ return x - 0.5;
+ case 2:
+ return x*(x - 1.0) + UNSIX;
+ case 3:
+ return ((2.0*x - 3.0) * x + 1.0)*x*0.5;
+ case 4:
+ return ((x - 2.0) * x + 1.0)*x*x - UNTRENTE;
+ case 5:
+ return (((x - 2.5) * x + CTIERS) *x*x - UNSIX) * x;
+ case 6:
+ return (((x - 3.0) * x + 2.5) * x*x - 0.5) * x*x + QUARAN;
+ case 7:
+ return ((((x - 3.5) * x + 3.5) *x*x - 7.0/6.0) * x*x + UNSIX) * x;
+ case 8:
+ return ((((x - 4.0) * x +
+ QTIERS) * x*x - STIERS) * x*x + DTIERS) * x*x - UNTRENTE;
+ default:
+ throw new IllegalArgumentException("n must be <= 8");
+ }
+ // return 0;
+ }\end{hide}
+\end{code}
+\begin{tabb} Evaluates the Bernoulli polynomial $B_n(x)$ of degree $n$
+ at $x$. Only degrees $n\le 8$ are programmed for now.
+ The first Bernoulli polynomials of even degree are:
+\begin{eqnarray}
+B_0(x) &=& 1 \nonumber \\
+B_2(x) &=& x^2-x+1/6 \nonumber \\
+B_4(x) &=& x^4-2x^3+x^2-1/30 \label{bernoulli}\\
+B_6(x) &=& x^6-3x^5+5x^4/2-x^2/2+1/42 \nonumber \\
+B_8(x) &=& x^8-4x^7+14x^6/3 - 7x^4/3 +2x^2/3-1/30. \nonumber
+\end{eqnarray}
+\end{tabb}
+\begin{code}
+
+ public static double evalCheby (double a[], int n, double x) \begin{hide} {
+ if (Math.abs (x) > 1.0)
+ System.err.println ("Chebychev polynomial evaluated "+
+ "at x outside [-1, 1]");
+ final double xx = 2.0*x;
+ double b0 = 0.0;
+ double b1 = 0.0;
+ double b2 = 0.0;
+ for (int j = n; j >= 0; j--) {
+ b2 = b1;
+ b1 = b0;
+ b0 = (xx*b1 - b2) + a[j];
+ }
+ return (b0 - b2)/2.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Evaluates a series of Chebyshev polynomials $T_j$ at
+ $x$ over the basic interval $[-1, \;1]$\latex{, using}\html{. It uses}
+ the method of Clenshaw
+ \cite{mCLE62a}, i.e., computes and returns
+ \[
+ y = \frac{a_0}2 + \sum_{j=1}^n a_j T_j (x).
+ \]
+\begin{htmlonly}
+ \param{a}{coefficients of the polynomials}
+ \param{n}{largest degree of polynomials}
+ \param{x}{the parameter of the $T_j$ functions}
+ \return{ the value of a series of Chebyshev polynomials $T_j$.}
+\end{htmlonly}
+ \end{tabb}
+\begin{code}
+
+ public static double evalChebyStar (double a[], int n, double x) \begin{hide} {
+ if (x > 1.0 || x < 0.0)
+ System.err.println ("Shifted Chebychev polynomial evaluated " +
+ "at x outside [0, 1]");
+ final double xx = 2.0*(2.0*x - 1.0);
+ double b0 = 0.0;
+ double b1 = 0.0;
+ double b2 = 0.0;
+ for (int j = n; j >= 0; j--) {
+ b2 = b1;
+ b1 = b0;
+ b0 = xx*b1 - b2 + a[j];
+ }
+ return (b0 - b2)/2.0;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Evaluates a series of shifted Chebyshev polynomials $T_j^*$
+ at $x$ over the basic interval $ [0, \;1]$\latex{, using}\html{. It uses}
+ the method of Clenshaw \cite{mCLE62a}, i.e., computes and returns
+ \[
+ y = \frac{a_0}2 + \sum_{j=1}^n a_j T_j^* (x).
+ \]
+\begin{htmlonly}
+ \param{a}{coefficients of the polynomials}
+ \param{n}{largest degree of polynomials}
+ \param{x}{the parameter of the $T_j^*$ functions}
+ \return{ the value of a series of Chebyshev polynomials $T_j^*$.}
+\end{htmlonly}
+ \end{tabb}
+\begin{code}
+
+ public static double besselK025 (double x)\begin{hide} {
+ if (Double.isNaN(x))
+ return Double.NaN;
+ if (x < 1.E-300)
+ return Double.MIN_VALUE;
+
+ final int DEG = 6;
+ final double c[] = {
+ 32177591145.0,
+ 2099336339520.0,
+ 16281990144000.0,
+ 34611957596160.0,
+ 26640289628160.0,
+ 7901666082816.0,
+ 755914244096.0
+ };
+
+ final double b[] = {
+ 75293843625.0,
+ 2891283595200.0,
+ 18691126272000.0,
+ 36807140966400.0,
+ 27348959232000.0,
+ 7972533043200.0,
+ 755914244096.0
+ };
+
+ /*------------------------------------------------------------------
+ * x > 0.6 => approximation asymptotique rationnelle dans Luke:
+ * Yudell L.Luke "Mathematical functions and their approximations",
+ * Academic Press Inc. New York, 1975, p.371
+ *------------------------------------------------------------------*/
+ if (x >= 0.6) {
+ double B = b[DEG];
+ double C = c[DEG];
+ for (int j = DEG; j >= 1; j--) {
+ B = B * x + b[j - 1];
+ C = C * x + c[j - 1];
+ }
+ double Res1 = Math.sqrt(Math.PI / (2.0*x)) * Math.exp(-x)*(C/B);
+ return Res1;
+ }
+
+ /*------------------------------------------------------------------
+ * x < 0.6 => la serie de K_{1/4} = Pi/Sqrt (2) [I_{-1/4} - I_{1/4}]
+ *------------------------------------------------------------------*/
+ double xx = x * x;
+ double rac = Math.pow (x/2.0, 0.25);
+ double Res = (((xx/1386.0 + 1.0/42.0)*xx + 1.0/3.0)*xx + 1.0)/
+ (1.225416702465177*rac);
+ double temp = (((xx/3510.0 + 1.0/90.0)*xx + 0.2)*xx + 1.0)*rac/
+ 0.906402477055477;
+ Res = Math.PI*(Res - temp)/RAC2;
+ return Res;
+ }\end{hide}
+\end{code}
+ \begin{tabb} Returns the value of $K_{1/4}(x)$, where \begin{latexonly}
+ $K_{\nu}$\end{latexonly}\begin{htmlonly}$K_a$ \end{htmlonly} is the modified
+ Bessel's function of the second kind.
+ The relative error on the returned value is less than
+ $0.5\times 10^{-6}$ for $x > 10^{-300}$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the function is calculated}
+ \return{the value of $K_{1/4}(x)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double expBesselK1 (double x, double y)\begin{hide} {
+ if (Double.isNaN(x) || Double.isNaN(y))
+ return Double.NaN;
+ if (y > 500.0) {
+ double sum = 1 + 3.0/(8.0*y) - 15.0 / (128.0*y*y) + 105.0 /(1024.0*y*y*y);
+ return Math.sqrt(PIsur2/ y) * sum * Math.exp(x - y);
+ } else if (Math.abs(x) > 500.0) {
+ double b = Bessel.k1(y);
+ return Math.exp(x + Math.log(b));
+ } else {
+ return Math.exp(x) * Bessel.k1(y);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of $e^x K_1(y)$, where $ K_1$ is the modified Bessel
+function of the second kind of order 1. Restriction: $y > 0$.
+\end{tabb}
+\begin{code}
+
+ public static double erf (double x)\begin{hide} {
+ if (Double.isNaN(x))
+ return Double.NaN;
+ if (x < 0.0)
+ return -erf(-x);
+ if (x >= 6.0)
+ return 1.0;
+ if (x >= 2.0)
+ return 1.0 - erfc(x);
+
+ double t = 0.5*x*x - 1.0;
+ double y = Num.evalCheby (AERF, 16, t);
+ return x*y;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of \texttt{erf}($x$), the error function. It is defined as
+\begin{latexonly}
+\[
+\mbox{erf}(x) = \frac2{\sqrt\pi}\int_0^x dt\, e^{-t^2}.
+\]
+\end{latexonly}
+\begin{htmlonly}
+\[
+\mbox{erf}(x) = 2/[\sqrt\pi]\int_0^x dt\, e^{-t^2}.
+\]
+\end{htmlonly}
+\begin{htmlonly}
+ \param{x}{value at which the function is calculated}
+ \return{the value of \texttt{erf}$(x)$}
+\end{htmlonly}
+\end{tabb}
+\begin{code}
+
+ public static double erfc (double x)\begin{hide} {
+ if (Double.isNaN(x))
+ return Double.NaN;
+ if (x < 0.0)
+ return 2.0 - erfc(-x);
+ if (x >= XBIG)
+ return 0.0;
+ double t = (x - 3.75)/(x + 3.75);
+ double y = Num.evalCheby (AERFC, 24, t);
+ y *= Math.exp(-x*x);
+ return y;
+ }\end{hide}
+\end{code}
+ \begin{tabb}
+Returns the value of \texttt{erfc}($x$), the complementary error function.
+It is defined as
+\begin{latexonly}
+\[
+\mbox{erfc}(x) = \frac2{\sqrt\pi}\int_x^\infty dt\, e^{-t^2}.
+\]
+\end{latexonly}
+\begin{htmlonly}
+\[
+\mbox{erfc}(x) = 2/[\sqrt\pi]\int_x^\infty dt\, e^{-t^2}.
+\]
+\end{htmlonly}
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value at which the function is calculated}
+ \return{the value of \texttt{erfc}$(x)$}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ private static final double[] InvP1 = {
+ 0.160304955844066229311e2,
+ -0.90784959262960326650e2,
+ 0.18644914861620987391e3,
+ -0.16900142734642382420e3,
+ 0.6545466284794487048e2,
+ -0.864213011587247794e1,
+ 0.1760587821390590
+ };
+
+ private static final double[] InvQ1 = {
+ 0.147806470715138316110e2,
+ -0.91374167024260313396e2,
+ 0.21015790486205317714e3,
+ -0.22210254121855132366e3,
+ 0.10760453916055123830e3,
+ -0.206010730328265443e2,
+ 0.1e1
+ };
+
+ private static final double[] InvP2 = {
+ -0.152389263440726128e-1,
+ 0.3444556924136125216,
+ -0.29344398672542478687e1,
+ 0.11763505705217827302e2,
+ -0.22655292823101104193e2,
+ 0.19121334396580330163e2,
+ -0.5478927619598318769e1,
+ 0.237516689024448000
+ };
+
+ private static final double[] InvQ2 = {
+ -0.108465169602059954e-1,
+ 0.2610628885843078511,
+ -0.24068318104393757995e1,
+ 0.10695129973387014469e2,
+ -0.23716715521596581025e2,
+ 0.24640158943917284883e2,
+ -0.10014376349783070835e2,
+ 0.1e1
+ };
+
+ private static final double[] InvP3 = {
+ 0.56451977709864482298e-4,
+ 0.53504147487893013765e-2,
+ 0.12969550099727352403,
+ 0.10426158549298266122e1,
+ 0.28302677901754489974e1,
+ 0.26255672879448072726e1,
+ 0.20789742630174917228e1,
+ 0.72718806231556811306,
+ 0.66816807711804989575e-1,
+ -0.17791004575111759979e-1,
+ 0.22419563223346345828e-2
+ };
+
+ private static final double[] InvQ3 = {
+ 0.56451699862760651514e-4,
+ 0.53505587067930653953e-2,
+ 0.12986615416911646934,
+ 0.10542932232626491195e1,
+ 0.30379331173522206237e1,
+ 0.37631168536405028901e1,
+ 0.38782858277042011263e1,
+ 0.20372431817412177929e1,
+ 0.1e1
+ };\end{hide}
+
+ public static double erfInv (double u) \begin{hide} {
+ if (Double.isNaN(u))
+ return Double.NaN;
+ if (u < 0.0)
+ return -erfInv (-u);
+
+ if (u > 1.0)
+ throw new IllegalArgumentException ("u is not in [-1, 1]");
+ if (u >= 1.0)
+ return Double.POSITIVE_INFINITY;
+
+ double t, z, v, w;
+
+ if (u <= 0.75) {
+ t = u * u - 0.5625;
+ v = Misc.evalPoly (InvP1, 6, t);
+ w = Misc.evalPoly (InvQ1, 6, t);
+ z = (v / w) * u;
+
+ } else if (u <= 0.9375) {
+ t = u * u - 0.87890625;
+ v = Misc.evalPoly (InvP2, 7, t);
+ w = Misc.evalPoly (InvQ2, 7, t);
+ z = (v / w) * u;
+
+ } else {
+ t = 1.0 / Math.sqrt (-Math.log (1.0 - u));
+ v = Misc.evalPoly (InvP3, 10, t);
+ w = Misc.evalPoly (InvQ3, 8, t);
+ z = (v / w) / t;
+ }
+
+ return z;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of \texttt{erf}${}^{-1}(u)$, the inverse of the error
+function. If $u =\ $\texttt{erf}$(x)$, then $x =\ $\texttt{erf}${}^{-1}(u)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{value at which the function is calculated}
+ \return{the value of {erfInv}$(u)$}
+\end{htmlonly}
+\begin{code}
+
+ public static double erfcInv (double u)\begin{hide} {
+ if (Double.isNaN(u))
+ return Double.NaN;
+ if (u < 0 || u > 2)
+ throw new IllegalArgumentException ("u is not in [0, 2]");
+
+ if (u > 0.005)
+ return erfInv (1.0 - u);
+
+ if (u <= 0)
+ return Double.POSITIVE_INFINITY;
+
+ double x, w;
+
+ // first term of asymptotic series
+ x = Math.sqrt (-Math.log (RACPI * u));
+ x = Math.sqrt (-Math.log (RACPI * u * x));
+
+ // Newton's method
+ for (int i = 0; i < 3; ++i) {
+ w = -2.0*Math.exp(-x * x) / RACPI;
+ x += (u - erfc(x))/w;
+ }
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value of \texttt{erfc}${}^{-1}(u)$, the inverse of the
+complementary error function. If $u =\ $\texttt{erfc}$(x)$,
+then $x =\ $\texttt{erfc}${}^{-1}(u)$.
+\end{tabb}
+\begin{htmlonly}
+ \param{u}{value at which the function is calculated}
+ \return{the value of {erfcInv}$(u)$}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/PrintfFormat.java b/source/umontreal/iro/lecuyer/util/PrintfFormat.java
new file mode 100644
index 0000000..6bae6af
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/PrintfFormat.java
@@ -0,0 +1,1345 @@
+
+
+/*
+ * Class: PrintfFormat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.text.NumberFormat;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+import java.util.Formatter;
+
+
+
+/**
+ * This class acts like a {@link StringBuffer} which defines new types
+ * of <TT>append</TT> methods.
+ * It defines certain functionalities of the ANSI C <TT>printf</TT>
+ * function that also can be accessed through static methods.
+ * The information given here is strongly inspired
+ * from the <TT>man</TT> page of the C <TT>printf</TT> function.
+ *
+ * <P>
+ * Most methods of this class format numbers for the English US
+ * locale only. One can use the Java class {@link java.util.Formatter Formatter}
+ * for performing locale-independent formatting.
+ *
+ */
+public class PrintfFormat implements CharSequence, Appendable {
+ private static NumberFormat nf = NumberFormat.getInstance (Locale.US);
+ private static DecimalFormatSymbols dfs =
+ new DecimalFormatSymbols (Locale.US);
+ private static final int NDEC = 50;
+ private static DecimalFormat[] dfe = new DecimalFormat[NDEC+1];
+ private static DecimalFormat[] dfg = new DecimalFormat[NDEC+1];
+ private StringBuffer sb;
+/*
+ private static void round (int b, StringBuffer num) {
+ // round a real number up if it is >= 0.5
+ // base is b
+
+ // round up the fractional part
+ int j = num.length() - 1;
+ String bm1 = String.valueOf (b - 1);
+ while (j >= 0 && num.charAt(j) == bm1.charAt(0)) {
+ num.deleteCharAt(j);
+ j--;
+ }
+
+ char c;
+ if (j < 0)
+ return;
+ if (num.charAt(j) != '.') {
+ c = num.charAt(j);
+ ++c;
+ num.replace(j, j + 1, Character.toString(c));
+ return;
+ }
+
+ // round up the integral part
+ j--;
+ while (j >= 0 && num.charAt(j) == bm1.charAt(0)) {
+ num.replace(j, j + 1, "0");
+ j--;
+ }
+
+ if (j < 0 || num.charAt(j) == '-') {
+ num.insert( j + 1, '1');
+ } else if (num.charAt(j) == ' ') {
+ num.replace(j, j + 1, "1");
+ } else {
+ c = num.charAt(j);
+ ++c;
+ num.replace(j, j + 1, Character.toString(c));
+ }
+ }*/
+
+
+ /**
+ * End-of-line symbol or line separator. It is ``
+ * <SPAN CLASS="MATH">\</SPAN>n''
+ * for Unix/Linux, ``
+ * <SPAN CLASS="MATH">\</SPAN>r
+ * <SPAN CLASS="MATH">\</SPAN>n" for MS-DOS/MS-Windows, and
+ * ``
+ * <SPAN CLASS="MATH">\</SPAN>r'' for Apple OSX.
+ *
+ */
+ public static final String NEWLINE =
+ System.getProperty("line.separator");
+
+
+ /**
+ * End-of-line symbol or line separator. Same as <TT>NEWLINE</TT>.
+ *
+ */
+ public static final String LINE_SEPARATOR =
+ System.getProperty("line.separator");
+
+
+ /**
+ * Constructs a new buffer object containing an empty string.
+ *
+ */
+ public PrintfFormat() {
+ sb = new StringBuffer();
+ }
+
+
+ /**
+ * Constructs a new buffer object with an initial capacity of <TT>length</TT>.
+ *
+ * @param length initial length of the buffer
+ *
+ *
+ */
+ public PrintfFormat (int length) {
+ sb = new StringBuffer (length);
+ }
+
+
+ /**
+ * Constructs a new buffer object containing the initial string <TT>str</TT>.
+ *
+ * @param str initial contents of the buffer
+ *
+ */
+ public PrintfFormat (String str) {
+ sb = new StringBuffer (str);
+ }
+
+
+ /**
+ * Appends <TT>str</TT> to the buffer.
+ *
+ * @param str string to append to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (String str) {
+ sb.append (str);
+ return this;
+ }
+
+
+ /**
+ * Uses the {@link #s(int,String) s} static method to append <TT>str</TT> to the buffer.
+ * A minimum of <TT>fieldwidth</TT> characters will be used.
+ *
+ * @param fieldwidth minimum number of characters that will be added to the buffer
+ *
+ * @param str string to append to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (int fieldwidth, String str) {
+ sb.append (s (fieldwidth, str));
+ return this;
+ }
+
+
+ /**
+ * Appends <TT>x</TT> to the buffer.
+ *
+ * @param x value being added to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (double x) {
+ sb.append (x);
+ return this;
+ }
+
+
+ /**
+ * Uses the {@link #f(int,double) f} static method to append <TT>x</TT> to the buffer.
+ * A minimum of <TT>fieldwidth</TT> characters will be used.
+ *
+ * @param fieldwidth minimum length of the converted string to be appended
+ *
+ * @param x value to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (int fieldwidth, double x) {
+ sb.append (f (fieldwidth, x));
+ return this;
+ }
+
+
+ /**
+ * Uses the {@link #f(int,int,double) f} static method to append <TT>x</TT> to the buffer.
+ * A minimum of <TT>fieldwidth</TT> characters will be used with the given <TT>precision</TT>.
+ *
+ * @param fieldwidth minimum length of the converted string to be appended
+ *
+ * @param precision number of digits after the decimal point of the converted value
+ *
+ * @param x value to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (int fieldwidth, int precision, double x) {
+ sb.append (f (fieldwidth, precision, x));
+ return this;
+ }
+
+
+ /**
+ * Appends <TT>x</TT> to the buffer.
+ *
+ * @param x value to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (int x) {
+ sb.append (x);
+ return this;
+ }
+
+
+ /**
+ * Uses the {@link #d(int,long) d} static method to append <TT>x</TT> to the buffer.
+ * A minimum of <TT>fieldwidth</TT> characters will be used.
+ *
+ * @param fieldwidth minimum length of the converted string to be appended
+ *
+ * @param x value to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (int fieldwidth, int x) {
+ sb.append (d (fieldwidth, x));
+ return this;
+ }
+
+
+ /**
+ * Appends <TT>x</TT> to the buffer.
+ *
+ * @param x value to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (long x) {
+ sb.append (x);
+ return this;
+ }
+
+
+ /**
+ * Uses the {@link #d(int,long) d} static method to append <TT>x</TT> to the buffer.
+ * A minimum of <TT>fieldwidth</TT> characters will be used.
+ *
+ * @param fieldwidth minimum length of the converted string to be appended
+ *
+ * @param x value to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (int fieldwidth, long x) {
+ sb.append (d (fieldwidth, x));
+ return this;
+ }
+
+
+ /**
+ * Uses the {@link #format(int,int,int,double) format}
+ * static method with the same four arguments to append <TT>x</TT> to the buffer.
+ *
+ * @param fieldwidth minimum length of the converted string to be appended
+ *
+ * @param accuracy number of digits after the decimal point
+ *
+ * @param precision number of significant digits
+ *
+ * @param x value to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (int fieldwidth, int accuracy, int precision,
+ double x) {
+ sb.append (format (fieldwidth, accuracy, precision, x));
+ return this;
+ }
+
+
+ /**
+ * Appends a single character to the buffer.
+ *
+ * @param c character to be appended to the buffer
+ *
+ * @return this object
+ *
+ */
+ public PrintfFormat append (char c) {
+ sb.append (c);
+ return this;
+ }
+
+
+ /**
+ * Clears the contents of the buffer.
+ *
+ */
+ public void clear() {
+ sb.setLength (0);
+ }
+
+
+ /**
+ * Returns the {@link StringBuffer} associated with that object.
+ *
+ * @return the internal {@link StringBuffer} object
+ *
+ */
+ public StringBuffer getBuffer() {
+ return sb;
+ }
+
+
+ /**
+ * Converts the buffer into a {@link String}.
+ *
+ * @return the {@link String} conversion of the internal buffer
+ *
+ */
+ public String toString() {
+ return sb.toString();
+ }
+
+
+ /**
+ * Same as {@link #s(int,String) s} <TT>(0, str)</TT>. If the string <TT>str</TT> is null,
+ * it returns the string ``null''.
+ *
+ * @param str the string to process
+ *
+ * @return the same string
+ *
+ */
+ public static String s (String str) {
+ if (str == null)
+ return "null";
+ else
+ return str;
+ }
+
+
+ /**
+ * Formats the string <TT>str</TT> like the <TT>%s</TT> in the C <TT>printf</TT>
+ * function. The <TT>fieldwidth</TT> argument gives the minimum length of the
+ * resulting string. If <TT>str</TT> is shorter than <TT>fieldwidth</TT>, it is
+ * left-padded with spaces. If <TT>fieldwidth</TT> is negative, the string
+ * is right-padded with spaces if necessary. The {@link String} will never
+ * be truncated. If <TT>str</TT> is null, it calls
+ * {@link #s(int, String) s} <TT>(fieldwidth, ``null'')</TT>.
+ * The <TT>fieldwidth</TT> argument
+ * has the same effect for the other methods in this class.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param str the string to process
+ *
+ * @return the same string padded with spaces if necessary
+ *
+ */
+ public static String s (int fieldwidth, String str) {
+ if (str == null)
+ return s (fieldwidth, "null");
+
+ int fw = Math.abs (fieldwidth);
+ if (str.length() < fw) {
+ // We have to pad with spaces
+ StringBuffer buf = new StringBuffer();
+ int sl = str.length();
+ for (int i = 0; i < fw-sl; i++)
+ buf.append (' ');
+ // Add the spaces before or after
+ return fieldwidth >= 0 ? buf.toString() + str
+ : str + buf.toString();
+ }
+ else
+ return str;
+ }
+
+
+ /**
+ * Same as {@link #d(int,int,long) d} <TT>(0, 1, x)</TT>.
+ *
+ * @param x the string to process
+ *
+ * @return the same string, padded with spaces or zeros if appropriate
+ *
+ */
+ public static String d (long x) {
+ return d (0, 1, x);
+ }
+
+
+ /**
+ * Same as {@link #d(int,int,long) d} <TT>(fieldwidth, 1, x)</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param x the string to process
+ *
+ * @return the same string, padded with spaces or zeros if appropriate
+ *
+ */
+ public static String d (int fieldwidth, long x) {
+ return d (fieldwidth, 1, x);
+ }
+
+
+ /**
+ * Formats the long integer <TT>x</TT> into a string like <TT>%d</TT>
+ * in the C <TT>printf</TT> function. It converts its argument to decimal
+ * notation, <TT>precision</TT> gives the minimum
+ * number of digits that must appear; if the converted
+ * value requires fewer digits, it is padded on the
+ * left with zeros. When
+ * zero is printed with an explicit precision 0, the
+ * output is empty.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param precision number of digits in the returned string
+ *
+ * @param x the string to process
+ *
+ * @return the same string, padded with spaces or zeros if appropriate
+ *
+ */
+ public static String d (int fieldwidth, int precision, long x) {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (precision == 0 && x == 0)
+ return s (fieldwidth, "");
+
+ nf.setGroupingUsed (false);
+ nf.setMinimumIntegerDigits (precision);
+ nf.setMaximumFractionDigits (0); // will also set the min to 0
+ return s (fieldwidth, nf.format (x));
+ }
+
+
+ /**
+ * Same as {@link #d(int,int,long) d} <TT>(0, 1, x)</TT>.
+ *
+ * @param x the value to be processed
+ *
+ * @return the string resulting from the conversion
+ *
+ */
+ public static String format (long x) {
+ return d (0, 1, x);
+ }
+
+
+ /**
+ * Converts a long integer to a {@link String} with a minimum length
+ * of <TT>fieldwidth</TT>, the result is right-padded with spaces if
+ * necessary but it is not truncated. If only one argument is specified,
+ * a <TT>fieldwidth</TT> of 0 is assumed.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param x the value to be processed
+ *
+ * @return the string resulting from the conversion
+ *
+ */
+ public static String format (int fieldwidth, long x) {
+ return d (fieldwidth, 1, x);
+ }
+
+
+ /**
+ * Same as {@link #formatBase(int,int,long) formatBase} <TT>(0, b, x)</TT>.
+ *
+ * @param b the base used for conversion
+ *
+ * @param x the value to be processed
+ *
+ * @return a string representing <TT>x</TT> in base <TT>b</TT>
+ *
+ */
+ public static String formatBase (int b, long x) {
+ return formatBase (0, b, x);
+ }
+
+
+ /**
+ * Converts the integer <TT>x</TT> to a {@link String} representation in base
+ * <TT>b</TT>. Restrictions: <SPAN CLASS="MATH">2 <= </SPAN> <TT>b</TT> <SPAN CLASS="MATH"> <= 10</SPAN>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param b the base used for conversion
+ *
+ * @param x the value to be processed
+ *
+ * @return a string representing <TT>x</TT> in base <TT>b</TT>
+ *
+ */
+ public static String formatBase (int fieldwidth, int b, long x) {
+ boolean neg = false; // insert a '-' if true
+ if (b < 2 || b > 10)
+ throw new IllegalArgumentException ("base must be between 2 and 10.");
+
+ if (x < 0) {
+ neg = true;
+ x = -x;
+ } else {
+ if (x == 0)
+ return "0";
+
+ neg = false;
+ }
+ StringBuffer sb = new StringBuffer();
+ while (x > 0) {
+ sb.insert(0, x % b);
+ x = x/b;
+ }
+ if (neg)
+ sb.insert(0, '-');
+ return s (fieldwidth, sb.toString());
+ }
+
+
+ /**
+ * Same as {@link #E(int,int,double) E} <TT>(0, 6, x)</TT>.
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String E (double x) {
+ return E (0, 6, x);
+ }
+
+
+ /**
+ * Same as {@link #E(int,int,double) E} <TT>(fieldwidth, 6, x)</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String E (int fieldwidth, double x) {
+ return E (fieldwidth, 6, x);
+ }
+
+
+ /**
+ * Formats a double-precision number <TT>x</TT> like <TT>%E</TT> in C <TT>printf</TT>. The double argument is rounded and converted in the
+ * style <TT>[-]d.dddE+-dd</TT> where there is one digit before
+ * the decimal-point character and the number of digits
+ * after it is equal to the precision;
+ * if the precision is 0, no decimal-point character appears.
+ * The exponent always
+ * contains at least two digits; if the value is zero,
+ * the exponent is <TT>00</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param precision number of digits after the decimal point
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String E (int fieldwidth, int precision, double x) {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "") + "Infinite");
+
+ DecimalFormat df;
+ if (precision >= dfe.length || dfe[precision] == null) {
+ // We need to create one pattern per precision value
+ StringBuffer pattern = new StringBuffer ("0.");
+ for (int i = 0; i < precision; i++)
+ pattern.append ("0");
+ pattern.append ("E00");
+ df = new DecimalFormat (pattern.toString(), dfs);
+ df.setGroupingUsed (false);
+ if (precision < dfe.length)
+ dfe[precision] = df;
+ }
+ else
+ df = dfe[precision];
+ String res = df.format (x);
+ // DecimalFormat doesn't add the + sign before the value of
+ // the exponent.
+ int exppos = res.indexOf ('E');
+ if (exppos != -1 && res.charAt (exppos+1) != '-')
+ res = res.substring (0, exppos+1) + "+" + res.substring (exppos+1);
+ return s (fieldwidth, res);
+ }
+
+
+ /**
+ * Same as {@link #e(int,int,double) e} <TT>(0, 6, x)</TT>.
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String e (double x) {
+ return e (0, 6, x);
+ }
+
+
+ /**
+ * Same as {@link #e(int,int,double) e} <TT>(fieldwidth, 6, x)</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String e (int fieldwidth, double x) {
+ return e (fieldwidth, 6, x);
+ }
+
+
+ /**
+ * The same as <TT>E</TT>, except that <TT>`e'</TT> is used as the exponent
+ * character instead of <TT>`E'</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param precision number of digits after the decimal point
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String e (int fieldwidth, int precision, double x) {
+ String res = E (fieldwidth, precision, x);
+ int exppos = res.indexOf ('E');
+ return exppos == -1 ? res : res.substring (0,
+ exppos) + 'e' + res.substring (exppos+1);
+ }
+
+
+ /**
+ * Same as {@link #f(int,int,double) f} <TT>(0, 6, x)</TT>.
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String f (double x) {
+ return f (0, 6, x);
+ }
+
+
+ /**
+ * Same as {@link #f(int,int,double) f} <TT>(fieldwidth, 6, x)</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String f (int fieldwidth, double x) {
+ return f (fieldwidth, 6, x);
+ }
+
+
+ /**
+ * Formats the double-precision <TT>x</TT> into a string like
+ * <TT>%f</TT> in C <TT>printf</TT>.
+ * The argument is rounded and converted to
+ * decimal notation in the style <TT>[-]ddd.ddd</TT>, where the
+ * number of digits after the decimal-point character
+ * is equal to the precision specification.
+ * If the precision is explicitly 0, no decimal-point
+ * character appears. If a decimal point appears, at least
+ * one digit appears before it.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param precision number of digits after the decimal point
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String f (int fieldwidth, int precision, double x) {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+
+ nf.setGroupingUsed (false);
+ nf.setMinimumIntegerDigits (1);
+ nf.setMinimumFractionDigits (precision);
+ nf.setMaximumFractionDigits (precision);
+ return s (fieldwidth, nf.format (x));
+ }
+
+
+ /**
+ * Same as {@link #G(int,int,double) G} <TT>(0, 6, x)</TT>.
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String G (double x) {
+ return G (0, 6, x);
+ }
+
+
+ /**
+ * Same as {@link #G(int,int,double) G} <TT>(fieldwidth, 6, x)</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String G (int fieldwidth, double x) {
+ return G (fieldwidth, 6, x);
+ }
+
+
+ /**
+ * Formats the double-precision <TT>x</TT> into a string like
+ * <TT>%G</TT> in C <TT>printf</TT>.
+ * The argument is converted in style <TT>%f</TT> or <TT>%E</TT>.
+ * <TT>precision</TT> specifies
+ * the number of significant digits. If it is
+ * 0, it is treated as 1. Style <TT>%E</TT> is used if the
+ * exponent from its conversion is less than <SPAN CLASS="MATH">-4</SPAN> or
+ * greater than or equal to <TT>precision</TT>. Trailing
+ * zeros are removed from the fractional part of the
+ * result; a decimal point appears only if it is followed
+ * by at least one digit.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param precision number of significant digits
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String G (int fieldwidth, int precision, double x) {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (precision == 0)
+ precision = 1;
+
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+
+ // Calculate the scientific notation format.
+ // We cannot use E because it can make trailing zeros
+ // that must be removed afterward.
+ DecimalFormat df;
+ if (precision >= dfg.length || dfg[precision] == null) {
+ StringBuffer pattern = new StringBuffer ("0.");
+ for (int i = 0; i < (precision-1); i++)
+ pattern.append ("#"); // Do not show trailing zeros
+ pattern.append ("E00");
+ df = new DecimalFormat (pattern.toString(), dfs);
+ df.setGroupingUsed (false);
+ if (precision < dfg.length)
+ dfg[precision] = df;
+ }
+ else
+ df = dfg[precision];
+ String res = df.format (x);
+
+ int exppos = res.indexOf ('E');
+ if (exppos == -1)
+ return res;
+ int expval = Integer.parseInt (res.substring (exppos+1));
+ if (expval < -4 || expval >= precision) {
+ // add the plus sign for the exponent if necessary.
+ if (res.charAt (exppos+1) != '-')
+ return s (fieldwidth, res.substring (0, exppos+1) + "+" +
+ res.substring (exppos+1));
+ else
+ return s (fieldwidth, res);
+ }
+
+ // Calculate the decimal notation format
+ nf.setGroupingUsed (false);
+ nf.setMinimumIntegerDigits (1);
+ nf.setMinimumFractionDigits (0);
+ // We need the number of digits after the decimal point to
+ // to get precisions significant digits.
+ // The integer part of x contains at most precision digits.
+ // If that was not true, expval would be greater than precision.
+ // If expval=0, we have a number of the form 1.234...
+ // To have precisions significant digits, we need precision-1 digits.
+ // If expval<0, x<1 and we need -expval additionnal
+ // decimal digits.
+ // If expval>0, we need less decimal digits.
+ nf.setMaximumFractionDigits (precision-expval-1);
+ res = nf.format (x);
+ return s (fieldwidth, res);
+ }
+
+
+ /**
+ * Same as {@link #g(int,int,double) g} <TT>(0, 6, x)</TT>.
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String g (double x) {
+ return g (0, 6, x);
+ }
+
+
+ /**
+ * Same as {@link #g(int,int,double) g} <TT>(fieldwidth, 6, x)</TT>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String g (int fieldwidth, double x) {
+ return g (fieldwidth, 6, x);
+ }
+
+
+ /**
+ * The same as <TT>G</TT>, except that <TT>`e'</TT> is used in the scientific
+ * notation.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param precision number of significant digits
+ *
+ * @param x the value to be converted to string
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String g (int fieldwidth, int precision, double x) {
+ String res = G (fieldwidth, precision, x);
+ int exppos = res.indexOf ('E');
+ return exppos == -1 ? res :
+ res.substring (0, exppos) + 'e' + res.substring (exppos+1);
+ }
+
+
+ /**
+ * Returns a {@link String} containing <TT>x</TT>. Uses a total of at least
+ * <TT>fieldwidth</TT> positions (including the sign and point when they appear),
+ * <TT>accuracy</TT> digits after the decimal point and at least <TT>precision</TT>
+ * significant digits. <TT>accuracy</TT> and <TT>precision</TT> must be strictly
+ * smaller than <TT>fieldwidth</TT>. The number is rounded if necessary.
+ * If there is not enough space to format the number in decimal notation
+ * with at least <TT>precision</TT> significant digits (<TT>accuracy</TT> or
+ * <TT>fieldwidth</TT> is too small), it will be converted to scientific
+ * notation with at least <TT>precision</TT> significant digits.
+ * In that case, <TT>fieldwidth</TT> is increased if necessary.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param accuracy number of digits after the decimal point
+ *
+ * @param precision number of significant digits
+ *
+ * @param x the value to be processed
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String format (int fieldwidth, int accuracy, int precision,
+ double x) {
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x))
+ return f (fieldwidth, accuracy, x);
+ // Use scientific notation
+ else {
+ String S = E (fieldwidth, precision - 1 , x);
+ return processExp (S);
+ }
+ }
+
+ private static boolean canUseDecimalNotation (int fieldwidth, int accuracy,
+ int precision, double x) {
+ // Le nombre de positions occupees par la partie entiere de x
+ int PosEntier = 0;
+ // Le nombre de chiffres significatifs avant le point
+ int EntierSign;
+ // La position de l'exposant dans le string S et la longueur de S
+ int Neg = 0;
+
+ if (x == 0.0)
+ EntierSign = 1;
+ else {
+ EntierSign = PosEntier = (int)Math.floor (
+ Math.log10 (Math.abs (x)) + 1);
+ if (x < 0.0)
+ Neg = 1;
+ }
+ if (EntierSign <= 0)
+ PosEntier = 1;
+ return x == 0.0 || (((EntierSign + accuracy) >= precision) &&
+ (fieldwidth >= (PosEntier + accuracy + Neg + 1)));
+ }
+
+ private static int getMinAccuracy (double x) {
+ if (Math.abs (x) >= 1 || x == 0)
+ return 0;
+ else
+ return -(int)Math.floor (Math.log10 (Math.abs (x)));
+ }
+
+ private static String processExp (String s) {
+ int p = s.indexOf ("E+0");
+ if (p == -1)
+ p = s.indexOf ("E-0");
+
+ // remove the 0 in E-0 and in E+0
+ if (p != -1)
+ s = " " + s.substring (0, p + 2) + s.substring (p + 3);
+
+ p = s.indexOf (".E");
+ if (p != -1)
+ s = " " + s.substring (0, p) + s.substring (p + 1);
+ return s;
+ }
+
+
+ /**
+ * This method is equivalent to
+ * {@link #format(int,int,int,double) format}, except it formats the given
+ * value for the locale <TT>locale</TT>.
+ *
+ * @param locale the locale being used for formatting
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param accuracy number of digits after the decimal point
+ *
+ * @param precision number of significant digits
+ *
+ * @param x the value to be processed
+ *
+ * @return the converted value as a string
+ *
+ */
+ public static String format (Locale locale, int fieldwidth, int accuracy,
+ int precision, double x) {
+ Formatter fmt = new Formatter (locale);
+ if (Double.isNaN (x))
+ return fmt.format ("%" + fieldwidth + "s", "NaN").toString();
+ if (Double.isInfinite (x))
+ return fmt.format ("%" + fieldwidth + "s", (x < 0 ? "-" : "" ) + "Infinite").toString();
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x))
+ return fmt.format ("%" + fieldwidth + "." + accuracy + "f", x).toString();
+ // Use scientific notation
+ else {
+ String S = fmt.format ("%" + fieldwidth + "." + (precision - 1) + "E", x).toString();
+ return processExp (S);
+ }
+ }
+
+
+ /**
+ * Converts <SPAN CLASS="MATH"><I>x</I></SPAN> to a String representation in base <SPAN CLASS="MATH"><I>b</I></SPAN> using formatting similar
+ * to the <SPAN CLASS="MATH"><I>f</I></SPAN> methods. Uses a total of at least <TT>fieldwidth</TT> positions
+ * (including the sign and point when they appear) and <TT>accuracy</TT> digits
+ * after the decimal point. If <TT>fieldwidth</TT> is negative, the number is
+ * printed left-justified, otherwise right-justified.
+ * Restrictions:
+ * <SPAN CLASS="MATH">2 <= <I>b</I> <= 10</SPAN> and
+ * <SPAN CLASS="MATH">| <I>x</I>| < 2<SUP>63</SUP></SPAN>.
+ *
+ * @param fieldwidth minimum length of the returned string
+ *
+ * @param accuracy number of digits after the decimal point
+ *
+ * @param b base
+ *
+ * @param x the value to be processed
+ *
+ * @return the converted value as a string
+ *
+ *
+ */
+ public static String formatBase (int fieldwidth, int accuracy, int b,
+ double x) {
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+ if (0. == x || -0. == x)
+ return s (fieldwidth, "0");
+ if (Math.abs(x) >= Num.TWOEXP[63])
+ throw new UnsupportedOperationException (" |x| >= 2^63");
+
+ long n = (long) x;
+ String mant = formatBase(-1, b, n);
+ if (n == x)
+ return s (fieldwidth, mant);
+ if (n == 0) {
+ if (x < 0) {
+ mant = "-0";
+ } else
+ mant = "0";
+ }
+ // round before printing
+ if (x > 0)
+ x += 0.5*Math.pow(b, -accuracy - 1);
+ else if (x < 0)
+ x -= 0.5*Math.pow(b, -accuracy - 1);
+ x -= n;
+ if (x < 0)
+ x = -x;
+
+ StringBuffer frac = new StringBuffer(".");
+ long y;
+ int j;
+ for (j = 0; j < accuracy; ++j) {
+ x *= b;
+ y = (long) x;
+ frac.append(y);
+ x -= y;
+ if (x == 0.)
+ break;
+ }
+
+ StringBuffer number = new StringBuffer(mant);
+ number.append(frac);
+
+ // remove trailing spaces and 0
+ j = number.length() - 1;
+ while (j > 0 && (number.charAt(j) == '0' || number.charAt(j) == ' ' )) {
+ number.deleteCharAt(j);
+ j--;
+ }
+
+ return s (fieldwidth, number.toString());
+ }
+
+ // Interface CharSequence
+ public char charAt (int index) {
+ return sb.charAt (index);
+ }
+
+ public int length() {
+ return sb.length();
+ }
+
+ public CharSequence subSequence (int start, int end) {
+ return sb.subSequence (start, end);
+ }
+
+ // Interface Appendable
+ public Appendable append (CharSequence csq) {
+ return sb.append (csq);
+ }
+
+ public Appendable append (CharSequence csq, int start, int end) {
+ return sb.append (csq, start, end);
+ }
+
+
+ /**
+ * Stores a string containing <TT>x</TT> into <TT>res[0]</TT>, and
+ * a string containing <TT>error</TT> into <TT>res[1]</TT>, both strings being
+ * formatted with the same notation.
+ * Uses a total of at least
+ * <TT>fieldwidth</TT> positions (including the sign and point when they appear)
+ * for <TT>x</TT>, <TT>fieldwidtherr</TT> positions for <TT>error</TT>,
+ * <TT>accuracy</TT> digits after the decimal point and at least <TT>precision</TT>
+ * significant digits. <TT>accuracy</TT> and <TT>precision</TT> must be strictly
+ * smaller than <TT>fieldwidth</TT>. The numbers are rounded if necessary.
+ * If there is not enough space to format <TT>x</TT> in decimal notation
+ * with at least <TT>precision</TT> significant digits (<TT>accuracy</TT> or
+ * <TT>fieldwidth</TT> are too small), it will be converted to scientific
+ * notation with at least <TT>precision</TT> significant digits.
+ * In that case, <TT>fieldwidth</TT> is increased if necessary, and
+ * the error is also formatted in scientific notation.
+ *
+ * @param fieldwidth minimum length of the value string
+ *
+ * @param fieldwidtherr minimum length of the error string
+ *
+ * @param accuracy number of digits after the decimal point for the value and error
+ *
+ * @param precision number of significant digits for the value
+ *
+ * @param x the value to be processed
+ *
+ * @param error the error on the value to be processed
+ *
+ * @param res an array that will be filled with the formatted value and formatted error
+ *
+ *
+ */
+ public static void formatWithError (int fieldwidth, int fieldwidtherr,
+ int accuracy, int precision, double x, double error, String[] res) {
+ if (res.length != 2)
+ throw new IllegalArgumentException ("The given res array must contain two elements");
+ if (Double.isNaN (x)) {
+ res[0] = s (fieldwidth, "NaN");
+ res[1] = s (fieldwidtherr, "");
+ return;
+ }
+ if (Double.isInfinite (x)) {
+ res[0] = s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+ res[1] = s (fieldwidtherr, "");
+ return;
+ }
+ if (accuracy < 0)
+ accuracy = 0;
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ res[0] = f (fieldwidth, accuracy, x);
+ res[1] = f (fieldwidtherr, accuracy, error);
+ }
+ // Use scientific notation
+ else {
+ res[0] = processExp (E (fieldwidth, precision - 1, x));
+ int xExp = x == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (x)));
+ int errorExp = error == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (error)));
+ int errorPrecision = precision - 1 - (xExp - errorExp);
+ if (errorPrecision < 0)
+ errorPrecision = 0;
+ res[1] = processExp (E (fieldwidtherr, errorPrecision, error));
+ }
+ }
+
+
+ /**
+ * Stores a string containing <TT>x</TT> into <TT>res[0]</TT>, and
+ * a string containing <TT>error</TT> into <TT>res[1]</TT>, both strings being
+ * formatted with the same notation.
+ * This calls {@link #formatWithError(int,int,int,int,double,double,String[]) formatWithError} with
+ * the minimal accuracy for which the formatted string for <TT>error</TT> is non-zero.
+ * If <TT>error</TT> is 0, the accuracy is 0.
+ * If this minimal accuracy causes the strings to be formatted using scientific
+ * notation, this method increases the accuracy until the decimal notation can be used.
+ *
+ * @param fieldwidth minimum length of the value string
+ *
+ * @param fieldwidtherr minimum length of the error string
+ *
+ * @param precision number of significant digits for the value
+ *
+ * @param x the value to be processed
+ *
+ * @param error the error on the value to be processed
+ *
+ * @param res an array that will be filled with the formatted value and formatted error
+ *
+ *
+ */
+ public static void formatWithError (int fieldwidth, int fieldwidtherr,
+ int precision, double x, double error, String[] res) {
+ int accuracy = getMinAccuracy (error);
+ if (!canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ int posEntier = (int)Math.floor (Math.log (Math.abs (x)) / Math.log (10) + 1);
+ if (posEntier < 0)
+ posEntier = 1;
+ int newAccuracy = precision - posEntier;
+ if (canUseDecimalNotation (fieldwidth, newAccuracy, precision, x))
+ accuracy = newAccuracy;
+ }
+ formatWithError (fieldwidth, fieldwidtherr, accuracy, precision, x, error, res);
+ }
+
+
+ /**
+ * This method is equivalent to
+ * {@link #formatWithError(int,int,int,double,double,String[]) formatWithError},
+ * except that it formats the given value and error for the
+ * locale <TT>locale</TT>.
+ *
+ * @param locale the locale being used
+ *
+ * @param fieldwidth minimum length of the value string
+ *
+ * @param fieldwidtherr minimum length of the error string
+ *
+ * @param accuracy number of digits after the decimal point for the value and error
+ *
+ * @param precision number of significant digits for the value
+ *
+ * @param x the value to be processed
+ *
+ * @param error the error on the value to be processed
+ *
+ * @param res an array that will be filled with the formatted value and formatted error
+ *
+ *
+ */
+ public static void formatWithError (Locale locale, int fieldwidth,
+ int fieldwidtherr, int accuracy, int precision, double x,
+ double error, String[] res) {
+ if (res.length != 2)
+ throw new IllegalArgumentException ("The given res array must contain two elements");
+ Formatter fmt = new Formatter (locale);
+ Formatter fmtErr = new Formatter (locale);
+ if (Double.isNaN (x)) {
+ res[0] = fmt.format ("%" + fieldwidth + "s", "NaN").toString();
+ res[1] = fmtErr.format ("%" + fieldwidtherr + "s", "").toString();
+ return;
+ }
+ if (Double.isInfinite (x)) {
+ res[0] = fmt.format ("%" + fieldwidth + "s", (x < 0 ? "-" : "" ) + "Infinite").toString();
+ res[1] = fmtErr.format ("%" + fieldwidtherr + "s", "").toString();
+ return;
+ }
+ if (accuracy < 0)
+ accuracy = 0;
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ res[0] = fmt.format ("%" + fieldwidth + "." + accuracy + "f", x).toString();
+ res[1] = fmtErr.format ("%" + fieldwidtherr + "." + accuracy + "f", error).toString();
+ }
+ // Use scientific notation
+ else {
+ res[0] = processExp (fmt.format ("%" + fieldwidth + "." + (precision - 1) + "E", x).toString());
+ int xExp = x == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (x)));
+ int errorExp = error == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (error)));
+ int errorPrecision = precision - 1 - (xExp - errorExp);
+ if (errorPrecision < 0)
+ errorPrecision = 0;
+ res[1] = processExp (fmtErr.format
+ ("%" + fieldwidtherr + "." + errorPrecision + "E", error).toString());
+ }
+ }
+
+
+ /**
+ * This method is equivalent to
+ * {@link #formatWithError(int,int,int,double,double,String[]) formatWithError},
+ * except that it formats the given value and error for the
+ * locale <TT>locale</TT>.
+ *
+ * @param locale the locale being used
+ *
+ * @param fieldwidth minimum length of the value string
+ *
+ * @param fieldwidtherr minimum length of the error string
+ *
+ * @param precision number of significant digits for the value
+ *
+ * @param x the value to be processed
+ *
+ * @param error the error on the value to be processed
+ *
+ * @param res an array that will be filled with the formatted value and formatted error
+ *
+ */
+ public static void formatWithError (Locale locale, int fieldwidth,
+ int fieldwidtherr, int precision, double x, double error,
+ String[] res) {
+ int accuracy = getMinAccuracy (error);
+ if (!canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ int posEntier = (int)Math.floor (Math.log (Math.abs (x)) / Math.log (10) + 1);
+ if (posEntier < 0)
+ posEntier = 1;
+ int newAccuracy = precision - posEntier;
+ if (canUseDecimalNotation (fieldwidth, newAccuracy, precision, x))
+ accuracy = newAccuracy;
+ }
+ formatWithError (locale, fieldwidth, fieldwidtherr, accuracy, precision, x, error, res);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/PrintfFormat.tex b/source/umontreal/iro/lecuyer/util/PrintfFormat.tex
new file mode 100644
index 0000000..65e66f4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/PrintfFormat.tex
@@ -0,0 +1,1277 @@
+\defmodule {PrintfFormat}
+
+This class acts like a \class{StringBuffer} which defines new types
+of \texttt{append} methods.
+It defines certain functionalities of the ANSI C \texttt{printf}
+function that also can be accessed through static methods.
+The information given here is strongly inspired
+from the \texttt{man} page of the C \texttt{printf} function.
+
+Most methods of this class format numbers for the English US
+locale only. One can use the Java class \externalclass{java.util}{Formatter}
+for performing locale-independent formatting.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: PrintfFormat
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util; \begin{hide}
+
+import java.text.NumberFormat;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+import java.util.Formatter;
+\end{hide}
+
+
+public class PrintfFormat implements CharSequence, Appendable\begin{hide} {
+ private static NumberFormat nf = NumberFormat.getInstance (Locale.US);
+ private static DecimalFormatSymbols dfs =
+ new DecimalFormatSymbols (Locale.US);
+ private static final int NDEC = 50;
+ private static DecimalFormat[] dfe = new DecimalFormat[NDEC+1];
+ private static DecimalFormat[] dfg = new DecimalFormat[NDEC+1];
+ private StringBuffer sb;
+/*
+ private static void round (int b, StringBuffer num) {
+ // round a real number up if it is >= 0.5
+ // base is b
+
+ // round up the fractional part
+ int j = num.length() - 1;
+ String bm1 = String.valueOf (b - 1);
+ while (j >= 0 && num.charAt(j) == bm1.charAt(0)) {
+ num.deleteCharAt(j);
+ j--;
+ }
+
+ char c;
+ if (j < 0)
+ return;
+ if (num.charAt(j) != '.') {
+ c = num.charAt(j);
+ ++c;
+ num.replace(j, j + 1, Character.toString(c));
+ return;
+ }
+
+ // round up the integral part
+ j--;
+ while (j >= 0 && num.charAt(j) == bm1.charAt(0)) {
+ num.replace(j, j + 1, "0");
+ j--;
+ }
+
+ if (j < 0 || num.charAt(j) == '-') {
+ num.insert( j + 1, '1');
+ } else if (num.charAt(j) == ' ') {
+ num.replace(j, j + 1, "1");
+ } else {
+ c = num.charAt(j);
+ ++c;
+ num.replace(j, j + 1, Character.toString(c));
+ }
+ }*/ \end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constants}
+
+\begin{code}
+
+ public static final String NEWLINE\begin{hide} =
+ System.getProperty("line.separator");\end{hide}
+\end{code}
+\begin{tabb} End-of-line symbol or line separator. It is ``$\backslash$n''
+ for Unix/Linux, ``$\backslash$r$\backslash$n" for MS-DOS/MS-Windows, and
+ ``$\backslash$r'' for Apple OSX.
+\end{tabb}
+\begin{code}
+
+ public static final String LINE_SEPARATOR\begin{hide} =
+ System.getProperty("line.separator");\end{hide}
+\end{code}
+\begin{tabb} End-of-line symbol or line separator. Same as \texttt{NEWLINE}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+\begin{code}
+
+ public PrintfFormat()\begin{hide} {
+ sb = new StringBuffer();
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new buffer object containing an empty string.
+\end{tabb}
+\begin{code}
+
+ public PrintfFormat (int length)\begin{hide} {
+ sb = new StringBuffer (length);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new buffer object with an initial capacity of \texttt{length}.
+\end{tabb}
+\begin{htmlonly}
+ \param{length}{initial length of the buffer}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat (String str)\begin{hide} {
+ sb = new StringBuffer (str);
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new buffer object containing the initial string \texttt{str}.
+\end{tabb}
+\begin{htmlonly}
+ \param{str}{initial contents of the buffer}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+\begin{code}
+
+ public PrintfFormat append (String str)\begin{hide} {
+ sb.append (str);
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Appends \texttt{str} to the buffer.
+\end{tabb}
+\begin{htmlonly}
+ \param{str}{string to append to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (int fieldwidth, String str)\begin{hide} {
+ sb.append (s (fieldwidth, str));
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses the \method{s}{int,String} static method to append \texttt{str} to the buffer.
+ A minimum of \texttt{fieldwidth} characters will be used.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum number of characters that will be added to the buffer}
+ \param{str}{string to append to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (double x)\begin{hide} {
+ sb.append (x);
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Appends \texttt{x} to the buffer.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value being added to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (int fieldwidth, double x)\begin{hide} {
+ sb.append (f (fieldwidth, x));
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses the \method{f}{int,double} static method to append \texttt{x} to the buffer.
+ A minimum of \texttt{fieldwidth} characters will be used.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the converted string to be appended}
+ \param{x}{value to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (int fieldwidth, int precision, double x)\begin{hide} {
+ sb.append (f (fieldwidth, precision, x));
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses the \method{f}{int,int,double} static method to append \texttt{x} to the buffer.
+ A minimum of \texttt{fieldwidth} characters will be used with the given \texttt{precision}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the converted string to be appended}
+ \param{precision}{number of digits after the decimal point of the converted value}
+ \param{x}{value to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (int x)\begin{hide} {
+ sb.append (x);
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Appends \texttt{x} to the buffer.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (int fieldwidth, int x)\begin{hide} {
+ sb.append (d (fieldwidth, x));
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses the \method{d}{int,long} static method to append \texttt{x} to the buffer.
+ A minimum of \texttt{fieldwidth} characters will be used.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the converted string to be appended}
+ \param{x}{value to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (long x)\begin{hide} {
+ sb.append (x);
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Appends \texttt{x} to the buffer.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{value to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (int fieldwidth, long x)\begin{hide} {
+ sb.append (d (fieldwidth, x));
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses the \method{d}{int,long} static method to append \texttt{x} to the buffer.
+ A minimum of \texttt{fieldwidth} characters will be used.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the converted string to be appended}
+ \param{x}{value to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (int fieldwidth, int accuracy, int precision,
+ double x)\begin{hide} {
+ sb.append (format (fieldwidth, accuracy, precision, x));
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses the \method{format}{int,int,int,double}
+ static method with the same four arguments to append \texttt{x} to the buffer.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the converted string to be appended}
+ \param{accuracy}{number of digits after the decimal point}
+ \param{precision}{number of significant digits}
+ \param{x}{value to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public PrintfFormat append (char c)\begin{hide} {
+ sb.append (c);
+ return this;
+ }\end{hide}
+\end{code}
+\begin{tabb} Appends a single character to the buffer.
+\end{tabb}
+\begin{htmlonly}
+ \param{c}{character to be appended to the buffer}
+ \return{this object}
+\end{htmlonly}
+\begin{code}
+
+ public void clear()\begin{hide} {
+ sb.setLength (0);
+ }\end{hide}
+\end{code}
+\begin{tabb} Clears the contents of the buffer.
+\end{tabb}
+\begin{code}
+
+ public StringBuffer getBuffer()\begin{hide} {
+ return sb;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the \class{StringBuffer} associated with that object.
+\end{tabb}
+\begin{htmlonly}
+ \return{the internal \class{StringBuffer} object}
+\end{htmlonly}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ return sb.toString();
+ }\end{hide}
+\end{code}
+\begin{tabb} Converts the buffer into a \class{String}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the \class{String} conversion of the internal buffer}
+\end{htmlonly}
+\begin{code}
+
+ public static String s (String str)\begin{hide} {
+ if (str == null)
+ return "null";
+ else
+ return str;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Same as \method{s}{int,String}~\texttt{(0, str)}. If the string \texttt{str} is null,
+ it returns the string ``null''.
+\end{tabb}
+\begin{htmlonly}
+ \param{str}{the string to process}
+ \return{the same string}
+\end{htmlonly}
+\begin{code}
+
+ public static String s (int fieldwidth, String str)\begin{hide} {
+ if (str == null)
+ return s (fieldwidth, "null");
+
+ int fw = Math.abs (fieldwidth);
+ if (str.length() < fw) {
+ // We have to pad with spaces
+ StringBuffer buf = new StringBuffer();
+ int sl = str.length();
+ for (int i = 0; i < fw-sl; i++)
+ buf.append (' ');
+ // Add the spaces before or after
+ return fieldwidth >= 0 ? buf.toString() + str
+ : str + buf.toString();
+ }
+ else
+ return str;
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats the string \texttt{str} like the \texttt{\%s} in the C \texttt{printf}
+ function. The \texttt{fieldwidth} argument gives the minimum length of the
+ resulting string. If \texttt{str} is shorter than \texttt{fieldwidth}, it is
+left-padded with spaces. If \texttt{fieldwidth} is negative, the string
+is right-padded with spaces if necessary. The \class{String} will never
+be truncated. If \texttt{str} is null, it calls
+\method{s}{int, String}~\texttt{(fieldwidth, ``null'')}.
+%
+The \texttt{fieldwidth} argument
+has the same effect for the other methods in this class.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{str}{the string to process}
+ \return{the same string padded with spaces if necessary}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Integers}
+\begin{code}
+
+ public static String d (long x)\begin{hide} {
+ return d (0, 1, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{d}{int,int,long}~\texttt{(0, 1, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the string to process}
+ \return{the same string, padded with spaces or zeros if appropriate}
+\end{htmlonly}
+\begin{code}
+
+ public static String d (int fieldwidth, long x)\begin{hide} {
+ return d (fieldwidth, 1, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{d}{int,int,long}~\texttt{(fieldwidth, 1, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{x}{the string to process}
+ \return{the same string, padded with spaces or zeros if appropriate}
+\end{htmlonly}
+\begin{code}
+
+ public static String d (int fieldwidth, int precision, long x)\begin{hide} {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (precision == 0 && x == 0)
+ return s (fieldwidth, "");
+
+ nf.setGroupingUsed (false);
+ nf.setMinimumIntegerDigits (precision);
+ nf.setMaximumFractionDigits (0); // will also set the min to 0
+ return s (fieldwidth, nf.format (x));
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats the long integer \texttt{x} into a string like \texttt{\%d}
+in the C \texttt{printf} function. It converts its argument to decimal
+notation, \texttt{precision} gives the minimum
+number of digits that must appear; if the converted
+value requires fewer digits, it is padded on the
+left with zeros. When
+zero is printed with an explicit precision 0, the
+output is empty.
+%
+%If the one-argument form is used, a \texttt{fieldwidth} of 0
+%is assumed and a \texttt{precision} of 1 is used.
+%If the two-arguments method is used, a \texttt{precision} of 1 is assumed.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{precision}{number of digits in the returned string}
+ \param{x}{the string to process}
+ \return{the same string, padded with spaces or zeros if appropriate}
+\end{htmlonly}
+\begin{code}
+
+ public static String format (long x)\begin{hide} {
+ return d (0, 1, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{d}{int,int,long}~\texttt{(0, 1, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the value to be processed}
+ \return{the string resulting from the conversion}
+\end{htmlonly}
+\begin{code}
+
+ public static String format (int fieldwidth, long x)\begin{hide} {
+ return d (fieldwidth, 1, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Converts a long integer to a \class{String} with a minimum length
+of \texttt{fieldwidth}, the result is right-padded with spaces if
+necessary but it is not truncated. If only one argument is specified,
+a \texttt{fieldwidth} of 0 is assumed.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{x}{the value to be processed}
+ \return{the string resulting from the conversion}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatBase (int b, long x)\begin{hide} {
+ return formatBase (0, b, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{formatBase}{int,int,long}~\texttt{(0, b, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{b}{the base used for conversion}
+ \param{x}{the value to be processed}
+ \return{a string representing \texttt{x} in base \texttt{b}}
+\end{htmlonly}\begin{code}
+
+ public static String formatBase (int fieldwidth, int b, long x)\begin{hide} {
+ boolean neg = false; // insert a '-' if true
+ if (b < 2 || b > 10)
+ throw new IllegalArgumentException ("base must be between 2 and 10.");
+
+ if (x < 0) {
+ neg = true;
+ x = -x;
+ } else {
+ if (x == 0)
+ return "0";
+
+ neg = false;
+ }
+ StringBuffer sb = new StringBuffer();
+ while (x > 0) {
+ sb.insert(0, x % b);
+ x = x/b;
+ }
+ if (neg)
+ sb.insert(0, '-');
+ return s (fieldwidth, sb.toString());
+ }\end{hide}
+\end{code}
+\begin{tabb} Converts the integer \texttt{x} to a \class{String} representation in base
+\texttt{b}. Restrictions: $2\le$ \texttt{b} $\le 10$.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{b}{the base used for conversion}
+ \param{x}{the value to be processed}
+ \return{a string representing \texttt{x} in base \texttt{b}}
+\end{htmlonly}
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reals}
+\begin{code}
+
+ public static String E (double x)\begin{hide} {
+ return E (0, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{E}{int,int,double}~\texttt{(0, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String E (int fieldwidth, double x)\begin{hide} {
+ return E (fieldwidth, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{E}{int,int,double}~\texttt{(fieldwidth, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String E (int fieldwidth, int precision, double x)\begin{hide} {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "") + "Infinite");
+
+ DecimalFormat df;
+ if (precision >= dfe.length || dfe[precision] == null) {
+ // We need to create one pattern per precision value
+ StringBuffer pattern = new StringBuffer ("0.");
+ for (int i = 0; i < precision; i++)
+ pattern.append ("0");
+ pattern.append ("E00");
+ df = new DecimalFormat (pattern.toString(), dfs);
+ df.setGroupingUsed (false);
+ if (precision < dfe.length)
+ dfe[precision] = df;
+ }
+ else
+ df = dfe[precision];
+ String res = df.format (x);
+ // DecimalFormat doesn't add the + sign before the value of
+ // the exponent.
+ int exppos = res.indexOf ('E');
+ if (exppos != -1 && res.charAt (exppos+1) != '-')
+ res = res.substring (0, exppos+1) + "+" + res.substring (exppos+1);
+ return s (fieldwidth, res);
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats a double-precision number \texttt{x} like \texttt{\%E} in C {\tt
+printf}. The double argument is rounded and converted in the
+style \texttt{[-]d.dddE+-dd} where there is one digit before
+the decimal-point character and the number of digits
+after it is equal to the precision;
+if the precision is 0, no decimal-point character appears.
+The exponent always
+contains at least two digits; if the value is zero,
+the exponent is \texttt{00}.
+%
+%If the one-argument form is used, a \texttt{fieldwidth} of 0 and
+%a \texttt{precision} of 6 are used.
+%If the two-arguments form is used, a \texttt{precision} of 6 is assumed.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{precision}{number of digits after the decimal point}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String e (double x)\begin{hide} {
+ return e (0, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{e}{int,int,double}~\texttt{(0, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String e (int fieldwidth, double x)\begin{hide} {
+ return e (fieldwidth, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{e}{int,int,double}~\texttt{(fieldwidth, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String e (int fieldwidth, int precision, double x)\begin{hide} {
+ String res = E (fieldwidth, precision, x);
+ int exppos = res.indexOf ('E');
+ return exppos == -1 ? res : res.substring (0,
+ exppos) + 'e' + res.substring (exppos+1);
+ }\end{hide}
+\end{code}
+\begin{tabb} The same as \texttt{E}, except that \texttt{`e'} is used as the exponent
+character instead of \texttt{`E'}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{precision}{number of digits after the decimal point}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String f (double x)\begin{hide} {
+ return f (0, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{f}{int,int,double}~\texttt{(0, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String f (int fieldwidth, double x)\begin{hide} {
+ return f (fieldwidth, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{f}{int,int,double}~\texttt{(fieldwidth, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String f (int fieldwidth, int precision, double x)\begin{hide} {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+
+ nf.setGroupingUsed (false);
+ nf.setMinimumIntegerDigits (1);
+ nf.setMinimumFractionDigits (precision);
+ nf.setMaximumFractionDigits (precision);
+ return s (fieldwidth, nf.format (x));
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats the double-precision \texttt{x} into a string like
+\texttt{\%f} in C \texttt{printf}.
+The argument is rounded and converted to
+decimal notation in the style \texttt{[-]ddd.ddd}, where the
+number of digits after the decimal-point character
+is equal to the precision specification.
+If the precision is explicitly 0, no decimal-point
+character appears. If a decimal point appears, at least
+one digit appears before it.
+%
+%If the one-argument form is used, a \texttt{fieldwidth} of 0 and a
+%\texttt{precision} of 6 are used.
+%If the two-arguments form is used, a \texttt{precision} of 6 is assumed.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{precision}{number of digits after the decimal point}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String G (double x)\begin{hide} {
+ return G (0, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{G}{int,int,double}~\texttt{(0, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String G (int fieldwidth, double x)\begin{hide} {
+ return G (fieldwidth, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{G}{int,int,double}~\texttt{(fieldwidth, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String G (int fieldwidth, int precision, double x)\begin{hide} {
+ if (precision < 0)
+ throw new IllegalArgumentException ("precision must " +
+ "not be negative.");
+ if (precision == 0)
+ precision = 1;
+
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+
+ // Calculate the scientific notation format.
+ // We cannot use E because it can make trailing zeros
+ // that must be removed afterward.
+ DecimalFormat df;
+ if (precision >= dfg.length || dfg[precision] == null) {
+ StringBuffer pattern = new StringBuffer ("0.");
+ for (int i = 0; i < (precision-1); i++)
+ pattern.append ("#"); // Do not show trailing zeros
+ pattern.append ("E00");
+ df = new DecimalFormat (pattern.toString(), dfs);
+ df.setGroupingUsed (false);
+ if (precision < dfg.length)
+ dfg[precision] = df;
+ }
+ else
+ df = dfg[precision];
+ String res = df.format (x);
+
+ int exppos = res.indexOf ('E');
+ if (exppos == -1)
+ return res;
+ int expval = Integer.parseInt (res.substring (exppos+1));
+ if (expval < -4 || expval >= precision) {
+ // add the plus sign for the exponent if necessary.
+ if (res.charAt (exppos+1) != '-')
+ return s (fieldwidth, res.substring (0, exppos+1) + "+" +
+ res.substring (exppos+1));
+ else
+ return s (fieldwidth, res);
+ }
+
+ // Calculate the decimal notation format
+ nf.setGroupingUsed (false);
+ nf.setMinimumIntegerDigits (1);
+ nf.setMinimumFractionDigits (0);
+ // We need the number of digits after the decimal point to
+ // to get precisions significant digits.
+ // The integer part of x contains at most precision digits.
+ // If that was not true, expval would be greater than precision.
+ // If expval=0, we have a number of the form 1.234...
+ // To have precisions significant digits, we need precision-1 digits.
+ // If expval<0, x<1 and we need -expval additionnal
+ // decimal digits.
+ // If expval>0, we need less decimal digits.
+ nf.setMaximumFractionDigits (precision-expval-1);
+ res = nf.format (x);
+ return s (fieldwidth, res);
+ }\end{hide}
+\end{code}
+\begin{tabb} Formats the double-precision \texttt{x} into a string like
+\texttt{\%G} in C \texttt{printf}.
+The argument is converted in style \texttt{\%f} or \texttt{\%E}.
+\texttt{precision} specifies
+the number of significant digits. If it is
+0, it is treated as 1. Style \texttt{\%E} is used if the
+exponent from its conversion is less than $-4$ or
+greater than or equal to \texttt{precision}. Trailing
+zeros are removed from the fractional part of the
+result; a decimal point appears only if it is followed
+by at least one digit.
+%
+%If the one-argument form is used, a \texttt{fieldwidth} of 0 and
+%a \texttt{precision} of 6 are used.
+%If the two-arguments form is used, a \texttt{precision} of 6 is assumed.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{precision}{number of significant digits}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String g (double x)\begin{hide} {
+ return g (0, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{g}{int,int,double}~\texttt{(0, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String g (int fieldwidth, double x)\begin{hide} {
+ return g (fieldwidth, 6, x);
+ }\end{hide}
+\end{code}
+\begin{tabb} Same as \method{g}{int,int,double}~\texttt{(fieldwidth, 6, x)}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String g (int fieldwidth, int precision, double x)\begin{hide} {
+ String res = G (fieldwidth, precision, x);
+ int exppos = res.indexOf ('E');
+ return exppos == -1 ? res :
+ res.substring (0, exppos) + 'e' + res.substring (exppos+1);
+ }\end{hide}
+\end{code}
+\begin{tabb} The same as \texttt{G}, except that \texttt{`e'} is used in the scientific
+notation.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{precision}{number of significant digits}
+ \param{x}{the value to be converted to string}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String format (int fieldwidth, int accuracy, int precision,
+ double x)\begin{hide} {
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x))
+ return f (fieldwidth, accuracy, x);
+ // Use scientific notation
+ else {
+ String S = E (fieldwidth, precision - 1 , x);
+ return processExp (S);
+ }
+ }
+
+ private static boolean canUseDecimalNotation (int fieldwidth, int accuracy,
+ int precision, double x) {
+ // Le nombre de positions occupees par la partie entiere de x
+ int PosEntier = 0;
+ // Le nombre de chiffres significatifs avant le point
+ int EntierSign;
+ // La position de l'exposant dans le string S et la longueur de S
+ int Neg = 0;
+
+ if (x == 0.0)
+ EntierSign = 1;
+ else {
+ EntierSign = PosEntier = (int)Math.floor (
+ Math.log10 (Math.abs (x)) + 1);
+ if (x < 0.0)
+ Neg = 1;
+ }
+ if (EntierSign <= 0)
+ PosEntier = 1;
+ return x == 0.0 || (((EntierSign + accuracy) >= precision) &&
+ (fieldwidth >= (PosEntier + accuracy + Neg + 1)));
+ }
+
+ private static int getMinAccuracy (double x) {
+ if (Math.abs (x) >= 1 || x == 0)
+ return 0;
+ else
+ return -(int)Math.floor (Math.log10 (Math.abs (x)));
+ }
+
+ private static String processExp (String s) {
+ int p = s.indexOf ("E+0");
+ if (p == -1)
+ p = s.indexOf ("E-0");
+
+ // remove the 0 in E-0 and in E+0
+ if (p != -1)
+ s = " " + s.substring (0, p + 2) + s.substring (p + 3);
+
+ p = s.indexOf (".E");
+ if (p != -1)
+ s = " " + s.substring (0, p) + s.substring (p + 1);
+ return s;
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Returns a \class{String} containing \texttt{x}. Uses a total of at least
+ \texttt{fieldwidth} positions (including the sign and point when they appear),
+ \texttt{accuracy} digits after the decimal point and at least \texttt{precision}
+ significant digits. \texttt{accuracy} and \texttt{precision} must be strictly
+ smaller than \texttt{fieldwidth}. The number is rounded if necessary.
+ If there is not enough space to format the number in decimal notation
+ with at least \texttt{precision} significant digits (\texttt{accuracy} or
+ \texttt{fieldwidth} is too small), it will be converted to scientific
+ notation with at least \texttt{precision} significant digits.
+ In that case, \texttt{fieldwidth} is increased if necessary.
+ \end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{accuracy}{number of digits after the decimal point}
+ \param{precision}{number of significant digits}
+ \param{x}{the value to be processed}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String format (Locale locale, int fieldwidth, int accuracy,
+ int precision, double x)\begin{hide} {
+ Formatter fmt = new Formatter (locale);
+ if (Double.isNaN (x))
+ return fmt.format ("%" + fieldwidth + "s", "NaN").toString();
+ if (Double.isInfinite (x))
+ return fmt.format ("%" + fieldwidth + "s", (x < 0 ? "-" : "" ) + "Infinite").toString();
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x))
+ return fmt.format ("%" + fieldwidth + "." + accuracy + "f", x).toString();
+ // Use scientific notation
+ else {
+ String S = fmt.format ("%" + fieldwidth + "." + (precision - 1) + "E", x).toString();
+ return processExp (S);
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ This method is equivalent to
+ \method{format}{int,int,int,double}, except it formats the given
+ value for the locale \texttt{locale}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{locale}{the locale being used for formatting}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{accuracy}{number of digits after the decimal point}
+ \param{precision}{number of significant digits}
+ \param{x}{the value to be processed}
+ \return{the converted value as a string}
+\end{htmlonly}
+\begin{code}
+
+ public static String formatBase (int fieldwidth, int accuracy, int b,
+ double x)\begin{hide} {
+ if (Double.isNaN (x))
+ return s (fieldwidth, "NaN");
+ if (Double.isInfinite (x))
+ return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+ if (0. == x || -0. == x)
+ return s (fieldwidth, "0");
+ if (Math.abs(x) >= Num.TWOEXP[63])
+ throw new UnsupportedOperationException (" |x| >= 2^63");
+
+ long n = (long) x;
+ String mant = formatBase(-1, b, n);
+ if (n == x)
+ return s (fieldwidth, mant);
+ if (n == 0) {
+ if (x < 0) {
+ mant = "-0";
+ } else
+ mant = "0";
+ }
+ // round before printing
+ if (x > 0)
+ x += 0.5*Math.pow(b, -accuracy - 1);
+ else if (x < 0)
+ x -= 0.5*Math.pow(b, -accuracy - 1);
+ x -= n;
+ if (x < 0)
+ x = -x;
+
+ StringBuffer frac = new StringBuffer(".");
+ long y;
+ int j;
+ for (j = 0; j < accuracy; ++j) {
+ x *= b;
+ y = (long) x;
+ frac.append(y);
+ x -= y;
+ if (x == 0.)
+ break;
+ }
+
+ StringBuffer number = new StringBuffer(mant);
+ number.append(frac);
+
+ // remove trailing spaces and 0
+ j = number.length() - 1;
+ while (j > 0 && (number.charAt(j) == '0' || number.charAt(j) == ' ' )) {
+ number.deleteCharAt(j);
+ j--;
+ }
+
+ return s (fieldwidth, number.toString());
+ }\end{hide}
+\end{code}
+\begin{tabb}
+Converts $x$ to a String representation in base $b$ using formatting similar
+to the $f$ methods. Uses a total of at least \texttt{fieldwidth} positions
+(including the sign and point when they appear) and \texttt{accuracy} digits
+ after the decimal point. If \texttt{fieldwidth} is negative, the number is
+ printed left-justified, otherwise right-justified.
+ Restrictions: $2 \le b \le 10$ and $|x| < 2^{63}$.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the returned string}
+ \param{accuracy}{number of digits after the decimal point}
+ \param{b}{base}
+ \param{x}{the value to be processed}
+ \return{the converted value as a string}
+\end{htmlonly}
+%
+\begin{hide}\begin{code}
+ // Interface CharSequence
+ public char charAt (int index) {
+ return sb.charAt (index);
+ }
+
+ public int length() {
+ return sb.length();
+ }
+
+ public CharSequence subSequence (int start, int end) {
+ return sb.subSequence (start, end);
+ }
+
+ // Interface Appendable
+ public Appendable append (CharSequence csq) {
+ return sb.append (csq);
+ }
+
+ public Appendable append (CharSequence csq, int start, int end) {
+ return sb.append (csq, start, end);
+ }
+\end{code}\end{hide}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Intervals}
+\begin{code}
+
+ public static void formatWithError (int fieldwidth, int fieldwidtherr,
+ int accuracy, int precision, double x, double error, String[] res)\begin{hide} {
+ if (res.length != 2)
+ throw new IllegalArgumentException ("The given res array must contain two elements");
+ if (Double.isNaN (x)) {
+ res[0] = s (fieldwidth, "NaN");
+ res[1] = s (fieldwidtherr, "");
+ return;
+ }
+ if (Double.isInfinite (x)) {
+ res[0] = s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
+ res[1] = s (fieldwidtherr, "");
+ return;
+ }
+ if (accuracy < 0)
+ accuracy = 0;
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ res[0] = f (fieldwidth, accuracy, x);
+ res[1] = f (fieldwidtherr, accuracy, error);
+ }
+ // Use scientific notation
+ else {
+ res[0] = processExp (E (fieldwidth, precision - 1, x));
+ int xExp = x == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (x)));
+ int errorExp = error == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (error)));
+ int errorPrecision = precision - 1 - (xExp - errorExp);
+ if (errorPrecision < 0)
+ errorPrecision = 0;
+ res[1] = processExp (E (fieldwidtherr, errorPrecision, error));
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Stores a string containing \texttt{x} into \texttt{res[0]}, and
+ a string containing \texttt{error} into \texttt{res[1]}, both strings being
+ formatted with the same notation.
+ Uses a total of at least
+ \texttt{fieldwidth} positions (including the sign and point when they appear)
+ for \texttt{x}, \texttt{fieldwidtherr} positions for \texttt{error},
+ \texttt{accuracy} digits after the decimal point and at least \texttt{precision}
+ significant digits. \texttt{accuracy} and \texttt{precision} must be strictly
+ smaller than \texttt{fieldwidth}. The numbers are rounded if necessary.
+ If there is not enough space to format \texttt{x} in decimal notation
+ with at least \texttt{precision} significant digits (\texttt{accuracy} or
+ \texttt{fieldwidth} are too small), it will be converted to scientific
+ notation with at least \texttt{precision} significant digits.
+ In that case, \texttt{fieldwidth} is increased if necessary, and
+ the error is also formatted in scientific notation.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the value string}
+ \param{fieldwidtherr}{minimum length of the error string}
+ \param{accuracy}{number of digits after the decimal point for the value and error}
+ \param{precision}{number of significant digits for the value}
+ \param{x}{the value to be processed}
+ \param{error}{the error on the value to be processed}
+ \param{res}{an array that will be filled with the formatted value and formatted error}
+\end{htmlonly}
+\begin{code}
+
+ public static void formatWithError (int fieldwidth, int fieldwidtherr,
+ int precision, double x, double error, String[] res)\begin{hide} {
+ int accuracy = getMinAccuracy (error);
+ if (!canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ int posEntier = (int)Math.floor (Math.log (Math.abs (x)) / Math.log (10) + 1);
+ if (posEntier < 0)
+ posEntier = 1;
+ int newAccuracy = precision - posEntier;
+ if (canUseDecimalNotation (fieldwidth, newAccuracy, precision, x))
+ accuracy = newAccuracy;
+ }
+ formatWithError (fieldwidth, fieldwidtherr, accuracy, precision, x, error, res);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ Stores a string containing \texttt{x} into \texttt{res[0]}, and
+ a string containing \texttt{error} into \texttt{res[1]}, both strings being
+ formatted with the same notation.
+ This calls \method{formatWithError}{int,int,int,int,double,double,String[]} with
+ the minimal accuracy for which the formatted string for \texttt{error} is non-zero.
+ If \texttt{error} is 0, the accuracy is 0.
+ If this minimal accuracy causes the strings to be formatted using scientific
+ notation, this method increases the accuracy until the decimal notation can be used.
+\end{tabb}
+\begin{htmlonly}
+ \param{fieldwidth}{minimum length of the value string}
+ \param{fieldwidtherr}{minimum length of the error string}
+ \param{precision}{number of significant digits for the value}
+ \param{x}{the value to be processed}
+ \param{error}{the error on the value to be processed}
+ \param{res}{an array that will be filled with the formatted value and formatted error}
+\end{htmlonly}
+\begin{code}
+
+ public static void formatWithError (Locale locale, int fieldwidth,
+ int fieldwidtherr, int accuracy, int precision, double x,
+ double error, String[] res)\begin{hide} {
+ if (res.length != 2)
+ throw new IllegalArgumentException ("The given res array must contain two elements");
+ Formatter fmt = new Formatter (locale);
+ Formatter fmtErr = new Formatter (locale);
+ if (Double.isNaN (x)) {
+ res[0] = fmt.format ("%" + fieldwidth + "s", "NaN").toString();
+ res[1] = fmtErr.format ("%" + fieldwidtherr + "s", "").toString();
+ return;
+ }
+ if (Double.isInfinite (x)) {
+ res[0] = fmt.format ("%" + fieldwidth + "s", (x < 0 ? "-" : "" ) + "Infinite").toString();
+ res[1] = fmtErr.format ("%" + fieldwidtherr + "s", "").toString();
+ return;
+ }
+ if (accuracy < 0)
+ accuracy = 0;
+
+ if (canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ res[0] = fmt.format ("%" + fieldwidth + "." + accuracy + "f", x).toString();
+ res[1] = fmtErr.format ("%" + fieldwidtherr + "." + accuracy + "f", error).toString();
+ }
+ // Use scientific notation
+ else {
+ res[0] = processExp (fmt.format ("%" + fieldwidth + "." + (precision - 1) + "E", x).toString());
+ int xExp = x == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (x)));
+ int errorExp = error == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (error)));
+ int errorPrecision = precision - 1 - (xExp - errorExp);
+ if (errorPrecision < 0)
+ errorPrecision = 0;
+ res[1] = processExp (fmtErr.format
+ ("%" + fieldwidtherr + "." + errorPrecision + "E", error).toString());
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ This method is equivalent to
+ \method{formatWithError}{int,int,int,double,double,String[]},
+ except that it formats the given value and error for the
+ locale \texttt{locale}.
+\end{tabb}
+\begin{htmlonly}
+ \param{locale}{the locale being used}
+ \param{fieldwidth}{minimum length of the value string}
+ \param{fieldwidtherr}{minimum length of the error string}
+ \param{accuracy}{number of digits after the decimal point for the value and error}
+ \param{precision}{number of significant digits for the value}
+ \param{x}{the value to be processed}
+ \param{error}{the error on the value to be processed}
+ \param{res}{an array that will be filled with the formatted value and formatted error}
+\end{htmlonly}
+\begin{code}
+
+ public static void formatWithError (Locale locale, int fieldwidth,
+ int fieldwidtherr, int precision, double x, double error,
+ String[] res)\begin{hide} {
+ int accuracy = getMinAccuracy (error);
+ if (!canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
+ int posEntier = (int)Math.floor (Math.log (Math.abs (x)) / Math.log (10) + 1);
+ if (posEntier < 0)
+ posEntier = 1;
+ int newAccuracy = precision - posEntier;
+ if (canUseDecimalNotation (fieldwidth, newAccuracy, precision, x))
+ accuracy = newAccuracy;
+ }
+ formatWithError (locale, fieldwidth, fieldwidtherr, accuracy, precision, x, error, res);
+ }\end{hide}
+\end{code}
+\begin{tabb}
+ This method is equivalent to
+ \method{formatWithError}{int,int,int,double,double,String[]},
+ except that it formats the given value and error for the
+ locale \texttt{locale}.
+\end{tabb}
+\begin{htmlonly}
+ \param{locale}{the locale being used}
+ \param{fieldwidth}{minimum length of the value string}
+ \param{fieldwidtherr}{minimum length of the error string}
+ \param{precision}{number of significant digits for the value}
+ \param{x}{the value to be processed}
+ \param{error}{the error on the value to be processed}
+ \param{res}{an array that will be filled with the formatted value and formatted error}
+\end{htmlonly}
+%
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/RatioFunction.java b/source/umontreal/iro/lecuyer/util/RatioFunction.java
new file mode 100644
index 0000000..e4ac31b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/RatioFunction.java
@@ -0,0 +1,125 @@
+
+
+/*
+ * Class: RatioFunction
+ * Description: Represents a function computing a ratio of two values.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+
+/**
+ * Represents a function computing a ratio of two values.
+ *
+ */
+public class RatioFunction implements MultivariateFunction {
+ private double zeroOverZero = Double.NaN;
+
+
+
+ /**
+ * Constructs a new ratio function.
+ *
+ */
+ public RatioFunction() {}
+
+
+ /**
+ * Constructs a new ratio function that returns
+ * <TT>zeroOverZero</TT> for the special case of <SPAN CLASS="MATH">0/0</SPAN>.
+ * See the {@link #getZeroOverZeroValue getZeroOverZeroValue} method for more information.
+ * The default value of <TT>zeroOverZero</TT> is <TT>Double.NaN</TT>.
+ *
+ * @param zeroOverZero the value for <SPAN CLASS="MATH">0/0</SPAN>.
+ *
+ *
+ */
+ public RatioFunction (double zeroOverZero) {
+ this.zeroOverZero = zeroOverZero;
+ }
+
+
+ /**
+ * Returns the value returned by {@link #evaluate((double[])) evaluate} in the
+ * case where the <SPAN CLASS="MATH">0/0</SPAN> function is calculated.
+ * The default value for <SPAN CLASS="MATH">0/0</SPAN> is <TT>Double.NaN</TT>.
+ *
+ * <P>
+ * Generally, <SPAN CLASS="MATH">0/0</SPAN> is undefined, and therefore associated with the <TT>Double.NaN</TT>
+ * constant, meaning <SPAN CLASS="textit">not-a-number</SPAN>.
+ * However, in certain applications, it can be defined differently to accomodate some special cases.
+ * For exemple, in a queueing system, if there are no arrivals, no customers are served,
+ * lost, queued, etc. As a result, many performance measures of interest turn out to be
+ * <SPAN CLASS="MATH">0/0</SPAN>. Specifically, the loss probability, i.e., the ratio of
+ * lost customers over the number of arrivals, should be 0 if there is no arrival;
+ * in this case, <SPAN CLASS="MATH">0/0</SPAN> means 0.
+ * On the other hand, the service level, i.e., the fraction of customers waiting less than
+ * a fixed threshold, could be fixed to 1 if there is no arrival.
+ *
+ * @return the value for <SPAN CLASS="MATH">0/0</SPAN>.
+ *
+ */
+ public double getZeroOverZeroValue() {
+ return zeroOverZero;
+ }
+
+
+ /**
+ * Sets the value returned by {@link #evaluate((double[])) evaluate} for
+ * the undefined function <SPAN CLASS="MATH">0/0</SPAN> to <TT>zeroOverZero</TT>.
+ * See {@link #getZeroOverZeroValue getZeroOverZeroValue} for more information.
+ *
+ * @param zeroOverZero the new value for <SPAN CLASS="MATH">0/0</SPAN>.
+ *
+ *
+ */
+ public void setZeroOverZeroValue (double zeroOverZero) {
+ this.zeroOverZero = zeroOverZero;
+ }
+
+
+ public int getDimension() {
+ return 2;
+ }
+
+ public double evaluate (double... x) {
+ if (x.length != 2)
+ throw new IllegalArgumentException
+ ("Invalid length of x");
+ if (x[0] == 0 && x[1] == 0)
+ return zeroOverZero;
+ return x[0]/x[1];
+ }
+
+ public double evaluateGradient (int i, double... x) {
+ if (x.length != 2)
+ throw new IllegalArgumentException
+ ("Invalid length of x");
+ switch (i) {
+ case 0: return 1.0/x[1];
+ case 1: return -x[0]/(x[1]*x[1]);
+ default: throw new IndexOutOfBoundsException
+ ("Invalid value of i: " + i);
+ }
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/util/RatioFunction.tex b/source/umontreal/iro/lecuyer/util/RatioFunction.tex
new file mode 100644
index 0000000..d03624d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/RatioFunction.tex
@@ -0,0 +1,127 @@
+\defmodule{RatioFunction}
+
+Represents a function computing a ratio of two values.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RatioFunction
+ * Description: Represents a function computing a ratio of two values.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+\end{hide}
+
+
+public class RatioFunction implements MultivariateFunction\begin{hide} {
+ private double zeroOverZero = Double.NaN;
+\end{hide}
+\end{code}
+\subsubsection*{Constructors}
+\begin{code}
+
+ public RatioFunction()\begin{hide} {}\end{hide}
+\end{code}
+\begin{tabb} Constructs a new ratio function.
+\end{tabb}
+\begin{code}
+
+ public RatioFunction (double zeroOverZero)\begin{hide} {
+ this.zeroOverZero = zeroOverZero;
+ }\end{hide}
+\end{code}
+\begin{tabb} Constructs a new ratio function that returns
+ \texttt{zeroOverZero} for the special case of $0/0$.
+ See the \method{getZeroOverZeroValue}{} method for more information.
+ The default value of \texttt{zeroOverZero} is \texttt{Double.NaN}.
+\end{tabb}
+\begin{htmlonly}
+ \param{zeroOverZero}{the value for $0/0$.}
+\end{htmlonly}
+\subsubsection*{Methods}
+\begin{code}
+
+ public double getZeroOverZeroValue()\begin{hide} {
+ return zeroOverZero;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the value returned by \method{evaluate}{(double[])} in the
+ case where the $0/0$ function is calculated.
+ The default value for $0/0$ is \texttt{Double.NaN}.
+
+ Generally, $0/0$ is undefined, and therefore associated with the \texttt{Double.NaN}
+ constant, meaning \emph{not-a-number}.
+ However, in certain applications, it can be defined differently to accomodate some special cases.
+ For exemple, in a queueing system, if there are no arrivals, no customers are served,
+ lost, queued, etc. As a result, many performance measures of interest turn out to be
+ $0/0$. Specifically, the loss probability, i.e., the ratio of
+ lost customers over the number of arrivals, should be 0 if there is no arrival;
+ in this case, $0/0$ means 0.
+ On the other hand, the service level, i.e., the fraction of customers waiting less than
+ a fixed threshold, could be fixed to 1 if there is no arrival.
+\end{tabb}
+\begin{htmlonly}
+ \return{the value for $0/0$.}
+\end{htmlonly}
+\begin{code}
+
+ public void setZeroOverZeroValue (double zeroOverZero)\begin{hide} {
+ this.zeroOverZero = zeroOverZero;
+ }\end{hide}
+\end{code}
+\begin{tabb} Sets the value returned by \method{evaluate}{(double[])} for
+ the undefined function $0/0$ to \texttt{zeroOverZero}.
+ See \method{getZeroOverZeroValue}{} for more information.
+\end{tabb}
+\begin{htmlonly}
+ \param{zeroOverZero}{the new value for $0/0$.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public int getDimension() {
+ return 2;
+ }
+
+ public double evaluate (double... x) {
+ if (x.length != 2)
+ throw new IllegalArgumentException
+ ("Invalid length of x");
+ if (x[0] == 0 && x[1] == 0)
+ return zeroOverZero;
+ return x[0]/x[1];
+ }
+
+ public double evaluateGradient (int i, double... x) {
+ if (x.length != 2)
+ throw new IllegalArgumentException
+ ("Invalid length of x");
+ switch (i) {
+ case 0: return 1.0/x[1];
+ case 1: return -x[0]/(x[1]*x[1]);
+ default: throw new IndexOutOfBoundsException
+ ("Invalid value of i: " + i);
+ }
+ }
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/RootFinder.java b/source/umontreal/iro/lecuyer/util/RootFinder.java
new file mode 100644
index 0000000..aa2899d
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/RootFinder.java
@@ -0,0 +1,241 @@
+
+
+/*
+ * Class: RootFinder
+ * Description: Provides methods to solve non-linear equations.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+ import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+/**
+ * This class provides methods to solve non-linear equations.
+ *
+ */
+public class RootFinder {
+ private static final double MINVAL = 5.0e-308;
+ private RootFinder() {}
+
+
+ /**
+ * Computes a root <SPAN CLASS="MATH"><I>x</I></SPAN> of the function in <TT>f</TT> using the
+ * Brent-Dekker method. The interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> must contain the root <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ * The calculations are done with an approximate relative precision
+ * <TT>tol</TT>. Returns <SPAN CLASS="MATH"><I>x</I></SPAN> such that <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN>.
+ *
+ * @param a left endpoint of initial interval
+ *
+ * @param b right endpoint of initial interval
+ *
+ * @param f the function which is evaluated
+ *
+ * @param tol accuracy goal
+ *
+ * @return the root <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double brentDekker (double a, double b,
+ MathFunction f, double tol) {
+ final double EPS = 0.5E-15;
+ final int MAXITER = 120; // Maximum number of iterations
+ double c, d, e;
+ double fa, fb, fc;
+ final boolean DEBUG = false;
+
+ // Special case I = [b, a]
+ if (b < a) {
+ double ctemp = a;
+ a = b;
+ b = ctemp;
+ }
+
+ // Initialization
+ fa = f.evaluate (a);
+ fb = f.evaluate (b);
+ c = a;
+ fc = fa;
+ d = e = b - a;
+ tol += EPS + Num.DBL_EPSILON; // in case tol is too small
+
+ if (Math.abs (fc) < Math.abs (fb)) {
+ a = b;
+ b = c;
+ c = a;
+ fa = fb;
+ fb = fc;
+ fc = fa;
+ }
+
+ int i;
+ for (i = 0; i < MAXITER; i++) {
+ double s, p, q, r;
+ double tol1 = tol + 4.0 * Num.DBL_EPSILON * Math.abs (b);
+ double xm = 0.5 * (c - b);
+ if (DEBUG) {
+ double err = Math.abs(fa - fb);
+ System.out.printf("[a, b] = [%g, %g] fa = %g, fb = %g |fa - fb| = %.2g%n",
+ a, b, fa, fb, err);
+ }
+
+ if (Math.abs (fb) <= MINVAL) {
+ return b;
+ }
+ if (Math.abs (xm) <= tol1) {
+ if (Math.abs (b) > MINVAL)
+ return b;
+ else
+ return 0;
+ }
+
+ if ((Math.abs (e) >= tol1) && (Math.abs (fa) > Math.abs (fb))) {
+ if (a != c) {
+ // Inverse quadratic interpolation
+ q = fa / fc;
+ r = fb / fc;
+ s = fb / fa;
+ p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0));
+ q = (q - 1.0) * (r - 1.0) * (s - 1.0);
+ } else {
+ // Linear interpolation
+ s = fb / fa;
+ p = 2.0 * xm * s;
+ q = 1.0 - s;
+ }
+
+ // Adjust signs
+ if (p > 0.0)
+ q = -q;
+ p = Math.abs (p);
+
+ // Is interpolation acceptable ?
+ if (((2.0 * p) >= (3.0 * xm * q - Math.abs (tol1 * q)))
+ || (p >= Math.abs (0.5 * e * q))) {
+ d = xm;
+ e = d;
+ } else {
+ e = d;
+ d = p / q;
+ }
+ } else {
+ // Bisection necessary
+ d = xm;
+ e = d;
+ }
+
+ a = b;
+ fa = fb;
+ if (Math.abs (d) > tol1)
+ b += d;
+ else if (xm < 0.0)
+ b -= tol1;
+ else
+ b += tol1;
+ fb = f.evaluate (b);
+ if (fb * (Math.signum (fc)) > 0.0) {
+ c = a;
+ fc = fa;
+ d = e = b - a;
+ } else {
+ a = b;
+ b = c;
+ c = a;
+ fa = fb;
+ fb = fc;
+ fc = fa;
+ }
+ }
+
+ if (i >= MAXITER)
+ System.err.println(" WARNING: root finding does not converge");
+ return b;
+ }
+
+
+ /**
+ * Computes a root <SPAN CLASS="MATH"><I>x</I></SPAN> of the function in <TT>f</TT> using the
+ * <SPAN CLASS="textit">bisection</SPAN> method. The interval <SPAN CLASS="MATH">[<I>a</I>, <I>b</I>]</SPAN> must contain the root <SPAN CLASS="MATH"><I>x</I></SPAN>.
+ * The calculations are done with an approximate relative precision
+ * <TT>tol</TT>. Returns <SPAN CLASS="MATH"><I>x</I></SPAN> such that <SPAN CLASS="MATH"><I>f</I> (<I>x</I>) = 0</SPAN>.
+ *
+ * @param a left endpoint of initial interval
+ *
+ * @param b right endpoint of initial interval
+ *
+ * @param f the function which is evaluated
+ *
+ * @param tol accuracy goal
+ *
+ * @return the root <SPAN CLASS="MATH"><I>x</I></SPAN>
+ *
+ */
+ public static double bisection (double a, double b,
+ MathFunction f, double tol) {
+ // Case I = [b, a]
+ if (b < a) {
+ double ctemp = a;
+ a = b;
+ b = ctemp;
+ }
+ double xa = a;
+ double xb = b;
+ double yb = f.evaluate (b);
+ double ya = f.evaluate (a);
+ double x = 0, y = 0;
+ final int MAXITER = 1200; // Maximum number of iterations
+ final boolean DEBUG = false;
+ tol += Num.DBL_EPSILON; // in case tol is too small
+
+ if (DEBUG)
+ System.out.println
+ ("\niter xa xb f(x)");
+
+ boolean fini = false;
+ int i = 0;
+ while (!fini) {
+ x = (xa + xb) / 2.0;
+ y = f.evaluate (x);
+ if ((Math.abs (y) <= MINVAL) ||
+ (Math.abs (xb - xa) <= tol * Math.abs (x)) ||
+ (Math.abs (xb - xa) <= MINVAL)) {
+ if (Math.abs(x) > MINVAL)
+ return x;
+ else
+ return 0;
+ }
+ if (y * ya < 0.0)
+ xb = x;
+ else
+ xa = x;
+ ++i;
+ if (DEBUG)
+ System.out.printf("%3d %18.12g %18.12g %14.4g%n",
+ i, xa, xb, y);
+ if (i > MAXITER) {
+ System.out.println ("***** bisection: SEARCH DOES NOT CONVERGE");
+ fini = true;
+ }
+ }
+ return x;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/RootFinder.tex b/source/umontreal/iro/lecuyer/util/RootFinder.tex
new file mode 100644
index 0000000..77b123e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/RootFinder.tex
@@ -0,0 +1,241 @@
+\defmodule {RootFinder}
+
+This class provides methods to solve non-linear equations.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: RootFinder
+ * Description: Provides methods to solve non-linear equations.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+ import umontreal.iro.lecuyer.functions.MathFunction;
+
+
+public class RootFinder\begin{hide} {
+ private static final double MINVAL = 5.0e-308;
+ private RootFinder() {}\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public static double brentDekker (double a, double b,
+ MathFunction f, double tol)\begin{hide} {
+ final double EPS = 0.5E-15;
+ final int MAXITER = 120; // Maximum number of iterations
+ double c, d, e;
+ double fa, fb, fc;
+ final boolean DEBUG = false;
+
+ // Special case I = [b, a]
+ if (b < a) {
+ double ctemp = a;
+ a = b;
+ b = ctemp;
+ }
+
+ // Initialization
+ fa = f.evaluate (a);
+ fb = f.evaluate (b);
+ c = a;
+ fc = fa;
+ d = e = b - a;
+ tol += EPS + Num.DBL_EPSILON; // in case tol is too small
+
+ if (Math.abs (fc) < Math.abs (fb)) {
+ a = b;
+ b = c;
+ c = a;
+ fa = fb;
+ fb = fc;
+ fc = fa;
+ }
+
+ int i;
+ for (i = 0; i < MAXITER; i++) {
+ double s, p, q, r;
+ double tol1 = tol + 4.0 * Num.DBL_EPSILON * Math.abs (b);
+ double xm = 0.5 * (c - b);
+ if (DEBUG) {
+ double err = Math.abs(fa - fb);
+ System.out.printf("[a, b] = [%g, %g] fa = %g, fb = %g |fa - fb| = %.2g%n",
+ a, b, fa, fb, err);
+ }
+
+ if (Math.abs (fb) <= MINVAL) {
+ return b;
+ }
+ if (Math.abs (xm) <= tol1) {
+ if (Math.abs (b) > MINVAL)
+ return b;
+ else
+ return 0;
+ }
+
+ if ((Math.abs (e) >= tol1) && (Math.abs (fa) > Math.abs (fb))) {
+ if (a != c) {
+ // Inverse quadratic interpolation
+ q = fa / fc;
+ r = fb / fc;
+ s = fb / fa;
+ p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0));
+ q = (q - 1.0) * (r - 1.0) * (s - 1.0);
+ } else {
+ // Linear interpolation
+ s = fb / fa;
+ p = 2.0 * xm * s;
+ q = 1.0 - s;
+ }
+
+ // Adjust signs
+ if (p > 0.0)
+ q = -q;
+ p = Math.abs (p);
+
+ // Is interpolation acceptable ?
+ if (((2.0 * p) >= (3.0 * xm * q - Math.abs (tol1 * q)))
+ || (p >= Math.abs (0.5 * e * q))) {
+ d = xm;
+ e = d;
+ } else {
+ e = d;
+ d = p / q;
+ }
+ } else {
+ // Bisection necessary
+ d = xm;
+ e = d;
+ }
+
+ a = b;
+ fa = fb;
+ if (Math.abs (d) > tol1)
+ b += d;
+ else if (xm < 0.0)
+ b -= tol1;
+ else
+ b += tol1;
+ fb = f.evaluate (b);
+ if (fb * (Math.signum (fc)) > 0.0) {
+ c = a;
+ fc = fa;
+ d = e = b - a;
+ } else {
+ a = b;
+ b = c;
+ c = a;
+ fa = fb;
+ fb = fc;
+ fc = fa;
+ }
+ }
+
+ if (i >= MAXITER)
+ System.err.println(" WARNING: root finding does not converge");
+ return b;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes a root $x$ of the function in \texttt{f} using the
+ Brent-Dekker method. The interval $[a, b]$ must contain the root $x$.
+ The calculations are done with an approximate relative precision
+ \texttt{tol}. Returns $x$ such that $f(x) = 0$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{left endpoint of initial interval}
+ \param{b}{right endpoint of initial interval}
+ \param{f}{the function which is evaluated}
+ \param{tol}{accuracy goal}
+ \return{the root $x$}
+\end{htmlonly}
+\begin{code}
+
+ public static double bisection (double a, double b,
+ MathFunction f, double tol)\begin{hide} {
+ // Case I = [b, a]
+ if (b < a) {
+ double ctemp = a;
+ a = b;
+ b = ctemp;
+ }
+ double xa = a;
+ double xb = b;
+ double yb = f.evaluate (b);
+ double ya = f.evaluate (a);
+ double x = 0, y = 0;
+ final int MAXITER = 1200; // Maximum number of iterations
+ final boolean DEBUG = false;
+ tol += Num.DBL_EPSILON; // in case tol is too small
+
+ if (DEBUG)
+ System.out.println
+ ("\niter xa xb f(x)");
+
+ boolean fini = false;
+ int i = 0;
+ while (!fini) {
+ x = (xa + xb) / 2.0;
+ y = f.evaluate (x);
+ if ((Math.abs (y) <= MINVAL) ||
+ (Math.abs (xb - xa) <= tol * Math.abs (x)) ||
+ (Math.abs (xb - xa) <= MINVAL)) {
+ if (Math.abs(x) > MINVAL)
+ return x;
+ else
+ return 0;
+ }
+ if (y * ya < 0.0)
+ xb = x;
+ else
+ xa = x;
+ ++i;
+ if (DEBUG)
+ System.out.printf("%3d %18.12g %18.12g %14.4g%n",
+ i, xa, xb, y);
+ if (i > MAXITER) {
+ System.out.println ("***** bisection: SEARCH DOES NOT CONVERGE");
+ fini = true;
+ }
+ }
+ return x;
+ }\end{hide}
+\end{code}
+\begin{tabb} Computes a root $x$ of the function in \texttt{f} using the
+ \emph{bisection} method. The interval $[a, b]$ must contain the root $x$.
+ The calculations are done with an approximate relative precision
+ \texttt{tol}. Returns $x$ such that $f(x) = 0$.
+ \end{tabb}
+\begin{htmlonly}
+ \param{a}{left endpoint of initial interval}
+ \param{b}{right endpoint of initial interval}
+ \param{f}{the function which is evaluated}
+ \param{tol}{accuracy goal}
+ \return{the root $x$}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/SystemTimeChrono.java b/source/umontreal/iro/lecuyer/util/SystemTimeChrono.java
new file mode 100644
index 0000000..5d91253
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/SystemTimeChrono.java
@@ -0,0 +1,61 @@
+
+
+/*
+ * Class: SystemTimeChrono
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+/**
+ * Extends the {@link AbstractChrono} class to compute
+ * the total system time using Java's builtin <TT>System.nanoTime</TT>.
+ * The system can be used as a rough approximation of the CPU time taken
+ * by a program if no
+ * other tasks are executed on the host while the program is running.
+ *
+ */
+public class SystemTimeChrono extends AbstractChrono {
+
+ protected void getTime (long[] tab) {
+ long rawTime = System.nanoTime();
+ final long DIV = 1000000000L;
+ long seconds = rawTime/DIV;
+ long micros = (rawTime % DIV)/1000L;
+ tab[0] = seconds;
+ tab[1] = micros;
+ }
+
+
+ /**
+ * Constructs a new chrono object and
+ * initializes it to zero.
+ *
+ */
+ public SystemTimeChrono() {
+ super();
+ init();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/SystemTimeChrono.tex b/source/umontreal/iro/lecuyer/util/SystemTimeChrono.tex
new file mode 100644
index 0000000..c1a7da3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/SystemTimeChrono.tex
@@ -0,0 +1,69 @@
+\defmodule{SystemTimeChrono}
+
+Extends the \class{AbstractChrono} class to compute
+the total system time using Java's builtin \texttt{System.nanoTime}.
+The system can be used as a rough approximation of the CPU time taken
+by a program if no
+other tasks are executed on the host while the program is running.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: SystemTimeChrono
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public class SystemTimeChrono extends AbstractChrono\begin{hide} {
+
+ protected void getTime (long[] tab) {
+ long rawTime = System.nanoTime();
+ final long DIV = 1000000000L;
+ long seconds = rawTime/DIV;
+ long micros = (rawTime % DIV)/1000L;
+ tab[0] = seconds;
+ tab[1] = micros;
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+
+\begin{code}
+
+ public SystemTimeChrono()\begin{hide} {
+ super();
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a new chrono object and
+ initializes it to zero.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/Systeme.java b/source/umontreal/iro/lecuyer/util/Systeme.java
new file mode 100644
index 0000000..b71f686
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Systeme.java
@@ -0,0 +1,92 @@
+
+/*
+ * Class: Systeme
+ * Description: Provides tools related to the system or the computer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+ import java.lang.management.*;
+ import java.util.*;
+ import java.text.*;
+ import java.net.*;
+
+
+
+/**
+ * This class provides a few tools related to the system
+ * or the computer.
+ *
+ */
+public class Systeme {
+ private Systeme() {}
+
+
+
+ /**
+ * Returns the name of the host computer.
+ *
+ * @return the name of the host computer
+ *
+ */
+ public static String getHostName() {
+ String host;
+ try {
+ InetAddress machine = InetAddress.getLocalHost ();
+ host = machine.getHostName ();
+ } catch (UnknownHostException uhe) {
+ host = "unknown host machine";
+ }
+ // host = System.getenv("HOSTNAME");
+ int j = host.indexOf ('.');
+ String name;
+ if (j >= 0)
+ name = host.substring (0, j);
+ else
+ name = host;
+ return name;
+ }
+
+
+ /**
+ * Returns information about the running process: name, id,
+ * host name, date and time.
+ *
+ * @return information about the running process
+ *
+ */
+ public static String getProcessInfo() {
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace ();
+ StackTraceElement mai = stack[stack.length - 1];
+ String str = mai.getClassName ();
+
+ RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+ Date startTime = new Date(runtime.getStartTime());
+ SimpleDateFormat dateFormat =
+ new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss");
+
+ str += " [" + runtime.getName() + "]";
+ str += " [" + dateFormat.format(startTime) + "]";
+
+ return str;
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/Systeme.tex b/source/umontreal/iro/lecuyer/util/Systeme.tex
new file mode 100644
index 0000000..24ca0e8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/Systeme.tex
@@ -0,0 +1,105 @@
+\defmodule{Systeme}
+
+This class provides a few tools related to the system
+or the computer.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: Systeme
+ * Description: Provides tools related to the system or the computer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since January 2011
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+ import java.lang.management.*;
+ import java.util.*;
+ import java.text.*;
+ import java.net.*;
+\end{hide}
+
+
+public class Systeme\begin{hide} {
+ private Systeme() {}
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+
+\begin{code}
+
+ public static String getHostName()\begin{hide} {
+ String host;
+ try {
+ InetAddress machine = InetAddress.getLocalHost ();
+ host = machine.getHostName ();
+ } catch (UnknownHostException uhe) {
+ host = "unknown host machine";
+ }
+ // host = System.getenv("HOSTNAME");
+ int j = host.indexOf ('.');
+ String name;
+ if (j >= 0)
+ name = host.substring (0, j);
+ else
+ name = host;
+ return name;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the name of the host computer.
+\end{tabb}
+\begin{htmlonly}
+ \return{the name of the host computer}
+\end{htmlonly}
+\begin{code}
+
+ public static String getProcessInfo()\begin{hide} {
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace ();
+ StackTraceElement mai = stack[stack.length - 1];
+ String str = mai.getClassName ();
+
+ RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+ Date startTime = new Date(runtime.getStartTime());
+ SimpleDateFormat dateFormat =
+ new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss");
+
+ str += " [" + runtime.getName() + "]";
+ str += " [" + dateFormat.format(startTime) + "]";
+
+ return str;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns information about the running process: name, id,
+host name, date and time.
+\end{tabb}
+\begin{htmlonly}
+ \return{information about the running process}
+\end{htmlonly}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/TableFormat.java b/source/umontreal/iro/lecuyer/util/TableFormat.java
new file mode 100644
index 0000000..7b63bbb
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TableFormat.java
@@ -0,0 +1,342 @@
+
+/*
+ * Class: TableFormat
+ * Description: Provides methods to format arrays into String's
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+/**
+ * This class provides methods to format arrays and matrices into
+ * {@link String}s in different styles.
+ * This could be useful for printing arrays and subarrays, or for
+ * putting them in files for further treatment by other softwares such
+ * as <EM>Mathematica</EM>, <EM>Matlab</EM>, etc.
+ *
+ */
+public class TableFormat {
+ private TableFormat() {}
+
+
+ /**
+ * Plain text matrix printing style
+ *
+ */
+ public static final int PLAIN = 0;
+
+
+ /**
+ * Mathematica matrix printing style
+ *
+ */
+ public static final int MATHEMATICA = 1;
+
+
+ /**
+ * Matlab matrix printing style
+ *
+ */
+ public static final int MATLAB = 2;
+
+
+ /**
+ * Formats a {@link String} containing the elements <TT>n1</TT>
+ * to <TT>n2</TT> (inclusive) of table <TT>V</TT>,
+ * <TT>k</TT> elements per line, <TT>p</TT> positions per element.
+ * If <TT>k</TT> = 1, the array index will also appear on the left
+ * of each element, i.e., each line <TT>i</TT> will have the form <TT>i V[i]</TT>.
+ *
+ * @param V array to be formated
+ *
+ * @param n1 index of the first element being formated
+ *
+ * @param n2 index of the last element being formated
+ *
+ * @param k number of elements per line
+ *
+ * @param p number of positions per element
+ *
+ * @return formated string repreenting the elements
+ *
+ */
+ public static String format (int V[], int n1, int n2, int k, int p) {
+ int i;
+ StringBuffer sb = new StringBuffer();
+ if (k > 1) {
+ sb.append ("Elements " + n1 + " to " + n2 +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++) {
+ sb.append (PrintfFormat.d (p, V[i]));
+ if (((i + 1 - n1) % k) == 0)
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ else {
+ sb.append (PrintfFormat.NEWLINE + " Index Element" +
+ PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++)
+ sb.append (PrintfFormat.d (6, i) + " " +
+ PrintfFormat.d (12, V[i]) + PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+
+ /**
+ * Similar to the previous method, but for an array of <TT>double</TT>'s.
+ * Gives at least <TT>p1</TT> positions per element,
+ * <TT>p2</TT> digits after the decimal point, and at least <TT>p3</TT>
+ * significant digits.
+ *
+ * @param V array to be formated
+ *
+ * @param n1 index of the first element being formated
+ *
+ * @param n2 index of the last element being formated
+ *
+ * @param k number of elements per line
+ *
+ * @param p1 number of positions per element
+ *
+ * @param p2 number of digits after the decimal point
+ *
+ * @param p3 number of significant digits
+ *
+ * @return formated string repreenting the elements
+ *
+ */
+ public static String format (double V[], int n1, int n2,
+ int k, int p1, int p2, int p3) {
+ int i;
+ StringBuffer sb = new StringBuffer();
+ if (k > 1) {
+ sb.append ("Elements " + n1 + " to " + n2 +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++) {
+ sb.append (PrintfFormat.format (p1, p2, p3, V[i]));
+ if (((i + 1 - n1) % k) == 0)
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+
+ } else {
+ sb.append (PrintfFormat.NEWLINE + " Index Element" +
+ PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++)
+ sb.append (PrintfFormat.d (6, i) + " " +
+ PrintfFormat.format (p1, p2, p3, V[i]) +
+ PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+
+ private static int Style = PLAIN;
+
+ private static char OuvrantMat = ' '; // Matrix delimitors
+ private static char FermantMat = ' ';
+
+ private static char OuvrantVec = ' '; // Vector delimitors
+ private static char FermantVec = ' ';
+
+ private static char SepareVec = ' '; // Element separators
+ private static char SepareElem = ' ';
+
+ private static void fixeDelim (int style) {
+ /* Fixe les delimiteurs pour imprimer une matrice selon un format
+ approprie */
+ Style = style;
+ switch (style) {
+ case MATHEMATICA:
+ OuvrantMat = '{';
+ FermantMat = '}';
+ OuvrantVec = '{';
+ FermantVec = '}';
+ SepareVec = ',';
+ SepareElem = ',';
+ break;
+ case MATLAB:
+ OuvrantMat = '[';
+ FermantMat = ']';
+ OuvrantVec = ' ';
+ FermantVec = ' ';
+ SepareVec = ' ';
+ SepareElem = ' ';
+ break;
+ default:
+ OuvrantMat = ' ';
+ FermantMat = ' ';
+ OuvrantVec = ' ';
+ FermantVec = ' ';
+ SepareVec = ' ';
+ SepareElem = ' ';
+ break;
+ }
+ }
+
+
+ @Deprecated
+ public static String format (int[][] Mat, int i1, int i2,
+ int j1, int j2, int w, int p,
+ int style, String Name) {
+ return format (Mat, i1, i2, j1, j2, w, style, Name);
+ }
+
+ /**
+ * Formats the submatrix with lines
+ * <TT>i1</TT> <SPAN CLASS="MATH"> <= <I>i</I> <= </SPAN> <TT>i2</TT> and columns
+ * <TT>j1</TT> <SPAN CLASS="MATH"> <= <I>j</I> <= </SPAN> <TT>j2</TT> of the matrix <TT>Mat</TT>, using the
+ * formatting style <TT>style</TT>. The elements are formated in <TT>w</TT>
+ * positions each, with a precision of <TT>p</TT> digits.
+ * <TT>Name</TT> provides an identifier for the submatrix.
+ * To be treated by <TT>Matlab</TT>, the returned string
+ * must be copied to a file with extension <TT>.m</TT>.
+ * If the file is named <TT>poil.m</TT>, for example, it can be accessed by
+ * calling <TT>poil</TT> in <TT>Matlab</TT>.
+ * For <TT>Mathematica</TT>, if the file is named <TT>poil</TT>,
+ * it will be read using <TT>« poil;</TT>.
+ *
+ * @param Mat matrix to be formated
+ *
+ * @param i1 index of the first row being formated
+ *
+ * @param i2 index of the last row being formated
+ *
+ * @param j1 index of the first column being formated
+ *
+ * @param j2 index of the last column being formated
+ *
+ * @param w number of positions for each element
+ *
+ * @param p number of digits after the decimal point of the elements
+ *
+ * @param style formating style of the submatrix, being one of
+ * {@link #MATHEMATICA MATHEMATICA}, {@link #MATLAB MATLAB}, or {@link #PLAIN PLAIN}
+ *
+ * @param Name descriptive name of the submatrix
+ *
+ * @return formated string representing the submatrix
+ *
+ */
+ public static String format (double[][] Mat, int i1, int i2,
+ int j1, int j2, int w, int p,
+ int style, String Name) {
+ int k;
+ int j;
+ int i;
+ double x;
+ String S;
+
+ fixeDelim (style);
+ StringBuffer sb = new StringBuffer();
+ if (Name.length() > 0)
+ sb.append (Name + " = ");
+
+ double prec = Math.pow (10.0, (double)p);
+ sb.append (OuvrantMat + PrintfFormat.NEWLINE);
+ for (i = i1; i <= i2; i++) {
+ sb.append (OuvrantVec);
+ for (j = j1; j <= j2; j++) {
+ sb.append (' ');
+ switch (style) {
+ case MATHEMATICA:
+ x = Mat[i][j];
+ if (((x != 0.0) && (Math.abs (x) < 0.1)) ||
+ (Math.abs (x) > prec)) {
+ S = PrintfFormat.G (0, p, x);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.f (0, p, x);
+ S = PrintfFormat.s (w, S);
+ break;
+ default:
+ // MATLAB, Default */
+ sb.append (PrintfFormat.G (w, p, Mat[i][j]));
+ break;
+ }
+ if (j < j2)
+ sb.append (SepareElem);
+ }
+ sb.append (FermantVec);
+ if (i < i2)
+ sb.append (SepareVec + PrintfFormat.NEWLINE);
+ }
+ sb.append (FermantMat + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+
+
+ /**
+ * Similar to the previous method, but for a matrix of <TT>int</TT>'s.
+ *
+ * @param Mat matrix to be formated
+ *
+ * @param i1 index of the first row being formated
+ *
+ * @param i2 index of the last row being formated
+ *
+ * @param j1 index of the first column being formated
+ *
+ * @param j2 index of the last column being formated
+ *
+ * @param w number of positions for each element
+ *
+ * @param style formating style of the submatrix, being one of
+ * {@link #MATHEMATICA MATHEMATICA}, {@link #MATLAB MATLAB}, or {@link #PLAIN PLAIN}
+ *
+ * @param Name descriptive name of the submatrix
+ *
+ * @return formated string representing the submatrix
+ */
+ public static String format (int[][] Mat, int i1, int i2, int j1, int j2,
+ int w, int style, String Name) {
+ int i;
+ int j;
+
+ fixeDelim (style);
+ StringBuffer sb = new StringBuffer();
+ if (Name.length() > 0)
+ sb.append (Name + " = ");
+
+ sb.append (OuvrantMat + PrintfFormat.NEWLINE);
+ for (i = i1; i <= i2; i++) {
+ sb.append (OuvrantVec);
+ for (j = j1; j <= j2; j++) {
+ sb.append (PrintfFormat.d (w, Mat[i][j]));
+ if (j < j2)
+ sb.append (SepareElem);
+ }
+ sb.append (FermantVec);
+ if (i < i2)
+ sb.append (SepareVec + PrintfFormat.NEWLINE);
+ }
+ sb.append (FermantMat + PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+ }
diff --git a/source/umontreal/iro/lecuyer/util/TableFormat.tex b/source/umontreal/iro/lecuyer/util/TableFormat.tex
new file mode 100644
index 0000000..652adbd
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TableFormat.tex
@@ -0,0 +1,321 @@
+\defmodule {TableFormat}
+
+This class provides methods to format arrays and matrices into
+\class{String}s in different styles.
+This could be useful for printing arrays and subarrays, or for
+putting them in files for further treatment by other softwares such
+as {\em Mathematica\/}, {\em Matlab\/}, etc.
+
+\bigskip\hrule
+
+\begin{code}\begin{hide}
+/*
+ * Class: TableFormat
+ * Description: Provides methods to format arrays into String's
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+public class TableFormat\begin{hide} {
+ private TableFormat() {}\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Formating styles}
+\begin{code}
+
+ public static final int PLAIN\begin{hide} = 0; \end{hide}
+\end{code}
+\begin{tabb} Plain text matrix printing style
+\end{tabb}
+\begin{code}
+
+ public static final int MATHEMATICA\begin{hide} = 1; \end{hide}
+\end{code}
+\begin{tabb} Mathematica matrix printing style
+\end{tabb}
+\begin{code}
+
+ public static final int MATLAB\begin{hide} = 2; \end{hide}
+\end{code}
+ \begin{tabb} Matlab matrix printing style
+ \end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Functions to convert arrays to \texttt{String}}
+\begin{code}
+
+ public static String format (int V[], int n1, int n2, int k, int p)\begin{hide} {
+ int i;
+ StringBuffer sb = new StringBuffer();
+ if (k > 1) {
+ sb.append ("Elements " + n1 + " to " + n2 +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++) {
+ sb.append (PrintfFormat.d (p, V[i]));
+ if (((i + 1 - n1) % k) == 0)
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ else {
+ sb.append (PrintfFormat.NEWLINE + " Index Element" +
+ PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++)
+ sb.append (PrintfFormat.d (6, i) + " " +
+ PrintfFormat.d (12, V[i]) + PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Formats a \class{String} containing the elements \texttt{n1}
+ to \texttt{n2} (inclusive) of table \texttt{V},
+ \texttt{k} elements per line, \texttt{p} positions per element.
+ If \texttt{k} = 1, the array index will also appear on the left
+ of each element, i.e., each line \texttt{i} will have the form \texttt{i V[i]}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{V}{array to be formated}
+ \param{n1}{index of the first element being formated}
+ \param{n2}{index of the last element being formated}
+ \param{k}{number of elements per line}
+ \param{p}{number of positions per element}
+ \return{formated string repreenting the elements}
+\end{htmlonly}
+\begin{code}
+
+ public static String format (double V[], int n1, int n2,
+ int k, int p1, int p2, int p3)\begin{hide} {
+ int i;
+ StringBuffer sb = new StringBuffer();
+ if (k > 1) {
+ sb.append ("Elements " + n1 + " to " + n2 +
+ PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++) {
+ sb.append (PrintfFormat.format (p1, p2, p3, V[i]));
+ if (((i + 1 - n1) % k) == 0)
+ sb.append (PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+
+ } else {
+ sb.append (PrintfFormat.NEWLINE + " Index Element" +
+ PrintfFormat.NEWLINE);
+ for (i = n1; i <= n2; i++)
+ sb.append (PrintfFormat.d (6, i) + " " +
+ PrintfFormat.format (p1, p2, p3, V[i]) +
+ PrintfFormat.NEWLINE);
+ }
+ sb.append (PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Similar to the previous method, but for an array of \texttt{double}'s.
+ Gives at least \texttt{p1} positions per element,
+ \texttt{p2} digits after the decimal point, and at least \texttt{p3}
+ significant digits.
+ \end{tabb}
+\begin{htmlonly}
+ \param{V}{array to be formated}
+ \param{n1}{index of the first element being formated}
+ \param{n2}{index of the last element being formated}
+ \param{k}{number of elements per line}
+ \param{p1}{number of positions per element}
+ \param{p2}{number of digits after the decimal point}
+ \param{p3}{number of significant digits}
+ \return{formated string repreenting the elements}
+\end{htmlonly}
+\begin{code}
+\begin{hide}
+ private static int Style = PLAIN;
+
+ private static char OuvrantMat = ' '; // Matrix delimitors
+ private static char FermantMat = ' ';
+
+ private static char OuvrantVec = ' '; // Vector delimitors
+ private static char FermantVec = ' ';
+
+ private static char SepareVec = ' '; // Element separators
+ private static char SepareElem = ' ';
+
+ private static void fixeDelim (int style) {
+ /* Fixe les delimiteurs pour imprimer une matrice selon un format
+ approprie */
+ Style = style;
+ switch (style) {
+ case MATHEMATICA:
+ OuvrantMat = '{';
+ FermantMat = '}';
+ OuvrantVec = '{';
+ FermantVec = '}';
+ SepareVec = ',';
+ SepareElem = ',';
+ break;
+ case MATLAB:
+ OuvrantMat = '[';
+ FermantMat = ']';
+ OuvrantVec = ' ';
+ FermantVec = ' ';
+ SepareVec = ' ';
+ SepareElem = ' ';
+ break;
+ default:
+ OuvrantMat = ' ';
+ FermantMat = ' ';
+ OuvrantVec = ' ';
+ FermantVec = ' ';
+ SepareVec = ' ';
+ SepareElem = ' ';
+ break;
+ }
+ }
+
+
+ @Deprecated
+ public static String format (int[][] Mat, int i1, int i2,
+ int j1, int j2, int w, int p,
+ int style, String Name) {
+ return format (Mat, i1, i2, j1, j2, w, style, Name);
+ }\end{hide}
+
+ public static String format (double[][] Mat, int i1, int i2,
+ int j1, int j2, int w, int p,
+ int style, String Name)\begin{hide} {
+ int k;
+ int j;
+ int i;
+ double x;
+ String S;
+
+ fixeDelim (style);
+ StringBuffer sb = new StringBuffer();
+ if (Name.length() > 0)
+ sb.append (Name + " = ");
+
+ double prec = Math.pow (10.0, (double)p);
+ sb.append (OuvrantMat + PrintfFormat.NEWLINE);
+ for (i = i1; i <= i2; i++) {
+ sb.append (OuvrantVec);
+ for (j = j1; j <= j2; j++) {
+ sb.append (' ');
+ switch (style) {
+ case MATHEMATICA:
+ x = Mat[i][j];
+ if (((x != 0.0) && (Math.abs (x) < 0.1)) ||
+ (Math.abs (x) > prec)) {
+ S = PrintfFormat.G (0, p, x);
+ int exppos = S.indexOf ('E');
+ if (exppos != -1)
+ S = S.substring (0, exppos) + "*10^(" +
+ S.substring (exppos+1) + ")";
+ }
+ else
+ S = PrintfFormat.f (0, p, x);
+ S = PrintfFormat.s (w, S);
+ break;
+ default:
+ // MATLAB, Default */
+ sb.append (PrintfFormat.G (w, p, Mat[i][j]));
+ break;
+ }
+ if (j < j2)
+ sb.append (SepareElem);
+ }
+ sb.append (FermantVec);
+ if (i < i2)
+ sb.append (SepareVec + PrintfFormat.NEWLINE);
+ }
+ sb.append (FermantMat + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Formats the submatrix with lines
+ \texttt{i1} $\le i \le $ \texttt{i2} and columns
+ \texttt{j1} $\le j \le $ \texttt{j2} of the matrix \texttt{Mat}, using the
+ formatting style \texttt{style}. The elements are formated in \texttt{w}
+ positions each, with a precision of \texttt{p} digits.
+ \texttt{Name} provides an identifier for the submatrix.
+%
+ To be treated by \texttt{Matlab}, the returned string
+ must be copied to a file with extension \texttt{.m}.
+ If the file is named \texttt{poil.m}, for example, it can be accessed by
+ calling \texttt{poil} in \texttt{Matlab}.
+ For \texttt{Mathematica}, if the file is named \texttt{poil},
+ it will be read using \texttt{<< poil;}.
+ \end{tabb}
+\begin{htmlonly}
+ \param{Mat}{matrix to be formated}
+ \param{i1}{index of the first row being formated}
+ \param{i2}{index of the last row being formated}
+ \param{j1}{index of the first column being formated}
+ \param{j2}{index of the last column being formated}
+ \param{w}{number of positions for each element}
+ \param{p}{number of digits after the decimal point of the elements}
+ \param{style}{formating style of the submatrix, being one of
+ \method{MATHEMATICA}{}, \method{MATLAB}{}, or \method{PLAIN}{}}
+ \param{Name}{descriptive name of the submatrix}
+ \return{formated string representing the submatrix}
+\end{htmlonly}
+\begin{code}
+
+ public static String format (int[][] Mat, int i1, int i2, int j1, int j2,
+ int w, int style, String Name)\begin{hide} {
+ int i;
+ int j;
+
+ fixeDelim (style);
+ StringBuffer sb = new StringBuffer();
+ if (Name.length() > 0)
+ sb.append (Name + " = ");
+
+ sb.append (OuvrantMat + PrintfFormat.NEWLINE);
+ for (i = i1; i <= i2; i++) {
+ sb.append (OuvrantVec);
+ for (j = j1; j <= j2; j++) {
+ sb.append (PrintfFormat.d (w, Mat[i][j]));
+ if (j < j2)
+ sb.append (SepareElem);
+ }
+ sb.append (FermantVec);
+ if (i < i2)
+ sb.append (SepareVec + PrintfFormat.NEWLINE);
+ }
+ sb.append (FermantMat + PrintfFormat.NEWLINE + PrintfFormat.NEWLINE);
+ return sb.toString();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Similar to the previous method, but for a matrix of \texttt{int}'s.
+\end{tabb}
+\begin{htmlonly}
+ \param{Mat}{matrix to be formated}
+ \param{i1}{index of the first row being formated}
+ \param{i2}{index of the last row being formated}
+ \param{j1}{index of the first column being formated}
+ \param{j2}{index of the last column being formated}
+ \param{w}{number of positions for each element}
+ \param{style}{formating style of the submatrix, being one of
+ \method{MATHEMATICA}{}, \method{MATLAB}{}, or \method{PLAIN}{}}
+ \param{Name}{descriptive name of the submatrix}
+ \return{formated string representing the submatrix}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/util/TextDataReader.java b/source/umontreal/iro/lecuyer/util/TextDataReader.java
new file mode 100644
index 0000000..da50752
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TextDataReader.java
@@ -0,0 +1,1000 @@
+
+
+/*
+ * Class: TextDataReader
+ * Description: Provides static methods to read data from text files
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.io.LineNumberReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URL;
+import java.util.logging.Logger;
+
+
+
+/**
+ * Provides static methods to read data from text files.
+ *
+ */
+public class TextDataReader {
+ private static Logger log = Logger.getLogger ("umontreal.iro.lecuyer.util");
+
+
+ /**
+ * Reads an array of double-precision values from the
+ * reader <TT>input</TT>.
+ * For each line of text obtained from the
+ * given reader, this method
+ * trims whitespaces, and parses the
+ * remaining text as a double-precision value.
+ * This method ignores every character
+ * other than the digits, the plus and minus signs,
+ * the period (<TT>.</TT>),
+ * and the letters <TT>e</TT> and <TT>E</TT>.
+ * Moreover, lines starting with a pound sign (<TT>#</TT>)
+ * are considered as comments and thus skipped.
+ * The method returns an array containing
+ * all the parsed values.
+ *
+ * @param input the reader to obtain data from.
+ *
+ * @return the obtained array of double-precision values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[] readDoubleData (Reader input) throws IOException {
+ LineNumberReader inb = new LineNumberReader (input);
+ double[] data = new double[5];
+ int n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Double.parseDouble (li);
+ if (n >= data.length) {
+ double[] newData = new double[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+ }
+ if (data.length != n) {
+ double[] data2 = new double[n];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+ return data;
+ }
+
+
+ /**
+ * Connects to the URL referred to by the URL object <TT>url</TT>,
+ * and calls {@link #readDoubleData((Reader)) readDoubleData} to
+ * obtain an array of double-precision values from
+ * the resource.
+ *
+ * @param url the URL object representing the resource to read.
+ *
+ * @return the obtained array of double-precision values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[] readDoubleData (URL url) throws IOException {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readDoubleData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * Opens the file referred to by the file object <TT>file</TT>,
+ * and calls {@link #readDoubleData((Reader)) readDoubleData} to
+ * obtain an array of double-precision values from
+ * the file.
+ *
+ * @param file the file object representing the file to read.
+ *
+ * @return the obtained array of double-precision values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[] readDoubleData (File file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * Opens the file with name <TT>file</TT>,
+ * and calls {@link #readDoubleData((Reader)) readDoubleData} to
+ * obtain an array of double-precision values from
+ * the file.
+ *
+ * @param file the name of the file to read.
+ *
+ * @return the obtained array of double-precision values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[] readDoubleData (String file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData((Reader)) readDoubleData},
+ * for reading integers.
+ *
+ * @param input the reader to obtain data from.
+ *
+ * @return the obtained array of integers.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[] readIntData (Reader input) throws IOException {
+ LineNumberReader inb = new LineNumberReader (input);
+ int[] data = new int[5];
+ int n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Integer.parseInt (li);
+ if (n >= data.length) {
+ int[] newData = new int[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+ }
+ if (data.length != n) {
+ int[] data2 = new int[n];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+ return data;
+ }
+
+
+ /**
+ * Connects to the URL referred to by the URL object <TT>url</TT>,
+ * and calls {@link #readIntData((Reader)) readIntData} to
+ * obtain an array of integers from
+ * the resource.
+ *
+ * @param url the URL object representing the resource to read.
+ *
+ * @return the obtained array of integers.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[] readIntData (URL url) throws IOException {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readIntData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData((File)) readDoubleData},
+ * for reading integers.
+ *
+ * @param file the file object represented to file to read.
+ *
+ * @return the array of integers.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[] readIntData (File file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData((String)) readDoubleData},
+ * for reading integers.
+ *
+ * @param file the name of the file to read.
+ *
+ * @return the array of integers.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[] readIntData (String file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * Reads an array of strings from the
+ * reader <TT>input</TT>.
+ * For each line of text obtained from the
+ * given reader, this method
+ * trims leading and trailing whitespaces, and stores the remaining string.
+ * Lines starting with a pound sign (<TT>#</TT>)
+ * are considered as comments and thus skipped.
+ * The method returns an array containing
+ * all the read strings.
+ *
+ * @param input the reader to obtain data from.
+ *
+ * @return the obtained array of strings.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[] readStringData (Reader input) throws IOException {
+ LineNumberReader inb = new LineNumberReader (input);
+ String[] data = new String[5];
+ int n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ data[n++] = li;
+ if (n >= data.length) {
+ String[] newData = new String[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ if (data.length != n) {
+ String[] data2 = new String[n];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+ return data;
+ }
+
+
+ /**
+ * Connects to the URL referred to by the URL object <TT>url</TT>,
+ * and calls {@link #readStringData((Reader)) readStringData} to
+ * obtain an array of integers from
+ * the resource.
+ *
+ * @param url the URL object representing the resource to read.
+ *
+ * @return the obtained array of strings.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[] readStringData (URL url) throws IOException {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readStringData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData((File)) readDoubleData},
+ * for reading strings.
+ *
+ * @param file the file object represented to file to read.
+ *
+ * @return the array of strings.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[] readStringData (File file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readStringData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData((String)) readDoubleData},
+ * for reading strings.
+ *
+ * @param file the name of the file to read.
+ *
+ * @return the array of strings.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[] readStringData (String file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readStringData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * Uses the reader <TT>input</TT> to obtain
+ * a 2-dimensional array of double-precision values.
+ * For each line of text obtained from the
+ * given reader, this method
+ * trims whitespaces, and parses the
+ * remaining text as an array of double-precision values.
+ * Every character
+ * other than the digits, the plus (<TT>+</TT>) and minus (<TT>-</TT>) signs,
+ * the period (<TT>.</TT>),
+ * and the letters <TT>e</TT> and <TT>E</TT> are
+ * ignored and can be used to separate
+ * numbers on a line.
+ * Moreover, lines starting with a pound sign (<TT>#</TT>)
+ * are considered as comments and thus skipped. The lines
+ * containing only a semicolon sign (<TT>;</TT>) are considered
+ * as empty lines.
+ * The method returns a 2D array containing
+ * all the parsed values.
+ * The returned array is not always rectangular.
+ *
+ * @param input the reader to obtain data from.
+ *
+ * @return the 2D array of double-precison values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[][] readDoubleData2D (Reader input)
+ throws IOException {
+ LineNumberReader inb = new LineNumberReader (input);
+ double[][] data = new double[5][];
+ int n = 0;
+ String li;
+ String number;
+
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ if (li.equals(";")) {
+ data[n++] = new double[0];
+ }
+ else {
+
+ int index = 0;
+ int begin = 0;
+ boolean end = false;
+
+ double[] row = new double[5];
+ int k = 0;
+
+ while (index < li.length() && (! end))
+ {
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ if (index >= li.length() || (Character.isWhitespace (li.charAt (index))))
+ {
+ number = li.substring (begin, index);
+ begin = ++index;
+
+ if (! number.equals("")) {
+ try {
+ row[k++] = Double.parseDouble (number);
+ if (k >= row.length) {
+ double[] newRow = new double[2*k];
+ System.arraycopy (row, 0, newRow, 0, row.length);
+ row = newRow;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid column " + k + " at line " + inb.getLineNumber() + ": " + number);
+ }
+ }
+ }
+ else {
+ end = true;
+ }
+ }
+
+ if (k > 0) {
+ data[n] = new double[k];
+ System.arraycopy (row, 0, data[n], 0, k);
+ n++;
+ }
+ else {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+
+ if (n == data.length) {
+ double[][] newData = new double[2*n][];
+ System.arraycopy (data, 0, newData, 0, n);
+ data = newData;
+ }
+ }
+
+ double[][] data2 = new double[n][];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+
+
+ /**
+ * Connects to the URL referred to by the URL object <TT>url</TT>,
+ * and calls {@link #readDoubleData2D((Reader)) readDoubleData2D} to
+ * obtain a matrix of double-precision values from
+ * the resource.
+ *
+ * @param url the URL object representing the resource to read.
+ *
+ * @return the obtained matrix of double-precision values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[][] readDoubleData2D (URL url) throws IOException {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readDoubleData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * Opens the file referred to by the file object <TT>file</TT>,
+ * and calls {@link #readDoubleData2D((Reader)) readDoubleData2D} to
+ * obtain a matrix of double-precision values from
+ * the file.
+ *
+ * @param file the file object representing the file to read.
+ *
+ * @return the obtained matrix of double-precision values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[][] readDoubleData2D (File file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * Opens the file with name <TT>file</TT>,
+ * and calls {@link #readDoubleData2D((Reader)) readDoubleData2D} to
+ * obtain a matrix of double-precision values from
+ * the file.
+ *
+ * @param file the name of the file to read.
+ *
+ * @return the obtained matrix of double-precision values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static double[][] readDoubleData2D (String file)
+ throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData2D((Reader)) readDoubleData2D},
+ * for reading integers.
+ *
+ * @param input the reader to obtain data from.
+ *
+ * @return the obtained 2D array of integers.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[][] readIntData2D (Reader input) throws IOException {
+ LineNumberReader inb = new LineNumberReader (input);
+ int[][] data = new int[5][];
+ int n = 0;
+ String li;
+ String number;
+
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ if (li.equals(";")) {
+ data[n++] = new int[0];
+ }
+ else {
+
+ int index = 0;
+ int begin = 0;
+ boolean end = false;
+
+ int[] row = new int[5];
+ int k = 0;
+
+ while (index < li.length() && (! end))
+ {
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ Character.isDigit (li.charAt (index))))
+ ++index;
+
+ if (index >= li.length() || (Character.isWhitespace (li.charAt (index))))
+ {
+ number = li.substring (begin, index);
+ begin = ++index;
+
+ if (! number.equals("")) {
+ try {
+ row[k++] = Integer.parseInt (number);
+ if (k >= row.length) {
+ int[] newRow = new int[2*k];
+ System.arraycopy (row, 0, newRow, 0, row.length);
+ row = newRow;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid column " + k + " at line " + inb.getLineNumber() + ": " + number);
+ }
+ }
+ }
+ else {
+ end = true;
+ }
+ }
+
+ if (k > 0) {
+ data[n] = new int[k];
+ System.arraycopy (row, 0, data[n], 0, k);
+ n++;
+ }
+ else {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+
+ if (n == data.length) {
+ int[][] newData = new int[2*n][];
+ System.arraycopy (data, 0, newData, 0, n);
+ data = newData;
+ }
+ }
+
+ int[][] data2 = new int[n][];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+
+
+ /**
+ * Connects to the URL referred to by the URL object <TT>url</TT>,
+ * and calls {@link #readDoubleData((Reader)) readDoubleData} to
+ * obtain a matrix of integers from
+ * the resource.
+ *
+ * @param url the URL object representing the resource to read.
+ *
+ * @return the obtained matrix of integers.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[][] readIntData2D (URL url) throws IOException {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readIntData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData2D((File)) readDoubleData2D},
+ * for reading integers.
+ *
+ * @param file the file object represented to file to read.
+ *
+ * @return the obtained matrix of integer values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[][] readIntData2D (File file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData2D((String)) readDoubleData2D},
+ * for reading integers.
+ *
+ * @param file the name of the file to read.
+ *
+ * @return the obtained matrix of integer values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static int[][] readIntData2D (String file) throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * Reads comma-separated values (CSV) from reader <TT>input</TT>, and
+ * returns a 2D array of strings corresponding to the read data.
+ * Lines are delimited using line separators <TT>r</TT>,
+ * <TT>n</TT>, and <TT>rn</TT>.
+ * Each line contains one or more values, separated by the column
+ * delimiter <TT>colDelim</TT>.
+ * If a string of characters is surrounded with the string delimiter
+ * <TT>stringDelim</TT>, any line separator and column separator appear in
+ * the string. The string delimiter can be inserted in such a string by
+ * putting it twice.
+ * Usually, the column delimiter is the comma, and the string delimiter is
+ * the quotation mark. The following example uses these default
+ * delimiters.
+ * <PRE>
+ * "One","Two","Three"
+ * 1,2,3
+ * "String with "" delimiter",n,m
+ * </PRE>
+ * This produces a matrix of strings with dimensions <SPAN CLASS="MATH">3×3</SPAN>.
+ * The first row contains the strings <TT>One</TT>, <TT>Two</TT>, and <TT>Three</TT>
+ * while the second row contains the strings <TT>1</TT>, <TT>2</TT>, and <TT>3</TT>.
+ * The first column of the last row contains the string
+ * <TT>String with " delimiter</TT>.
+ *
+ * @param input the reader to obtain data from.
+ *
+ * @param colDelim the column delimiter.
+ *
+ * @param stringDelim the string delimiter.
+ *
+ * @return the obtained 2D array of strings.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[][] readCSVData (Reader input, char colDelim,
+ char stringDelim)
+ throws IOException {
+ // Using a buffered reader is important here for performance
+ // LineNumberReader is a subclass of BufferedReader
+ LineNumberReader inb = new LineNumberReader (input);
+ StringBuffer sb = new StringBuffer();
+ boolean stringMode = false;
+ String[][] data = new String[5][];
+ int numRows = 0;
+ int numColumns = 0;
+ boolean newRow = false, newColumn = false;
+ int ich = -1;
+ char ch = ' ';
+ boolean readDone = false;
+ while (!readDone) {
+ if (ich == -2)
+ // A character is pending
+ ich = 0;
+ else {
+ ich = inb.read();
+ if (ich == -1)
+ // End of stream: process the last column and row, and exit
+ newRow = newColumn = readDone = true;
+ else
+ ch = (char)ich;
+ }
+ if (ich != -1) {
+ if (stringMode) {
+ if (ch == stringDelim) {
+ // Check if there is a second string delimiter
+ int ichNext = inb.read();
+ if (ichNext >= 0) {
+ char chNext = (char)ichNext;
+ if (chNext == stringDelim)
+ // Append the quoted string delimiter
+ sb.append (stringDelim);
+ else {
+ // Indicate the end of the string, and a new pending character
+ stringMode = false;
+ ich = -2;
+ ch = chNext;
+ }
+ }
+ }
+ else
+ sb.append (ch);
+ }
+ else {
+ if (ch == '\n' || ch == '\r') {
+ int ichNext = inb.read();
+ if (ichNext >= 0) {
+ char chNext = (char)ichNext;
+ if (ch == '\r' && chNext == '\n') {
+ ichNext = inb.read();
+ if (ichNext >= 0) {
+ chNext = (char)ichNext;
+ ich = -2;
+ ch = chNext;
+ newRow = true;
+ }
+ }
+ else {
+ ich = -2;
+ ch = chNext;
+ newRow = true;
+ }
+ }
+ }
+ else if (ch == colDelim)
+ newColumn = true;
+ else if (ch == stringDelim)
+ stringMode = true;
+ else
+ sb.append (ch);
+ }
+ }
+ if (newColumn || newRow) {
+ if (numColumns == 0) {
+ ++numRows;
+ numColumns = 1;
+ }
+ else
+ ++numColumns;
+ if (data.length < numRows) {
+ String[][] newData = new String[2*data.length][];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ if (data[numRows - 1] == null)
+ data[numRows - 1] = new String[5];
+ else if (data[numRows - 1].length < numColumns) {
+ String[] newData = new String[2*data[numRows - 1].length];
+ System.arraycopy (data[numRows - 1], 0, newData, 0, data[numRows - 1].length);
+ data[numRows - 1] = newData;
+ }
+ data[numRows - 1][numColumns - 1] = sb.toString();
+ sb.delete (0, sb.length());
+ newColumn = false;
+ }
+ if (newRow) {
+ if (data[numRows - 1].length != numColumns) {
+ String[] data2 = new String[numColumns];
+ System.arraycopy (data[numRows - 1], 0, data2, 0, numColumns);
+ data[numRows - 1] = data2;
+ }
+ numColumns = 0;
+ newRow = false;
+ }
+ }
+
+ if (stringMode)
+ throw new IllegalArgumentException ("Too many string delimiters " + stringDelim);
+ if (data.length != numRows) {
+ String[][] data2 = new String[numRows][];
+ System.arraycopy (data, 0, data2, 0, numRows);
+ return data2;
+ }
+ return data;
+ }
+
+
+ /**
+ * Connects to the URL referred to by the URL object <TT>url</TT>,
+ * and calls {@link #readCSVData((Reader,char,char)) readCSVData} to
+ * obtain a matrix of strings from
+ * the resource.
+ *
+ * @param url the URL object representing the resource to read.
+ *
+ * @param colDelim the column delimiter.
+ *
+ * @param stringDelim the string delimiter.
+ *
+ * @return the obtained matrix of strings.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[][] readCSVData (URL url, char colDelim,
+ char stringDelim)
+ throws IOException {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readCSVData (reader, colDelim, stringDelim);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData2D((File)) readDoubleData2D},
+ * for reading strings.
+ *
+ * @param file the file object represented to file to read.
+ *
+ * @param colDelim the column delimiter.
+ *
+ * @param stringDelim the string delimiter.
+ *
+ * @return the obtained matrix of string values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[][] readCSVData (File file, char colDelim,
+ char stringDelim)
+ throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readCSVData (reader, colDelim, stringDelim);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+
+ /**
+ * This is equivalent to {@link #readDoubleData2D((String)) readDoubleData2D},
+ * for reading strings.
+ *
+ * @param file the name of the file to read.
+ *
+ * @param colDelim the column delimiter.
+ *
+ * @param stringDelim the string delimiter.
+ *
+ * @return the obtained matrix of string values.
+ * @exception IOException if an I/O error occurs.
+ *
+ *
+ */
+ public static String[][] readCSVData (String file, char colDelim,
+ char stringDelim)
+ throws IOException {
+ FileReader reader = new FileReader (file);
+ try {
+ return readCSVData (reader, colDelim, stringDelim);
+ }
+ finally {
+ reader.close();
+ }
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/TextDataReader.tex b/source/umontreal/iro/lecuyer/util/TextDataReader.tex
new file mode 100644
index 0000000..f273122
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TextDataReader.tex
@@ -0,0 +1,946 @@
+\defmodule{TextDataReader}
+
+Provides static methods to read data from text files.
+
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: TextDataReader
+ * Description: Provides static methods to read data from text files
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+
+import java.io.LineNumberReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URL;
+import java.util.logging.Logger;
+\end{hide}
+
+
+public class TextDataReader\begin{hide} {
+ private static Logger log = Logger.getLogger ("umontreal.iro.lecuyer.util");
+\end{hide}
+
+ public static double[] readDoubleData (Reader input) throws IOException\begin{hide} {
+ LineNumberReader inb = new LineNumberReader (input);
+ double[] data = new double[5];
+ int n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Double.parseDouble (li);
+ if (n >= data.length) {
+ double[] newData = new double[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+ }
+ if (data.length != n) {
+ double[] data2 = new double[n];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+ return data;
+ }\end{hide}
+\end{code}
+\begin{tabb} Reads an array of double-precision values from the
+ reader \texttt{input}.
+ For each line of text obtained from the
+ given reader, this method
+ trims whitespaces, and parses the
+ remaining text as a double-precision value.
+ This method ignores every character
+ other than the digits, the plus and minus signs,
+ the period (\texttt{.}),
+ and the letters \texttt{e} and \texttt{E}.
+ Moreover, lines starting with a pound sign (\texttt{\#})
+ are considered as comments and thus skipped.
+ The method returns an array containing
+ all the parsed values.
+\end{tabb}
+\begin{htmlonly}
+ \param{input}{the reader to obtain data from.}
+ \return{the obtained array of double-precision values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] readDoubleData (URL url) throws IOException\begin{hide} {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readDoubleData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Connects to the URL referred to by the URL object \texttt{url},
+ and calls \method{readDoubleData}{(Reader)} to
+ obtain an array of double-precision values from
+ the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{url}{the URL object representing the resource to read.}
+ \return{the obtained array of double-precision values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] readDoubleData (File file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Opens the file referred to by the file object \texttt{file},
+ and calls \method{readDoubleData}{(Reader)} to
+ obtain an array of double-precision values from
+ the file.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the file object representing the file to read.}
+ \return{the obtained array of double-precision values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[] readDoubleData (String file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Opens the file with name \texttt{file},
+ and calls \method{readDoubleData}{(Reader)} to
+ obtain an array of double-precision values from
+ the file.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the name of the file to read.}
+ \return{the obtained array of double-precision values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[] readIntData (Reader input) throws IOException\begin{hide} {
+ LineNumberReader inb = new LineNumberReader (input);
+ int[] data = new int[5];
+ int n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ // look for the first non-digit character on the read line
+ int index = 0;
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ Character.isDigit (li.charAt (index))))
+ ++index;
+
+ // truncate the line
+ li = li.substring (0, index);
+ if (!li.equals ("")) {
+ try {
+ data[n++] = Integer.parseInt (li);
+ if (n >= data.length) {
+ int[] newData = new int[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+ }
+ if (data.length != n) {
+ int[] data2 = new int[n];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+ return data;
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData}{(Reader)},
+ for reading integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{input}{the reader to obtain data from.}
+ \return{the obtained array of integers.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[] readIntData (URL url) throws IOException\begin{hide} {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readIntData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Connects to the URL referred to by the URL object \texttt{url},
+ and calls \method{readIntData}{(Reader)} to
+ obtain an array of integers from
+ the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{url}{the URL object representing the resource to read.}
+ \return{the obtained array of integers.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[] readIntData (File file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData}{(File)},
+ for reading integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the file object represented to file to read.}
+ \return{the array of integers.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[] readIntData (String file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData}{(String)},
+ for reading integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the name of the file to read.}
+ \return{the array of integers.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[] readStringData (Reader input) throws IOException\begin{hide} {
+ LineNumberReader inb = new LineNumberReader (input);
+ String[] data = new String[5];
+ int n = 0;
+ String li;
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ data[n++] = li;
+ if (n >= data.length) {
+ String[] newData = new String[2*n];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ }
+ if (data.length != n) {
+ String[] data2 = new String[n];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }
+ return data;
+ }\end{hide}
+\end{code}
+\begin{tabb} Reads an array of strings from the
+ reader \texttt{input}.
+ For each line of text obtained from the
+ given reader, this method
+ trims leading and trailing whitespaces, and stores the remaining string.
+ Lines starting with a pound sign (\texttt{\#})
+ are considered as comments and thus skipped.
+ The method returns an array containing
+ all the read strings.
+\end{tabb}
+\begin{htmlonly}
+ \param{input}{the reader to obtain data from.}
+ \return{the obtained array of strings.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[] readStringData (URL url) throws IOException\begin{hide} {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readStringData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Connects to the URL referred to by the URL object \texttt{url},
+ and calls \method{readStringData}{(Reader)} to
+ obtain an array of integers from
+ the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{url}{the URL object representing the resource to read.}
+ \return{the obtained array of strings.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[] readStringData (File file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readStringData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData}{(File)},
+ for reading strings.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the file object represented to file to read.}
+ \return{the array of strings.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[] readStringData (String file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readStringData (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData}{(String)},
+ for reading strings.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the name of the file to read.}
+ \return{the array of strings.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] readDoubleData2D (Reader input)
+ throws IOException\begin{hide} {
+ LineNumberReader inb = new LineNumberReader (input);
+ double[][] data = new double[5][];
+ int n = 0;
+ String li;
+ String number;
+
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ if (li.equals(";")) {
+ data[n++] = new double[0];
+ }
+ else {
+
+ int index = 0;
+ int begin = 0;
+ boolean end = false;
+
+ double[] row = new double[5];
+ int k = 0;
+
+ while (index < li.length() && (! end))
+ {
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ li.charAt (index) == 'e' || li.charAt (index) == 'E' ||
+ li.charAt (index) == '.' || Character.isDigit (li.charAt (index))))
+ ++index;
+
+ if (index >= li.length() || (Character.isWhitespace (li.charAt (index))))
+ {
+ number = li.substring (begin, index);
+ begin = ++index;
+
+ if (! number.equals("")) {
+ try {
+ row[k++] = Double.parseDouble (number);
+ if (k >= row.length) {
+ double[] newRow = new double[2*k];
+ System.arraycopy (row, 0, newRow, 0, row.length);
+ row = newRow;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid column " + k + " at line " + inb.getLineNumber() + ": " + number);
+ }
+ }
+ }
+ else {
+ end = true;
+ }
+ }
+
+ if (k > 0) {
+ data[n] = new double[k];
+ System.arraycopy (row, 0, data[n], 0, k);
+ n++;
+ }
+ else {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+
+ if (n == data.length) {
+ double[][] newData = new double[2*n][];
+ System.arraycopy (data, 0, newData, 0, n);
+ data = newData;
+ }
+ }
+
+ double[][] data2 = new double[n][];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }\end{hide}
+\end{code}
+\begin{tabb} Uses the reader \texttt{input} to obtain
+ a 2-dimensional array of double-precision values.
+ For each line of text obtained from the
+ given reader, this method
+ trims whitespaces, and parses the
+ remaining text as an array of double-precision values.
+ Every character
+ other than the digits, the plus (\texttt{+}) and minus (\texttt{-}) signs,
+ the period (\texttt{.}),
+ and the letters \texttt{e} and \texttt{E} are
+ ignored and can be used to separate
+ numbers on a line.
+ Moreover, lines starting with a pound sign (\texttt{\#})
+ are considered as comments and thus skipped. The lines
+ containing only a semicolon sign (\texttt{;}) are considered
+ as empty lines.
+ The method returns a 2D array containing
+ all the parsed values.
+ The returned array is not always rectangular.
+\end{tabb}
+\begin{htmlonly}
+ \param{input}{the reader to obtain data from.}
+ \return{the 2D array of double-precison values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] readDoubleData2D (URL url) throws IOException\begin{hide} {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readDoubleData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Connects to the URL referred to by the URL object \texttt{url},
+ and calls \method{readDoubleData2D}{(Reader)} to
+ obtain a matrix of double-precision values from
+ the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{url}{the URL object representing the resource to read.}
+ \return{the obtained matrix of double-precision values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] readDoubleData2D (File file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Opens the file referred to by the file object \texttt{file},
+ and calls \method{readDoubleData2D}{(Reader)} to
+ obtain a matrix of double-precision values from
+ the file.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the file object representing the file to read.}
+ \return{the obtained matrix of double-precision values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static double[][] readDoubleData2D (String file)
+ throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readDoubleData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Opens the file with name \texttt{file},
+ and calls \method{readDoubleData2D}{(Reader)} to
+ obtain a matrix of double-precision values from
+ the file.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the name of the file to read.}
+ \return{the obtained matrix of double-precision values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[][] readIntData2D (Reader input) throws IOException\begin{hide} {
+ LineNumberReader inb = new LineNumberReader (input);
+ int[][] data = new int[5][];
+ int n = 0;
+ String li;
+ String number;
+
+ while ((li = inb.readLine()) != null) {
+ li = li.trim();
+ if (li.startsWith ("#"))
+ continue;
+
+ if (li.equals(";")) {
+ data[n++] = new int[0];
+ }
+ else {
+
+ int index = 0;
+ int begin = 0;
+ boolean end = false;
+
+ int[] row = new int[5];
+ int k = 0;
+
+ while (index < li.length() && (! end))
+ {
+ while (index < li.length() &&
+ (li.charAt (index) == '+' || li.charAt (index) == '-' ||
+ Character.isDigit (li.charAt (index))))
+ ++index;
+
+ if (index >= li.length() || (Character.isWhitespace (li.charAt (index))))
+ {
+ number = li.substring (begin, index);
+ begin = ++index;
+
+ if (! number.equals("")) {
+ try {
+ row[k++] = Integer.parseInt (number);
+ if (k >= row.length) {
+ int[] newRow = new int[2*k];
+ System.arraycopy (row, 0, newRow, 0, row.length);
+ row = newRow;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ log.warning ("Invalid column " + k + " at line " + inb.getLineNumber() + ": " + number);
+ }
+ }
+ }
+ else {
+ end = true;
+ }
+ }
+
+ if (k > 0) {
+ data[n] = new int[k];
+ System.arraycopy (row, 0, data[n], 0, k);
+ n++;
+ }
+ else {
+ log.warning ("Invalid line " + inb.getLineNumber() + ": " + li);
+ }
+ }
+
+ if (n == data.length) {
+ int[][] newData = new int[2*n][];
+ System.arraycopy (data, 0, newData, 0, n);
+ data = newData;
+ }
+ }
+
+ int[][] data2 = new int[n][];
+ System.arraycopy (data, 0, data2, 0, n);
+ return data2;
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData2D}{(Reader)},
+ for reading integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{input}{the reader to obtain data from.}
+ \return{the obtained 2D array of integers.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[][] readIntData2D (URL url) throws IOException\begin{hide} {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readIntData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Connects to the URL referred to by the URL object \texttt{url},
+ and calls \method{readDoubleData}{(Reader)} to
+ obtain a matrix of integers from
+ the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{url}{the URL object representing the resource to read.}
+ \return{the obtained matrix of integers.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[][] readIntData2D (File file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData2D}{(File)},
+ for reading integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the file object represented to file to read.}
+ \return{the obtained matrix of integer values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static int[][] readIntData2D (String file) throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readIntData2D (reader);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData2D}{(String)},
+ for reading integers.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the name of the file to read.}
+ \return{the obtained matrix of integer values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[][] readCSVData (Reader input, char colDelim,
+ char stringDelim)
+ throws IOException\begin{hide} {
+ // Using a buffered reader is important here for performance
+ // LineNumberReader is a subclass of BufferedReader
+ LineNumberReader inb = new LineNumberReader (input);
+ StringBuffer sb = new StringBuffer();
+ boolean stringMode = false;
+ String[][] data = new String[5][];
+ int numRows = 0;
+ int numColumns = 0;
+ boolean newRow = false, newColumn = false;
+ int ich = -1;
+ char ch = ' ';
+ boolean readDone = false;
+ while (!readDone) {
+ if (ich == -2)
+ // A character is pending
+ ich = 0;
+ else {
+ ich = inb.read();
+ if (ich == -1)
+ // End of stream: process the last column and row, and exit
+ newRow = newColumn = readDone = true;
+ else
+ ch = (char)ich;
+ }
+ if (ich != -1) {
+ if (stringMode) {
+ if (ch == stringDelim) {
+ // Check if there is a second string delimiter
+ int ichNext = inb.read();
+ if (ichNext >= 0) {
+ char chNext = (char)ichNext;
+ if (chNext == stringDelim)
+ // Append the quoted string delimiter
+ sb.append (stringDelim);
+ else {
+ // Indicate the end of the string, and a new pending character
+ stringMode = false;
+ ich = -2;
+ ch = chNext;
+ }
+ }
+ }
+ else
+ sb.append (ch);
+ }
+ else {
+ if (ch == '\n' || ch == '\r') {
+ int ichNext = inb.read();
+ if (ichNext >= 0) {
+ char chNext = (char)ichNext;
+ if (ch == '\r' && chNext == '\n') {
+ ichNext = inb.read();
+ if (ichNext >= 0) {
+ chNext = (char)ichNext;
+ ich = -2;
+ ch = chNext;
+ newRow = true;
+ }
+ }
+ else {
+ ich = -2;
+ ch = chNext;
+ newRow = true;
+ }
+ }
+ }
+ else if (ch == colDelim)
+ newColumn = true;
+ else if (ch == stringDelim)
+ stringMode = true;
+ else
+ sb.append (ch);
+ }
+ }
+ if (newColumn || newRow) {
+ if (numColumns == 0) {
+ ++numRows;
+ numColumns = 1;
+ }
+ else
+ ++numColumns;
+ if (data.length < numRows) {
+ String[][] newData = new String[2*data.length][];
+ System.arraycopy (data, 0, newData, 0, data.length);
+ data = newData;
+ }
+ if (data[numRows - 1] == null)
+ data[numRows - 1] = new String[5];
+ else if (data[numRows - 1].length < numColumns) {
+ String[] newData = new String[2*data[numRows - 1].length];
+ System.arraycopy (data[numRows - 1], 0, newData, 0, data[numRows - 1].length);
+ data[numRows - 1] = newData;
+ }
+ data[numRows - 1][numColumns - 1] = sb.toString();
+ sb.delete (0, sb.length());
+ newColumn = false;
+ }
+ if (newRow) {
+ if (data[numRows - 1].length != numColumns) {
+ String[] data2 = new String[numColumns];
+ System.arraycopy (data[numRows - 1], 0, data2, 0, numColumns);
+ data[numRows - 1] = data2;
+ }
+ numColumns = 0;
+ newRow = false;
+ }
+ }
+
+ if (stringMode)
+ throw new IllegalArgumentException ("Too many string delimiters " + stringDelim);
+ if (data.length != numRows) {
+ String[][] data2 = new String[numRows][];
+ System.arraycopy (data, 0, data2, 0, numRows);
+ return data2;
+ }
+ return data;
+ }\end{hide}
+\end{code}
+\begin{tabb} Reads comma-separated values (CSV) from reader \texttt{input}, and
+ returns a 2D array of strings corresponding to the read data.
+ Lines are delimited using line separators \texttt{\bs r},
+ \texttt{\bs n}, and \texttt{\bs r\bs n}.
+ Each line contains one or more values, separated by the column
+ delimiter \texttt{colDelim}.
+ If a string of characters is surrounded with the string delimiter
+ \texttt{stringDelim}, any line separator and column separator appear in
+ the string. The string delimiter can be inserted in such a string by
+ putting it twice.
+ Usually, the column delimiter is the comma, and the string delimiter is
+ the quotation mark. The following example uses these default
+ delimiters.
+\begin{verbatim}
+ "One","Two","Three"
+ 1,2,3
+ "String with "" delimiter",n,m
+\end{verbatim}
+This produces a matrix of strings with dimensions $3\times 3$.
+The first row contains the strings \texttt{One}, \texttt{Two}, and \texttt{Three}
+while the second row contains the strings \texttt{1}, \texttt{2}, and \texttt{3}.
+The first column of the last row contains the string
+\texttt{String with " delimiter}.
+\end{tabb}
+\begin{htmlonly}
+ \param{input}{the reader to obtain data from.}
+ \param{colDelim}{the column delimiter.}
+ \param{stringDelim}{the string delimiter.}
+ \return{the obtained 2D array of strings.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[][] readCSVData (URL url, char colDelim,
+ char stringDelim)
+ throws IOException\begin{hide} {
+ Reader reader = new InputStreamReader (url.openStream());
+ try {
+ return readCSVData (reader, colDelim, stringDelim);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} Connects to the URL referred to by the URL object \texttt{url},
+ and calls \method{readCSVData}{(Reader,char,char)} to
+ obtain a matrix of strings from
+ the resource.
+\end{tabb}
+\begin{htmlonly}
+ \param{url}{the URL object representing the resource to read.}
+ \param{colDelim}{the column delimiter.}
+ \param{stringDelim}{the string delimiter.}
+ \return{the obtained matrix of strings.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[][] readCSVData (File file, char colDelim,
+ char stringDelim)
+ throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readCSVData (reader, colDelim, stringDelim);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData2D}{(File)},
+ for reading strings.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the file object represented to file to read.}
+ \param{colDelim}{the column delimiter.}
+ \param{stringDelim}{the string delimiter.}
+ \return{the obtained matrix of string values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}
+
+ public static String[][] readCSVData (String file, char colDelim,
+ char stringDelim)
+ throws IOException\begin{hide} {
+ FileReader reader = new FileReader (file);
+ try {
+ return readCSVData (reader, colDelim, stringDelim);
+ }
+ finally {
+ reader.close();
+ }
+ }\end{hide}
+\end{code}
+\begin{tabb} This is equivalent to \method{readDoubleData2D}{(String)},
+ for reading strings.
+\end{tabb}
+\begin{htmlonly}
+ \param{file}{the name of the file to read.}
+ \param{colDelim}{the column delimiter.}
+ \param{stringDelim}{the string delimiter.}
+ \return{the obtained matrix of string values.}
+ \exception{IOException}{if an I/O error occurs.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/ThreadCPUTimeChrono.java b/source/umontreal/iro/lecuyer/util/ThreadCPUTimeChrono.java
new file mode 100644
index 0000000..a09eaf8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ThreadCPUTimeChrono.java
@@ -0,0 +1,110 @@
+
+
+/*
+ * Class: ThreadCPUTimeChrono
+ * Description: Compute the CPU time for a single thread
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+
+
+/**
+ * Extends the {@link AbstractChrono}
+ * class to compute the CPU time for a single thread. It is available
+ * only under Java 1.5 which provides platform-independent
+ * facilities to get the CPU time for a single thread through management API.
+ *
+ * <P>
+ * Note that this chrono might not work properly on some systems running Linux because
+ * of a bug in Sun's implementation or Linux kernel.
+ * For instance, this class unexpectedly computes the global CPU time
+ * under Fedora
+ * Core 4, kernel 2.6.17 and JRE version 1.5.0-09.
+ * With Fedora Core 6, kernel 2.6.20, the function is working properly.
+ * As a result, one should not rely on this bug to get the global CPU time.
+ *
+ * <P>
+ * Note that the above bug does not prevent one from using this chrono to
+ * compute the CPU time for a single-threaded application. In that case,
+ * the global CPU time corresponds to the CPU time of the current thread.
+ *
+ * <P>
+ * Running timer fonctions when the associated thread is dead will return 0.
+ *
+ */
+public class ThreadCPUTimeChrono extends AbstractChrono {
+ private long myThreadId;
+ static ThreadMXBean threadMXBean = null;
+
+ protected void getTime (long[] tab) {
+ long rawTime = getTime();
+ final long DIV = 1000000000L;
+ long seconds = rawTime/DIV;
+ long micros = (rawTime % DIV)/1000L;
+ tab[0] = seconds;
+ tab[1] = micros;
+ }
+
+ protected long getTime() {
+ if (threadMXBean == null) {
+ // We use lazy initialization to avoid a potential exception being wrapped into a confusing
+ // ExceptionInInitializerError. That would happen if this initialization was in a static block instead of
+ // in this method.
+ threadMXBean = ManagementFactory.getThreadMXBean();
+ if (!threadMXBean.isThreadCpuTimeEnabled())
+ // Call this only when necessary, because this can throw a SecurityException if
+ // run under a security manager.
+ threadMXBean.setThreadCpuTimeEnabled (true);
+ }
+ long time = threadMXBean.getThreadCpuTime(myThreadId);
+ return time < 0 ? 0 : time;
+ }
+
+
+ /**
+ * Constructs a <TT>ThreadCPUTimeChrono</TT> object associated
+ * with current thread and initializes it to zero.
+ *
+ */
+ public ThreadCPUTimeChrono() {
+ super();
+ myThreadId = Thread.currentThread().getId();
+ init();
+ }
+
+
+ /**
+ * Constructs a <TT>ThreadCPUTimeChrono</TT> object associated
+ * with the given {@link Thread} variable and initializes it to zero.
+ *
+ */
+ public ThreadCPUTimeChrono(Thread inThread) {
+ super();
+ myThreadId = inThread.getId();
+ init();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/ThreadCPUTimeChrono.tex b/source/umontreal/iro/lecuyer/util/ThreadCPUTimeChrono.tex
new file mode 100644
index 0000000..0302466
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/ThreadCPUTimeChrono.tex
@@ -0,0 +1,113 @@
+\defmodule{ThreadCPUTimeChrono}
+
+Extends the \class{AbstractChrono}
+class to compute the CPU time for a single thread. It is available
+only under Java 1.5 which provides platform-independent
+facilities to get the CPU time for a single thread through management API.
+
+Note that this chrono might not work properly on some systems running Linux because
+of a bug in Sun's implementation or Linux kernel.
+For instance, this class unexpectedly computes the global CPU time
+under Fedora
+Core~4, kernel 2.6.17 and JRE version 1.5.0-09.
+With Fedora Core~6, kernel 2.6.20, the function is working properly.
+As a result, one should not rely on this bug to get the global CPU time.
+
+Note that the above bug does not prevent one from using this chrono to
+compute the CPU time for a single-threaded application. In that case,
+the global CPU time corresponds to the CPU time of the current thread.
+
+Running timer fonctions when the associated thread is dead will return 0.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\bigskip\hrule
+\begin{code}
+\begin{hide}
+/*
+ * Class: ThreadCPUTimeChrono
+ * Description: Compute the CPU time for a single thread
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author Éric Buist
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;\end{hide}
+
+
+public class ThreadCPUTimeChrono extends AbstractChrono\begin{hide} {
+ private long myThreadId;
+ static ThreadMXBean threadMXBean = null;
+
+ protected void getTime (long[] tab) {
+ long rawTime = getTime();
+ final long DIV = 1000000000L;
+ long seconds = rawTime/DIV;
+ long micros = (rawTime % DIV)/1000L;
+ tab[0] = seconds;
+ tab[1] = micros;
+ }
+
+ protected long getTime() {
+ if (threadMXBean == null) {
+ // We use lazy initialization to avoid a potential exception being wrapped into a confusing
+ // ExceptionInInitializerError. That would happen if this initialization was in a static block instead of
+ // in this method.
+ threadMXBean = ManagementFactory.getThreadMXBean();
+ if (!threadMXBean.isThreadCpuTimeEnabled())
+ // Call this only when necessary, because this can throw a SecurityException if
+ // run under a security manager.
+ threadMXBean.setThreadCpuTimeEnabled (true);
+ }
+ long time = threadMXBean.getThreadCpuTime(myThreadId);
+ return time < 0 ? 0 : time;
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public ThreadCPUTimeChrono()\begin{hide} {
+ super();
+ myThreadId = Thread.currentThread().getId();
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ThreadCPUTimeChrono} object associated
+ with current thread and initializes it to zero.
+ \end{tabb}
+\begin{code}
+
+ public ThreadCPUTimeChrono(Thread inThread)\begin{hide} {
+ super();
+ myThreadId = inThread.getId();
+ init();
+ }\end{hide}
+\end{code}
+ \begin{tabb} Constructs a \texttt{ThreadCPUTimeChrono} object associated
+ with the given \class{Thread} variable and initializes it to zero.
+ \end{tabb}
+\begin{code}
+\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/TimeUnit.java b/source/umontreal/iro/lecuyer/util/TimeUnit.java
new file mode 100644
index 0000000..c6015fc
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TimeUnit.java
@@ -0,0 +1,200 @@
+/**
+ * Represents a time unit for conversion of time durations.
+ * A time unit instance can be used to get information about
+ * the time unit and as a selector to perform conversions.
+ * Each time unit has a short name used when representing a time unit,
+ * a full descriptive name, and the number of hours corresponding to one unit.
+ *
+ */
+
+
+/*
+ * Class: TimeUnit
+ * Description: Represents a time unit for conversion of time durations.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+
+public enum TimeUnit {
+
+
+ /**
+ * Represents a nanosecond which has short name <TT>ns</TT>.
+ *
+ */
+ NANOSECOND
+
+("ns", "nanosecond", 1.0/3600000000000.0),
+
+
+
+ /**
+ * Represents a microsecond which has short name <TT>us</TT>.
+ *
+ */
+ MICROSECOND
+
+("us", "microsecond", 1.0/3600000000.0),
+
+
+
+ /**
+ * Represents a millisecond which has short name <TT>ms</TT>.
+ *
+ */
+ MILLISECOND
+
+("ms", "millisecond", 1.0/3600000.0),
+
+
+
+ /**
+ * Represents a second which has short name <TT>s</TT>.
+ *
+ */
+ SECOND
+
+("s", "second", 1.0/3600.0),
+
+
+
+ /**
+ * Represents a minute which has short name <TT>min</TT>.
+ *
+ */
+ MINUTE
+
+("min", "minute", 1.0/60.0),
+
+
+
+ /**
+ * Represents an hour which has short name <TT>h</TT>.
+ *
+ */
+ HOUR
+
+("h", "hour", 1.0),
+
+
+
+ /**
+ * Represents a day which has short name <TT>d</TT>.
+ *
+ */
+ DAY
+
+("d", "day", 24.0),
+
+
+
+ /**
+ * Represents a week which has short name <TT>w</TT>.
+ *
+ */
+ WEEK
+
+("w", "week", 24.0*7);
+
+
+
+ private String shortName;
+ private String longName;
+ private transient double numHours;
+
+ private TimeUnit (String shortName, String longName, double numHours) {
+ this.shortName = shortName;
+ this.longName = longName;
+ this.numHours = numHours;
+ }
+
+
+ /**
+ * Returns the short name representing this unit in
+ * a string specifying a time duration.
+ *
+ * @return the short name of this time unit.
+ *
+ */
+ public String getShortName() {
+ return shortName;
+ }
+
+
+ /**
+ * Returns the long name of this time unit.
+ *
+ * @return the long name of this time unit.
+ *
+ */
+ public String getLongName() {
+ return longName;
+ }
+
+
+ /**
+ * Calls {@link #getLongName(()) getLongName}.
+ *
+ * @return the result of {@link #getLongName(()) getLongName}.
+ *
+ */
+ public String toString() {
+ return longName;
+ }
+
+
+ /**
+ * Returns this time unit represented in hours.
+ * This returns the number of hours corresponding to
+ * one unit.
+ *
+ * @return the time unit represented in hours.
+ *
+ */
+ public double getHours() {
+ return numHours;
+ }
+
+
+ /**
+ * Converts <TT>value</TT> expressed in time unit <TT>srcUnit</TT> to
+ * a time duration expressed in <TT>dstUnit</TT> and returns
+ * the result of the conversion.
+ *
+ * @param value the value being converted.
+ *
+ * @param srcUnit the source time unit.
+ *
+ * @param dstUnit the destination time unit.
+ *
+ * @return the converted value.
+ * @exception NullPointerException if <TT>srcUnit</TT> or
+ * <TT>dstUnit</TT> are <TT>null</TT>.
+ *
+ */
+ public static double convert (double value, TimeUnit srcUnit,
+ TimeUnit dstUnit) {
+ double hours = value*srcUnit.getHours();
+ return hours/dstUnit.getHours();
+ }
+}
diff --git a/source/umontreal/iro/lecuyer/util/TimeUnit.tex b/source/umontreal/iro/lecuyer/util/TimeUnit.tex
new file mode 100644
index 0000000..7cfacc5
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TimeUnit.tex
@@ -0,0 +1,198 @@
+\defmodule{TimeUnit}
+
+Represents a time unit for conversion of time durations.
+A time unit instance can be used to get information about
+the time unit and as a selector to perform conversions.
+Each time unit has a short name used when representing a time unit,
+a full descriptive name, and the number of hours corresponding to one unit.
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: TimeUnit
+ * Description: Represents a time unit for conversion of time durations.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;
+
+
+public enum TimeUnit\begin{hide} {
+\end{hide}\end{code}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {enum values}
+\begin{code}
+
+ NANOSECOND\begin{hide}
+
+("ns", "nanosecond", 1.0/3600000000000.0),
+\end{hide}
+\end{code}
+\begin{tabb} Represents a nanosecond which has short name \texttt{ns}.
+\end{tabb}
+\begin{code}
+
+ MICROSECOND\begin{hide}
+
+("us", "microsecond", 1.0/3600000000.0),
+\end{hide}
+\end{code}
+\begin{tabb} Represents a microsecond which has short name \texttt{us}.
+\end{tabb}
+\begin{code}
+
+ MILLISECOND\begin{hide}
+
+("ms", "millisecond", 1.0/3600000.0),
+\end{hide}
+\end{code}
+\begin{tabb} Represents a millisecond which has short name \texttt{ms}.
+\end{tabb}
+\begin{code}
+
+ SECOND\begin{hide}
+
+("s", "second", 1.0/3600.0),
+\end{hide}
+\end{code}
+\begin{tabb} Represents a second which has short name \texttt{s}.
+\end{tabb}
+\begin{code}
+
+ MINUTE\begin{hide}
+
+("min", "minute", 1.0/60.0),
+\end{hide}
+\end{code}
+\begin{tabb} Represents a minute which has short name \texttt{min}.
+\end{tabb}
+\begin{code}
+
+ HOUR\begin{hide}
+
+("h", "hour", 1.0),
+\end{hide}
+\end{code}
+\begin{tabb} Represents an hour which has short name \texttt{h}.
+\end{tabb}
+\begin{code}
+
+ DAY\begin{hide}
+
+("d", "day", 24.0),
+\end{hide}
+\end{code}
+\begin{tabb} Represents a day which has short name \texttt{d}.
+\end{tabb}
+\begin{code}
+
+ WEEK\begin{hide}
+
+("w", "week", 24.0*7);
+\end{hide}
+\end{code}
+\begin{tabb} Represents a week which has short name \texttt{w}.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ private String shortName;
+ private String longName;
+ private transient double numHours;
+
+ private TimeUnit (String shortName, String longName, double numHours) {
+ this.shortName = shortName;
+ this.longName = longName;
+ this.numHours = numHours;
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection* {Methods}
+\begin{code}
+
+ public String getShortName()\begin{hide} {
+ return shortName;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the short name representing this unit in
+ a string specifying a time duration.
+\end{tabb}
+\begin{htmlonly}
+ \return{the short name of this time unit.}
+\end{htmlonly}
+\begin{code}
+
+ public String getLongName()\begin{hide} {
+ return longName;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns the long name of this time unit.
+\end{tabb}
+\begin{htmlonly}
+ \return{the long name of this time unit.}
+\end{htmlonly}
+\begin{code}
+
+ public String toString()\begin{hide} {
+ return longName;
+ }\end{hide}
+\end{code}
+\begin{tabb} Calls \method{getLongName}{()}.
+\end{tabb}
+\begin{htmlonly}
+ \return{the result of \method{getLongName}{()}.}
+\end{htmlonly}
+\begin{code}
+
+ public double getHours()\begin{hide} {
+ return numHours;
+ }\end{hide}
+\end{code}
+\begin{tabb} Returns this time unit represented in hours.
+ This returns the number of hours corresponding to
+ one unit.
+\end{tabb}
+\begin{htmlonly}
+ \return{the time unit represented in hours.}
+\end{htmlonly}
+\begin{code}
+
+ public static double convert (double value, TimeUnit srcUnit,
+ TimeUnit dstUnit)\begin{hide} {
+ double hours = value*srcUnit.getHours();
+ return hours/dstUnit.getHours();
+ }
+}\end{hide}
+\end{code}
+\begin{tabb} Converts \texttt{value} expressed in time unit \texttt{srcUnit} to
+ a time duration expressed in \texttt{dstUnit} and returns
+ the result of the conversion.
+\end{tabb}
+\begin{htmlonly}
+ \param{value}{the value being converted.}
+ \param{srcUnit}{the source time unit.}
+ \param{dstUnit}{the destination time unit.}
+ \return{the converted value.}
+ \exception{NullPointerException}{if \texttt{srcUnit} or
+ \texttt{dstUnit} are \texttt{null}.}
+\end{htmlonly}
diff --git a/source/umontreal/iro/lecuyer/util/TransformingList.java b/source/umontreal/iro/lecuyer/util/TransformingList.java
new file mode 100644
index 0000000..f0bbe6c
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TransformingList.java
@@ -0,0 +1,208 @@
+
+
+/*
+ * Class: TransformingList
+ * Description: List that dynamically transforms the elements of another list.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util;
+
+import java.util.AbstractList;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ListIterator;
+
+
+
+/**
+ * Represents a list that dynamically transforms the elements
+ * of another list.
+ * This abstract class defines a list
+ * containing an inner list of elements of a certain type,
+ * and provides facilities to convert these
+ * inner elements to outer elements of another
+ * type.
+ * A concrete subclass simply needs to provide
+ * methods for converting between the inner
+ * and the outer types. at param <IE> the inner type of the elements.
+ *
+ * @param <OE> the type of the outer elements.
+ *
+ */
+public abstract class TransformingList<OE,IE> extends AbstractList<OE> {
+ private List<IE> fromList;
+
+
+ /**
+ * Creates a new transforming list wrapping
+ * the inner list <TT>fromList</TT>.
+ *
+ * @param fromList the inner list.
+ *
+ *
+ */
+ public TransformingList (List<IE> fromList) {
+ this.fromList = fromList;
+ }
+
+
+ public List<IE> getInnerList() {
+ return fromList;
+ }
+
+ /**
+ * Converts an element in the inner list to
+ * an element of the outer type.
+ *
+ * @param e the inner element.
+ *
+ * @return the outer element.
+ *
+ */
+ public abstract OE convertFromInnerType (IE e);
+
+
+ /**
+ * Converts an element of the outer type to
+ * an element for the inner list.
+ *
+ * @param e the outer element.
+ *
+ * @return the inner element.
+ *
+ */
+ public abstract IE convertToInnerType (OE e);
+
+
+ public void add (int index, OE element) {
+ IE fe = convertToInnerType (element);
+ fromList.add (fe);
+ }
+
+ @Override
+ public void clear () {
+ fromList.clear();
+ }
+
+ @Override
+ public OE get (int index) {
+ return convertFromInnerType (fromList.get (index));
+ }
+
+ public Iterator<OE> iterator() {
+ return new MyIterator (fromList.iterator());
+ }
+
+ public ListIterator<OE> listIterator() {
+ return new MyListIterator (fromList.listIterator());
+ }
+
+ public ListIterator<OE> listIterator (int index) {
+ return new MyListIterator (fromList.listIterator (index));
+ }
+
+ @Override
+ public OE remove (int index) {
+ return convertFromInnerType (fromList.remove (index));
+ }
+
+ @Override
+ public OE set (int index, OE element) {
+ IE from = convertToInnerType (element);
+ from = fromList.set (index, from);
+ return convertFromInnerType (from);
+ }
+
+ @Override
+ public int size () {
+ return fromList.size();
+ }
+
+ private class MyIterator implements Iterator<OE> {
+ private Iterator<IE> itr;
+
+ public MyIterator (Iterator<IE> itr) {
+ this.itr = itr;
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public OE next() {
+ return convertFromInnerType (itr.next());
+ }
+
+ public void remove() {
+ itr.remove();
+ }
+ }
+
+ private class MyListIterator implements ListIterator<OE> {
+ private ListIterator<IE> itr;
+
+ public MyListIterator (ListIterator<IE> itr) {
+ this.itr = itr;
+ }
+
+ public void add (OE o) {
+ IE fe = convertToInnerType (o);
+ itr.add (fe);
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public boolean hasPrevious() {
+ return itr.hasPrevious();
+ }
+
+ public OE next() {
+ return convertFromInnerType (itr.next());
+ }
+
+ public int nextIndex() {
+ return itr.nextIndex();
+ }
+
+ public OE previous() {
+ return convertFromInnerType (itr.previous());
+ }
+
+ public int previousIndex() {
+ return itr.previousIndex();
+ }
+
+ public void remove() {
+ itr.remove();
+ }
+
+ public void set (OE o) {
+ IE fe = convertToInnerType (o);
+ itr.set (fe);
+ }
+ }
+
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/TransformingList.tex b/source/umontreal/iro/lecuyer/util/TransformingList.tex
new file mode 100644
index 0000000..a1963c2
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/TransformingList.tex
@@ -0,0 +1,212 @@
+\defmodule{TransformingList}
+
+Represents a list that dynamically transforms the elements
+of another list.
+This abstract class defines a list
+containing an inner list of elements of a certain type,
+and provides facilities to convert these
+inner elements to outer elements of another
+type.
+A concrete subclass simply needs to provide
+methods for converting between the inner
+and the outer types.\begin{htmlonly}
+ \param{<IE>}{the inner type of the elements.}
+ \param{<OE>}{the type of the outer elements.}
+\end{htmlonly}
+
+
+\bigskip\hrule
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: TransformingList
+ * Description: List that dynamically transforms the elements of another list.
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author
+ * @since
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util;\begin{hide}
+
+import java.util.AbstractList;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ListIterator;
+\end{hide}
+
+
+public abstract class TransformingList<OE,IE> extends AbstractList<OE>\begin{hide} {
+ private List<IE> fromList;
+\end{hide}
+
+ public TransformingList (List<IE> fromList)\begin{hide} {
+ this.fromList = fromList;
+ }\end{hide}
+\end{code}
+\begin{tabb} Creates a new transforming list wrapping
+ the inner list \texttt{fromList}.
+\end{tabb}
+\begin{htmlonly}
+ \param{fromList}{the inner list.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public List<IE> getInnerList() {
+ return fromList;
+ }\end{hide}
+
+ public abstract OE convertFromInnerType (IE e)\begin{hide};\end{hide}
+\end{code}
+\begin{tabb} Converts an element in the inner list to
+ an element of the outer type.
+\end{tabb}
+\begin{htmlonly}
+ \param{e}{the inner element.}
+ \return{the outer element.}
+\end{htmlonly}
+\begin{code}
+
+ public abstract IE convertToInnerType (OE e)\begin{hide};\end{hide}
+\end{code}
+\begin{tabb} Converts an element of the outer type to
+ an element for the inner list.
+\end{tabb}
+\begin{htmlonly}
+ \param{e}{the outer element.}
+ \return{the inner element.}
+\end{htmlonly}
+\begin{code}\begin{hide}
+
+ public void add (int index, OE element) {
+ IE fe = convertToInnerType (element);
+ fromList.add (fe);
+ }
+
+ @Override
+ public void clear () {
+ fromList.clear();
+ }
+
+ @Override
+ public OE get (int index) {
+ return convertFromInnerType (fromList.get (index));
+ }
+
+ public Iterator<OE> iterator() {
+ return new MyIterator (fromList.iterator());
+ }
+
+ public ListIterator<OE> listIterator() {
+ return new MyListIterator (fromList.listIterator());
+ }
+
+ public ListIterator<OE> listIterator (int index) {
+ return new MyListIterator (fromList.listIterator (index));
+ }
+
+ @Override
+ public OE remove (int index) {
+ return convertFromInnerType (fromList.remove (index));
+ }
+
+ @Override
+ public OE set (int index, OE element) {
+ IE from = convertToInnerType (element);
+ from = fromList.set (index, from);
+ return convertFromInnerType (from);
+ }
+
+ @Override
+ public int size () {
+ return fromList.size();
+ }
+
+ private class MyIterator implements Iterator<OE> {
+ private Iterator<IE> itr;
+
+ public MyIterator (Iterator<IE> itr) {
+ this.itr = itr;
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public OE next() {
+ return convertFromInnerType (itr.next());
+ }
+
+ public void remove() {
+ itr.remove();
+ }
+ }
+
+ private class MyListIterator implements ListIterator<OE> {
+ private ListIterator<IE> itr;
+
+ public MyListIterator (ListIterator<IE> itr) {
+ this.itr = itr;
+ }
+
+ public void add (OE o) {
+ IE fe = convertToInnerType (o);
+ itr.add (fe);
+ }
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public boolean hasPrevious() {
+ return itr.hasPrevious();
+ }
+
+ public OE next() {
+ return convertFromInnerType (itr.next());
+ }
+
+ public int nextIndex() {
+ return itr.nextIndex();
+ }
+
+ public OE previous() {
+ return convertFromInnerType (itr.previous());
+ }
+
+ public int previousIndex() {
+ return itr.previousIndex();
+ }
+
+ public void remove() {
+ itr.remove();
+ }
+
+ public void set (OE o) {
+ IE fe = convertToInnerType (o);
+ itr.set (fe);
+ }
+ }
+
+\end{hide}
+\end{code}
+
+\begin{code}\begin{hide}
+}\end{hide}
+\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/guideutil.bbl b/source/umontreal/iro/lecuyer/util/guideutil.bbl
new file mode 100644
index 0000000..f3d7eb8
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/guideutil.bbl
@@ -0,0 +1,25 @@
+\begin{thebibliography}{1}
+
+\bibitem{mCLE62a}
+C.~W. Clenshaw.
+\newblock Chebychev series for mathematical functions.
+\newblock National Physical Laboratory Mathematical Tables~5, Her Majesty's
+ Stationery Office, London, 1962.
+
+\bibitem{iGOS00a}
+J.~Gosling, B.~Joy, and G.~L. Steele~Jr.
+\newblock {\em The {J}ava Language Specification}.
+\newblock Addison-Wesley, second edition, 2000.
+\newblock Also available from \url{http://java.sun.com/docs/books/jls}.
+
+\bibitem{mHIG09a}
+N.~J. Higham.
+\newblock The scaling and squaring method for the matrix exponential revisited.
+\newblock {\em {SIAM} Review}, 51(4):747--764, 2009.
+
+\bibitem{iKNU73a}
+D.~E. Knuth.
+\newblock {\em The Art of Computer Programming, Vol. 1}.
+\newblock Addison-Wesley, Reading, MA, second edition, 1973.
+
+\end{thebibliography}
diff --git a/source/umontreal/iro/lecuyer/util/guideutil.tex b/source/umontreal/iro/lecuyer/util/guideutil.tex
new file mode 100644
index 0000000..27dbb99
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/guideutil.tex
@@ -0,0 +1,87 @@
+\documentclass[12pt]{article}
+\usepackage{amsmath}
+\usepackage{ssj}
+\mytwoheads
+\dateheadtrue
+\usepackage{tikz}
+\usepackage{color}
+\usepackage{crayola}
+\usepackage[procnames]{listings}
+\usepackage{lstpatch}
+
+\lstloadlanguages{Java}
+\lstset{language=Java,
+float=tbhp,
+captionpos=t,
+frame=trbl,
+abovecaptionskip=1.5em,
+belowskip=2em,
+basicstyle=\small\ttfamily,
+stringstyle=\color{OliveGreen},
+commentstyle=\color{red},
+identifierstyle=\color{Bittersweet},
+basewidth={0.5em},
+showstringspaces=false,
+framerule=0.8pt,
+procnamestyle=\bfseries\color{blue},
+emphstyle=\bfseries\color{Cerulean},
+procnamekeys={class,extends,interface,implements}
+}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\begin{document}
+\begin{titlepage}
+\title{util}{General basic utilities}
+
+\vfill
+\input{overview}
+\vfill
+\end{titlepage}
+\pagenumbering{roman}
+\tableofcontents
+
+\pagenumbering{arabic}
+\include{Num}
+\include{TextDataReader}
+\include{PrintfFormat}
+\include{TableFormat}
+\include{AbstractChrono}
+\include{SystemTimeChrono}
+\include{GlobalCPUTimeChrono}
+\include{ThreadCPUTimeChrono}
+\include{Chrono}
+\include{ChronoSingleThread}
+\include{TimeUnit}
+\include{Systeme}
+\include{ArithmeticMod}
+\include{BitVector}
+\include{BitMatrix}
+\include{DMatrix}
+\include{RootFinder}
+\include{MultivariateFunction}
+\include{RatioFunction}
+\include{Misc}
+\include{JDBCManager}
+\include{ClassFinder}
+\include{NameConflictException}
+\include{Introspection}
+\include{TransformingList}
+\include{DoubleArrayComparator}
+
+\include{io/overview}
+\include{io/DataWriter}
+\include{io/AbstractDataWriter}
+\include{io/CachedDataWriter}
+\include{io/TextDataWriter}
+\include{io/BinaryDataWriter}
+\include{io/DataReader}
+\include{io/AbstractDataReader}
+\include{io/BinaryDataReader}
+\include{io/DataField}
+
+
+\bibliographystyle{plain}
+\bibliography{simul,random,ift,stat,prob,math} % Dans texmac.
+
+\end{document}
diff --git a/source/umontreal/iro/lecuyer/util/io/AbstractDataReader.java b/source/umontreal/iro/lecuyer/util/io/AbstractDataReader.java
new file mode 100644
index 0000000..f93d4ba
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/AbstractDataReader.java
@@ -0,0 +1,199 @@
+
+/*
+ * Class: AbstractDataReader
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+
+
+/**
+ * This abstract class implements shared functionality for data readers.
+ *
+ */
+public abstract class AbstractDataReader implements DataReader {
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its <TT>String</TT> value.
+ *
+ */
+ public String readString (String label) throws IOException {
+ return readField(label).asString();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its <TT>int</TT> value.
+ *
+ */
+ public int readInt (String label) throws IOException {
+ return readField(label).asInt();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its <TT>float</TT> value.
+ *
+ */
+ public float readFloat (String label) throws IOException {
+ return readField(label).asFloat();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its <TT>double</TT> value.
+ *
+ */
+ public double readDouble (String label) throws IOException {
+ return readField(label).asDouble();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>String</TT>'s.
+ *
+ */
+ public String[] readStringArray (String label) throws IOException {
+ return readField(label).asStringArray();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>int</TT>'s.
+ *
+ */
+ public int[] readIntArray (String label) throws IOException {
+ return readField(label).asIntArray();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>float</TT>'s.
+ *
+ */
+ public float[] readFloatArray (String label) throws IOException {
+ return readField(label).asFloatArray();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>double</TT>'s.
+ *
+ */
+ public double[] readDoubleArray (String label) throws IOException {
+ return readField(label).asDoubleArray();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>String</TT>'s.
+ *
+ */
+ public String[][] readStringArray2D (String label) throws IOException {
+ return readField(label).asStringArray2D();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>int</TT>'s.
+ *
+ */
+ public int[][] readIntArray2D (String label) throws IOException {
+ return readField(label).asIntArray2D();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>float</TT>'s.
+ *
+ */
+ public float[][] readFloatArray2D (String label) throws IOException {
+ return readField(label).asFloatArray2D();
+ }
+
+
+
+ /**
+ * Reads first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>double</TT>'s.
+ *
+ */
+ public double[][] readDoubleArray2D (String label) throws IOException {
+ return readField(label).asDoubleArray2D();
+ }
+
+
+
+ /**
+ * Reads all remaining fields in the file and returns a hashmap indexed
+ * by field labels. Anonymous fields are mapped to <code>"_data01_"</code>, <code>"_data02_"</code>, ...
+ *
+ */
+ public Map<String, DataField> readAllNextFields() throws IOException {
+
+ HashMap<String,DataField> fields = new HashMap<String,DataField>();
+
+ int iAnonymous = 0;
+
+ while (dataPending()) {
+
+ DataField data = readNextField();
+
+ String key = data.getLabel();
+ if (key == null)
+ key = String.format("_data%02d_", ++iAnonymous);
+ fields.put(key, data);
+
+ }
+
+ return fields;
+ }
+
+
+
+ /**
+ * Reads all fields in the file and returns a hashmap indexed
+ * by field labels. Anonymous fields are mapped to <code>"_data01_"</code>, <code>"_data02_"</code>, ...
+ *
+ */
+ public Map<String, DataField> readAllFields() throws IOException {
+ reset();
+ return readAllNextFields();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/AbstractDataReader.tex b/source/umontreal/iro/lecuyer/util/io/AbstractDataReader.tex
new file mode 100644
index 0000000..5e188ba
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/AbstractDataReader.tex
@@ -0,0 +1,218 @@
+\defmodule {AbstractDataReader}
+
+This abstract class implements shared functionality for data readers.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}\begin{hide}
+/*
+ * Class: AbstractDataReader
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+\end{hide}
+
+public abstract class AbstractDataReader implements DataReader \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading atomic data}
+
+\begin{code}
+
+ public String readString (String label) throws IOException \begin{hide} {
+ return readField(label).asString();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its \texttt{String} value.
+\end{tabb}
+\begin{code}
+
+ public int readInt (String label) throws IOException \begin{hide} {
+ return readField(label).asInt();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its \texttt{int} value.
+\end{tabb}
+\begin{code}
+
+ public float readFloat (String label) throws IOException \begin{hide} {
+ return readField(label).asFloat();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its \texttt{float} value.
+\end{tabb}
+\begin{code}
+
+ public double readDouble (String label) throws IOException \begin{hide} {
+ return readField(label).asDouble();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its \texttt{double} value.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading one-dimensional arrays}
+\begin{code}
+
+ public String[] readStringArray (String label) throws IOException \begin{hide} {
+ return readField(label).asStringArray();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{String}'s.
+\end{tabb}
+\begin{code}
+
+ public int[] readIntArray (String label) throws IOException \begin{hide} {
+ return readField(label).asIntArray();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{int}'s.
+\end{tabb}
+\begin{code}
+
+ public float[] readFloatArray (String label) throws IOException \begin{hide} {
+ return readField(label).asFloatArray();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{float}'s.
+\end{tabb}
+\begin{code}
+
+ public double[] readDoubleArray (String label) throws IOException \begin{hide} {
+ return readField(label).asDoubleArray();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{double}'s.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading two-dimensional arrays}
+\begin{code}
+
+ public String[][] readStringArray2D (String label) throws IOException \begin{hide} {
+ return readField(label).asStringArray2D();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{String}'s.
+\end{tabb}
+\begin{code}
+
+ public int[][] readIntArray2D (String label) throws IOException \begin{hide} {
+ return readField(label).asIntArray2D();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{int}'s.
+\end{tabb}
+\begin{code}
+
+ public float[][] readFloatArray2D (String label) throws IOException \begin{hide} {
+ return readField(label).asFloatArray2D();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{float}'s.
+\end{tabb}
+\begin{code}
+
+ public double[][] readDoubleArray2D (String label) throws IOException \begin{hide} {
+ return readField(label).asDoubleArray2D();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{double}'s.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading fields of unknown type}
+\begin{code}
+
+ public Map<String, DataField> readAllNextFields() throws IOException \begin{hide} {
+
+ HashMap<String,DataField> fields = new HashMap<String,DataField>();
+
+ int iAnonymous = 0;
+
+ while (dataPending()) {
+
+ DataField data = readNextField();
+
+ String key = data.getLabel();
+ if (key == null)
+ key = String.format("_data%02d_", ++iAnonymous);
+ fields.put(key, data);
+
+ }
+
+ return fields;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads all remaining fields in the file and returns a hashmap indexed
+by field labels. Anonymous fields are mapped to \verb|"_data01_"|, \verb|"_data02_"|, \ldots
+\end{tabb}
+\begin{code}
+
+ public Map<String, DataField> readAllFields() throws IOException \begin{hide} {
+ reset();
+ return readAllNextFields();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads all fields in the file and returns a hashmap indexed
+by field labels. Anonymous fields are mapped to \verb|"_data01_"|, \verb|"_data02_"|, \ldots
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/AbstractDataWriter.java b/source/umontreal/iro/lecuyer/util/io/AbstractDataWriter.java
new file mode 100644
index 0000000..16d6bb3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/AbstractDataWriter.java
@@ -0,0 +1,79 @@
+
+
+/*
+ * Class: AbstractDataWriter
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.io.IOException;
+
+
+/**
+ * This abstract class implements shared functionality for data writers.
+ *
+ */
+public abstract class AbstractDataWriter implements DataWriter {
+
+
+
+ /**
+ * Writes a one-dimensional array of strings.
+ * If <TT>label</TT> is <TT>null</TT>, writes an anonymous field.
+ *
+ */
+ public void write (String label, String[] a) throws IOException {
+ write(label, a, a.length);
+ }
+
+
+ /**
+ * Writes a one-dimensional array of 32-bit integers (big endian).
+ * If <TT>label</TT> is <TT>null</TT>, writes an anonymous field.
+ *
+ */
+ public void write (String label, int[] a) throws IOException {
+ write(label, a, a.length);
+ }
+
+
+ /**
+ * Writes a one-dimensional array of 32-bit floats (big endian).
+ * If <TT>label</TT> is <TT>null</TT>, writes an anonymous field.
+ *
+ */
+ public void write (String label, float[] a) throws IOException {
+ write(label, a, a.length);
+ }
+
+
+ /**
+ * Writes a one-dimensional array of 64-bit doubles (big endian).
+ * If <TT>label</TT> is <TT>null</TT>, writes an anonymous field.
+ *
+ */
+ public void write (String label, double[] a) throws IOException {
+ write(label, a, a.length);
+ }
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/AbstractDataWriter.tex b/source/umontreal/iro/lecuyer/util/io/AbstractDataWriter.tex
new file mode 100644
index 0000000..e7e1bd6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/AbstractDataWriter.tex
@@ -0,0 +1,89 @@
+\defmodule{AbstractDataWriter}
+
+This abstract class implements shared functionality for data writers.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: AbstractDataWriter
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.io.IOException;
+\end{hide}
+
+public abstract class AbstractDataWriter implements DataWriter \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing one-dimensional arrays}
+\begin{code}
+
+ public void write (String label, String[] a) throws IOException \begin{hide} {
+ write(label, a, a.length);
+ }
+ \end{hide}\end{code}
+\begin{tabb}
+Writes a one-dimensional array of strings.
+If \texttt{label} is \texttt{null}, writes an anonymous field.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, int[] a) throws IOException \begin{hide} {
+ write(label, a, a.length);
+ }
+ \end{hide}\end{code}
+\begin{tabb}
+Writes a one-dimensional array of 32-bit integers (big endian).
+If \texttt{label} is \texttt{null}, writes an anonymous field.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, float[] a) throws IOException \begin{hide} {
+ write(label, a, a.length);
+ }
+ \end{hide}\end{code}
+\begin{tabb}
+Writes a one-dimensional array of 32-bit floats (big endian).
+If \texttt{label} is \texttt{null}, writes an anonymous field.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, double[] a) throws IOException \begin{hide} {
+ write(label, a, a.length);
+ }
+ \end{hide}\end{code}
+\begin{tabb}
+Writes a one-dimensional array of 64-bit doubles (big endian).
+If \texttt{label} is \texttt{null}, writes an anonymous field.
+\end{tabb}
+
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/BinaryDataReader.java b/source/umontreal/iro/lecuyer/util/io/BinaryDataReader.java
new file mode 100644
index 0000000..8a492e3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/BinaryDataReader.java
@@ -0,0 +1,438 @@
+
+
+/*
+ * Class: BinaryDataReader
+ * Description: Binary data reader
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+import java.io.*;
+import java.net.URL;
+
+
+
+/**
+ * Binary data reader. This class implements a module for importing data written
+ * with {@link BinaryDataWriter}.
+ *
+ */
+public class BinaryDataReader extends AbstractDataReader {
+ protected DataInputStream in;
+
+ // only one of these will be used
+ protected String filename = null;
+ protected URL url = null;
+ protected File file = null;
+ protected boolean canReset = false;
+
+ /* *
+ * Reads current field label.
+ *
+ */
+ protected String readLabel() throws IOException {
+
+ byte typechar = in.readByte();
+ if (typechar != BinaryDataWriter.TYPECHAR_LABEL)
+ throw new IOException("Expected a label");
+
+ return readStringData();
+ }
+
+
+ /* *
+ * Reads string data.
+ *
+ */
+ protected String readStringData() throws IOException {
+ int length = in.readInt();
+ if (length == 0)
+ return null;
+ byte[] s = new byte[length];
+ for (int i = 0; i < length; i++)
+ s[i] = in.readByte();
+ return new String(s);
+ }
+
+ /* *
+ * Reads string-array data.
+ *
+ */
+ protected String[] readStringArrayData(int dim) throws IOException {
+ String[] a = new String[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readStringData();
+ return a;
+ }
+
+ /* *
+ * Reads 2D string-array data.
+ *
+ */
+ protected String[][] readStringArray2DData(int dims[]) throws IOException {
+ String[][] a = new String[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readStringData();
+ return a;
+ }
+
+
+ /* *
+ * Reads integer data.
+ *
+ */
+ protected int readIntData() throws IOException {
+ return in.readInt();
+ }
+
+ /* *
+ * Reads integer-array data.
+ *
+ */
+ protected int[] readIntArrayData(int dim) throws IOException {
+ int[] a = new int[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readIntData();
+ return a;
+ }
+
+ /* *
+ * Reads 2D integer-array data.
+ *
+ */
+ protected int[][] readIntArray2DData(int dims[]) throws IOException {
+ int[][] a = new int[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readIntData();
+ return a;
+ }
+
+
+ /* *
+ * Reads float data.
+ *
+ */
+ protected float readFloatData() throws IOException {
+ return in.readFloat();
+ }
+
+ /* *
+ * Reads float-array data.
+ *
+ */
+ protected float[] readFloatArrayData(int dim) throws IOException {
+ float[] a = new float[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readFloatData();
+ return a;
+ }
+
+ /* *
+ * Reads 2D float-array data.
+ *
+ */
+ protected float[][] readFloatArray2DData(int dims[]) throws IOException {
+ float[][] a = new float[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readFloatData();
+ return a;
+ }
+
+
+ /* *
+ * Reads double data.
+ *
+ */
+ protected double readDoubleData() throws IOException {
+ return in.readDouble();
+ }
+
+ /* *
+ * Reads double-array data.
+ *
+ */
+ protected double[] readDoubleArrayData(int dim) throws IOException {
+ double[] a = new double[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readDoubleData();
+ return a;
+ }
+
+ /* *
+ * Reads 2D double-array data.
+ *
+ */
+ protected double[][] readDoubleArray2DData(int dims[]) throws IOException {
+ double[][] a = new double[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readDoubleData();
+ return a;
+ }
+
+
+ /* *
+ * Reads field data of arbitrary type.
+ *
+ * @param typechar type code character (see {@link BinaryDataWriter} constants)
+ * @param nDims number of dimensions (0 for atomic data)
+ * @param dims length of each dimension
+ *
+ * @return an instance of the data or {@code null} if data type is unknown
+ */
+ protected Object readFieldData(byte typechar, int nDims, int dims[])
+ throws IOException {
+
+ if (nDims < 0 || nDims > 2)
+ throw new IOException("unsupported number of dimensions: " + nDims);
+
+ Object data = null;
+
+ switch (typechar) {
+
+ case BinaryDataWriter.TYPECHAR_STRING:
+ if (nDims == 0)
+ data = readStringData();
+ else if (nDims == 1)
+ data = readStringArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readStringArray2DData(dims);
+ break;
+
+ case BinaryDataWriter.TYPECHAR_INTEGER:
+ if (nDims == 0)
+ data = readIntData();
+ else if (nDims == 1)
+ data = readIntArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readIntArray2DData(dims);
+ break;
+
+ case BinaryDataWriter.TYPECHAR_FLOAT:
+ if (nDims == 0)
+ data = readFloatData();
+ else if (nDims == 1)
+ data = readFloatArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readFloatArray2DData(dims);
+ break;
+
+ case BinaryDataWriter.TYPECHAR_DOUBLE:
+ if (nDims == 0)
+ data = readDoubleData();
+ else if (nDims == 1)
+ data = readDoubleArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readDoubleArray2DData(dims);
+ break;
+ }
+
+ return data;
+ }
+
+
+
+ /**
+ * Opens the file with the specified name for reading.
+ *
+ * @param filename name of the file to read the data from
+ *
+ *
+ */
+ public BinaryDataReader (String filename) throws IOException {
+ this.filename = filename;
+ canReset = true;
+ reset();
+ }
+
+
+
+ /**
+ * Opens the file at the specified url for reading.
+ *
+ * @param url url of the file to read the data from
+ *
+ *
+ */
+ public BinaryDataReader (URL url) throws IOException {
+ this.url = url;
+ canReset = true;
+ reset();
+ }
+
+
+
+ /**
+ * Opens the specified file for reading.
+ *
+ * @param file file to read the data from
+ *
+ *
+ */
+ public BinaryDataReader (File file) throws IOException {
+ this.file = file;
+ canReset = true;
+ reset();
+ }
+
+
+
+ /**
+ * Opens the specified input stream for reading.
+ * When using this constructor, the method {@link #readField readField} might will not be able to read
+ * a field that is before the current reading position.
+ *
+ * @param inputStream input stream to read the data from
+ *
+ */
+ public BinaryDataReader (InputStream inputStream) throws IOException {
+ this.in = new DataInputStream(inputStream);
+ }
+
+
+
+ /**
+ * Reads the next available field.
+ *
+ * @return a newly created DataField instance or null if not found
+ *
+ */
+ public DataField readNextField() throws IOException {
+
+ String label = readLabel();
+
+ byte typechar = in.readByte();
+
+ int nDims = in.readByte();
+
+ int[] dims = new int[nDims];
+ for (int i = 0; i < nDims; i++)
+ dims[i] = in.readInt();
+
+
+ return new DataField(label, readFieldData(typechar, nDims, dims));
+ }
+
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT>.
+ *
+ * @return a newly created DataField instance or null if not found
+ *
+ */
+ public DataField readField (String label) throws IOException {
+
+ reset();
+
+ while (in.available() > 0) {
+
+ String fieldLabel = readLabel();
+
+ byte typechar = in.readByte();
+
+ int nDims = in.readByte();
+ int[] dims = new int[nDims];
+ for (int i = 0; i < nDims; i++)
+ dims[i] = in.readInt();
+
+ // if found, return field instance
+ if (fieldLabel.compareTo(label) == 0)
+ return new DataField(fieldLabel, readFieldData(typechar, nDims, dims));
+
+ // otherwise, just skip the current field
+
+ // strings don't have a fixed size, so just read them out
+ if (typechar == BinaryDataWriter.TYPECHAR_STRING) {
+ readFieldData(typechar, nDims, dims);
+ continue;
+ }
+
+ // compute the number of bytes to be skipped
+ int skipSize = 0;
+ switch (typechar) {
+
+ case BinaryDataWriter.TYPECHAR_INTEGER:
+ skipSize = Integer.SIZE / 8;
+ break;
+
+ case BinaryDataWriter.TYPECHAR_FLOAT:
+ skipSize = Float.SIZE / 8;
+ break;
+
+ case BinaryDataWriter.TYPECHAR_DOUBLE:
+ skipSize = Double.SIZE / 8;
+ break;
+ }
+
+ if (nDims > 0)
+ skipSize *= dims[0];
+ if (nDims > 1)
+ skipSize *= dims[1];
+
+ in.skipBytes(skipSize);
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Reopens the file (does not work with the constructor that takes an input stream).
+ *
+ */
+ public void reset() throws IOException {
+ if (!canReset)
+ return;
+
+ if (filename != null)
+ this.in = new DataInputStream(new FileInputStream(filename));
+ else if (file != null)
+ this.in = new DataInputStream(new FileInputStream(file));
+ else if (url != null)
+ this.in = new DataInputStream(url.openStream());
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if there remains data to be read.
+ *
+ */
+ public boolean dataPending() throws IOException {
+ return (in.available() > 0);
+ }
+
+
+
+ /**
+ * Closes the file.
+ *
+ */
+ public void close() throws IOException {
+ in.close();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/BinaryDataReader.tex b/source/umontreal/iro/lecuyer/util/io/BinaryDataReader.tex
new file mode 100644
index 0000000..5d5576f
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/BinaryDataReader.tex
@@ -0,0 +1,459 @@
+\defmodule{BinaryDataReader}
+
+Binary data reader. This class implements a module for importing data written
+ with \class{BinaryDataWriter}.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BinaryDataReader
+ * Description: Binary data reader
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;\begin{hide}
+import java.io.*;
+import java.net.URL;
+\end{hide}
+
+
+public class BinaryDataReader extends AbstractDataReader \begin{hide} {
+ protected DataInputStream in;
+
+ // only one of these will be used
+ protected String filename = null;
+ protected URL url = null;
+ protected File file = null;
+ protected boolean canReset = false;
+
+ /**
+ * Reads current field label.
+ *
+ */
+ protected String readLabel() throws IOException {
+
+ byte typechar = in.readByte();
+ if (typechar != BinaryDataWriter.TYPECHAR_LABEL)
+ throw new IOException("Expected a label");
+
+ return readStringData();
+ }
+
+
+ /**
+ * Reads string data.
+ *
+ */
+ protected String readStringData() throws IOException {
+ int length = in.readInt();
+ if (length == 0)
+ return null;
+ byte[] s = new byte[length];
+ for (int i = 0; i < length; i++)
+ s[i] = in.readByte();
+ return new String(s);
+ }
+
+ /**
+ * Reads string-array data.
+ *
+ */
+ protected String[] readStringArrayData(int dim) throws IOException {
+ String[] a = new String[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readStringData();
+ return a;
+ }
+
+ /**
+ * Reads 2D string-array data.
+ *
+ */
+ protected String[][] readStringArray2DData(int dims[]) throws IOException {
+ String[][] a = new String[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readStringData();
+ return a;
+ }
+
+
+ /**
+ * Reads integer data.
+ *
+ */
+ protected int readIntData() throws IOException {
+ return in.readInt();
+ }
+
+ /**
+ * Reads integer-array data.
+ *
+ */
+ protected int[] readIntArrayData(int dim) throws IOException {
+ int[] a = new int[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readIntData();
+ return a;
+ }
+
+ /**
+ * Reads 2D integer-array data.
+ *
+ */
+ protected int[][] readIntArray2DData(int dims[]) throws IOException {
+ int[][] a = new int[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readIntData();
+ return a;
+ }
+
+
+ /**
+ * Reads float data.
+ *
+ */
+ protected float readFloatData() throws IOException {
+ return in.readFloat();
+ }
+
+ /**
+ * Reads float-array data.
+ *
+ */
+ protected float[] readFloatArrayData(int dim) throws IOException {
+ float[] a = new float[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readFloatData();
+ return a;
+ }
+
+ /**
+ * Reads 2D float-array data.
+ *
+ */
+ protected float[][] readFloatArray2DData(int dims[]) throws IOException {
+ float[][] a = new float[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readFloatData();
+ return a;
+ }
+
+
+ /**
+ * Reads double data.
+ *
+ */
+ protected double readDoubleData() throws IOException {
+ return in.readDouble();
+ }
+
+ /**
+ * Reads double-array data.
+ *
+ */
+ protected double[] readDoubleArrayData(int dim) throws IOException {
+ double[] a = new double[dim];
+ for (int i = 0; i < dim; i++)
+ a[i] = readDoubleData();
+ return a;
+ }
+
+ /**
+ * Reads 2D double-array data.
+ *
+ */
+ protected double[][] readDoubleArray2DData(int dims[]) throws IOException {
+ double[][] a = new double[dims[0]][dims[1]];
+ for (int i = 0; i < dims[0]; i++)
+ for (int j = 0; j < dims[1]; j++)
+ a[i][j] = readDoubleData();
+ return a;
+ }
+
+
+ /**
+ * Reads field data of arbitrary type.
+ *
+ * @param typechar type code character (see {@link BinaryDataWriter} constants)
+ * @param nDims number of dimensions (0 for atomic data)
+ * @param dims length of each dimension
+ *
+ * @return an instance of the data or {@code null} if data type is unknown
+ */
+ protected Object readFieldData(byte typechar, int nDims, int dims[])
+ throws IOException {
+
+ if (nDims < 0 || nDims > 2)
+ throw new IOException("unsupported number of dimensions: " + nDims);
+
+ Object data = null;
+
+ switch (typechar) {
+
+ case BinaryDataWriter.TYPECHAR_STRING:
+ if (nDims == 0)
+ data = readStringData();
+ else if (nDims == 1)
+ data = readStringArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readStringArray2DData(dims);
+ break;
+
+ case BinaryDataWriter.TYPECHAR_INTEGER:
+ if (nDims == 0)
+ data = readIntData();
+ else if (nDims == 1)
+ data = readIntArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readIntArray2DData(dims);
+ break;
+
+ case BinaryDataWriter.TYPECHAR_FLOAT:
+ if (nDims == 0)
+ data = readFloatData();
+ else if (nDims == 1)
+ data = readFloatArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readFloatArray2DData(dims);
+ break;
+
+ case BinaryDataWriter.TYPECHAR_DOUBLE:
+ if (nDims == 0)
+ data = readDoubleData();
+ else if (nDims == 1)
+ data = readDoubleArrayData(dims[0]);
+ else if (nDims == 2)
+ data = readDoubleArray2DData(dims);
+ break;
+ }
+
+ return data;
+ }
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public BinaryDataReader (String filename) throws IOException \begin{hide} {
+ this.filename = filename;
+ canReset = true;
+ reset();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Opens the file with the specified name for reading.
+\end{tabb}
+\begin{htmlonly}
+\param{filename}{name of the file to read the data from}
+\end{htmlonly}
+\begin{code}
+
+ public BinaryDataReader (URL url) throws IOException \begin{hide} {
+ this.url = url;
+ canReset = true;
+ reset();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Opens the file at the specified url for reading.
+\end{tabb}
+\begin{htmlonly}
+\param{url}{url of the file to read the data from}
+\end{htmlonly}
+\begin{code}
+
+ public BinaryDataReader (File file) throws IOException \begin{hide} {
+ this.file = file;
+ canReset = true;
+ reset();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Opens the specified file for reading.
+\end{tabb}
+\begin{htmlonly}
+\param{file}{file to read the data from}
+\end{htmlonly}
+\begin{code}
+
+ public BinaryDataReader (InputStream inputStream) throws IOException \begin{hide} {
+ this.in = new DataInputStream(inputStream);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Opens the specified input stream for reading.
+When using this constructor, the method \method{readField}{} might will not be able to read
+a field that is before the current reading position.
+\end{tabb}
+\begin{htmlonly}
+\param{inputStream}{input stream to read the data from}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading fields of unknown type}
+\begin{code}
+
+ public DataField readNextField() throws IOException \begin{hide} {
+
+ String label = readLabel();
+
+ byte typechar = in.readByte();
+
+ int nDims = in.readByte();
+
+ int[] dims = new int[nDims];
+ for (int i = 0; i < nDims; i++)
+ dims[i] = in.readInt();
+
+
+ return new DataField(label, readFieldData(typechar, nDims, dims));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads the next available field.
+\end{tabb}
+\begin{htmlonly}
+\return{a newly created DataField instance or \text{null} if not found}
+\end{htmlonly}
+\begin{code}
+
+ public DataField readField (String label) throws IOException \begin{hide} {
+
+ reset();
+
+ while (in.available() > 0) {
+
+ String fieldLabel = readLabel();
+
+ byte typechar = in.readByte();
+
+ int nDims = in.readByte();
+ int[] dims = new int[nDims];
+ for (int i = 0; i < nDims; i++)
+ dims[i] = in.readInt();
+
+ // if found, return field instance
+ if (fieldLabel.compareTo(label) == 0)
+ return new DataField(fieldLabel, readFieldData(typechar, nDims, dims));
+
+ // otherwise, just skip the current field
+
+ // strings don't have a fixed size, so just read them out
+ if (typechar == BinaryDataWriter.TYPECHAR_STRING) {
+ readFieldData(typechar, nDims, dims);
+ continue;
+ }
+
+ // compute the number of bytes to be skipped
+ int skipSize = 0;
+ switch (typechar) {
+
+ case BinaryDataWriter.TYPECHAR_INTEGER:
+ skipSize = Integer.SIZE / 8;
+ break;
+
+ case BinaryDataWriter.TYPECHAR_FLOAT:
+ skipSize = Float.SIZE / 8;
+ break;
+
+ case BinaryDataWriter.TYPECHAR_DOUBLE:
+ skipSize = Double.SIZE / 8;
+ break;
+ }
+
+ if (nDims > 0)
+ skipSize *= dims[0];
+ if (nDims > 1)
+ skipSize *= dims[1];
+
+ in.skipBytes(skipSize);
+ }
+
+ return null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label}.
+\end{tabb}
+\begin{htmlonly}
+\return{a newly created DataField instance or \text{null} if not found}
+\end{htmlonly}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Other methods}
+
+\begin{code}
+ public void reset() throws IOException \begin{hide} {
+ if (!canReset)
+ return;
+
+ if (filename != null)
+ this.in = new DataInputStream(new FileInputStream(filename));
+ else if (file != null)
+ this.in = new DataInputStream(new FileInputStream(file));
+ else if (url != null)
+ this.in = new DataInputStream(url.openStream());
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Reopens the file (does not work with the constructor that takes an input stream).
+\end{tabb}
+\begin{code}
+
+ public boolean dataPending() throws IOException \begin{hide} {
+ return (in.available() > 0);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if there remains data to be read.
+\end{tabb}
+\begin{code}
+
+ public void close() throws IOException \begin{hide} {
+ in.close();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Closes the file.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/BinaryDataWriter.java b/source/umontreal/iro/lecuyer/util/io/BinaryDataWriter.java
new file mode 100644
index 0000000..3d42b40
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/BinaryDataWriter.java
@@ -0,0 +1,438 @@
+
+
+/*
+ * Class: BinaryDataWriter
+ * Description: Binary data writer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.io.*;
+
+
+/**
+ * Binary data writer.
+ *
+ * <P>
+ * Stores a sequence of fields in binary file, which can be either atoms or arrays,
+ * each of which having the following format:
+ *
+ * <UL>
+ * <LI>Field label:
+ *
+ * <UL>
+ * <LI>Pipe character (<TT>|</TT>)
+ * </LI>
+ * <LI>Label length (32-bit integer, big endian)
+ * </LI>
+ * <LI>Label string (array of bytes of the specified length)
+ *
+ * </LI>
+ * </UL>
+ *
+ * <P>
+ * </LI>
+ * <LI>Field type (byte):
+ *
+ * <UL>
+ * <LI><TT>i</TT> (32-bit integer)
+ * </LI>
+ * <LI><TT>f</TT> (32-bit float)
+ * </LI>
+ * <LI><TT>d</TT> (64-bit double)
+ * </LI>
+ * <LI><TT>S</TT> (string)
+ *
+ * </LI>
+ * </UL>
+ *
+ * <P>
+ * </LI>
+ * <LI>Number of dimensions (8-bit integer)
+ * </LI>
+ * <LI>Dimensions (array of 32-bit integers, big endian)
+ * </LI>
+ * <LI>Field data (in the specified format, big endian)
+ * </LI>
+ * </UL>
+ *
+ * <P>
+ * In the case of an atomic field, the number of dimensions is set to zero.
+ *
+ * <P>
+ * A string field is stored in the following format:
+ *
+ * <UL>
+ * <LI>String length (32-bit integer)
+ * </LI>
+ * <LI>Array of bytes of the specified length
+ * </LI>
+ * </UL>
+ *
+ * <P>
+ * Also supports anonymous fields (fields with an empty label).
+ *
+ * <P>
+ * Arrays up to two dimensions are supported.
+ *
+ * <P>
+ * Modules for reading data exported with this class are available in Java ({@link BinaryDataReader}), Matlab and Python (numpy).
+ *
+ */
+public class BinaryDataWriter extends AbstractDataWriter {
+ protected DataOutputStream out;
+
+ /* *
+ * Utility method to write string data.
+ *
+ */
+ protected void writeStringData(String s) throws IOException {
+ if (s != null) {
+ out.writeInt(s.length());
+ out.writeBytes(s);
+ }
+ else {
+ out.writeInt(0);
+ }
+ }
+
+ /* *
+ * Starts a new field by writing its label.
+ *
+ * @param label name of the field (can be {@code null})
+ *
+ */
+ protected void writeLabel(String label) throws IOException {
+ out.writeByte(TYPECHAR_LABEL);
+ writeStringData(label);
+ }
+
+
+ /**
+ * <SPAN CLASS="textit">Field-type</SPAN> symbol indicating a label (it more accurately a field separator symbol).
+ *
+ */
+ public final static byte TYPECHAR_LABEL = '|';
+
+
+ /**
+ * <SPAN CLASS="textit">Field-type</SPAN> symbol indicating <TT>String</TT> data.
+ *
+ */
+ public final static byte TYPECHAR_STRING = 'S';
+
+
+ /**
+ * <SPAN CLASS="textit">Field-type</SPAN> symbol indicating <TT>int</TT> data.
+ *
+ */
+ public final static byte TYPECHAR_INTEGER = 'i';
+
+
+ /**
+ * <SPAN CLASS="textit">Field-type</SPAN> symbol indicating <TT>float</TT> data.
+ *
+ */
+ public final static byte TYPECHAR_FLOAT = 'f';
+
+
+ /**
+ * <SPAN CLASS="textit">Field-type</SPAN> symbol indicating <TT>double</TT> data.
+ *
+ */
+ public final static byte TYPECHAR_DOUBLE = 'd';
+
+
+ /**
+ * Data will be output to the file with the specified name.
+ *
+ * @param filename name of the file to be created or appended to
+ *
+ * @param append an existing file with the specified name will be appended to if <TT>true</TT> or truncated if <TT>false</TT>
+ *
+ *
+ */
+ public BinaryDataWriter (String filename, boolean append)
+ throws IOException {
+ this.out = new DataOutputStream(new FileOutputStream(filename, append));
+ }
+
+
+
+ /**
+ * Data will be output to the specified file.
+ *
+ * @param file file to be created or appended to
+ *
+ * @param append an existing file with the specified name will be appended to if <TT>true</TT> or truncated if <TT>false</TT>
+ *
+ *
+ */
+ public BinaryDataWriter (File file, boolean append) throws IOException {
+ this.out = new DataOutputStream(new FileOutputStream(file, append));
+ }
+
+
+
+ /**
+ * Truncates any existing file with the specified name.
+ *
+ * @param filename name of the file to be created
+ *
+ *
+ */
+ public BinaryDataWriter (String filename) throws IOException {
+ this.out = new DataOutputStream(new FileOutputStream(filename));
+ }
+
+
+
+ /**
+ * Truncates any existing file with the specified name.
+ *
+ * @param file file to be created
+ *
+ *
+ */
+ public BinaryDataWriter (File file) throws IOException {
+ this.out = new DataOutputStream(new FileOutputStream(file));
+ }
+
+
+
+ /**
+ * Constructor.
+ *
+ * @param outputStream output stream to write to
+ *
+ */
+ public BinaryDataWriter (OutputStream outputStream) throws IOException {
+ this.out = new DataOutputStream(outputStream);
+ }
+
+
+ /**
+ * Writes an atomic string field.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String s) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_STRING);
+ out.writeByte(0);
+ writeStringData(s);
+ }
+
+
+
+ /**
+ * Writes an atomic 32-bit integer (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int a) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_INTEGER);
+ out.writeByte(0);
+ out.writeInt(a);
+ }
+
+
+
+ /**
+ * Writes an atomic 32-bit float (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float a) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_FLOAT);
+ out.writeByte(0);
+ out.writeFloat(a);
+ }
+
+
+
+ /**
+ * Writes an atomic 64-bit double (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double a) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_DOUBLE);
+ out.writeByte(0);
+ out.writeDouble(a);
+ }
+
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of strings.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String[] a, int n) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_STRING);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ writeStringData(a[i]);
+ }
+
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of 32-bit integers (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int[] a, int n) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_INTEGER);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ out.writeInt(a[i]);
+ }
+
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of 32-bit floats (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float[] a, int n) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_FLOAT);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ out.writeFloat(a[i]);
+ }
+
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of 64-bit doubles (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double[] a, int n) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_DOUBLE);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ out.writeDouble(a[i]);
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of strings.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String[][] a) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_STRING);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ writeStringData(a[i][j]);
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of 32-bit integers (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int[][] a) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_INTEGER);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ out.writeInt(a[i][j]);
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of 32-bit floats (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float[][] a) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_FLOAT);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ out.writeFloat(a[i][j]);
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of 64-bit doubles (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double[][] a) throws IOException {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_DOUBLE);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ out.writeDouble(a[i][j]);
+ }
+
+
+
+ /**
+ * Flushes any pending data and closes the file.
+ *
+ */
+ public void close() throws IOException {
+ out.close();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/BinaryDataWriter.tex b/source/umontreal/iro/lecuyer/util/io/BinaryDataWriter.tex
new file mode 100644
index 0000000..4f38ce6
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/BinaryDataWriter.tex
@@ -0,0 +1,447 @@
+\defmodule{BinaryDataWriter}
+
+Binary data writer.
+
+Stores a sequence of fields in binary file, which can be either atoms or arrays,
+each of which having the following format:
+\begin{itemize}
+ \item Field label:
+ \begin{itemize}
+ \item Pipe character (\texttt{|})
+ \item Label length (32-bit integer, big endian)
+ \item Label string (array of bytes of the specified length)
+ \end{itemize}
+
+ \item Field type (byte):
+ \begin{itemize}
+ \item \texttt{i} (32-bit integer)
+ \item \texttt{f} (32-bit float)
+ \item \texttt{d} (64-bit double)
+ \item \texttt{S} (string)
+ \end{itemize}
+
+ \item Number of dimensions (8-bit integer)
+ \item Dimensions (array of 32-bit integers, big endian)
+ \item Field data (in the specified format, big endian)
+\end{itemize}
+
+In the case of an atomic field, the number of dimensions is set to zero.
+
+A string field is stored in the following format:
+\begin{itemize}
+ \item String length (32-bit integer)
+ \item Array of bytes of the specified length
+\end{itemize}
+
+Also supports anonymous fields (fields with an empty label).
+
+Arrays up to two dimensions are supported.
+
+Modules for reading data exported with this class are available in Java (\class{BinaryDataReader}), Matlab and Python (numpy).
+
+\begin{comment}
+Provide links for the import modules.
+\end{comment}
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: BinaryDataWriter
+ * Description: Binary data writer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.io.*;
+\end{hide}
+
+public class BinaryDataWriter extends AbstractDataWriter \begin{hide} {
+ protected DataOutputStream out;
+
+ /**
+ * Utility method to write string data.
+ *
+ */
+ protected void writeStringData(String s) throws IOException {
+ if (s != null) {
+ out.writeInt(s.length());
+ out.writeBytes(s);
+ }
+ else {
+ out.writeInt(0);
+ }
+ }
+
+ /**
+ * Starts a new field by writing its label.
+ *
+ * @param label name of the field (can be {@code null})
+ *
+ */
+ protected void writeLabel(String label) throws IOException {
+ out.writeByte(TYPECHAR_LABEL);
+ writeStringData(label);
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Fields}
+
+\begin{code}
+ public final static byte TYPECHAR_LABEL = '|';
+\end{code}
+\begin{tabb}
+\emph{Field-type} symbol indicating a label (it more accurately a field separator symbol).
+\end{tabb}
+\begin{code}
+
+ public final static byte TYPECHAR_STRING = 'S';
+\end{code}
+\begin{tabb}
+\emph{Field-type} symbol indicating \texttt{String} data.
+\end{tabb}
+\begin{code}
+
+ public final static byte TYPECHAR_INTEGER = 'i';
+\end{code}
+\begin{tabb}
+\emph{Field-type} symbol indicating \texttt{int} data.
+\end{tabb}
+\begin{code}
+
+ public final static byte TYPECHAR_FLOAT = 'f';
+\end{code}
+\begin{tabb}
+\emph{Field-type} symbol indicating \texttt{float} data.
+\end{tabb}
+\begin{code}
+
+ public final static byte TYPECHAR_DOUBLE = 'd';
+\end{code}
+\begin{tabb}
+\emph{Field-type} symbol indicating \texttt{double} data.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public BinaryDataWriter (String filename, boolean append)
+ throws IOException \begin{hide} {
+ this.out = new DataOutputStream(new FileOutputStream(filename, append));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Data will be output to the file with the specified name.
+\end{tabb}
+\begin{htmlonly}
+\param{filename}{name of the file to be created or appended to}
+\param{append} {an existing file with the specified name will be appended to if \texttt{true} or truncated if \texttt{false}}
+\end{htmlonly}
+\begin{code}
+
+ public BinaryDataWriter (File file, boolean append) throws IOException \begin{hide} {
+ this.out = new DataOutputStream(new FileOutputStream(file, append));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Data will be output to the specified file.
+\end{tabb}
+\begin{htmlonly}
+\param{file} {file to be created or appended to}
+\param{append} {an existing file with the specified name will be appended to if \texttt{true} or truncated if \texttt{false}}
+\end{htmlonly}
+\begin{code}
+
+ public BinaryDataWriter (String filename) throws IOException \begin{hide} {
+ this.out = new DataOutputStream(new FileOutputStream(filename));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Truncates any existing file with the specified name.
+\end{tabb}
+\begin{htmlonly}
+\param{filename}{name of the file to be created}
+\end{htmlonly}
+\begin{code}
+
+ public BinaryDataWriter (File file) throws IOException \begin{hide} {
+ this.out = new DataOutputStream(new FileOutputStream(file));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+ Truncates any existing file with the specified name.
+\end{tabb}
+\begin{htmlonly}
+\param{file}{file to be created}
+\end{htmlonly}
+\begin{code}
+
+ public BinaryDataWriter (OutputStream outputStream) throws IOException \begin{hide} {
+ this.out = new DataOutputStream(outputStream);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Constructor.
+\end{tabb}
+\begin{htmlonly}
+\param{outputStream} {output stream to write to}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing atomic data}
+
+\begin{code}
+ public void write (String label, String s) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_STRING);
+ out.writeByte(0);
+ writeStringData(s);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic string field.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, int a) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_INTEGER);
+ out.writeByte(0);
+ out.writeInt(a);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic 32-bit integer (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, float a) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_FLOAT);
+ out.writeByte(0);
+ out.writeFloat(a);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic 32-bit float (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, double a) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_DOUBLE);
+ out.writeByte(0);
+ out.writeDouble(a);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic 64-bit double (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing one-dimensional arrays}
+
+\begin{code}
+
+ public void write (String label, String[] a, int n) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_STRING);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ writeStringData(a[i]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of strings.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, int[] a, int n) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_INTEGER);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ out.writeInt(a[i]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of 32-bit integers (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, float[] a, int n) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_FLOAT);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ out.writeFloat(a[i]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of 32-bit floats (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, double[] a, int n) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_DOUBLE);
+ out.writeByte(1);
+ out.writeInt(n);
+ for (int i = 0; i < n; i++)
+ out.writeDouble(a[i]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of 64-bit doubles (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing two-dimensional arrays}
+
+\begin{code}
+
+ public void write (String label, String[][] a) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_STRING);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ writeStringData(a[i][j]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of strings.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, int[][] a) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_INTEGER);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ out.writeInt(a[i][j]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 32-bit integers (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, float[][] a) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_FLOAT);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ out.writeFloat(a[i][j]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 32-bit floats (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, double[][] a) throws IOException \begin{hide} {
+ writeLabel(label);
+ out.writeByte(TYPECHAR_DOUBLE);
+ out.writeByte(2);
+ out.writeInt(a.length);
+ out.writeInt(a[0].length);
+ for (int i = 0; i < a.length; i++)
+ for (int j = 0; j < a[i].length; j++)
+ out.writeDouble(a[i][j]);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 64-bit doubles (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Other methods}
+\begin{code}
+
+ public void close() throws IOException \begin{hide} {
+ out.close();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Flushes any pending data and closes the file.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/CachedDataWriter.java b/source/umontreal/iro/lecuyer/util/io/CachedDataWriter.java
new file mode 100644
index 0000000..10a8f36
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/CachedDataWriter.java
@@ -0,0 +1,193 @@
+
+
+/*
+ * Class: CachedDataWriter
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.io.*;
+import java.util.LinkedList;
+
+
+/**
+ * This abstract class implements shared functionality for data writers that store
+ * all fields in memory before outputing them with {@link umontreal.iro.lecuyer.util.io.DataWriter#close close}.
+ *
+ */
+public abstract class CachedDataWriter extends AbstractDataWriter {
+
+ // don't use a map because ordering is important
+ protected LinkedList<DataField> fields;
+
+ protected LinkedList<DataField> getFields() {
+ return fields;
+ }
+
+
+
+ /**
+ * Class constructor.
+ *
+ */
+ public CachedDataWriter() {
+ this.fields = new LinkedList<DataField>();
+ }
+
+
+
+ /**
+ * Writes an atomic string field.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String s) throws IOException {
+ fields.add(new DataField(label, s));
+ }
+
+
+
+ /**
+ * Writes an atomic 32-bit integer (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int a) throws IOException {
+ fields.add(new DataField(label, a));
+ }
+
+
+
+ /**
+ * Writes an atomic 32-bit float (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float a) throws IOException {
+ fields.add(new DataField(label, a));
+ }
+
+
+
+ /**
+ * Writes an atomic 64-bit double (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double a) throws IOException {
+ fields.add(new DataField(label, a));
+ }
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of strings.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String[] a, int n) throws IOException {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of 32-bit integers (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int[] a, int n) throws IOException {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of 32-bit floats (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float[] a, int n) throws IOException {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array
+ * of 64-bit doubles (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double[] a, int n) throws IOException {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of strings.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String[][] a) throws IOException {
+ fields.add(new DataField(label, a.clone()));
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of 32-bit integers (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int[][] a) throws IOException {
+ fields.add(new DataField(label, a.clone()));
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of 32-bit floats (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float[][] a) throws IOException {
+ fields.add(new DataField(label, a.clone()));
+ }
+
+
+
+ /**
+ * Writes a two-dimensional array of 64-bit doubles (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double[][] a) throws IOException {
+ fields.add(new DataField(label, a.clone()));
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/CachedDataWriter.tex b/source/umontreal/iro/lecuyer/util/io/CachedDataWriter.tex
new file mode 100644
index 0000000..9d31183
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/CachedDataWriter.tex
@@ -0,0 +1,217 @@
+\defmodule{CachedDataWriter}
+
+This abstract class implements shared functionality for data writers that store
+all fields in memory before outputing them with \externalmethod{umontreal.iro.lecuyer.util.io}{DataWriter}{close}{}.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: CachedDataWriter
+ * Description:
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.io.*;
+import java.util.LinkedList;
+\end{hide}
+
+public abstract class CachedDataWriter extends AbstractDataWriter \begin{hide} {
+
+ // don't use a map because ordering is important
+ protected LinkedList<DataField> fields;
+
+ protected LinkedList<DataField> getFields() {
+ return fields;
+ }
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructor}
+\begin{code}
+
+ public CachedDataWriter() \begin{hide} {
+ this.fields = new LinkedList<DataField>();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Class constructor.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing atomic data}
+
+\begin{code}
+
+ public void write (String label, String s) throws IOException \begin{hide} {
+ fields.add(new DataField(label, s));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic string field.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, int a) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic 32-bit integer (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, float a) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic 32-bit float (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, double a) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes an atomic 64-bit double (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing one-dimensional arrays}
+
+\begin{code}
+ public void write (String label, String[] a, int n) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of strings.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, int[] a, int n) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of 32-bit integers (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, float[] a, int n) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of 32-bit floats (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, double[] a, int n) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone(), n));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array
+of 64-bit doubles (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing two-dimensional arrays}
+
+\begin{code}
+
+ public void write (String label, String[][] a) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone()));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of strings.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, int[][] a) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone()));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 32-bit integers (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, float[][] a) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone()));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 32-bit floats (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+\begin{code}
+
+ public void write (String label, double[][] a) throws IOException \begin{hide} {
+ fields.add(new DataField(label, a.clone()));
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 64-bit doubles (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/DataField.java b/source/umontreal/iro/lecuyer/util/io/DataField.java
new file mode 100644
index 0000000..546c69b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/DataField.java
@@ -0,0 +1,313 @@
+
+
+/*
+ * Class: DataField
+ * Description: Represents a data field
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.lang.reflect.Array;
+
+
+/**
+ * This class represents a data field from a file read by an instance of a class
+ * implementing {@link DataReader}.
+ *
+ */
+public class DataField {
+ protected String label;
+ protected Object data;
+ protected int effectiveLength;
+
+
+ /**
+ * Constructor. Creates a field named <TT>label</TT> of value <TT>data</TT>.
+ *
+ * @param label name of the field
+ *
+ * @param data value of the field
+ *
+ *
+ */
+ public DataField (String label, Object data) {
+ this(label, data, -1);
+ }
+
+
+
+ /**
+ * Constructor. Creates a field named <TT>label</TT> of value <TT>data</TT>.
+ * <TT>effectiveLength</TT> is the number of significant elements contained in
+ * <TT>data</TT> if it is an array.
+ *
+ * @param label name of the field
+ *
+ * @param data value of the field
+ *
+ * @param effectiveLength number of significant elements contained in <TT>data</TT>
+ *
+ */
+ public DataField (String label, Object data, int effectiveLength) {
+ this.label = label;
+ this.data = data;
+ this.effectiveLength = effectiveLength;
+ }
+
+
+ /**
+ * Returns the field label (or name).
+ *
+ */
+ public String getLabel() {
+ return label;
+ }
+
+
+
+ /**
+ * Returns the type of the field.
+ *
+ */
+ public Class getType() {
+ return data.getClass();
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the field value is atomic data.
+ *
+ */
+ public boolean isAtomic() {
+ return !isArray();
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the field contains an array.
+ *
+ */
+ public boolean isArray() {
+ return data.getClass().isArray();
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the field contains a two-dimensional array.
+ *
+ */
+ public boolean isArray2D() {
+ return isArray() && Array.get(data, 0).getClass().isArray();
+ }
+
+
+
+ /**
+ * Returns the length of the array contained by the field, or <TT>-1</TT>
+ * if it is not an array.
+ *
+ */
+ public int getArrayLength() {
+ if (!isArray()) return -1;
+ if (effectiveLength < 0) return Array.getLength(data);
+ return effectiveLength;
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the field value is an atomic <TT>String</TT>.
+ *
+ */
+ public boolean isString() {
+ return (data instanceof String);
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the field value is an atomic <TT>int</TT>.
+ *
+ */
+ public boolean isInt() {
+ return (data instanceof Integer);
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the field value is an atomic <TT>float</TT>.
+ *
+ */
+ public boolean isFloat() {
+ return (data instanceof Float);
+ }
+
+
+
+ /**
+ * Returns <TT>true</TT> if the field value is an atomic <TT>double</TT>.
+ *
+ */
+ public boolean isDouble() {
+ return (data instanceof Double);
+ }
+
+
+
+ /**
+ * Returns the value as <TT>String</TT>, or <TT>null</TT> if it is not
+ * of type <TT>String</TT>.
+ * See {@link #isString isString}.
+ *
+ */
+ public String asString() {
+ return (data instanceof String) ? (String)data : null;
+ }
+
+
+
+ /**
+ * Returns the value as <TT>int</TT> or <TT>0</TT> if it is not of type <TT>int</TT>
+ * See {@link #isInt isInt}.
+ *
+ */
+ public int asInt() {
+ return (data instanceof Integer) ? ((Integer)data).intValue() : 0;
+ }
+
+
+
+ /**
+ * Returns the value as <TT>float</TT> or <TT>0</TT> if it is not of type <TT>float</TT>
+ * See {@link #isFloat isFloat}.
+ *
+ */
+ public float asFloat() {
+ return (data instanceof Float) ? ((Float)data).floatValue() : 0;
+ }
+
+
+
+ /**
+ * Returns the value as <TT>double</TT> or <TT>0</TT> if it is not of type <TT>double</TT>
+ * See {@link #isDouble isDouble}.
+ *
+ */
+ public double asDouble() {
+ return (data instanceof Double) ? ((Double)data).doubleValue() : 0;
+ }
+
+
+
+ /**
+ * Returns the value as one-dimensional <TT>String</TT> array or <TT>null</TT> if it is not of type <TT>String[]</TT>.
+ *
+ */
+ public String[] asStringArray() {
+ return (data instanceof String[]) ? (String[])data : null;
+ }
+
+
+
+ /**
+ * Returns the value as one-dimensional <TT>int</TT> array or <TT>null</TT> if it is not of type <TT>int[]</TT>.
+ *
+ */
+ public int[] asIntArray() {
+ return (data instanceof int[]) ? (int[])data : null;
+ }
+
+
+
+ /**
+ * Returns the value as one-dimensional <TT>float</TT> array or <TT>null</TT> if it is not of type <TT>float[]</TT>.
+ *
+ */
+ public float[] asFloatArray() {
+ return (data instanceof float[]) ? (float[])data : null;
+ }
+
+
+
+ /**
+ * Returns the value as one-dimensional <TT>double</TT> array or <TT>null</TT> if it is not of type <TT>double[]</TT>.
+ *
+ */
+ public double[] asDoubleArray() {
+ return (data instanceof double[]) ? (double[])data : null;
+ }
+
+
+
+ /**
+ * Returns the value as two-dimensional <TT>String</TT> array or <TT>null</TT> if it is not of type <TT>String[][]</TT>.
+ *
+ */
+ public String[][] asStringArray2D() {
+ return (data instanceof String[][]) ? (String[][])data : null;
+ }
+
+
+
+ /**
+ * Returns the value as two-dimensional <TT>int</TT> array or <TT>null</TT> if it is not of type <TT>int[][]</TT>.
+ *
+ */
+ public int[][] asIntArray2D() {
+ return (data instanceof int[][]) ? (int[][])data : null;
+ }
+
+
+
+ /**
+ * Returns the value as two-dimensional <TT>float</TT> array or <TT>null</TT> if it is not of type <TT>float[][]</TT>.
+ *
+ */
+ public float[][] asFloatArray2D() {
+ return (data instanceof float[][]) ? (float[][])data : null;
+ }
+
+
+
+ /**
+ * Returns the value as two-dimensional <TT>double</TT> array or <TT>null</TT> if it is not of type <TT>double[][]</TT>.
+ *
+ */
+ public double[][] asDoubleArray2D() {
+ return (data instanceof double[][]) ? (double[][])data : null;
+ }
+
+
+ /**
+ * Returns the value of the field as an <TT>Object</TT>.
+ *
+ */
+ public Object asObject() {
+ return data;
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/DataField.tex b/source/umontreal/iro/lecuyer/util/io/DataField.tex
new file mode 100644
index 0000000..41e76e3
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/DataField.tex
@@ -0,0 +1,345 @@
+\defmodule{DataField}
+
+This class represents a data field from a file read by an instance of a class
+ implementing \class{DataReader}.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DataField
+ * Description: Represents a data field
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.lang.reflect.Array;
+\end{hide}
+
+public class DataField \begin{hide} {
+ protected String label;
+ protected Object data;
+ protected int effectiveLength;
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+ public DataField (String label, Object data) \begin{hide} {
+ this(label, data, -1);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Constructor. Creates a field named \texttt{label} of value \texttt{data}.
+\end{tabb}
+\begin{htmlonly}
+\param{label}{name of the field}
+\param{data}{value of the field}
+\end{htmlonly}
+\begin{code}
+
+ public DataField (String label, Object data, int effectiveLength) \begin{hide} {
+ this.label = label;
+ this.data = data;
+ this.effectiveLength = effectiveLength;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Constructor. Creates a field named \texttt{label} of value \texttt{data}.
+ \texttt{effectiveLength} is the number of significant elements contained in
+ \texttt{data} if it is an array.
+\end{tabb}
+\begin{htmlonly}
+\param{label}{name of the field}
+\param{data}{value of the field}
+\param{effectiveLength}{number of significant elements contained in \texttt{data}}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Information on the field}
+
+\begin{code}
+ public String getLabel() \begin{hide} {
+ return label;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the field label (or name).
+\end{tabb}
+\begin{code}
+
+ public Class getType() \begin{hide} {
+ return data.getClass();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the type of the field.
+\end{tabb}
+\begin{code}
+
+ public boolean isAtomic() \begin{hide} {
+ return !isArray();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if the field value is atomic data.
+\end{tabb}
+\begin{code}
+
+ public boolean isArray() \begin{hide} {
+ return data.getClass().isArray();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if the field contains an array.
+\end{tabb}
+\begin{code}
+
+ public boolean isArray2D() \begin{hide} {
+ return isArray() && Array.get(data, 0).getClass().isArray();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if the field contains a two-dimensional array.
+\end{tabb}
+\begin{code}
+
+ public int getArrayLength() \begin{hide} {
+ if (!isArray()) return -1;
+ if (effectiveLength < 0) return Array.getLength(data);
+ return effectiveLength;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the length of the array contained by the field, or \texttt{-1}
+ if it is not an array.
+\end{tabb}
+\begin{code}
+
+ public boolean isString() \begin{hide} {
+ return (data instanceof String);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if the field value is an atomic \texttt{String}.
+\end{tabb}
+\begin{code}
+
+ public boolean isInt() \begin{hide} {
+ return (data instanceof Integer);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if the field value is an atomic \texttt{int}.
+\end{tabb}
+\begin{code}
+
+ public boolean isFloat() \begin{hide} {
+ return (data instanceof Float);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if the field value is an atomic \texttt{float}.
+\end{tabb}
+\begin{code}
+
+ public boolean isDouble() \begin{hide} {
+ return (data instanceof Double);
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if the field value is an atomic \texttt{double}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Obtaining the value as atomic data}
+
+\begin{code}
+
+ public String asString() \begin{hide} {
+ return (data instanceof String) ? (String)data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as \texttt{String}, or \texttt{null} if it is not
+ of type \texttt{String}.
+See \method{isString}{}.
+\end{tabb}
+\begin{code}
+
+ public int asInt() \begin{hide} {
+ return (data instanceof Integer) ? ((Integer)data).intValue() : 0;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as \texttt{int} or \texttt{0} if it is not of type \texttt{int}
+See \method{isInt}{}.
+\end{tabb}
+\begin{code}
+
+ public float asFloat() \begin{hide} {
+ return (data instanceof Float) ? ((Float)data).floatValue() : 0;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as \texttt{float} or \texttt{0} if it is not of type \texttt{float}
+See \method{isFloat}{}.
+\end{tabb}
+\begin{code}
+
+ public double asDouble() \begin{hide} {
+ return (data instanceof Double) ? ((Double)data).doubleValue() : 0;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as \texttt{double} or \texttt{0} if it is not of type \texttt{double}
+See \method{isDouble}{}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Obtaining the value as a one-dimensional array}
+
+\begin{code}
+
+ public String[] asStringArray() \begin{hide} {
+ return (data instanceof String[]) ? (String[])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as one-dimensional \texttt{String} array or \texttt{null} if it is not of type \texttt{String[]}.
+\end{tabb}
+\begin{code}
+
+ public int[] asIntArray() \begin{hide} {
+ return (data instanceof int[]) ? (int[])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as one-dimensional \texttt{int} array or \texttt{null} if it is not of type \texttt{int[]}.
+\end{tabb}
+\begin{code}
+
+ public float[] asFloatArray() \begin{hide} {
+ return (data instanceof float[]) ? (float[])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as one-dimensional \texttt{float} array or \texttt{null} if it is not of type \texttt{float[]}.
+\end{tabb}
+\begin{code}
+
+ public double[] asDoubleArray() \begin{hide} {
+ return (data instanceof double[]) ? (double[])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as one-dimensional \texttt{double} array or \texttt{null} if it is not of type \texttt{double[]}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Obtaining the value as a two-dimensional array}
+
+\begin{code}
+
+ public String[][] asStringArray2D() \begin{hide} {
+ return (data instanceof String[][]) ? (String[][])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as two-dimensional \texttt{String} array or \texttt{null} if it is not of type \texttt{String[][]}.
+\end{tabb}
+\begin{code}
+
+ public int[][] asIntArray2D() \begin{hide} {
+ return (data instanceof int[][]) ? (int[][])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as two-dimensional \texttt{int} array or \texttt{null} if it is not of type \texttt{int[][]}.
+\end{tabb}
+\begin{code}
+
+ public float[][] asFloatArray2D() \begin{hide} {
+ return (data instanceof float[][]) ? (float[][])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as two-dimensional \texttt{float} array or \texttt{null} if it is not of type \texttt{float[][]}.
+\end{tabb}
+\begin{code}
+
+ public double[][] asDoubleArray2D() \begin{hide} {
+ return (data instanceof double[][]) ? (double[][])data : null;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value as two-dimensional \texttt{double} array or \texttt{null} if it is not of type \texttt{double[][]}.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Obtaining the value as an \texttt{Object}}
+
+\begin{code}
+ public Object asObject() \begin{hide} {
+ return data;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Returns the value of the field as an \texttt{Object}.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/DataReader.java b/source/umontreal/iro/lecuyer/util/io/DataReader.java
new file mode 100644
index 0000000..bfd9104
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/DataReader.java
@@ -0,0 +1,179 @@
+
+
+/*
+ * Class: DataReader
+ * Description: Data reader interface
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.io.IOException;
+import java.util.Map;
+
+
+/**
+ * Data reader interface.
+ *
+ */
+public interface DataReader {
+
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its <TT>String</TT> value.
+ *
+ */
+ public String readString (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its <TT>int</TT> value.
+ *
+ */
+ public int readInt (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its <TT>float</TT> value.
+ *
+ */
+ public float readFloat (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its <TT>double</TT> value.
+ *
+ */
+ public double readDouble (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>String</TT>'s.
+ *
+ */
+ public String[] readStringArray (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>int</TT>'s.
+ *
+ */
+ public int[] readIntArray (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>float</TT>'s.
+ *
+ */
+ public float[] readFloatArray (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a one-dimensional array of <TT>double</TT>'s.
+ *
+ */
+ public double[] readDoubleArray (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>String</TT>'s.
+ *
+ */
+ public String[][] readStringArray2D (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>int</TT>'s.
+ *
+ */
+ public int[][] readIntArray2D (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>float</TT>'s.
+ *
+ */
+ public float[][] readFloatArray2D (String label) throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT> and returns its value as a two-dimensional array of <TT>double</TT>'s.
+ *
+ */
+ public double[][] readDoubleArray2D (String label) throws IOException;
+
+
+ /**
+ * Reads all remaining fields in the file and returns a hashmap indexed
+ * by field labels. Anonymous fields are mapped to <code>"_data01_"</code>, <code>"_data02_"</code>, ...
+ *
+ */
+ public Map<String, DataField> readAllNextFields() throws IOException;
+
+
+ /**
+ * Reads all fields in the file and returns a hashmap indexed
+ * by field labels. Anonymous fields are mapped to <code>"_data01_"</code>, <code>"_data02_"</code>, ...
+ *
+ */
+ public Map<String, DataField> readAllFields() throws IOException;
+
+
+ /**
+ * Reads the next available field.
+ *
+ * @return a newly created DataField instance or null if not found
+ *
+ */
+ public DataField readNextField() throws IOException;
+
+
+ /**
+ * Reads the first field labeled as <TT>label</TT>.
+ *
+ * @return a newly created DataField instance or null if not found
+ *
+ */
+ public DataField readField (String label) throws IOException;
+
+
+ /**
+ * Closes the input stream.
+ *
+ */
+ public void close() throws IOException;
+
+
+ /**
+ * Resets the reader to its initial state, i.e. goes back to the beginning of the data stream, if possible.
+ *
+ */
+ public void reset() throws IOException;
+
+
+ /**
+ * Returns <TT>true</TT> if there remains data to be read.
+ *
+ */
+ public boolean dataPending() throws IOException;
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/DataReader.tex b/source/umontreal/iro/lecuyer/util/io/DataReader.tex
new file mode 100644
index 0000000..ead0b89
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/DataReader.tex
@@ -0,0 +1,206 @@
+\defmodule{DataReader}
+
+Data reader interface.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DataReader
+ * Description: Data reader interface
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.io.IOException;
+import java.util.Map;
+\end{hide}
+
+public interface DataReader \begin{hide} {
+\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading atomic data}
+\begin{code}
+
+ public String readString (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its \texttt{String} value.
+\end{tabb}
+\begin{code}
+
+ public int readInt (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its \texttt{int} value.
+\end{tabb}
+\begin{code}
+
+ public float readFloat (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its \texttt{float} value.
+\end{tabb}
+\begin{code}
+
+ public double readDouble (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its \texttt{double} value.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading one-dimensional arrays}
+\begin{code}
+
+ public String[] readStringArray (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{String}'s.
+\end{tabb}
+\begin{code}
+
+ public int[] readIntArray (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{int}'s.
+\end{tabb}
+\begin{code}
+
+ public float[] readFloatArray (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{float}'s.
+\end{tabb}
+\begin{code}
+
+ public double[] readDoubleArray (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a one-dimensional array of \texttt{double}'s.
+\end{tabb}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading two-dimensional arrays}
+\begin{code}
+
+ public String[][] readStringArray2D (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{String}'s.
+\end{tabb}
+\begin{code}
+
+ public int[][] readIntArray2D (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{int}'s.
+\end{tabb}
+\begin{code}
+
+ public float[][] readFloatArray2D (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{float}'s.
+\end{tabb}
+\begin{code}
+
+ public double[][] readDoubleArray2D (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label} and returns its value as a two-dimensional array of \texttt{double}'s.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Reading fields of unknown type}
+\begin{code}
+
+ public Map<String, DataField> readAllNextFields() throws IOException;
+\end{code}
+\begin{tabb}
+Reads all remaining fields in the file and returns a hashmap indexed
+by field labels. Anonymous fields are mapped to \verb|"_data01_"|, \verb|"_data02_"|, \ldots
+\end{tabb}
+\begin{code}
+
+ public Map<String, DataField> readAllFields() throws IOException;
+\end{code}
+\begin{tabb}
+Reads all fields in the file and returns a hashmap indexed
+by field labels. Anonymous fields are mapped to \verb|"_data01_"|, \verb|"_data02_"|, \ldots
+\end{tabb}
+\begin{code}
+
+ public DataField readNextField() throws IOException;
+\end{code}
+\begin{tabb}
+Reads the next available field.
+\end{tabb}
+\begin{htmlonly}
+\return{a newly created DataField instance or \text{null} if not found}
+\end{htmlonly}
+\begin{code}
+
+ public DataField readField (String label) throws IOException;
+\end{code}
+\begin{tabb}
+Reads the first field labeled as \texttt{label}.
+\end{tabb}
+\begin{htmlonly}
+\return{a newly created DataField instance or \text{null} if not found}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Other methods}
+
+\begin{code}
+
+ public void close() throws IOException;
+\end{code}
+\begin{tabb}
+Closes the input stream.
+\end{tabb}
+\begin{code}
+
+ public void reset() throws IOException;
+\end{code}
+\begin{tabb}
+Resets the reader to its initial state, i.e.\ goes back to the beginning of the data stream, if possible.
+\end{tabb}
+\begin{code}
+
+ public boolean dataPending() throws IOException;
+\end{code}
+\begin{tabb}
+Returns \texttt{true} if there remains data to be read.
+\end{tabb}
+
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/DataWriter.java b/source/umontreal/iro/lecuyer/util/io/DataWriter.java
new file mode 100644
index 0000000..10792f4
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/DataWriter.java
@@ -0,0 +1,175 @@
+
+
+/*
+ * Class: DataWriter
+ * Description: Data writer interface
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.io.IOException;
+
+
+/**
+ * Data writer interface.
+ *
+ */
+public interface DataWriter {
+
+
+
+
+ /**
+ * Writes an atomic string field.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String s) throws IOException;
+
+
+ /**
+ * Writes an atomic 32-bit integer (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int a) throws IOException;
+
+
+ /**
+ * Writes an atomic 32-bit float (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float a) throws IOException;
+
+
+ /**
+ * Writes an atomic 64-bit double (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double a) throws IOException;
+
+
+ /**
+ * Writes a one-dimensional array of strings.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String[] a) throws IOException;
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array of strings.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String[] a, int n) throws IOException;
+
+
+ /**
+ * Writes a one-dimensional array of 32-bit integers (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int[] a) throws IOException;
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array of 32-bit integers (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int[] a, int n) throws IOException;
+
+
+ /**
+ * Writes a one-dimensional array of 32-bit floats (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float[] a) throws IOException;
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array of 32-bit floats (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float[] a, int n) throws IOException;
+
+
+ /**
+ * Writes a one-dimensional array of 64-bit doubles (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double[] a) throws IOException;
+
+
+ /**
+ * Writes the first <TT>n</TT> elements of a one-dimensional array of 64-bit doubles (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double[] a, int n) throws IOException;
+
+
+ /**
+ * Writes a two-dimensional array of strings.
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, String[][] a) throws IOException;
+
+
+ /**
+ * Writes a two-dimensional array of 32-bit integers (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, int[][] a) throws IOException;
+
+
+ /**
+ * Writes a two-dimensional array of 32-bit floats (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, float[][] a) throws IOException;
+
+
+ /**
+ * Writes a two-dimensional array of 64-bit doubles (big endian).
+ * Writes an anonymous field if <TT>label</TT> is <TT>null</TT>.
+ *
+ */
+ public void write (String label, double[][] a) throws IOException;
+
+
+ /**
+ * Flushes any pending data and closes the output stream.
+ *
+ */
+ public void close() throws IOException;
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/DataWriter.tex b/source/umontreal/iro/lecuyer/util/io/DataWriter.tex
new file mode 100644
index 0000000..90a7360
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/DataWriter.tex
@@ -0,0 +1,213 @@
+\defmodule{DataWriter}
+
+Data writer interface.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: DataWriter
+ * Description: Data writer interface
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.io.IOException;
+\end{hide}
+
+public interface DataWriter \begin{hide} {
+
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing atomic data}
+\begin{code}
+
+ public void write (String label, String s) throws IOException;
+\end{code}
+\begin{tabb}
+Writes an atomic string field.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, int a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes an atomic 32-bit integer (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, float a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes an atomic 32-bit float (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, double a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes an atomic 64-bit double (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing one-dimensional arrays}
+
+\begin{code}
+
+ public void write (String label, String[] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a one-dimensional array of strings.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, String[] a, int n) throws IOException;
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array of strings.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, int[] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a one-dimensional array of 32-bit integers (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, int[] a, int n) throws IOException;
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array of 32-bit integers (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, float[] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a one-dimensional array of 32-bit floats (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, float[] a, int n) throws IOException;
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array of 32-bit floats (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, double[] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a one-dimensional array of 64-bit doubles (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, double[] a, int n) throws IOException;
+\end{code}
+\begin{tabb}
+Writes the first \texttt{n} elements of a one-dimensional array of 64-bit doubles (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Writing two-dimensional arrays}
+
+\begin{code}
+
+ public void write (String label, String[][] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of strings.
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, int[][] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 32-bit integers (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, float[][] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 32-bit floats (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+\begin{code}
+
+ public void write (String label, double[][] a) throws IOException;
+\end{code}
+\begin{tabb}
+Writes a two-dimensional array of 64-bit doubles (big endian).
+Writes an anonymous field if \texttt{label} is \texttt{null}.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Other methods}
+
+\begin{code}
+
+ public void close() throws IOException;
+\end{code}
+\begin{tabb}
+ Flushes any pending data and closes the output stream.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/TextDataWriter.java b/source/umontreal/iro/lecuyer/util/io/TextDataWriter.java
new file mode 100644
index 0000000..785392b
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/TextDataWriter.java
@@ -0,0 +1,320 @@
+
+
+/*
+ * Class: TextDataWriter
+ * Description: Text data writer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+
+package umontreal.iro.lecuyer.util.io;
+
+import java.io.*;
+import java.lang.reflect.Array;
+
+
+/**
+ * Text data writer. Writes fields as columns or as rows in a text file.
+ *
+ */
+public class TextDataWriter extends CachedDataWriter {
+
+
+ /**
+ * Default value for the column separator.
+ *
+ */
+ public final String DEFAULT_COLUMN_SEPARATOR = "\t";
+
+
+ /**
+ * Default value for the header prefix.
+ *
+ */
+ public final String DEFAULT_HEADER_PREFIX = "";
+
+
+ protected BufferedWriter out;
+
+ protected Format format;
+ protected boolean withHeaders;
+
+ protected String columnSeparator = DEFAULT_COLUMN_SEPARATOR;
+ protected String headerPrefix = DEFAULT_HEADER_PREFIX;
+
+ protected String floatFormatString = null;
+
+
+ /* *
+ * Returns the maximum field length.
+ *
+ */
+ protected int getMaxFieldLength() {
+ int nRows = 0;
+ for (DataField f : super.getFields()) {
+ if (f.isArray())
+ nRows = Math.max(nRows, f.getArrayLength());
+ }
+ return nRows;
+ }
+
+ /* *
+ * Outputs fields as columns.
+ *
+ */
+ protected void outputAsColumns() throws IOException {
+
+ if (withHeaders) {
+ // output field headers
+ out.write(headerPrefix);
+ int iAnonymous = 0;
+ boolean firstColumn = true;
+ for (DataField f : super.getFields()) {
+ // separator
+ if (!firstColumn)
+ out.write(columnSeparator);
+ else
+ firstColumn = false;
+
+ if (f.getLabel() == null)
+ // anonymous field
+ out.write(String.format("_data%02d_", ++iAnonymous));
+ else
+ // named field
+ out.write(f.getLabel());
+ }
+ out.write("\n");
+ }
+
+ int nRows = getMaxFieldLength();
+
+ for (int iRow = 0; iRow < nRows; iRow++) {
+ boolean firstColumn = true;
+ for (DataField f : super.getFields()) {
+
+ // separator
+ if (!firstColumn)
+ out.write(columnSeparator);
+ else
+ firstColumn = false;
+
+ // output field data
+ if (f.isArray()) {
+ // field is an array, output its current entry
+ if (iRow < f.getArrayLength())
+ writeFormat(Array.get(f.asObject(), iRow));
+ }
+ else {
+ // field is not an array, output only in first row
+ if (iRow == 0)
+ writeFormat(f.asObject());
+ }
+ }
+ out.write("\n");
+ }
+ }
+
+
+ /* *
+ * Outputs fields as rows.
+ *
+ */
+ protected void outputAsRows() throws IOException {
+
+ int iAnonymous = 0;
+
+ for (DataField f : super.getFields()) {
+
+ // output field header
+ if (withHeaders) {
+ if (f.getLabel() == null)
+ // anonymous field
+ out.write(String.format("_data%02d_", ++iAnonymous));
+ else
+ // named field
+ out.write(f.getLabel());
+
+ out.write(columnSeparator);
+ }
+
+ // output field data
+
+ if (f.isArray()) {
+
+ int nCols = f.getArrayLength();
+
+ for (int iCol = 0; iCol < nCols; iCol++) {
+
+ // separator
+ if (iCol > 0)
+ out.write(columnSeparator);
+
+ writeFormat(Array.get(f.asObject(), iCol));
+ }
+ }
+ else {
+ writeFormat(f.asObject());
+ }
+
+ out.write("\n");
+
+ }
+ }
+
+ /* *
+ * Formats the object in accordance with the current format strings settings.
+ *
+ */
+ protected void writeFormat(Object o) throws IOException {
+ String s = null;
+ if (floatFormatString != null && (o instanceof Double || o instanceof Float))
+ s = String.format((java.util.Locale)null, floatFormatString, o); // pass null to avoid localization
+ else
+ s = o.toString();
+ out.write(s);
+ }
+
+
+ /**
+ * Output format: organize fields as columns or as rows.
+ *
+ */
+ public enum Format { COLUMNS, ROWS }
+
+
+ /**
+ * Class constructor. Truncates any existing file with the specified name.
+ *
+ * @param filename name of the file to write to
+ *
+ * @param format organize fields as columns if set to <TT>COLUMNS</TT> or as rows if set to <TT>ROWS</TT>
+ *
+ * @param withHeaders output headers or not
+ *
+ *
+ */
+ public TextDataWriter (String filename, Format format, boolean withHeaders)
+ throws IOException {
+ this.out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename)));
+ this.format = format;
+ this.withHeaders = withHeaders;
+ }
+
+
+
+ /**
+ * Class constructor. Truncates any conflicting file.
+ *
+ * @param file file to write to
+ *
+ * @param format organize fields as columns if set to <TT>COLUMNS</TT> or as rows if set to <TT>ROWS</TT>
+ *
+ * @param withHeaders output headers or not
+ *
+ *
+ */
+ public TextDataWriter (File file, Format format, boolean withHeaders)
+ throws IOException {
+ this.out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
+ this.format = format;
+ this.withHeaders = withHeaders;
+ }
+
+
+
+ /**
+ * Class constructor.
+ *
+ * @param outputStream output stream to write to
+ *
+ * @param format organize fields as columns if set to <TT>COLUMNS</TT> or as rows if set to <TT>ROWS</TT>
+ *
+ * @param withHeaders output headers or not
+ *
+ */
+ public TextDataWriter (OutputStream outputStream, Format format,
+ boolean withHeaders)
+ throws IOException {
+ this.out = new BufferedWriter(new OutputStreamWriter(outputStream));
+ this.format = format;
+ this.withHeaders = withHeaders;
+ }
+
+
+ /**
+ * Changes the output format.
+ *
+ * @param format organize fields as columns if set to <TT>COLUMNS</TT> or as rows if set to <TT>ROWS</TT>
+ *
+ *
+ */
+ public void setFormat (Format format) {
+ this.format = format;
+ }
+
+
+
+ /**
+ * Sets the format string used to output floating point numbers.
+ *
+ * @param formatString format string (e.g., <TT>%.4g</TT>)
+ *
+ *
+ */
+ public void setFloatFormatString (String formatString) {
+ this.floatFormatString = formatString;
+ }
+
+
+
+ /**
+ * Changes the column separator.
+ *
+ */
+ public void setColumnSeparator (String columnSeparator) {
+ this.columnSeparator = columnSeparator;
+ }
+
+
+
+ /**
+ * Changes the header prefix (a string that indicates the beginning of the header line for the <TT>COLUMNS</TT> format).
+ *
+ */
+ public void setHeaderPrefix (String headerPrefix) {
+ this.headerPrefix = headerPrefix;
+ }
+
+
+
+ /**
+ * Flushes any pending data and closes the file or stream.
+ *
+ */
+ public void close() throws IOException {
+ if (format == Format.COLUMNS)
+ outputAsColumns();
+ else if (format == Format.ROWS)
+ outputAsRows();
+ out.close();
+ }
+
+
+}
diff --git a/source/umontreal/iro/lecuyer/util/io/TextDataWriter.tex b/source/umontreal/iro/lecuyer/util/io/TextDataWriter.tex
new file mode 100644
index 0000000..6a67200
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/TextDataWriter.tex
@@ -0,0 +1,340 @@
+\defmodule{TextDataWriter}
+
+Text data writer. Writes fields as columns or as rows in a text file.
+
+\bigskip\hrule
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{code}
+\begin{hide}
+/*
+ * Class: TextDataWriter
+ * Description: Text data writer
+ * Environment: Java
+ * Software: SSJ
+ * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
+ * Organization: DIRO, Universite de Montreal
+ * @author David Munger
+ * @since August 2009
+
+ * SSJ is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License (GPL) as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * any later version.
+
+ * SSJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * A copy of the GNU General Public License is available at
+ <a href="http://www.gnu.org/licenses">GPL licence site</a>.
+ */
+\end{hide}
+package umontreal.iro.lecuyer.util.io;
+\begin{hide}
+import java.io.*;
+import java.lang.reflect.Array;
+\end{hide}
+
+public class TextDataWriter extends CachedDataWriter \begin{hide} {
+\end{hide}
+\end{code}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Fields}
+
+\begin{code}
+ public final String DEFAULT_COLUMN_SEPARATOR = "\t";
+\end{code}
+\begin{tabb}
+Default value for the column separator.
+\end{tabb}
+\begin{code}
+
+ public final String DEFAULT_HEADER_PREFIX = "";
+\end{code}
+\begin{tabb}
+Default value for the header prefix.
+\end{tabb}
+\begin{code}\begin{hide}
+
+ protected BufferedWriter out;
+
+ protected Format format;
+ protected boolean withHeaders;
+
+ protected String columnSeparator = DEFAULT_COLUMN_SEPARATOR;
+ protected String headerPrefix = DEFAULT_HEADER_PREFIX;
+
+ protected String floatFormatString = null;
+
+
+ /**
+ * Returns the maximum field length.
+ *
+ */
+ protected int getMaxFieldLength() {
+ int nRows = 0;
+ for (DataField f : super.getFields()) {
+ if (f.isArray())
+ nRows = Math.max(nRows, f.getArrayLength());
+ }
+ return nRows;
+ }
+
+ /**
+ * Outputs fields as columns.
+ *
+ */
+ protected void outputAsColumns() throws IOException {
+
+ if (withHeaders) {
+ // output field headers
+ out.write(headerPrefix);
+ int iAnonymous = 0;
+ boolean firstColumn = true;
+ for (DataField f : super.getFields()) {
+ // separator
+ if (!firstColumn)
+ out.write(columnSeparator);
+ else
+ firstColumn = false;
+
+ if (f.getLabel() == null)
+ // anonymous field
+ out.write(String.format("_data%02d_", ++iAnonymous));
+ else
+ // named field
+ out.write(f.getLabel());
+ }
+ out.write("\n");
+ }
+
+ int nRows = getMaxFieldLength();
+
+ for (int iRow = 0; iRow < nRows; iRow++) {
+ boolean firstColumn = true;
+ for (DataField f : super.getFields()) {
+
+ // separator
+ if (!firstColumn)
+ out.write(columnSeparator);
+ else
+ firstColumn = false;
+
+ // output field data
+ if (f.isArray()) {
+ // field is an array, output its current entry
+ if (iRow < f.getArrayLength())
+ writeFormat(Array.get(f.asObject(), iRow));
+ }
+ else {
+ // field is not an array, output only in first row
+ if (iRow == 0)
+ writeFormat(f.asObject());
+ }
+ }
+ out.write("\n");
+ }
+ }
+
+
+ /**
+ * Outputs fields as rows.
+ *
+ */
+ protected void outputAsRows() throws IOException {
+
+ int iAnonymous = 0;
+
+ for (DataField f : super.getFields()) {
+
+ // output field header
+ if (withHeaders) {
+ if (f.getLabel() == null)
+ // anonymous field
+ out.write(String.format("_data%02d_", ++iAnonymous));
+ else
+ // named field
+ out.write(f.getLabel());
+
+ out.write(columnSeparator);
+ }
+
+ // output field data
+
+ if (f.isArray()) {
+
+ int nCols = f.getArrayLength();
+
+ for (int iCol = 0; iCol < nCols; iCol++) {
+
+ // separator
+ if (iCol > 0)
+ out.write(columnSeparator);
+
+ writeFormat(Array.get(f.asObject(), iCol));
+ }
+ }
+ else {
+ writeFormat(f.asObject());
+ }
+
+ out.write("\n");
+
+ }
+ }
+
+ /**
+ * Formats the object in accordance with the current format strings settings.
+ *
+ */
+ protected void writeFormat(Object o) throws IOException {
+ String s = null;
+ if (floatFormatString != null && (o instanceof Double || o instanceof Float))
+ s = String.format((java.util.Locale)null, floatFormatString, o); // pass null to avoid localization
+ else
+ s = o.toString();
+ out.write(s);
+ }\end{hide}
+\end{code}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Enums}
+\begin{code}
+
+ public enum Format { COLUMNS, ROWS }
+\end{code}
+\begin{tabb}
+Output format: organize fields as columns or as rows.
+\end{tabb}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Constructors}
+
+\begin{code}
+
+ public TextDataWriter (String filename, Format format, boolean withHeaders)
+ throws IOException \begin{hide} {
+ this.out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename)));
+ this.format = format;
+ this.withHeaders = withHeaders;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Class constructor. Truncates any existing file with the specified name.
+\end{tabb}
+\begin{htmlonly}
+\param{filename} {name of the file to write to}
+\param{format} {organize fields as columns if set to \texttt{COLUMNS} or as rows if set to \texttt{ROWS}}
+\param{withHeaders}{output headers or not}
+\end{htmlonly}
+\begin{code}
+
+ public TextDataWriter (File file, Format format, boolean withHeaders)
+ throws IOException \begin{hide} {
+ this.out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
+ this.format = format;
+ this.withHeaders = withHeaders;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Class constructor. Truncates any conflicting file.
+\end{tabb}
+\begin{htmlonly}
+\param{file} {file to write to}
+\param{format} {organize fields as columns if set to \texttt{COLUMNS} or as rows if set to \texttt{ROWS}}
+\param{withHeaders}{output headers or not}
+\end{htmlonly}
+\begin{code}
+
+ public TextDataWriter (OutputStream outputStream, Format format,
+ boolean withHeaders)
+ throws IOException \begin{hide} {
+ this.out = new BufferedWriter(new OutputStreamWriter(outputStream));
+ this.format = format;
+ this.withHeaders = withHeaders;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Class constructor.
+\end{tabb}
+\begin{htmlonly}
+\param{outputStream}{output stream to write to}
+\param{format} {organize fields as columns if set to \texttt{COLUMNS} or as rows if set to \texttt{ROWS}}
+\param{withHeaders} {output headers or not}
+\end{htmlonly}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Methods}
+
+\begin{code}
+ public void setFormat (Format format) \begin{hide} {
+ this.format = format;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Changes the output format.
+\end{tabb}
+\begin{htmlonly}
+\param{format} {organize fields as columns if set to \texttt{COLUMNS} or as rows if set to \texttt{ROWS}}
+\end{htmlonly}
+\begin{code}
+
+ public void setFloatFormatString (String formatString) \begin{hide} {
+ this.floatFormatString = formatString;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Sets the format string used to output floating point numbers.
+\end{tabb}
+\begin{htmlonly}
+\param{formatString}{format string (e.g., \texttt{\%.4g})}
+\end{htmlonly}
+\begin{code}
+
+ public void setColumnSeparator (String columnSeparator) \begin{hide} {
+ this.columnSeparator = columnSeparator;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Changes the column separator.
+\end{tabb}
+\begin{code}
+
+ public void setHeaderPrefix (String headerPrefix) \begin{hide} {
+ this.headerPrefix = headerPrefix;
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Changes the header prefix (a string that indicates the beginning of the header line for the \texttt{COLUMNS} format).
+\end{tabb}
+\begin{code}
+
+ public void close() throws IOException \begin{hide} {
+ if (format == Format.COLUMNS)
+ outputAsColumns();
+ else if (format == Format.ROWS)
+ outputAsRows();
+ out.close();
+ }
+ \end{hide}
+\end{code}
+\begin{tabb}
+Flushes any pending data and closes the file or stream.
+\end{tabb}
+
+\begin{code}\begin{hide}
+}
+\end{hide}\end{code}
diff --git a/source/umontreal/iro/lecuyer/util/io/overview.tex b/source/umontreal/iro/lecuyer/util/io/overview.tex
new file mode 100644
index 0000000..585ee12
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/io/overview.tex
@@ -0,0 +1,127 @@
+\latex{\section*{Overview of package \texttt{util.io}}\addcontentsline{toc}{section}{Overview of package \texttt{util.io}}}
+
+This package provides tools for exporting data to text and binary files, as
+well as for importing data from files.
+
+Each of the \texttt{write()} methods takes a \emph{field label} as their first
+ argument. This label can always be set to \texttt{null}, in which case an
+ anonymous field will be written.
+The \texttt{write()} methods that take one-dimensional array argument can also
+ take an additional integer argument, for convenience, to specify the number
+ of elements to write in the array.
+
+For a quick start, consult the following examples and the documentation for
+\externalclass{umontreal.iro.lecuyer.util.io}{DataWriter} and
+\externalclass{umontreal.iro.lecuyer.util.io}{DataReader}, as well as the
+constructors of implementing classes
+ (\externalclass{umontreal.iro.lecuyer.util.io}{TextDataWriter},
+ \externalclass{umontreal.iro.lecuyer.util.io}{BinaryDataWriter} and
+ \externalclass{umontreal.iro.lecuyer.util.io}{BinaryDataReader}).
+
+\paragraph*{Example of how to write data to a file:}
+
+\begin{vcode}
+
+public static void writerExample() throws IOException {
+ String filename = "test.dat";
+ DataWriter out = new BinaryDataWriter(filename);
+ out.write("zero", 0);
+ out.write("zerotxt", "ZERO");
+ out.write("n", new int[]{1,2,3,4,5});
+ out.write("pi", Math.PI);
+ out.write("str", new String[]{"text1", "text2"});
+ out.write("real", new double[]{2.5, 3.7, 8.9});
+ out.write("real2", new float[]{2.5f, 3.7f, 8.9f});
+ out.write(null, 24);
+ out.write(null, 39);
+ out.write(null, 116);
+ out.close();
+}
+\end{vcode}
+
+
+\paragraph*{Example of how to read data from a file --- specific fields:}
+
+\begin{vcode}
+
+public static void readerExample1() throws IOException {
+ String filename = "test.dat";
+ DataReader in = new BinaryDataReader(filename);
+
+ // read double field labeled "pi"
+ System.out.println("[pi] (double) " + in.readField("pi").asDouble());
+
+ // read integer-array field labeled "n"
+ int[] n = in.readIntArray("n");
+ System.out.print("[n] (int[]) ");
+ for (int i = 0; i < n.length; i++)
+ System.out.print(" " + n[i]);
+ System.out.println();
+
+ in.close();
+}
+\end{vcode}
+
+
+\paragraph*{Example of how to read data from a file --- list all fields:}
+
+\begin{vcode}
+
+public static void readerExample2() throws IOException {
+ String filename = "test.dat";
+ DataReader in = new BinaryDataReader(filename);
+
+ Map<String,DataField> fields = in.readAllFields();
+ in.close();
+
+ // sort keys
+ Set<String> allKeys = new TreeSet<String>(fields.keySet());
+
+ for (String key : allKeys) {
+ System.out.print("[" + key + "]");
+ DataField d = fields.get(key);
+
+ if (d.isString())
+ System.out.print(" (String) " + d.asString());
+
+ if (d.isInt())
+ System.out.print(" (int) " + d.asInt());
+
+ if (d.isFloat())
+ System.out.print(" (float) " + d.asFloat());
+
+ if (d.isDouble())
+ System.out.print(" (double) " + d.asDouble());
+
+ if (d.asStringArray() != null) {
+ System.out.print(" (String[]) ");
+ String[] a = d.asStringArray();
+ for (int i = 0; i < a.length; i++)
+ System.out.print(" " + a[i]);
+ }
+
+ if (d.asIntArray() != null) {
+ System.out.print(" (int[]) ");
+ int[] a = d.asIntArray();
+ for (int i = 0; i < a.length; i++)
+ System.out.print(" " + a[i]);
+ }
+
+ if (d.asFloatArray() != null) {
+ System.out.print(" (float[]) ");
+ float[] a = d.asFloatArray();
+ for (int i = 0; i < a.length; i++)
+ System.out.print(" " + a[i]);
+ }
+
+ if (d.asDoubleArray() != null) {
+ System.out.print(" (double[]) ");
+ double[] a = d.asDoubleArray();
+ for (int i = 0; i < a.length; i++)
+ System.out.print(" " + a[i]);
+ }
+
+ System.out.println();
+ }
+}
+\end{vcode}
diff --git a/source/umontreal/iro/lecuyer/util/overview.tex b/source/umontreal/iro/lecuyer/util/overview.tex
new file mode 100644
index 0000000..693d642
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/overview.tex
@@ -0,0 +1,7 @@
+\latex{This document describes a set of basic utilities }%, implemented in Java,
+\html{This package contains utility classes}
+used in the Java software developed in the {\em simulation laboratory\/}
+of the DIRO, at the Universit\'e de Montr\'eal.
+Many of these tools were originally implemented in the Modula-2 language
+and have been translated in C and then in Java, with some adaptations
+along the road.
diff --git a/source/umontreal/iro/lecuyer/util/umontreal_iro_lecuyer_util_GlobalCPUTimeChrono.h b/source/umontreal/iro/lecuyer/util/umontreal_iro_lecuyer_util_GlobalCPUTimeChrono.h
new file mode 100644
index 0000000..f0c6b1e
--- /dev/null
+++ b/source/umontreal/iro/lecuyer/util/umontreal_iro_lecuyer_util_GlobalCPUTimeChrono.h
@@ -0,0 +1,22 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class umontreal_iro_lecuyer_util_GlobalCPUTimeChrono */
+
+#ifndef _Included_umontreal_iro_lecuyer_util_GlobalCPUTimeChrono
+#define _Included_umontreal_iro_lecuyer_util_GlobalCPUTimeChrono
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Inaccessible static: ssjUtilLoaded */
+/*
+ * Class: umontreal_iro_lecuyer_util_GlobalCPUTimeChrono
+ * Method: Heure
+ * Signature: ([J)V
+ */
+JNIEXPORT void JNICALL Java_umontreal_iro_lecuyer_util_GlobalCPUTimeChrono_Heure
+ (JNIEnv *, jclass, jlongArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/ssj.properties b/ssj.properties
new file mode 100644
index 0000000..70bd8a4
--- /dev/null
+++ b/ssj.properties
@@ -0,0 +1,15 @@
+# Properties specific to SSJ
+# ssj.debug configures how javac will be called
+ssj.debug = on
+
+# These properties, when set, activate the compilation of JNI C files.
+# ssj.buildjnichrono
+# ssj.buildjniunuran
+
+# Documentation building switches
+ssj.pdfdoc
+ssj.htmldoc
+
+# Properties specific to TCode
+texjava.texjava = ${env.TCODEHOME}/texjava.pl
+texjava.html = yes
diff --git a/ssjant.bat b/ssjant.bat
new file mode 100644
index 0000000..83ce938
--- /dev/null
+++ b/ssjant.bat
@@ -0,0 +1,3 @@
+ at echo off
+
+ant -Dbasedir=%SSJHOME% -f %SSJHOME%\build.xml %1 %2 %3 %4 %5 %6 %7 %8 %9
diff --git a/usessj b/usessj
new file mode 100755
index 0000000..d33be41
--- /dev/null
+++ b/usessj
@@ -0,0 +1,39 @@
+#!/bin/csh
+
+setenv SSJHOME <SSJ home path>/ssj-2
+
+# For LaTex documentation of package charts, packages tikz and pgf
+setenv PGFLIBRARY <path to pgf>/tex/pgf-2.00
+
+if ( ! $?SSJHOME ) then
+ echo "You must set SSJHOME before calling this script."
+ exit
+endif
+
+if ( ! $?CLASSPATH ) then
+ setenv CLASSPATH
+endif
+if ( ! $?LD_LIBRARY_PATH ) then
+ setenv LD_LIBRARY_PATH
+endif
+if ( ! $?TEXINPUTS ) then
+ setenv TEXINPUTS
+endif
+
+setenv CLASSPATH .:$SSJHOME/lib/ssj.jar:$SSJHOME/lib/colt.jar:$SSJHOME/lib/tcode.jar:$CLASSPATH
+
+setenv TEXINPUTS .:$SSJHOME/source:$PGFLIBRARY//:$TEXINPUTS
+
+switch ( "`uname -m`" )
+ case 'i686':
+ setenv LD_LIBRARY_PATH $SSJHOME/lib:$LD_LIBRARY_PATH
+ breaksw
+
+ case 'x86_64':
+ setenv LD_LIBRARY_PATH $SSJHOME/lib64:$LD_LIBRARY_PATH
+ breaksw
+endsw
+
+alias cdssj 'cd $SSJHOME/\!*'
+alias cdssjsrc 'cd $SSJHOME/source/umontreal/iro/lecuyer/\!*'
+
diff --git a/usessj.sh b/usessj.sh
new file mode 100755
index 0000000..2faae57
--- /dev/null
+++ b/usessj.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+export SSJHOME=<SSJ home path>/ssj-2
+
+if [ ! $SSJHOME ]; then
+ echo "You must set SSJHOME before calling this script."
+ exit
+fi
+if [ ! $CLASSPATH ]; then
+ export CLASSPATH=
+fi
+if [ ! $LD_LIBRARY_PATH ]; then
+ export LD_LIBRARY_PATH=
+fi
+
+export CLASSPATH=.:$SSJHOME/lib/ssj.jar:$SSJHOME/lib/colt.jar:$SSJHOME/lib/tcode.jar:$CLASSPATH
+
+case "`uname -m`" in
+ 'i686' )
+ export LD_LIBRARY_PATH=$SSJHOME/lib:$LD_LIBRARY_PATH ;;
+ 'x86_64' )
+ export LD_LIBRARY_PATH=$SSJHOME/lib64:$LD_LIBRARY_PATH ;;
+esac
+
+function cdssj() {
+ cd $SSJHOME/$1
+}
+
+function cdssjsrc() {
+ cd $SSJHOME/source/umontreal/iro/lecuyer/$1
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/libssj-java.git
More information about the debian-med-commit
mailing list